fix(mobile): Spaces tab remembers last-selected space across DM/Friends/Settings detours
Adds sticky `lastSelectedSpaceId` to useSpaceStore. Updated on every
setCurrentSpace(non-null) and on loadSpaceDetail success; NOT cleared by
setCurrentSpace(null), so the @me URL effect in AppLayout no longer wipes
the memory. Cleared only when the remembered space is actually removed
(deleteSpace / leaveSpace / removeSpace / removeInstanceSpaces / reset).
Ephemeral — URL drives initial state on reload.
MobileBottomNav.handleTab('spaces') and MobileSpacesScreen's initial
useState now resolve via `currentSpaceId ?? lastSelectedSpaceId`. The
prior Object.keys(lastChannelPerSpace)[0] fallback was wrong twice over:
first-inserted ordering locked the tab to whichever space the user
opened first in their session, and any approach reading currentSpaceId
alone breaks after a /channels/@me detour.
Also fixes a pre-existing render loop in MobileSpacesScreen's
currentSpaceId sync effect — `selectedSpaceId` removed from deps,
functional setState makes the update idempotent.
Spec: docs/systems/mobile-ui.md updates Tab Tap Behavior, MobileScreenHeader,
and LocalStorage Persistence sections.
This commit is contained in:
@@ -59,6 +59,19 @@ export interface UserViewEntry {
|
||||
interface SpaceState {
|
||||
spaces: TaggedSpace[];
|
||||
currentSpaceId: string | null;
|
||||
/**
|
||||
* Sticky memory of the most-recently-selected space. Updated on every
|
||||
* `setCurrentSpace(non-null)` and when `loadSpaceDetail` lands. Crucially, it
|
||||
* is NOT cleared by `setCurrentSpace(null)` (the @me / DMs navigation case)
|
||||
* so mobile callers can answer "which space should the Spaces tab return to
|
||||
* after a side trip through DMs?" — `currentSpaceId` is wiped on @me by
|
||||
* AppLayout's URL effect, which would otherwise force a fallback to
|
||||
* `spaces[0]`. Cleared only when the remembered space is actually removed
|
||||
* (deleteSpace / leaveSpace / removeSpaceFromState / removeInstanceSpaces /
|
||||
* reset). Ephemeral — not persisted, since URL drives initial state on
|
||||
* reload.
|
||||
*/
|
||||
lastSelectedSpaceId: string | null;
|
||||
channels: Channel[];
|
||||
categories: ChannelCategory[];
|
||||
members: MemberWithUser[];
|
||||
@@ -172,6 +185,7 @@ async function pushLayoutToOrigin(
|
||||
export const useSpaceStore = create<SpaceState>((set, get) => ({
|
||||
spaces: [],
|
||||
currentSpaceId: null,
|
||||
lastSelectedSpaceId: null,
|
||||
channels: [],
|
||||
categories: [],
|
||||
members: [],
|
||||
@@ -196,6 +210,7 @@ export const useSpaceStore = create<SpaceState>((set, get) => ({
|
||||
set({
|
||||
spaces: [],
|
||||
currentSpaceId: null,
|
||||
lastSelectedSpaceId: null,
|
||||
channels: [],
|
||||
categories: [],
|
||||
members: [],
|
||||
@@ -218,7 +233,14 @@ export const useSpaceStore = create<SpaceState>((set, get) => ({
|
||||
},
|
||||
|
||||
setSpaces: (spaces) => set({ spaces }),
|
||||
setCurrentSpace: (spaceId) => set({ currentSpaceId: spaceId }),
|
||||
setCurrentSpace: (spaceId) =>
|
||||
set((state) => ({
|
||||
currentSpaceId: spaceId,
|
||||
// Only update sticky memory when actually selecting a space. Clearing
|
||||
// currentSpaceId (e.g. on @me navigation) must NOT wipe the memory —
|
||||
// that's the whole point of this slot.
|
||||
lastSelectedSpaceId: spaceId !== null ? spaceId : state.lastSelectedSpaceId,
|
||||
})),
|
||||
setChannels: (channels) => set({ channels }),
|
||||
setCategories: (categories) => set({ categories }),
|
||||
setMembers: (members) => set({ members }),
|
||||
@@ -351,6 +373,7 @@ export const useSpaceStore = create<SpaceState>((set, get) => ({
|
||||
set({
|
||||
loadingSpaceId: null,
|
||||
currentSpaceId: spaceId,
|
||||
lastSelectedSpaceId: spaceId,
|
||||
channels: detail.channels.sort((a, b) => a.position - b.position),
|
||||
categories: (detail.categories || []).sort((a, b) => a.position - b.position),
|
||||
members: detail.members,
|
||||
@@ -392,6 +415,8 @@ export const useSpaceStore = create<SpaceState>((set, get) => ({
|
||||
set((state) => ({
|
||||
spaces: state.spaces.filter(s => s.id !== spaceId),
|
||||
currentSpaceId: state.currentSpaceId === spaceId ? null : state.currentSpaceId,
|
||||
lastSelectedSpaceId:
|
||||
state.lastSelectedSpaceId === spaceId ? null : state.lastSelectedSpaceId,
|
||||
}));
|
||||
},
|
||||
|
||||
@@ -405,6 +430,8 @@ export const useSpaceStore = create<SpaceState>((set, get) => ({
|
||||
set((state) => ({
|
||||
spaces: state.spaces.filter(s => s.id !== spaceId),
|
||||
currentSpaceId: state.currentSpaceId === spaceId ? null : state.currentSpaceId,
|
||||
lastSelectedSpaceId:
|
||||
state.lastSelectedSpaceId === spaceId ? null : state.lastSelectedSpaceId,
|
||||
}));
|
||||
},
|
||||
|
||||
@@ -553,6 +580,8 @@ export const useSpaceStore = create<SpaceState>((set, get) => ({
|
||||
return {
|
||||
spaces: state.spaces.filter(s => s.id !== spaceId),
|
||||
currentSpaceId: state.currentSpaceId === spaceId ? null : state.currentSpaceId,
|
||||
lastSelectedSpaceId:
|
||||
state.lastSelectedSpaceId === spaceId ? null : state.lastSelectedSpaceId,
|
||||
channelToSpaceMap,
|
||||
channelPermissions,
|
||||
channelOriginMap,
|
||||
@@ -1022,6 +1051,9 @@ export const useSpaceStore = create<SpaceState>((set, get) => ({
|
||||
currentSpaceId: remainingSpaces.find(s => s.id === state.currentSpaceId)
|
||||
? state.currentSpaceId
|
||||
: null,
|
||||
lastSelectedSpaceId: remainingSpaces.find(s => s.id === state.lastSelectedSpaceId)
|
||||
? state.lastSelectedSpaceId
|
||||
: null,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user