dheeraj12347 commented on issue #37211:
URL: https://github.com/apache/beam/issues/37211#issuecomment-3706913240

   Hi, I would like to work on this issue and have a concrete implementation 
plan for the persistence layer.
   
   Short intent
   I plan to add a small, self‑contained frontend persistence layer using 
localStorage to keep:
   
   Current Monaco editor content
   
   Selected SDK (Java / Python / Go / SCIO)
   
   Whether the user is on an example or custom code
   
   This will be implemented fully on the client, with debounced writes and 
defensive parsing so that corrupted data never breaks the UI.
   
   Proposed implementation plan (file‑by‑file)
   Paths are approximate and I’ll adjust to the actual structure in 
playground/frontend.
   
   Editor state store (e.g. playground/frontend/src/app/store/editorStore.ts)
   
   Extend the editor store shape with optional persisted fields, for example:
   
   persistedContent: string | null
   
   persistedSdk: SdkType | null
   
   persistedMode: 'example' | 'custom' | null
   
   On store initialization, hydrate these from localStorage (using a helper 
described below):
   
   Call getPersistedPlaygroundState() once, and if present, seed the initial 
editor text, SDK, and mode before rendering to avoid flicker.
   
   Add actions such as:
   
   setEditorContentWithPersist(content: string)
   
   setSdkWithPersist(sdk: SdkType)
   
   setModeWithPersist(mode: 'example' | 'custom')
   Each of these updates in‑memory state and schedules a debounced write to 
localStorage.
   
   Persistence helper (new file, e.g. 
playground/frontend/src/app/utils/localStoragePersistence.ts)
   
   Define a PersistedState interface, something like:
   
   ts
   interface PersistedState {
     content: string;
     sdk: SdkType;
     mode: 'example' | 'custom';
   }
   Use a single namespaced key for the JSON blob, e.g. 
beam.playground.session.v1.
   
   Export helper functions:
   
   getPersistedPlaygroundState(): PersistedState | null
   
   try/catch around localStorage.getItem and JSON.parse.
   
   If parsing fails or the shape is invalid, return null without throwing.
   
   setPersistedPlaygroundState(state: PersistedState): void
   
   try/catch around JSON.stringify and localStorage.setItem.
   
   No‑op on errors (private mode / storage disabled).
   
   Implement a debounce wrapper, e.g. createDebouncedSetter(delayMs = 500) that 
returns a debounced version of setPersistedPlaygroundState using setTimeout (or 
reuse an existing debounce utility if the project already has one).
   
   The editor store will hold a single debounced function instance and call it 
on relevant state changes.
   
   Monaco editor component (e.g. 
playground/frontend/src/app/components/Editor/MonacoEditor.tsx)
   
   Ensure the editor’s initial value comes from the hydrated store state, which 
has already read from localStorage.
   
   Wire the onChange callback to call setEditorContentWithPersist(newValue) 
instead of a non‑persisting setter.
   
   Confirm that the editor doesn’t briefly show an empty value before hydration 
(initial store state should be computed synchronously from localStorage).
   
   SDK selector component (e.g. 
playground/frontend/src/app/components/Header/LanguageSelector.tsx)
   
   When the user selects a new SDK, call setSdkWithPersist(selectedSdk).
   
   On initialization, read the SDK from the store (which in turn was hydrated 
from localStorage); if no persisted value exists, keep the current default 
behavior.
   
   Example/custom toggle (e.g. 
playground/frontend/src/app/components/Examples/ExampleToggle.tsx)
   
   Persist whether the user is viewing an example vs custom code via 
setModeWithPersist('example' | 'custom').
   
   On load:
   
   If mode === 'custom' and there is persisted content, keep the user’s code 
instead of overwriting it with an example.
   
   If no persisted state exists, keep the current behavior (load default 
examples).
   
   Tests (new file, e.g. 
playground/frontend/src/app/__tests__/localStoragePersistence.test.ts)
   
   Add unit tests around the helper functions, for example:
   
   Successful round‑trip of a PersistedState object via localStorage.
   
   Handling of corrupted JSON in localStorage (should not throw, should return 
null).
   
   Debounce behavior: multiple rapid updates result in a single final write 
once the delay elapses (using fake timers / mocks according to the existing 
test setup).
   
   Mock window.localStorage and timers so tests are deterministic.
   
   Docs (optional; e.g. playground/TASKS.md or playground/frontend/README.md)
   
   Add a short note that:
   
   “Playground persists your editor content and SDK selection in your browser’s 
local storage. Clearing site data will reset the Playground session.”
   
   Open questions / confirmation
   Before I start the PR, I would appreciate feedback on:
   
   Key strategy: Is a single key like beam.playground.session.v1 acceptable, or 
would you prefer separate keys per field (content / sdk / mode)?
   
   Debounce interval: Is a 500 ms debounce for writes to localStorage 
reasonable, or would you prefer 1000 ms?
   
   File placement: Do you have a preferred place for the persistence helper 
(e.g. an existing utilities directory / hook) so I align with the current 
frontend structure?
   
   If this direction looks good, I will start implementation and open a PR 
referencing this issue.
   
   Thanks!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to