stash
This commit is contained in:
parent
18a3e40435
commit
15dad8e3be
6 changed files with 130 additions and 153 deletions
|
|
@ -18,47 +18,6 @@ from hostadmin.models import Domain
|
||||||
|
|
||||||
router = routers.SimpleRouter()
|
router = routers.SimpleRouter()
|
||||||
|
|
||||||
# Schema for account-level preferences a client may store server-side (see AccountPreference);
|
|
||||||
# device-level preferences stay in the browser's local storage instead.
|
|
||||||
PREFERENCE_DEFINITIONS = [
|
|
||||||
{
|
|
||||||
'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.',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class UserAuthToken(ObtainAuthToken):
|
class UserAuthToken(ObtainAuthToken):
|
||||||
|
|
||||||
|
|
@ -199,14 +158,6 @@ def registerUser(request):
|
||||||
return Response({'errors': {'domain': 'Domain does not exist or is not open for registration'}}, status=400)
|
return Response({'errors': {'domain': 'Domain does not exist or is not open for registration'}}, status=400)
|
||||||
|
|
||||||
|
|
||||||
@api_view(['GET'])
|
|
||||||
@permission_classes([])
|
|
||||||
@authentication_classes([])
|
|
||||||
def preference_definitions(request):
|
|
||||||
"""Return the schema (types, defaults, labels) for the account preferences clients may set."""
|
|
||||||
return Response(PREFERENCE_DEFINITIONS)
|
|
||||||
|
|
||||||
|
|
||||||
@api_view(['GET', 'PUT'])
|
@api_view(['GET', 'PUT'])
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
@authentication_classes([SignatureAuthenticationLocal])
|
@authentication_classes([SignatureAuthenticationLocal])
|
||||||
|
|
@ -241,7 +192,6 @@ urlpatterns = [
|
||||||
path('user/<str:handle>/', getUserProfile),
|
path('user/<str:handle>/', getUserProfile),
|
||||||
path('register/', registerUser),
|
path('register/', registerUser),
|
||||||
path('token/', UserAuthToken.as_view()),
|
path('token/', UserAuthToken.as_view()),
|
||||||
path('preferences/', preference_definitions),
|
|
||||||
path('self/preferences/', account_preferences),
|
path('self/preferences/', account_preferences),
|
||||||
path('self/preferences/<str:key>/', account_preference_detail),
|
path('self/preferences/<str:key>/', account_preference_detail),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -249,8 +249,8 @@ function positionTree(node, ownAxis, x, y) {
|
||||||
// MIN_RECOMMENDED_QR_PX_PER_MODULE/MIN_RECOMMENDED_QR_MODULE_MM the leaf falls short of (still
|
// MIN_RECOMMENDED_QR_PX_PER_MODULE/MIN_RECOMMENDED_QR_MODULE_MM the leaf falls short of (still
|
||||||
// printable, just flagged as a scan-reliability risk), and unconditionally appends its raw
|
// printable, just flagged as a scan-reliability risk), and unconditionally appends its raw
|
||||||
// {scale, moduleMm} to `qrInfo` - callers needing to show those numbers regardless of whether
|
// {scale, moduleMm} to `qrInfo` - callers needing to show those numbers regardless of whether
|
||||||
// they tripped a threshold (see LabelLayoutPreview.vue's debug line) shouldn't have to re-derive
|
// they tripped a threshold shouldn't have to re-derive them. See
|
||||||
// them. See docs/implementation.md#crisp-qr-sizing.
|
// docs/implementation.md#crisp-qr-sizing.
|
||||||
function snapQrToCrispSize(node, pxPerMm, warnings, qrInfo) {
|
function snapQrToCrispSize(node, pxPerMm, warnings, qrInfo) {
|
||||||
if (isSplit(node)) {
|
if (isSplit(node)) {
|
||||||
node.forEach(child => snapQrToCrispSize(child, pxPerMm, warnings, qrInfo));
|
node.forEach(child => snapQrToCrispSize(child, pxPerMm, warnings, qrInfo));
|
||||||
|
|
@ -349,7 +349,7 @@ function measureTextBlock(ctx, lines, referencePx) {
|
||||||
return {width, height};
|
return {width, height};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts a resolved content tree (see templateContent) into one ready for layout. See
|
// Converts a resolved content tree (see label-layouts.js's resolveContent) into one ready for layout. See
|
||||||
// docs/implementation.md#render-tree-construction.
|
// docs/implementation.md#render-tree-construction.
|
||||||
function buildRenderTree(ctx, node, referencePx) {
|
function buildRenderTree(ctx, node, referencePx) {
|
||||||
if (isSplit(node)) {
|
if (isSplit(node)) {
|
||||||
|
|
@ -483,7 +483,7 @@ function layoutContent(ctx, content, fixedSize, maxLength, referencePx, pxPerMm,
|
||||||
return {tree, length, warnings, qrInfo, textInfo};
|
return {tree, length, warnings, qrInfo, textInfo};
|
||||||
}
|
}
|
||||||
|
|
||||||
// The tape-fed layout: draws a fully resolved content tree (see templateContent) at the tape's
|
// The tape-fed layout: draws a fully resolved content tree (see label-layouts.js's resolveContent) at the tape's
|
||||||
// real pixel dimensions. See docs/implementation.md#tape-fed-label-drawing. Returns
|
// real pixel dimensions. See docs/implementation.md#tape-fed-label-drawing. Returns
|
||||||
// {textSizesPx}: each "text" leaf's effective font size, in the tree's own left-to-right,
|
// {textSizesPx}: each "text" leaf's effective font size, in the tree's own left-to-right,
|
||||||
// top-to-bottom order; {warnings}: {short, message} scan-reliability entries from
|
// top-to-bottom order; {warnings}: {short, message} scan-reliability entries from
|
||||||
|
|
@ -602,7 +602,7 @@ function splitUserHandle(userHandle) {
|
||||||
|
|
||||||
// Seeds label-layouts.js's BASE_VARS, keyed by `kind`; derived vars (userHandle, itemUrl, …) are
|
// Seeds label-layouts.js's BASE_VARS, keyed by `kind`; derived vars (userHandle, itemUrl, …) are
|
||||||
// computed live elsewhere (see DERIVED_VARS, Print.vue's `shortUrl`), and omitting a field
|
// computed live elsewhere (see DERIVED_VARS, Print.vue's `shortUrl`), and omitting a field
|
||||||
// (rather than leaving it present-but-empty) signals "not available" to templateIsAvailable.
|
// (rather than leaving it present-but-empty) signals "not available" to contentIsAvailable.
|
||||||
const LABEL_FIELD_BUILDERS = {
|
const LABEL_FIELD_BUILDERS = {
|
||||||
"item": ({userHandle, item}) => {
|
"item": ({userHandle, item}) => {
|
||||||
const split = splitUserHandle(userHandle);
|
const split = splitUserHandle(userHandle);
|
||||||
|
|
|
||||||
89
frontend/src/preferences.js
Normal file
89
frontend/src/preferences.js
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Preference schema catalog, device/account resolution, and device-local (localStorage)
|
||||||
|
// persistence. See docs/implementation.md#preferences for account vs device precedence.
|
||||||
|
|
||||||
|
export const DEVICE_STORAGE_KEY = 'toolshed.preferences.device'
|
||||||
|
|
||||||
|
export 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.'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export function loadDevicePreferences(storageKey = DEVICE_STORAGE_KEY) {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(storageKey)
|
||||||
|
if (!raw) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
const parsed = JSON.parse(raw)
|
||||||
|
return parsed && typeof parsed === 'object' ? parsed : {}
|
||||||
|
} catch (_error) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Best-effort - a disabled/full localStorage shouldn't break whatever triggered the save (see
|
||||||
|
// e.g. Print.vue's rememberLayout).
|
||||||
|
export function saveDevicePreferences(preferences, storageKey = DEVICE_STORAGE_KEY) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(storageKey, JSON.stringify(preferences))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save device preferences:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolution order: device -> account -> schema default -> fallbackDefault.
|
||||||
|
export function resolvePreference(key, {devicePreferences = {}, accountPreferences = {}, definitions = []} = {}, fallbackDefault = null) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(devicePreferences, key) && devicePreferences[key] !== null) {
|
||||||
|
return devicePreferences[key]
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(accountPreferences, key) && accountPreferences[key] !== null) {
|
||||||
|
return accountPreferences[key]
|
||||||
|
}
|
||||||
|
const definition = definitions.find((pref) => pref.key === key)
|
||||||
|
if (definition) {
|
||||||
|
return definition.default
|
||||||
|
}
|
||||||
|
return fallbackDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
DEVICE_STORAGE_KEY,
|
||||||
|
defaultPreferenceDefinitions,
|
||||||
|
loadDevicePreferences,
|
||||||
|
saveDevicePreferences,
|
||||||
|
resolvePreference,
|
||||||
|
};
|
||||||
|
|
@ -5,6 +5,7 @@ import NeighborsCache from "@/neigbors";
|
||||||
import {createNullAuth, createSignAuth, createTokenAuth, ServerSet, ServerSetUnion} from "@/federation";
|
import {createNullAuth, createSignAuth, createTokenAuth, ServerSet, ServerSetUnion} from "@/federation";
|
||||||
import {parseIdentityRecord, serializeIdentityRecord} from "@/identity";
|
import {parseIdentityRecord, serializeIdentityRecord} from "@/identity";
|
||||||
import {serializeWorkflowPayload, deserializeWorkflowPayload} from "@/workflows.js";
|
import {serializeWorkflowPayload, deserializeWorkflowPayload} from "@/workflows.js";
|
||||||
|
import {defaultPreferenceDefinitions, loadDevicePreferences, saveDevicePreferences, resolvePreference} from "@/preferences.js";
|
||||||
//import sharedStatePlugin from "@/../extras/shared-state-plugin";
|
//import sharedStatePlugin from "@/../extras/shared-state-plugin";
|
||||||
//import persistentStatePlugin from "@/../extras/persistent-state-plugin";
|
//import persistentStatePlugin from "@/../extras/persistent-state-plugin";
|
||||||
|
|
||||||
|
|
@ -13,59 +14,6 @@ function splitGroupHandle(handle) {
|
||||||
return {name, domain}
|
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({
|
export default createStore({
|
||||||
state: {
|
state: {
|
||||||
local_loaded: false,
|
local_loaded: false,
|
||||||
|
|
@ -97,9 +45,12 @@ export default createStore({
|
||||||
domains: [],
|
domains: [],
|
||||||
storage_locations: [],
|
storage_locations: [],
|
||||||
active_workflows: [],
|
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: {},
|
accountPreferences: {},
|
||||||
devicePreferences: {},
|
devicePreferences: loadDevicePreferences(),
|
||||||
preferencesLoaded: false,
|
preferencesLoaded: false,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
|
@ -161,15 +112,9 @@ export default createStore({
|
||||||
setActiveWorkflows(state, workflows) {
|
setActiveWorkflows(state, workflows) {
|
||||||
state.active_workflows = workflows;
|
state.active_workflows = workflows;
|
||||||
},
|
},
|
||||||
setPreferenceDefinitions(state, definitions) {
|
|
||||||
state.preferenceDefinitions = definitions;
|
|
||||||
},
|
|
||||||
setAccountPreferences(state, preferences) {
|
setAccountPreferences(state, preferences) {
|
||||||
state.accountPreferences = preferences;
|
state.accountPreferences = preferences;
|
||||||
},
|
},
|
||||||
setDevicePreferences(state, preferences) {
|
|
||||||
state.devicePreferences = preferences;
|
|
||||||
},
|
|
||||||
setAccountPreference(state, {key, value}) {
|
setAccountPreference(state, {key, value}) {
|
||||||
state.accountPreferences = {...state.accountPreferences, [key]: value};
|
state.accountPreferences = {...state.accountPreferences, [key]: value};
|
||||||
},
|
},
|
||||||
|
|
@ -237,9 +182,8 @@ export default createStore({
|
||||||
state.token = null;
|
state.token = null;
|
||||||
state.keypair = null;
|
state.keypair = null;
|
||||||
state.accountPreferences = {};
|
state.accountPreferences = {};
|
||||||
state.preferenceDefinitions = [];
|
|
||||||
state.preferencesLoaded = false;
|
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('user');
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token');
|
||||||
localStorage.removeItem('keypair');
|
localStorage.removeItem('keypair');
|
||||||
|
|
@ -266,19 +210,13 @@ export default createStore({
|
||||||
actions: {
|
actions: {
|
||||||
async loadUserPreferences({state, commit, dispatch, getters}) {
|
async loadUserPreferences({state, commit, dispatch, getters}) {
|
||||||
if (state.preferencesLoaded) return
|
if (state.preferencesLoaded) return
|
||||||
const deviceKey = 'toolshed.preferences.device'
|
|
||||||
commit('setDevicePreferences', parseStoredPreferences(deviceKey))
|
|
||||||
|
|
||||||
let definitions = defaultPreferenceDefinitions
|
|
||||||
let accountPreferences = {}
|
let accountPreferences = {}
|
||||||
try {
|
try {
|
||||||
const servers = await dispatch('getHomeServers')
|
const servers = await dispatch('getHomeServers')
|
||||||
definitions = await servers.get(getters.nullAuth, '/auth/preferences/') || defaultPreferenceDefinitions
|
|
||||||
accountPreferences = await servers.get(getters.signAuth, '/auth/self/preferences/') || {}
|
accountPreferences = await servers.get(getters.signAuth, '/auth/self/preferences/') || {}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load user preferences:', error)
|
console.error('Failed to load user preferences:', error)
|
||||||
}
|
}
|
||||||
commit('setPreferenceDefinitions', definitions)
|
|
||||||
commit('setAccountPreferences', accountPreferences)
|
commit('setAccountPreferences', accountPreferences)
|
||||||
commit('setPreferencesLoaded', true)
|
commit('setPreferencesLoaded', true)
|
||||||
},
|
},
|
||||||
|
|
@ -294,11 +232,11 @@ export default createStore({
|
||||||
},
|
},
|
||||||
async setDevicePreference({state, commit}, {key, value}) {
|
async setDevicePreference({state, commit}, {key, value}) {
|
||||||
commit('setDevicePreference', {key, value})
|
commit('setDevicePreference', {key, value})
|
||||||
localStorage.setItem('toolshed.preferences.device', JSON.stringify(state.devicePreferences))
|
saveDevicePreferences(state.devicePreferences)
|
||||||
},
|
},
|
||||||
async resetDevicePreference({state, commit}, key) {
|
async resetDevicePreference({state, commit}, key) {
|
||||||
commit('deleteDevicePreference', 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}) {
|
async login({commit, dispatch, state, getters}, {username, password, remember}) {
|
||||||
commit('setRemember', remember);
|
commit('setRemember', remember);
|
||||||
|
|
@ -949,19 +887,11 @@ export default createStore({
|
||||||
time: Date.now() - 1000 * 60 * 60 * 24
|
time: Date.now() - 1000 * 60 * 60 * 24
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
getPreference: (state) => (key, fallbackDefault = null) => {
|
getPreference: (state) => (key, fallbackDefault = null) => resolvePreference(key, {
|
||||||
if (Object.prototype.hasOwnProperty.call(state.devicePreferences, key) && state.devicePreferences[key] !== null) {
|
devicePreferences: state.devicePreferences,
|
||||||
return state.devicePreferences[key]
|
accountPreferences: state.accountPreferences,
|
||||||
}
|
definitions: state.preferenceDefinitions,
|
||||||
if (Object.prototype.hasOwnProperty.call(state.accountPreferences, key) && state.accountPreferences[key] !== null) {
|
}, fallbackDefault),
|
||||||
return state.accountPreferences[key]
|
|
||||||
}
|
|
||||||
const definition = state.preferenceDefinitions.find((pref) => pref.key === key)
|
|
||||||
if (definition) {
|
|
||||||
return definition.default
|
|
||||||
}
|
|
||||||
return fallbackDefault
|
|
||||||
},
|
|
||||||
/** Extracts the name from a handle like "git:tools#tag:drill"; returns non-handles unchanged. */
|
/** Extracts the name from a handle like "git:tools#tag:drill"; returns non-handles unchanged. */
|
||||||
getNameFromHandle: () => (handle) => {
|
getNameFromHandle: () => (handle) => {
|
||||||
if (typeof handle !== 'string') {
|
if (typeof handle !== 'string') {
|
||||||
|
|
|
||||||
|
|
@ -132,19 +132,23 @@ export default {
|
||||||
default: null
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
layout: "grid",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
AuthenticatedImage,
|
AuthenticatedImage,
|
||||||
BaseLayout,
|
BaseLayout,
|
||||||
...BIcons
|
...BIcons
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(["inventory_items", "groupInventoryItems", "loaded_items", "identityIdByHandle", "groupIdByHandle"]),
|
...mapGetters(["inventory_items", "groupInventoryItems", "loaded_items", "identityIdByHandle", "groupIdByHandle", "getPreference"]),
|
||||||
...mapState(["user", "storage_locations", "groups", "groupMemberships", "friends"]),
|
...mapState(["user", "storage_locations", "groups", "groupMemberships", "friends"]),
|
||||||
|
// Implicit preference: remembers the last-used view without a settings-page entry. See docs/implementation.md#preferences.
|
||||||
|
layout: {
|
||||||
|
get() {
|
||||||
|
return this.getPreference('ui.remembered.inventory_view_mode', 'grid')
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.setDevicePreference({key: 'ui.remembered.inventory_view_mode', value})
|
||||||
|
}
|
||||||
|
},
|
||||||
// Groups hosted here plus GroupMembership-only ones - see Groups.vue's allGroups for the same merge/dedupe.
|
// Groups hosted here plus GroupMembership-only ones - see Groups.vue's allGroups for the same merge/dedupe.
|
||||||
ownerGroups() {
|
ownerGroups() {
|
||||||
const hostedHandles = new Set(this.groups.map(group => group.handle))
|
const hostedHandles = new Set(this.groups.map(group => group.handle))
|
||||||
|
|
@ -181,7 +185,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(["fetchInventoryItems", "fetchGroupInventoryItems", "fetchFriendInventoryItems",
|
...mapActions(["fetchInventoryItems", "fetchGroupInventoryItems", "fetchFriendInventoryItems",
|
||||||
"deleteInventoryItem", "fetchStorageLocations", "fetchIdMap", "fetchGroups",
|
"deleteInventoryItem", "fetchStorageLocations", "fetchIdMap", "fetchGroups",
|
||||||
"fetchGroupMemberships", "fetchFriends"]),
|
"fetchGroupMemberships", "fetchFriends", "setDevicePreference"]),
|
||||||
fetchItemsForOwner() {
|
fetchItemsForOwner() {
|
||||||
if (this.selectedOwner === this.user) return this.fetchInventoryItems()
|
if (this.selectedOwner === this.user) return this.fetchInventoryItems()
|
||||||
if (this.isGroupSelected) return this.fetchGroupInventoryItems({groupHandle: this.selectedOwner})
|
if (this.isGroupSelected) return this.fetchGroupInventoryItems({groupHandle: this.selectedOwner})
|
||||||
|
|
|
||||||
|
|
@ -130,18 +130,22 @@ export default {
|
||||||
default: null
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
layout: "grid",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
BaseLayout,
|
BaseLayout,
|
||||||
...BIcons
|
...BIcons
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(["identityIdByHandle", "groupIdByHandle", "groupStorageLocations"]),
|
...mapGetters(["identityIdByHandle", "groupIdByHandle", "groupStorageLocations", "getPreference"]),
|
||||||
...mapState(["user", "storage_locations", "groups", "groupMemberships"]),
|
...mapState(["user", "storage_locations", "groups", "groupMemberships"]),
|
||||||
|
// Implicit preference: remembers the last-used view without a settings-page entry. See docs/implementation.md#preferences.
|
||||||
|
layout: {
|
||||||
|
get() {
|
||||||
|
return this.getPreference('ui.remembered.storage_location_view_mode', 'grid')
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.setDevicePreference({key: 'ui.remembered.storage_location_view_mode', value})
|
||||||
|
}
|
||||||
|
},
|
||||||
// Groups hosted here plus GroupMembership-only ones - see Groups.vue's allGroups for the same merge/dedupe.
|
// Groups hosted here plus GroupMembership-only ones - see Groups.vue's allGroups for the same merge/dedupe.
|
||||||
ownerGroups() {
|
ownerGroups() {
|
||||||
const hostedHandles = new Set(this.groups.map(group => group.handle))
|
const hostedHandles = new Set(this.groups.map(group => group.handle))
|
||||||
|
|
@ -169,7 +173,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(["fetchStorageLocations", "fetchGroupStorageLocations", "deleteStorageLocation",
|
...mapActions(["fetchStorageLocations", "fetchGroupStorageLocations", "deleteStorageLocation",
|
||||||
"fetchIdMap", "fetchGroups", "fetchGroupMemberships"]),
|
"fetchIdMap", "fetchGroups", "fetchGroupMemberships", "setDevicePreference"]),
|
||||||
fetchLocationsForOwner() {
|
fetchLocationsForOwner() {
|
||||||
return this.selectedOwner === this.user
|
return this.selectedOwner === this.user
|
||||||
? this.fetchStorageLocations()
|
? this.fetchStorageLocations()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue