Minor js riorganization and dev setup enhancements
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import './style.scss'
|
||||
|
||||
/**
|
||||
* `BASE_URL` string from environment file without final slash (for more readable interpolated strings)
|
||||
*/
|
||||
export const BASE_URL = import.meta.env.BASE_URL.replace(/\/$/, '')
|
||||
|
||||
const NotCached = Symbol('not cached')
|
||||
|
||||
export const User = {
|
||||
_user: NotCached,
|
||||
|
||||
async getLogged() {
|
||||
if (User._user === NotCached) {
|
||||
console.log('Caching user data...')
|
||||
|
||||
const res = await fetch(`${BASE_URL}/api/user`)
|
||||
const user = await res.json()
|
||||
|
||||
User._user = user
|
||||
}
|
||||
|
||||
return User._user
|
||||
},
|
||||
async login(username, password) {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/api/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, password }),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
return { error: await response.text() }
|
||||
}
|
||||
|
||||
// If successful redirect to homepage
|
||||
location.href = `${BASE_URL}/`
|
||||
} catch (e) {
|
||||
return { error: e }
|
||||
}
|
||||
},
|
||||
async logout() {
|
||||
await fetch(`${BASE_URL}/api/logout`, { method: 'POST' })
|
||||
location.href = `${BASE_URL}/`
|
||||
},
|
||||
}
|
||||
|
||||
export function createRoomEventStream(roomId) {
|
||||
return new EventSource(`${BASE_URL}/api/room_events?id=${roomId}`)
|
||||
}
|
||||
|
||||
export const Database = {
|
||||
async getSeats(roomId) {
|
||||
const seatList = await (await fetch(`${BASE_URL}/api/room/seats?id=${roomId}`)).json()
|
||||
const seats = {}
|
||||
|
||||
seatList.forEach(seat => {
|
||||
seats[seat.id] = seat
|
||||
})
|
||||
|
||||
return seats
|
||||
},
|
||||
async occupySeat(seatId) {
|
||||
const response = await fetch(`${BASE_URL}/api/seat/occupy?id=${seatId}`, {
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text())
|
||||
}
|
||||
|
||||
await response.json()
|
||||
},
|
||||
async leaveSeat(seatId) {
|
||||
const response = await fetch(`${BASE_URL}/api/seat/leave?id=${seatId}`, {
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text())
|
||||
}
|
||||
|
||||
await response.json()
|
||||
},
|
||||
}
|
||||
|
||||
// Always append BASE_URL to all <a> links in the page.
|
||||
document.querySelectorAll('a').forEach($a => {
|
||||
const url = $a.getAttribute('href')
|
||||
let newUrl = BASE_URL + url
|
||||
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
if (newUrl.endsWith('/')) newUrl += 'index'
|
||||
|
||||
newUrl += '.html'
|
||||
}
|
||||
|
||||
$a.href = newUrl
|
||||
})
|
||||
@@ -47,5 +47,5 @@ export function createGridLineCanvas($roomGrid) {
|
||||
const render = () => renderGridLinesCanvas($canvas, [rows, cols])
|
||||
|
||||
window.addEventListener('resize', render)
|
||||
render()
|
||||
requestAnimationFrame(render)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { User } from '../common.js'
|
||||
|
||||
function elements(el) {
|
||||
return {
|
||||
elLoggedLabel: el.querySelector('.logged-label'),
|
||||
elLoginLabel: el.querySelector('.login-label'),
|
||||
elLogoutLabel: el.querySelector('.logout-label'),
|
||||
elLogoutButton: el.querySelector('.logout-button'),
|
||||
}
|
||||
}
|
||||
|
||||
function setup(el) {
|
||||
const { elLogoutButton } = elements(el)
|
||||
|
||||
elLogoutButton.addEventListener('click', () => User.logout())
|
||||
}
|
||||
|
||||
function update(el, { user }) {
|
||||
const { elLoggedLabel, elLoginLabel, elLogoutLabel } = elements(el)
|
||||
|
||||
elLoginLabel.classList.toggle('hidden', user)
|
||||
|
||||
const roleString =
|
||||
user && user.permissions.length > 0 ? ` (${user.permissions.join(', ')})` : ''
|
||||
|
||||
elLoggedLabel.innerText = user ? `@${user.id}${roleString}` : ''
|
||||
|
||||
elLoggedLabel.classList.toggle('hidden', !user)
|
||||
elLogoutLabel.classList.toggle('hidden', !user)
|
||||
}
|
||||
|
||||
export function createNavUser(el) {
|
||||
setup(el)
|
||||
|
||||
// Load current user
|
||||
;(async () => {
|
||||
const user = await User.getLogged()
|
||||
update(el, { user })
|
||||
})()
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { BASE_URL, createRoomEventStream, Database, getLoggedUser } from '../index.js'
|
||||
import { addTooltipElementListener, resetTooltip, setTooltipText } from './tooltip.js'
|
||||
import { BASE_URL, createRoomEventStream, Database, User } from '../common'
|
||||
import { addTooltipElementListener, setTooltipText } from './tooltip'
|
||||
|
||||
async function renderWidget(elSeatMap, seats) {
|
||||
const user = await getLoggedUser()
|
||||
const user = await User.getLogged()
|
||||
|
||||
Object.values(seats).forEach(seat => {
|
||||
const { id, occupiedBy } = seat
|
||||
@@ -26,7 +26,7 @@ async function renderWidget(elSeatMap, seats) {
|
||||
}
|
||||
|
||||
export async function createSeatWidget($roomGrid, roomId) {
|
||||
const user = await getLoggedUser()
|
||||
const user = await User.getLogged()
|
||||
|
||||
const elSeats = [...$roomGrid.querySelectorAll('[data-seat-id]')]
|
||||
const elSeatMap = {}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import './style.scss'
|
||||
|
||||
export const BASE_URL = import.meta.env.BASE_URL.replace(/\/$/, '')
|
||||
|
||||
let USER = false
|
||||
export async function getLoggedUser() {
|
||||
if (USER === false) {
|
||||
console.log('Caching user data...')
|
||||
USER = await (await fetch(`${BASE_URL}/api/user`)).json()
|
||||
}
|
||||
|
||||
return USER
|
||||
}
|
||||
|
||||
export function createRoomEventStream(roomId) {
|
||||
return new EventSource(`${BASE_URL}/api/room_events?id=${roomId}`)
|
||||
}
|
||||
|
||||
export const Database = {
|
||||
async getSeats(roomId) {
|
||||
const seatList = await (await fetch(`${BASE_URL}/api/room/seats?id=${roomId}`)).json()
|
||||
const seats = {}
|
||||
|
||||
seatList.forEach(seat => {
|
||||
seats[seat.id] = seat
|
||||
})
|
||||
|
||||
return seats
|
||||
},
|
||||
async occupySeat(seatId) {
|
||||
const response = await fetch(`${BASE_URL}/api/seat/occupy?id=${seatId}`, {
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text())
|
||||
}
|
||||
|
||||
await response.json()
|
||||
},
|
||||
async leaveSeat(seatId) {
|
||||
const response = await fetch(`${BASE_URL}/api/seat/leave?id=${seatId}`, {
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text())
|
||||
}
|
||||
|
||||
await response.json()
|
||||
},
|
||||
}
|
||||
+11
-29
@@ -1,48 +1,30 @@
|
||||
import { createGridLineCanvas } from '../components/gridlines.js'
|
||||
import { BASE_URL, getLoggedUser } from '../index.js'
|
||||
import { createSeatWidget } from '../components/seats-widget.js'
|
||||
import { createClock } from '../components/clock.js'
|
||||
import { attachTooltip } from '../components/tooltip.js'
|
||||
import '../common'
|
||||
|
||||
import { createGridLineCanvas } from '../components/grid-lines'
|
||||
import { BASE_URL } from '../common'
|
||||
import { createSeatWidget } from '../components/seats-widget'
|
||||
import { createClock } from '../components/clock'
|
||||
import { attachTooltip } from '../components/tooltip'
|
||||
import { createNavUser } from '../components/nav-user.js'
|
||||
|
||||
const elClock = document.querySelector('#clock')
|
||||
|
||||
const elLoggedLabel = document.querySelector('#logged-label')
|
||||
const elLoginLabel = document.querySelector('#login-label')
|
||||
const elLogoutLabel = document.querySelector('#logout-label')
|
||||
|
||||
const elLogoutButton = document.querySelector('#logout-button')
|
||||
const elNavUser = document.querySelector('#nav-user')
|
||||
|
||||
const elRoomGrid = document.querySelector('.room-grid')
|
||||
|
||||
async function logout() {
|
||||
await fetch(`${BASE_URL}/api/logout`, { method: 'POST' })
|
||||
location.href = `${BASE_URL}`
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
console.log(params)
|
||||
|
||||
elLogoutButton.addEventListener('click', () => logout())
|
||||
|
||||
const user = await getLoggedUser()
|
||||
|
||||
if (user) {
|
||||
elLoginLabel.classList.add('hidden')
|
||||
|
||||
elLoggedLabel.innerText =
|
||||
'@' + user.id + (user.permissions.length > 0 ? ` (${user.permissions.join(', ')})` : '')
|
||||
elLoggedLabel.classList.remove('hidden')
|
||||
|
||||
elLogoutLabel.classList.remove('hidden')
|
||||
}
|
||||
|
||||
// Widgets
|
||||
// createClock(elClock)
|
||||
createGridLineCanvas(elRoomGrid)
|
||||
createSeatWidget(elRoomGrid, 'aula-stud')
|
||||
|
||||
createNavUser(elNavUser)
|
||||
|
||||
// Use tooltips only on desktop
|
||||
if (matchMedia('(pointer: fine)').matches) {
|
||||
attachTooltip()
|
||||
|
||||
+13
-29
@@ -1,4 +1,6 @@
|
||||
import { BASE_URL, getLoggedUser } from '../index.js'
|
||||
import '../common'
|
||||
|
||||
import { BASE_URL, User } from '../common'
|
||||
|
||||
//
|
||||
// Page Element
|
||||
@@ -16,8 +18,7 @@ const elLoginButton = document.querySelector('#button-login')
|
||||
//
|
||||
|
||||
let errorTimeoutHandle = null
|
||||
|
||||
function displayErrorString(e) {
|
||||
function displayFormErrorMessage(e) {
|
||||
if (errorTimeoutHandle) clearTimeout(errorTimeoutHandle)
|
||||
elErrorString.classList.toggle('hidden', false)
|
||||
elErrorString.innerText = e.toString()
|
||||
@@ -31,28 +32,10 @@ function displayErrorString(e) {
|
||||
// Handle Login Button
|
||||
//
|
||||
|
||||
async function login() {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/api/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: elLoginUsernameInput.value,
|
||||
password: elLoginPasswordInput.value,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
displayErrorString(await response.text())
|
||||
return
|
||||
}
|
||||
|
||||
location.href = `${BASE_URL}/`
|
||||
} catch (e) {
|
||||
displayErrorString(e)
|
||||
async function onLoginButtonClick() {
|
||||
const res = await User.login(elLoginUsernameInput.value, elLoginPasswordInput.value)
|
||||
if (res.error) {
|
||||
displayFormErrorMessage(res.error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,16 +44,17 @@ async function login() {
|
||||
//
|
||||
|
||||
async function main() {
|
||||
const user = await getLoggedUser()
|
||||
console.log(user)
|
||||
const user = await User.getLogged()
|
||||
if (user) {
|
||||
location.href = `${BASE_URL}/`
|
||||
}
|
||||
|
||||
elLoginButton.addEventListener('click', () => login())
|
||||
elLoginButton.addEventListener('click', () => {
|
||||
onLoginButtonClick()
|
||||
})
|
||||
|
||||
elLoginPasswordInput.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter') login()
|
||||
if (e.key === 'Enter') onLoginButtonClick()
|
||||
})
|
||||
|
||||
elLoginUsernameInput.focus()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import '../common'
|
||||
Reference in New Issue
Block a user