openflexure-microscope-server/webapp/src/store.js
2026-02-15 17:11:55 +00:00

173 lines
4.6 KiB
JavaScript

import { createStore } from "vuex";
import wotStoreModule from "./wot-client";
function getOriginFromLocation() {
// This will default to the same origin that's serving
// the web app - but can be overridden by the URL.
// See also devTools.vue which can change the origin.
let url = new URL(window.location.href);
let origin = url.searchParams.get("overrideOrigin");
if (origin) {
return origin;
} else {
return url.origin;
}
}
/**
* Converts a Vuex state key (e.g. "appTheme") into a corresponding
* Vuex mutation name (e.g. "changeAppTheme") using the `change<Key>`
* convention.
*
* @param {string} key - The Vuex state key to convert.
* @returns {string} - The formatted mutation name.
*/
function keyToMutationName(key) {
return `change${key.charAt(0).toUpperCase() + key.slice(1)}`;
}
/**
* Converts a Vuex mutation name (e.g. "changeAppTheme") back into
* the corresponding state key (e.g. "appTheme") using the
* `change<Key>` convention.
*
* @param {string} mutationName - The Vuex mutation name to reverse.
* @returns {string|null} - The derived state key, or null if the
* mutation name doesn't match the `change<Key>` convention.
*/
function mutationToKey(mutationName) {
const prefix = "change";
if (!mutationName.startsWith(prefix)) {
return null; // Not a mutation we care about
}
const key = mutationName.slice(prefix.length);
return key.charAt(0).toLowerCase() + key.slice(1);
}
const LOCALSTORAGE_KEYS = [
"appTheme",
"disableStream",
"overrideOrigin",
"navigationStepSize",
"navigationInvert",
];
export default createStore({
modules: {
wot: wotStoreModule,
},
state: {
origin: getOriginFromLocation(),
available: false,
waiting: false,
error: "",
autoGpuPreview: false,
trackWindow: true,
activeStreams: {},
microscopeHostname: "",
// Persistent items:
// The app theme (e.g. light/dark)
appTheme: "system",
disableStream: false,
// The origin to use if overriding with dev tools
overrideOrigin: "http://microscope.local:5000",
// The step sizes for navigation via control pane/keys presses
navigationStepSize: {
x: 200,
y: 200,
z: 50,
},
// The axis inversion for navigation via control pane/keys presses
navigationInvert: {
x: false,
y: false,
z: false,
},
},
mutations: {
changeOrigin(state, origin) {
state.origin = origin;
},
changeWaiting(state, waiting) {
state.waiting = waiting;
},
changeDisableStream(state, disabled) {
state.disableStream = disabled;
},
changeAutoGpuPreview(state, enabled) {
state.autoGpuPreview = enabled;
},
changeTrackWindow(state, enabled) {
state.trackWindow = enabled;
},
changeAppTheme(state, theme) {
state.appTheme = theme;
},
resetState(state) {
state.waiting = false;
state.available = false;
state.error = null;
},
setConnected(state) {
state.waiting = false;
state.available = true;
},
setErrorMessage(state, msg) {
state.error = msg;
},
addStream(state, id) {
state.activeStreams[id] = true;
},
removeStream(state, id) {
state.activeStreams[id] = false;
},
changeMicroscopeHostname(state, value) {
state.microscopeHostname = value;
},
changeOverrideOrigin(state, value) {
state.overrideOrigin = value;
},
changeNavigationStepSize(state, value) {
state.navigationStepSize = value;
},
changeNavigationInvert(state, value) {
state.navigationInvert = value;
},
},
actions: {},
getters: {
baseUri: (state) => state.origin,
ready: (state) => state.available,
},
plugins: [
(store) => {
// Load initial state from localStorage
LOCALSTORAGE_KEYS.forEach((key) => {
const saved = localStorage.getItem(key);
if (saved !== null) {
try {
const parsed = JSON.parse(saved);
const mutationName = keyToMutationName(key);
store.commit(mutationName, parsed);
} catch (e) {
console.warn(`Failed to parse localStorage key "${key}":`, e);
localStorage.removeItem(key);
}
}
});
// Subscribe to mutations
store.subscribe((mutation, state) => {
const key = mutationToKey(mutation.type);
// If the mutation is chacning a local storage key then update localStorage
if (key && LOCALSTORAGE_KEYS.includes(key)) {
localStorage.setItem(key, JSON.stringify(state[key]));
}
});
},
],
});