ui_migration chore(store) move store files for pinia refactor
This commit is contained in:
parent
d1c40238d5
commit
b580a63675
2 changed files with 0 additions and 0 deletions
169
webapp/src/stores/settings.js
Normal file
169
webapp/src/stores/settings.js
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
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: "",
|
||||
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;
|
||||
},
|
||||
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]));
|
||||
}
|
||||
});
|
||||
},
|
||||
],
|
||||
});
|
||||
121
webapp/src/stores/wot.js
Normal file
121
webapp/src/stores/wot.js
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import axios from "axios";
|
||||
|
||||
export const wotStoreModule = {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
thingDescriptions: {},
|
||||
servient: null,
|
||||
helpers: null,
|
||||
}),
|
||||
mutations: {
|
||||
addThingDescription(state, { thingName, thingDescription }) {
|
||||
state.thingDescriptions[thingName] = thingDescription;
|
||||
},
|
||||
removeThingDescription(state, thingName) {
|
||||
delete state.thingDescriptions[thingName];
|
||||
},
|
||||
removeAllThingDescriptions(state) {
|
||||
state.thingDescriptions = {};
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async start() {
|
||||
// Set up thing client - not currently used.
|
||||
},
|
||||
async fetchThingDescription({ commit }, { uri, name = null }) {
|
||||
// Fetch the thing description from the given URI and consume it
|
||||
// NB this should only be called once, or we'll duplicate effort.
|
||||
// Deduplication should be done elsewhere.
|
||||
let response = await axios.get(uri);
|
||||
let td = response.data;
|
||||
let thing_name = name || uri.replace(/\/$/, "").split("/").pop();
|
||||
commit("addThingDescription", {
|
||||
thingName: thing_name,
|
||||
thingDescription: td,
|
||||
});
|
||||
},
|
||||
async fetchThingDescriptions({ commit }, uri) {
|
||||
// Fetch thing descriptions from the given URI
|
||||
let response = await axios.get(uri);
|
||||
if (response.status !== 200) throw "Could not retrieve thing descriptions";
|
||||
for (const k in response.data) {
|
||||
let thing_name = k.replace(/\/$/, "").replace(/^\//, "");
|
||||
commit("addThingDescription", {
|
||||
thingName: thing_name,
|
||||
thingDescription: response.data[k],
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
thingDescriptions: (state) => {
|
||||
return state.thingDescriptions;
|
||||
},
|
||||
thingList: (state) => {
|
||||
return Object.keys(state.thingDescriptions);
|
||||
},
|
||||
thingDescription: (state) => (thingName) => {
|
||||
return state.thingDescriptions[thingName];
|
||||
},
|
||||
thingAvailable: (state) => (thingName) => {
|
||||
return thingName in state.thingDescriptions;
|
||||
},
|
||||
thingAffordanceAvailable: (state) => (thing, affordanceType, affordance) => {
|
||||
let td = state.thingDescriptions[thing];
|
||||
if (!td) {
|
||||
return false;
|
||||
}
|
||||
return affordance in td[affordanceType];
|
||||
},
|
||||
thingFormUrl:
|
||||
(state) =>
|
||||
(thing, affordanceType, affordance, op, allowUndefined = true) => {
|
||||
// Find the URL for a particular operation
|
||||
let td = state.thingDescriptions[thing];
|
||||
if (!td) {
|
||||
if (allowUndefined) return undefined;
|
||||
throw `Could not find form for ${affordanceType} ${thing}/${affordance} with op ${op}`;
|
||||
}
|
||||
let affordances = td[affordanceType];
|
||||
|
||||
if (!affordances || !(affordance in affordances)) {
|
||||
if (allowUndefined) return undefined;
|
||||
throw `Could not find form for ${affordanceType} ${thing}/${affordance} with op ${op}`;
|
||||
}
|
||||
|
||||
let href = findFormHref(affordances[affordance], op);
|
||||
if (href === undefined) {
|
||||
if (allowUndefined) return undefined;
|
||||
throw `Could not find form for ${affordanceType} ${thing}/${affordance} with op ${op}`;
|
||||
}
|
||||
// If we've found an href, prepend the `base` URL if appropriate
|
||||
if (href.startsWith("http")) return href;
|
||||
if ("base" in td) {
|
||||
let base = td.base;
|
||||
if (href.startsWith("/")) href = href.slice(1);
|
||||
if (!base.endsWith("/")) base += "/";
|
||||
return base + href;
|
||||
}
|
||||
return href;
|
||||
},
|
||||
thingPropertyUrl: (_state, getters) => (thing, property, op, allowUndefined) => {
|
||||
// Find the URL for a particular property
|
||||
return getters.thingFormUrl(thing, "properties", property, op, allowUndefined);
|
||||
},
|
||||
thingActionUrl: (_state, getters) => (thing, action, op, allowUndefined) => {
|
||||
// Find the URL for a particular action
|
||||
return getters.thingFormUrl(thing, "actions", action, op, allowUndefined);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function findFormHref(affordance, op) {
|
||||
// Find the form in the affordance that matches the given operation type
|
||||
if (affordance === undefined) return undefined;
|
||||
let forms = affordance.forms;
|
||||
let matchingForm = forms.find((f) => f.op == op || f.op.includes(op));
|
||||
if (matchingForm === undefined) return undefined;
|
||||
return matchingForm.href;
|
||||
}
|
||||
|
||||
export default wotStoreModule;
|
||||
Loading…
Add table
Add a link
Reference in a new issue