This commit is contained in:
j3d1 2026-08-31 16:05:05 +02:00
parent 18a3e40435
commit 15dad8e3be
6 changed files with 130 additions and 153 deletions

View file

@ -5,6 +5,7 @@ import NeighborsCache from "@/neigbors";
import {createNullAuth, createSignAuth, createTokenAuth, ServerSet, ServerSetUnion} from "@/federation";
import {parseIdentityRecord, serializeIdentityRecord} from "@/identity";
import {serializeWorkflowPayload, deserializeWorkflowPayload} from "@/workflows.js";
import {defaultPreferenceDefinitions, loadDevicePreferences, saveDevicePreferences, resolvePreference} from "@/preferences.js";
//import sharedStatePlugin from "@/../extras/shared-state-plugin";
//import persistentStatePlugin from "@/../extras/persistent-state-plugin";
@ -13,59 +14,6 @@ function splitGroupHandle(handle) {
return {name, domain}
}
const defaultPreferenceDefinitions = [
{
key: 'ui.compact_mode',
type: 'boolean',
default: false,
label: 'Compact mode',
description: 'Show denser item rows and reduce spacing in lists.'
},
{
key: 'ui.default_search_scope',
type: 'enum',
options: ['inventory', 'friends', 'all'],
default: 'inventory',
label: 'Default search scope',
description: 'Choose where the global search starts.'
},
{
key: 'notifications.desktop_enabled',
type: 'boolean',
default: true,
label: 'Desktop notifications',
description: 'Enable in-browser notifications for important updates.'
},
{
key: 'files.max_upload_mb',
type: 'integer',
default: 25,
label: 'Default upload size limit (MB)',
description: 'Used as a prefill hint in upload dialogs.'
},
{
key: 'ui.experimental_flags',
type: 'json',
default: {},
label: 'Experimental flags',
description: 'Optional JSON toggles for feature previews.'
},
]
const parseStoredPreferences = (storageKey) => {
try {
const raw = localStorage.getItem(storageKey)
if (!raw) {
return {}
}
const parsed = JSON.parse(raw)
return parsed && typeof parsed === 'object' ? parsed : {}
} catch (_error) {
return {}
}
}
export default createStore({
state: {
local_loaded: false,
@ -97,9 +45,12 @@ export default createStore({
domains: [],
storage_locations: [],
active_workflows: [],
preferenceDefinitions: [],
// Static schema and device-local values are available synchronously - see
// docs/implementation.md#preferences - unlike accountPreferences, which needs a network
// round trip and is gated by preferencesLoaded.
preferenceDefinitions: defaultPreferenceDefinitions,
accountPreferences: {},
devicePreferences: {},
devicePreferences: loadDevicePreferences(),
preferencesLoaded: false,
},
mutations: {
@ -161,15 +112,9 @@ export default createStore({
setActiveWorkflows(state, workflows) {
state.active_workflows = workflows;
},
setPreferenceDefinitions(state, definitions) {
state.preferenceDefinitions = definitions;
},
setAccountPreferences(state, preferences) {
state.accountPreferences = preferences;
},
setDevicePreferences(state, preferences) {
state.devicePreferences = preferences;
},
setAccountPreference(state, {key, value}) {
state.accountPreferences = {...state.accountPreferences, [key]: value};
},
@ -237,9 +182,8 @@ export default createStore({
state.token = null;
state.keypair = null;
state.accountPreferences = {};
state.preferenceDefinitions = [];
state.preferencesLoaded = false;
// Note: devicePreferences are NOT cleared on logout (device-specific)
// Note: devicePreferences/preferenceDefinitions are NOT cleared on logout (device-specific / static)
localStorage.removeItem('user');
localStorage.removeItem('token');
localStorage.removeItem('keypair');
@ -266,19 +210,13 @@ export default createStore({
actions: {
async loadUserPreferences({state, commit, dispatch, getters}) {
if (state.preferencesLoaded) return
const deviceKey = 'toolshed.preferences.device'
commit('setDevicePreferences', parseStoredPreferences(deviceKey))
let definitions = defaultPreferenceDefinitions
let accountPreferences = {}
try {
const servers = await dispatch('getHomeServers')
definitions = await servers.get(getters.nullAuth, '/auth/preferences/') || defaultPreferenceDefinitions
accountPreferences = await servers.get(getters.signAuth, '/auth/self/preferences/') || {}
} catch (error) {
console.error('Failed to load user preferences:', error)
}
commit('setPreferenceDefinitions', definitions)
commit('setAccountPreferences', accountPreferences)
commit('setPreferencesLoaded', true)
},
@ -294,11 +232,11 @@ export default createStore({
},
async setDevicePreference({state, commit}, {key, value}) {
commit('setDevicePreference', {key, value})
localStorage.setItem('toolshed.preferences.device', JSON.stringify(state.devicePreferences))
saveDevicePreferences(state.devicePreferences)
},
async resetDevicePreference({state, commit}, key) {
commit('deleteDevicePreference', key)
localStorage.setItem('toolshed.preferences.device', JSON.stringify(state.devicePreferences))
saveDevicePreferences(state.devicePreferences)
},
async login({commit, dispatch, state, getters}, {username, password, remember}) {
commit('setRemember', remember);
@ -949,19 +887,11 @@ export default createStore({
time: Date.now() - 1000 * 60 * 60 * 24
}]
},
getPreference: (state) => (key, fallbackDefault = null) => {
if (Object.prototype.hasOwnProperty.call(state.devicePreferences, key) && state.devicePreferences[key] !== null) {
return state.devicePreferences[key]
}
if (Object.prototype.hasOwnProperty.call(state.accountPreferences, key) && state.accountPreferences[key] !== null) {
return state.accountPreferences[key]
}
const definition = state.preferenceDefinitions.find((pref) => pref.key === key)
if (definition) {
return definition.default
}
return fallbackDefault
},
getPreference: (state) => (key, fallbackDefault = null) => resolvePreference(key, {
devicePreferences: state.devicePreferences,
accountPreferences: state.accountPreferences,
definitions: state.preferenceDefinitions,
}, fallbackDefault),
/** Extracts the name from a handle like "git:tools#tag:drill"; returns non-handles unchanged. */
getNameFromHandle: () => (handle) => {
if (typeof handle !== 'string') {