Skip to content

Commit 092eef4

Browse files
authored
Merge pull request #32 from iamriajul/vk/46d8-org-selection-sh
feat: scope org selection persistence per authenticated user (Vibe Kanban)
2 parents e596c46 + 7bb202f commit 092eef4

2 files changed

Lines changed: 97 additions & 0 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
From 6e88d05061d78b85121c3fc920570d8e7becf404 Mon Sep 17 00:00:00 2001
2+
From: Riajul Islam <riajul@kahf.co>
3+
Date: Tue, 21 Apr 2026 15:11:38 +0000
4+
Subject: [PATCH] fix: scope org selection persistence per authenticated user
5+
6+
---
7+
.../shared/hooks/useOrganizationSelection.ts | 13 ++++++++++-
8+
.../src/shared/stores/useOrganizationStore.ts | 22 ++++++++++++++++++-
9+
2 files changed, 33 insertions(+), 2 deletions(-)
10+
11+
diff --git a/packages/web-core/src/shared/hooks/useOrganizationSelection.ts b/packages/web-core/src/shared/hooks/useOrganizationSelection.ts
12+
index 9e0b5274f..eaed39adb 100644
13+
--- a/packages/web-core/src/shared/hooks/useOrganizationSelection.ts
14+
+++ b/packages/web-core/src/shared/hooks/useOrganizationSelection.ts
15+
@@ -3,7 +3,11 @@ import type {
16+
OrganizationWithRole,
17+
ListOrganizationsResponse,
18+
} from 'shared/types';
19+
-import { useOrganizationStore } from '@/shared/stores/useOrganizationStore';
20+
+import { useAuth } from '@/shared/hooks/auth/useAuth';
21+
+import {
22+
+ hydrateOrganizationSelectionForUser,
23+
+ useOrganizationStore,
24+
+} from '@/shared/stores/useOrganizationStore';
25+
26+
interface UseOrganizationSelectionOptions {
27+
organizations: ListOrganizationsResponse | undefined;
28+
@@ -13,6 +17,7 @@ interface UseOrganizationSelectionOptions {
29+
export function useOrganizationSelection(
30+
options: UseOrganizationSelectionOptions
31+
) {
32+
+ const { isLoaded, userId } = useAuth();
33+
const { organizations, onSelectionChange } = options;
34+
const selectedOrgId = useOrganizationStore((s) => s.selectedOrgId);
35+
const setSelectedOrgId = useOrganizationStore((s) => s.setSelectedOrgId);
36+
@@ -22,6 +27,12 @@ export function useOrganizationSelection(
37+
[organizations]
38+
);
39+
40+
+ // Scope persisted selection by signed-in user to avoid cross-user leakage.
41+
+ useEffect(() => {
42+
+ if (!isLoaded) return;
43+
+ hydrateOrganizationSelectionForUser(userId);
44+
+ }, [isLoaded, userId]);
45+
+
46+
// Default to first available organization if none selected or selection is invalid
47+
useEffect(() => {
48+
if (orgList.length === 0) return;
49+
diff --git a/packages/web-core/src/shared/stores/useOrganizationStore.ts b/packages/web-core/src/shared/stores/useOrganizationStore.ts
50+
index 893a242de..f537a96ea 100644
51+
--- a/packages/web-core/src/shared/stores/useOrganizationStore.ts
52+
+++ b/packages/web-core/src/shared/stores/useOrganizationStore.ts
53+
@@ -8,6 +8,11 @@ type State = {
54+
clearSelectedOrgId: () => void;
55+
};
56+
57+
+const ORGANIZATION_SELECTION_KEY_PREFIX = 'organization-selection';
58+
+const SIGNED_OUT_SCOPE = 'signed-out';
59+
+
60+
+let activePersistKey: string | null = null;
61+
+
62+
export const useOrganizationStore = create<State>()(
63+
persist(
64+
(set) => ({
65+
@@ -16,12 +21,27 @@ export const useOrganizationStore = create<State>()(
66+
clearSelectedOrgId: () => set({ selectedOrgId: null }),
67+
}),
68+
{
69+
- name: 'organization-selection',
70+
+ name: ORGANIZATION_SELECTION_KEY_PREFIX,
71+
partialize: (state) => ({ selectedOrgId: state.selectedOrgId }),
72+
+ skipHydration: true,
73+
}
74+
)
75+
);
76+
77+
+export function hydrateOrganizationSelectionForUser(userId: string | null) {
78+
+ const userScope = userId ?? SIGNED_OUT_SCOPE;
79+
+ const nextPersistKey = `${ORGANIZATION_SELECTION_KEY_PREFIX}:${userScope}`;
80+
+
81+
+ if (activePersistKey === nextPersistKey) {
82+
+ return;
83+
+ }
84+
+
85+
+ activePersistKey = nextPersistKey;
86+
+ useOrganizationStore.persist.setOptions({ name: nextPersistKey });
87+
+ useOrganizationStore.setState({ selectedOrgId: null });
88+
+ void useOrganizationStore.persist.rehydrate();
89+
+}
90+
+
91+
// Sync org store changes into the UI preferences store for server persistence
92+
useOrganizationStore.subscribe((state) => {
93+
useUiPreferencesStore.getState().setSelectedOrgId(state.selectedOrgId);
94+
--
95+
2.50.1
96+

patches/series

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@
1818
0017-fix-add-30s-WebSocket-ping-keepalive.patch
1919
0018-feat-allowed-email-domains-restriction.patch
2020
0019-feat-support-env-configured-editor-onboarding.patch
21+
0020-fix-scope-org-selection-persistence-per-authenticated-user.patch

0 commit comments

Comments
 (0)