From 4649754873e3deaf4d4e9216eda0bce38d622ffd Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Mon, 4 Aug 2025 20:56:55 +0200 Subject: [PATCH 1/7] script uuid --- .../Data/Playground/Playground.react.js | 40 ++++++++----------- src/lib/ScriptManager.js | 26 ++++++------ 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/src/dashboard/Data/Playground/Playground.react.js b/src/dashboard/Data/Playground/Playground.react.js index 8ba356101..69bbc54b3 100644 --- a/src/dashboard/Data/Playground/Playground.react.js +++ b/src/dashboard/Data/Playground/Playground.react.js @@ -176,11 +176,11 @@ export default function Playground() { const containerRef = useRef(null); // Tab management state + const initialTabId = useMemo(() => crypto.randomUUID(), []); const [tabs, setTabs] = useState([ - { id: 1, name: 'Tab 1', code: DEFAULT_CODE_EDITOR_VALUE } + { id: initialTabId, name: 'Tab 1', code: DEFAULT_CODE_EDITOR_VALUE } ]); - const [activeTabId, setActiveTabId] = useState(1); - const [nextTabId, setNextTabId] = useState(2); + const [activeTabId, setActiveTabId] = useState(initialTabId); const [renamingTabId, setRenamingTabId] = useState(null); const [renamingValue, setRenamingValue] = useState(''); const [savedTabs, setSavedTabs] = useState([]); // All saved tabs including closed ones @@ -235,8 +235,6 @@ export default function Playground() { if (tabsToOpen.length > 0) { setTabs(tabsToOpen); - const maxId = Math.max(...allScripts.map(tab => tab.id)); - setNextTabId(maxId + 1); // Set active tab to the first one setActiveTabId(tabsToOpen[0].id); @@ -249,8 +247,6 @@ export default function Playground() { const firstScript = { ...allScripts[0], order: 0 }; setTabs([firstScript]); setActiveTabId(firstScript.id); - const maxId = Math.max(...allScripts.map(tab => tab.id)); - setNextTabId(maxId + 1); // Save it as open await scriptManagerRef.current.openScript(context.applicationId, firstScript.id, 0); @@ -258,17 +254,17 @@ export default function Playground() { setSavedTabs(allScripts.filter(script => script.saved !== false)); } else { // Fallback to default tab if no scripts exist - setTabs([{ id: 1, name: 'Tab 1', code: DEFAULT_CODE_EDITOR_VALUE, order: 0 }]); - setActiveTabId(1); - setNextTabId(2); + const defaultTabId = crypto.randomUUID(); + setTabs([{ id: defaultTabId, name: 'Tab 1', code: DEFAULT_CODE_EDITOR_VALUE, order: 0 }]); + setActiveTabId(defaultTabId); } } } catch (error) { console.warn('Failed to load scripts via ScriptManager:', error); // Fallback to default tab if loading fails - setTabs([{ id: 1, name: 'Tab 1', code: DEFAULT_CODE_EDITOR_VALUE, order: 0 }]); - setActiveTabId(1); - setNextTabId(2); + const defaultTabId = crypto.randomUUID(); + setTabs([{ id: defaultTabId, name: 'Tab 1', code: DEFAULT_CODE_EDITOR_VALUE, order: 0 }]); + setActiveTabId(defaultTabId); } // Load other data from localStorage @@ -317,18 +313,19 @@ export default function Playground() { // Tab management functions const createNewTab = useCallback(() => { + const newTabId = crypto.randomUUID(); + const tabCount = tabs.length + 1; const newTab = { - id: nextTabId, - name: `Tab ${nextTabId}`, + id: newTabId, + name: `Tab ${tabCount}`, code: '', // Start with empty code instead of default value saved: false, // Mark as unsaved initially order: tabs.length // Assign order as the last position }; const updatedTabs = [...tabs, newTab]; setTabs(updatedTabs); - setActiveTabId(nextTabId); - setNextTabId(nextTabId + 1); - }, [tabs, nextTabId]); + setActiveTabId(newTabId); + }, [tabs]); const closeTab = useCallback(async (tabId) => { if (tabs.length <= 1) { @@ -591,11 +588,6 @@ export default function Playground() { setTabs(updatedTabs); setActiveTabId(savedTab.id); - // Update nextTabId if necessary - if (savedTab.id >= nextTabId) { - setNextTabId(savedTab.id + 1); - } - // Save the open state through ScriptManager if (scriptManagerRef.current && context?.applicationId) { try { @@ -604,7 +596,7 @@ export default function Playground() { console.error('Failed to open script:', error); } } - }, [tabs, nextTabId, switchTab, context?.applicationId]); + }, [tabs, switchTab, context?.applicationId]); // Focus input when starting to rename useEffect(() => { diff --git a/src/lib/ScriptManager.js b/src/lib/ScriptManager.js index 4bbb4bcb6..0e7872f60 100644 --- a/src/lib/ScriptManager.js +++ b/src/lib/ScriptManager.js @@ -233,6 +233,14 @@ export default class ScriptManager { return this.serverStorage.isServerConfigEnabled(); } + /** + * Generates a unique ID for a new script/tab + * @returns {string} A UUID string + */ + generateScriptId() { + return this._generateScriptId(); + } + /** * Gets scripts from server storage * @private @@ -248,7 +256,7 @@ export default class ScriptManager { const scriptId = key.replace('console.js.script.', ''); scripts.push({ - id: parseInt(scriptId, 10), + id: scriptId, // Keep as string (UUID) instead of parsing as integer ...config }); } @@ -338,7 +346,7 @@ export default class ScriptManager { if (legacyCode && legacyCode.trim()) { // Create a script with the legacy code, marked as unsaved const script = { - id: this._generateScriptId({ name: 'Legacy Script', code: legacyCode }), + id: this._generateScriptId(), name: 'Legacy Script', code: legacyCode, saved: false, // Mark as unsaved so user can choose to save it @@ -377,19 +385,11 @@ export default class ScriptManager { } /** - * Generates a unique ID for a script + * Generates a unique ID for a script using UUID * @private */ - _generateScriptId(script) { - // Use a hash of the script name and code as a fallback ID - const str = `${script.name || 'script'}-${script.code || ''}`; - let hash = 0; - for (let i = 0; i < str.length; i++) { - const char = str.charCodeAt(i); - hash = ((hash << 5) - hash) + char; - hash = hash & hash; // Convert to 32bit integer - } - return Math.abs(hash); + _generateScriptId() { + return crypto.randomUUID(); } } From c2643d3b2d880ce4ad7d3c2d1c12fe55d98b4318 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Mon, 4 Aug 2025 21:29:47 +0200 Subject: [PATCH 2/7] delete local js scripts --- .../DashboardSettings.react.js | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js b/src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js index 4e2cd7b0f..c7d8f11aa 100644 --- a/src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js +++ b/src/dashboard/Settings/DashboardSettings/DashboardSettings.react.js @@ -18,6 +18,7 @@ import Notification from 'dashboard/Data/Browser/Notification.react'; import * as ColumnPreferences from 'lib/ColumnPreferences'; import * as ClassPreferences from 'lib/ClassPreferences'; import ViewPreferencesManager from 'lib/ViewPreferencesManager'; +import ScriptManager from 'lib/ScriptManager'; import bcrypt from 'bcryptjs'; import * as OTPAuth from 'otpauth'; import QRCode from 'qrcode'; @@ -28,6 +29,7 @@ export default class DashboardSettings extends DashboardView { this.section = 'App Settings'; this.subsection = 'Dashboard Configuration'; this.viewPreferencesManager = null; + this.scriptManager = null; this.state = { createUserInput: false, @@ -57,12 +59,13 @@ export default class DashboardSettings extends DashboardView { } componentDidMount() { - this.initializeViewPreferencesManager(); + this.initializeManagers(); } - initializeViewPreferencesManager() { + initializeManagers() { if (this.context) { this.viewPreferencesManager = new ViewPreferencesManager(this.context); + this.scriptManager = new ScriptManager(this.context); this.loadStoragePreference(); } } @@ -123,11 +126,18 @@ export default class DashboardSettings extends DashboardView { return; } - const success = this.viewPreferencesManager.deleteFromBrowser(this.context.applicationId); - if (success) { - this.showNote('Successfully deleted views from browser storage.'); + if (!this.scriptManager) { + this.showNote('ScriptManager not initialized'); + return; + } + + const viewsSuccess = this.viewPreferencesManager.deleteFromBrowser(this.context.applicationId); + const scriptsSuccess = this.scriptManager.deleteFromBrowser(this.context.applicationId); + + if (viewsSuccess && scriptsSuccess) { + this.showNote('Successfully deleted dashboard settings from browser storage.'); } else { - this.showNote('Failed to delete views from browser storage.'); + this.showNote('Failed to delete all dashboard settings from browser storage.'); } } @@ -461,13 +471,16 @@ export default class DashboardSettings extends DashboardView { } /> - {this.viewPreferencesManager && this.viewPreferencesManager.isServerConfigEnabled() && ( + {this.viewPreferencesManager && this.scriptManager && this.viewPreferencesManager.isServerConfigEnabled() && (
+
+ Storing dashboard settings on the server rather than locally in the browser storage makes the settings available across devices and browsers. It also prevents them from getting lost when resetting the browser website data. Settings that can be stored on the server are currently Views and JS Console scripts. +
} input={ @@ -487,7 +500,7 @@ export default class DashboardSettings extends DashboardView { label={