mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-06 22:46:06 +02:00
242 lines
6.2 KiB
JavaScript
242 lines
6.2 KiB
JavaScript
import { defineStore } from 'pinia';
|
|
import { computed, ref } from 'vue';
|
|
|
|
import configRepository from '../services/config';
|
|
import {
|
|
DASHBOARD_NAV_KEY_PREFIX,
|
|
DASHBOARD_STORAGE_KEY,
|
|
DEFAULT_DASHBOARD_ICON
|
|
} from '../shared/constants/dashboard';
|
|
|
|
function clonePanel(panel) {
|
|
if (typeof panel === 'string' && panel) {
|
|
return panel;
|
|
}
|
|
if (
|
|
panel &&
|
|
typeof panel === 'object' &&
|
|
typeof panel.key === 'string' &&
|
|
panel.key
|
|
) {
|
|
return {
|
|
key: panel.key,
|
|
config:
|
|
panel.config && typeof panel.config === 'object'
|
|
? JSON.parse(JSON.stringify(panel.config))
|
|
: {}
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function cloneRows(rows) {
|
|
if (!Array.isArray(rows)) {
|
|
return [];
|
|
}
|
|
return rows
|
|
.map((row) => {
|
|
const panels = Array.isArray(row?.panels)
|
|
? row.panels.slice(0, 2).map(clonePanel)
|
|
: [];
|
|
if (!panels.length) {
|
|
return null;
|
|
}
|
|
const direction =
|
|
row?.direction === 'vertical' ? 'vertical' : 'horizontal';
|
|
return { panels, direction };
|
|
})
|
|
.filter(Boolean);
|
|
}
|
|
|
|
function sanitizeDashboard(dashboard) {
|
|
if (!dashboard || typeof dashboard !== 'object') {
|
|
return null;
|
|
}
|
|
|
|
const id =
|
|
typeof dashboard.id === 'string' && dashboard.id ? dashboard.id : null;
|
|
if (!id) {
|
|
return null;
|
|
}
|
|
|
|
const name =
|
|
typeof dashboard.name === 'string' && dashboard.name.trim()
|
|
? dashboard.name.trim()
|
|
: 'Dashboard';
|
|
|
|
const icon =
|
|
typeof dashboard.icon === 'string' && dashboard.icon.trim()
|
|
? dashboard.icon.trim()
|
|
: DEFAULT_DASHBOARD_ICON;
|
|
|
|
return {
|
|
id,
|
|
name,
|
|
icon,
|
|
rows: cloneRows(dashboard.rows)
|
|
};
|
|
}
|
|
|
|
export const useDashboardStore = defineStore('dashboard', () => {
|
|
const dashboards = ref([]);
|
|
const loaded = ref(false);
|
|
const editingDashboardId = ref(null);
|
|
|
|
const dashboardNavKeys = computed(
|
|
() =>
|
|
new Set(
|
|
dashboards.value.map(
|
|
(dashboard) => `${DASHBOARD_NAV_KEY_PREFIX}${dashboard.id}`
|
|
)
|
|
)
|
|
);
|
|
|
|
async function loadDashboards() {
|
|
try {
|
|
const stored = await configRepository.getString(
|
|
DASHBOARD_STORAGE_KEY,
|
|
null
|
|
);
|
|
if (!stored) {
|
|
dashboards.value = [];
|
|
loaded.value = true;
|
|
return;
|
|
}
|
|
|
|
const parsed = JSON.parse(stored);
|
|
const source = Array.isArray(parsed?.dashboards)
|
|
? parsed.dashboards
|
|
: [];
|
|
dashboards.value = source.map(sanitizeDashboard).filter(Boolean);
|
|
} catch {
|
|
dashboards.value = [];
|
|
} finally {
|
|
loaded.value = true;
|
|
}
|
|
}
|
|
|
|
async function saveDashboards() {
|
|
await configRepository.setString(
|
|
DASHBOARD_STORAGE_KEY,
|
|
JSON.stringify({ dashboards: dashboards.value })
|
|
);
|
|
}
|
|
|
|
function ensureLoaded() {
|
|
if (!loaded.value) {
|
|
loadDashboards();
|
|
}
|
|
}
|
|
|
|
function getDashboard(id) {
|
|
return (
|
|
dashboards.value.find((dashboard) => dashboard.id === id) || null
|
|
);
|
|
}
|
|
|
|
function generateDashboardId() {
|
|
if (
|
|
typeof crypto !== 'undefined' &&
|
|
typeof crypto.randomUUID === 'function'
|
|
) {
|
|
return crypto.randomUUID();
|
|
}
|
|
return `dashboard-${Date.now()}-${Math.random().toString().slice(2, 8)}`;
|
|
}
|
|
|
|
function generateNextDashboardName(baseName = 'Dashboard') {
|
|
const existingNames = new Set(dashboards.value.map((d) => d.name));
|
|
if (!existingNames.has(baseName)) {
|
|
return baseName;
|
|
}
|
|
let n = 1;
|
|
while (existingNames.has(`${baseName} ${n}`)) {
|
|
n++;
|
|
}
|
|
return `${baseName} ${n}`;
|
|
}
|
|
|
|
async function createDashboard(baseName = 'Dashboard') {
|
|
const id = generateDashboardId();
|
|
const name = generateNextDashboardName(baseName);
|
|
const dashboard = {
|
|
id,
|
|
name,
|
|
icon: DEFAULT_DASHBOARD_ICON,
|
|
rows: []
|
|
};
|
|
dashboards.value.push(dashboard);
|
|
await saveDashboards();
|
|
return dashboard;
|
|
}
|
|
|
|
async function updateDashboard(id, updates) {
|
|
const index = dashboards.value.findIndex(
|
|
(dashboard) => dashboard.id === id
|
|
);
|
|
if (index < 0) {
|
|
return;
|
|
}
|
|
|
|
const next = sanitizeDashboard({
|
|
...dashboards.value[index],
|
|
...updates,
|
|
id
|
|
});
|
|
|
|
if (!next) {
|
|
return;
|
|
}
|
|
|
|
dashboards.value[index] = next;
|
|
await saveDashboards();
|
|
}
|
|
|
|
async function deleteDashboard(id) {
|
|
dashboards.value = dashboards.value.filter(
|
|
(dashboard) => dashboard.id !== id
|
|
);
|
|
if (editingDashboardId.value === id) {
|
|
editingDashboardId.value = null;
|
|
}
|
|
await saveDashboards();
|
|
}
|
|
|
|
function setEditingDashboardId(id) {
|
|
editingDashboardId.value = id || null;
|
|
}
|
|
|
|
function clearEditingDashboardId() {
|
|
editingDashboardId.value = null;
|
|
}
|
|
|
|
function getDashboardNavDefinitions() {
|
|
return dashboards.value.map((dashboard) => ({
|
|
key: `${DASHBOARD_NAV_KEY_PREFIX}${dashboard.id}`,
|
|
icon: dashboard.icon || DEFAULT_DASHBOARD_ICON,
|
|
tooltip: dashboard.name,
|
|
labelKey: dashboard.name,
|
|
routeName: 'dashboard',
|
|
routeParams: { id: dashboard.id },
|
|
isDashboard: true
|
|
}));
|
|
}
|
|
|
|
return {
|
|
dashboards,
|
|
loaded,
|
|
editingDashboardId,
|
|
dashboardNavKeys,
|
|
ensureLoaded,
|
|
loadDashboards,
|
|
saveDashboards,
|
|
createDashboard,
|
|
getDashboard,
|
|
updateDashboard,
|
|
deleteDashboard,
|
|
getDashboardNavDefinitions,
|
|
setEditingDashboardId,
|
|
clearEditingDashboardId
|
|
};
|
|
});
|