mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
feat(frontend): sync active workspace into ?workspace= URL param (stage 1)
Make the logged layout the single place that keeps the URL's ?workspace= param in sync with the active workspace store, in both directions: URL -> store on navigation (existing, now gated) and store -> URL via replaceState (new). Excludes auth/selection/oauth routes via a new workspaceParamAllowed predicate. replaceState avoids history pollution; guards keep the sync idempotent and loop-free. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3345837574
commit
07231fec7d
@@ -0,0 +1,20 @@
|
||||
// Routes that must never carry the `?workspace=` query param.
|
||||
//
|
||||
// `/user/*` covers auth, workspace selection, onboarding, invites and
|
||||
// instance-level settings — none of which are scoped to an active workspace
|
||||
// (and several run before one is chosen). `/oauth/*` covers OAuth callbacks
|
||||
// and `mcp_authorize`, which carries its own `workspace_id`.
|
||||
//
|
||||
// Public, path-scoped routes (`/a`, `/public`, `/approve`,
|
||||
// `/apps_raw/[workspace]`) live outside the logged layout, so this predicate is
|
||||
// never consulted for them — the workspace already lives in their path.
|
||||
const WORKSPACE_PARAM_EXCLUDED_PREFIXES = ['/user/', '/oauth/']
|
||||
|
||||
/**
|
||||
* Whether the `?workspace=` query param should be kept in sync with the active
|
||||
* workspace store on the given route. Drives both directions of the sync in the
|
||||
* logged layout: URL → store on navigation, and store → URL via replaceState.
|
||||
*/
|
||||
export function workspaceParamAllowed(pathname: string): boolean {
|
||||
return !WORKSPACE_PARAM_EXCLUDED_PREFIXES.some((prefix) => pathname.startsWith(prefix))
|
||||
}
|
||||
@@ -36,8 +36,9 @@
|
||||
globalForkModal
|
||||
} from '$lib/stores'
|
||||
import CenteredModal from '$lib/components/CenteredModal.svelte'
|
||||
import { afterNavigate, beforeNavigate } from '$app/navigation'
|
||||
import { afterNavigate, beforeNavigate, replaceState } from '$app/navigation'
|
||||
import { goto } from '$lib/navigation'
|
||||
import { workspaceParamAllowed } from '$lib/workspaceParam'
|
||||
import UserSettings from '$lib/components/UserSettings.svelte'
|
||||
import SuperadminSettings from '$lib/components/SuperadminSettings.svelte'
|
||||
import WindmillIcon from '$lib/components/icons/WindmillIcon.svelte'
|
||||
@@ -113,9 +114,11 @@
|
||||
}
|
||||
|
||||
function onQueryChange() {
|
||||
let queryWorkspace = page.url.searchParams.get('workspace')
|
||||
if (queryWorkspace) {
|
||||
$workspaceStore = queryWorkspace
|
||||
if (workspaceParamAllowed(page.url.pathname)) {
|
||||
const queryWorkspace = page.url.searchParams.get('workspace')
|
||||
if (queryWorkspace && queryWorkspace !== $workspaceStore) {
|
||||
$workspaceStore = queryWorkspace
|
||||
}
|
||||
}
|
||||
|
||||
menuHidden =
|
||||
@@ -123,6 +126,25 @@
|
||||
page.url.pathname.startsWith('/oauth/callback/')
|
||||
}
|
||||
|
||||
// Reflect the active workspace into the URL as `?workspace=<id>` so links are
|
||||
// workspace-explicit and shareable, and so separate tabs stay independent.
|
||||
// Uses replaceState to avoid polluting browser history on every navigation.
|
||||
// Guarded to be idempotent: it only writes when the param actually differs,
|
||||
// which also prevents a ping-pong with the URL → store sync in onQueryChange.
|
||||
function syncWorkspaceToUrl() {
|
||||
if (!BROWSER) return
|
||||
const ws = $workspaceStore
|
||||
if (!ws || !workspaceParamAllowed(page.url.pathname)) return
|
||||
if (page.url.searchParams.get('workspace') === ws) return
|
||||
try {
|
||||
const url = new URL(page.url)
|
||||
url.searchParams.set('workspace', ws)
|
||||
replaceState(url, page.state)
|
||||
} catch (e) {
|
||||
console.warn('Could not sync workspace to URL', e)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateUserStore(workspace: string | undefined) {
|
||||
if (workspace) {
|
||||
try {
|
||||
@@ -413,6 +435,13 @@
|
||||
$effect(() => {
|
||||
page.url && untrack(() => onQueryChange())
|
||||
})
|
||||
$effect(() => {
|
||||
// Re-run on both store changes (workspace switch) and navigation (route
|
||||
// entered without the param). Both reads register as dependencies.
|
||||
$workspaceStore
|
||||
page.url
|
||||
untrack(() => syncWorkspaceToUrl())
|
||||
})
|
||||
$effect(() => {
|
||||
$workspaceStore
|
||||
untrack(() => updateUserStore($workspaceStore))
|
||||
|
||||
Reference in New Issue
Block a user