diff --git a/web/src/components/AuthenticatedImage.vue b/web/src/components/AuthenticatedImage.vue index 921b562..9e1a963 100644 --- a/web/src/components/AuthenticatedImage.vue +++ b/web/src/components/AuthenticatedImage.vue @@ -7,7 +7,7 @@ \ No newline at end of file diff --git a/web/src/components/TimelineMail.vue b/web/src/components/TimelineMail.vue index 0c0f6f7..065d56a 100644 --- a/web/src/components/TimelineMail.vue +++ b/web/src/components/TimelineMail.vue @@ -25,8 +25,8 @@ diff --git a/web/src/persistent-state-plugin/index.js b/web/src/persistent-state-plugin/index.js new file mode 100644 index 0000000..b7cff69 --- /dev/null +++ b/web/src/persistent-state-plugin/index.js @@ -0,0 +1,71 @@ +import {isProxy, toRaw} from 'vue'; + +export default (config) => (store) => { + if (!('isLoadedKey' in config)) { + throw new Error("isLoadedKey not defined in config"); + } + + const initialize = () => { + config.state.forEach(k => { + try { + if (config.debug) console.log("localStorage init", k, localStorage.getItem(config.prefix + k)); + const parsed = JSON.parse(localStorage.getItem(config.prefix + k)); + if (parsed !== store.state[k] && parsed !== null) { + store.state[k] = parsed; + } else { + if (config.debug) console.log("localStorage not loaded", k, localStorage.getItem(config.prefix + k)); + } + } catch (e) { + if (config.debug) console.log("localStorage parse error", k, e); + } + }); + store.state[config.isLoadedKey] = true; + } + + const reload = initialize; + + if (store.state[config.isLoadedKey] !== true) + initialize(); + + addEventListener('storage', reload); + + if ('state' in config) { + config.state.forEach((member) => { + store.watch((state, getters) => state[member], (newValue, oldValue) => { + try { + if (config.debug) console.log('watch', member, + isProxy(newValue) ? toRaw(newValue) : newValue, + isProxy(oldValue) ? toRaw(oldValue) : oldValue); + const key = config.prefix + member; + const encoded = JSON.stringify(isProxy(newValue) ? toRaw(newValue) : newValue); + if (encoded !== localStorage.getItem(key)) { + if (config.debug) console.log("localStorage replace", member, localStorage.getItem(key), encoded); + if (newValue === null) + localStorage.removeItem(key); + else + localStorage.setItem(key, encoded); + } else { + if (config.debug) console.log("localStorage not saved", member, localStorage.getItem(key), encoded); + } + } catch (e) { + if (config.debug) console.log("localsorage save error", member, e); + } + }); + }); + } + + if ('clearingMutation' in config) { + store.subscribe((mutation, state) => { + if (mutation.type === config.clearingMutation) { + removeEventListener('storage', reload) + for (let key in config.state) { + localStorage.removeItem(config.prefix + key); + } + for (let key in config.state) { + store.state[key] = null; + } + addEventListener('storage', reload) + } + }); + } +}; \ No newline at end of file diff --git a/web/src/store.js b/web/src/store.js index a47239d..94bfc70 100644 --- a/web/src/store.js +++ b/web/src/store.js @@ -5,6 +5,8 @@ import * as base64 from 'base-64'; import * as utf8 from 'utf8'; import {ticketStateColorLookup, ticketStateIconLookup, http} from "@/utils"; +import persistentStatePlugin from "@/persistent-state-plugin"; + const store = createStore({ state: { keyIncrement: 0, @@ -17,10 +19,8 @@ const store = createStore({ users: [], groups: [], state_options: [], - lastEvent: localStorage.getItem('lf_lastEvent') || '37C3', - lastUsed: JSON.parse(localStorage.getItem('lf_lastUsed') || '{}'), + lastEvent: '37C3', remember: false, - user: { username: null, password: null, @@ -29,7 +29,8 @@ const store = createStore({ expiry: null, }, - local_loaded: false, + thumbnailCache: {}, + persistent_loaded: false, showAddBoxModal: false, }, getters: { @@ -38,8 +39,10 @@ const store = createStore({ getActiveView: state => router.currentRoute.value.name || 'items', getFilters: state => router.currentRoute.value.query, getBoxes: state => state.loadedBoxes, - checkPermission: state => (event, perm) => state.user.permissions.includes(`${event}:${perm}`) || state.user.permissions.includes(`*:${perm}`), - hasPermissions: state => state.user.permissions.length > 0, + checkPermission: state => (event, perm) => state.user.permissions && + (state.user.permissions.includes(`${event}:${perm}`) || state.user.permissions.includes(`*:${perm}`)), + hasPermissions: state => state.user.permissions && state.user.permissions.length > 0, + activeUser: state => state.user.username || 'anonymous', stateInfo: state => (slug) => { const obj = state.state_options.filter((s) => s.value === slug)[0]; if (obj) { @@ -67,27 +70,18 @@ const store = createStore({ return 'tasks'; }, isLoggedIn(state) { - if (!state.local_loaded) { - state.remember = localStorage.getItem('remember') === 'true'; - state.user.username = localStorage.getItem('user'); - //state.password = localStorage.getItem('password'); - state.user.permissions = JSON.parse(localStorage.getItem('permissions') || '[]'); - state.user.token = localStorage.getItem('token'); - state.user.expiry = localStorage.getItem('token_expiry'); - state.local_loaded = true; - } - return state.user && state.user.username !== null && state.user.token !== null; }, + getThumbnail: (state) => (url) => { + if (!url) return null; + if (!(url in state.thumbnailCache)) + return null; + return state.thumbnailCache[url]; + }, }, mutations: { - updateLastUsed(state, diff) { - state.lastUsed = {...state.lastUsed, ...diff}; - localStorage.setItem('lf_lastUsed', JSON.stringify(state.lastUsed)); - }, updateLastEvent(state, slug) { state.lastEvent = slug; - localStorage.setItem('lf_lastEvent', slug); }, replaceEvents(state, events) { state.events = events; @@ -147,29 +141,21 @@ const store = createStore({ }, setRemember(state, remember) { state.remember = remember; - localStorage.setItem('remember', remember); }, setUser(state, user) { state.user.username = user; - if (user) - localStorage.setItem('user', user); }, setPassword(state, password) { state.user.password = password; }, setPermissions(state, permissions) { state.user.permissions = permissions; - if (permissions) - localStorage.setItem('permissions', JSON.stringify(permissions)); }, setToken(state, {token, expiry}) { const user = {...state.user}; user.token = token; user.expiry = expiry; state.user = user; - if (token) - localStorage.setItem('token', token); - localStorage.setItem('token_expiry', expiry); }, setUserInfo(state, user) { state.user = user; @@ -183,9 +169,12 @@ const store = createStore({ user.permissions = null; state.user = user; }, + setThumbnail(state, {url, data}) { + state.thumbnailCache[url] = data; + }, }, actions: { - async login({commit, dispatch, state}, {username, password, remember}) { + async login({commit}, {username, password, remember}) { commit('setRemember', remember); try { const data = await fetch('/api/2/login/', { @@ -195,10 +184,8 @@ const store = createStore({ credentials: 'omit' }).then(r => r.json()) if (data && data.token) { - commit('setToken', data); - commit('setUser', username); - commit('setPassword', password); - dispatch('afterLogin'); + const {data: {permissions}} = await http.get('/2/self/', data.token); + commit('setUserInfo', {...data, permissions, username, password}); return true; } else { return false; @@ -372,10 +359,21 @@ const store = createStore({ commit('updateTicket', data); } }, + plugins: [ + persistentStatePlugin({ // TODO change remember to some kind of enable field + prefix: "lf_", + debug: false, + isLoadedKey: "persistent_loaded", + state: [ + "remember", + "user", + "events", + ] + }), + ], }); store.watch((state) => state.user, (user) => { - console.log('user changed', user); if (store.getters.isLoggedIn) { if (router.currentRoute.value.name === 'login' && router.currentRoute.value.query.redirect) router.push(router.currentRoute.value.query.redirect); diff --git a/web/src/views/Empty.vue b/web/src/views/Empty.vue index 4c7c3eb..ef221da 100644 --- a/web/src/views/Empty.vue +++ b/web/src/views/Empty.vue @@ -4,9 +4,16 @@
-

User: {{user}}

+

User: {{ activeUser }}

-
+
+

Your Account is activated. Got to + Items + or + Tickets +

+
+

Your Account is not yet activated. Please contact an admin.

@@ -16,11 +23,13 @@ diff --git a/web/src/views/Items.vue b/web/src/views/Items.vue index 04d08fb..2eec96f 100644 --- a/web/src/views/Items.vue +++ b/web/src/views/Items.vue @@ -45,7 +45,7 @@ v-slot="{ item }" @itemActivated="openLightboxModalWith($event)" > -