96 lines
3.6 KiB
TypeScript
96 lines
3.6 KiB
TypeScript
// Bi-directional mapping between the mobile workspaces screen's local view model
|
|
// and the desktop's shared PersistedUIState (read/written via the ui.get/ui.set
|
|
// RPCs). Keeping these settings in the same global store is what lets a grouping
|
|
// or filter change on the phone show up on desktop and vice-versa.
|
|
|
|
import type { WorkspaceStatusDefinition } from '../../../src/shared/types'
|
|
import { coerceMobileWorkspaceStatuses } from './mobile-workspace-statuses'
|
|
|
|
export type MobileGroupMode = 'none' | 'workspaceStatus' | 'repo' | 'prStatus'
|
|
// Desktop sort adds 'manual'; mobile renders it but sorts by server order.
|
|
export type MobileSortMode = 'smart' | 'name' | 'recent' | 'repo' | 'manual'
|
|
|
|
// Desktop PersistedUIState fields this screen syncs (a structural subset).
|
|
export type WorkspaceViewSettings = {
|
|
groupBy?: 'none' | 'workspace-status' | 'repo' | 'pr-status'
|
|
sortBy?: 'name' | 'smart' | 'recent' | 'repo' | 'manual'
|
|
hideSleepingWorkspaces?: boolean
|
|
hideDefaultBranchWorkspace?: boolean
|
|
alwaysShowDefaultBranchWorkspace?: boolean
|
|
filterRepoIds?: string[]
|
|
collapsedGroups?: string[]
|
|
workspaceStatuses?: WorkspaceStatusDefinition[]
|
|
}
|
|
|
|
const GROUP_TO_DESKTOP: Record<MobileGroupMode, NonNullable<WorkspaceViewSettings['groupBy']>> = {
|
|
none: 'none',
|
|
workspaceStatus: 'workspace-status',
|
|
repo: 'repo',
|
|
prStatus: 'pr-status'
|
|
}
|
|
|
|
const GROUP_FROM_DESKTOP: Record<NonNullable<WorkspaceViewSettings['groupBy']>, MobileGroupMode> = {
|
|
none: 'none',
|
|
'workspace-status': 'workspaceStatus',
|
|
repo: 'repo',
|
|
'pr-status': 'prStatus'
|
|
}
|
|
|
|
const SORT_VALUES: readonly MobileSortMode[] = ['smart', 'name', 'recent', 'repo', 'manual']
|
|
|
|
export function groupModeToDesktop(
|
|
mode: MobileGroupMode
|
|
): NonNullable<WorkspaceViewSettings['groupBy']> {
|
|
return GROUP_TO_DESKTOP[mode]
|
|
}
|
|
|
|
export function groupModeFromDesktop(
|
|
groupBy: WorkspaceViewSettings['groupBy']
|
|
): MobileGroupMode | null {
|
|
return groupBy ? (GROUP_FROM_DESKTOP[groupBy] ?? null) : null
|
|
}
|
|
|
|
export function sortModeFromDesktop(
|
|
sortBy: WorkspaceViewSettings['sortBy']
|
|
): MobileSortMode | null {
|
|
return sortBy && SORT_VALUES.includes(sortBy) ? sortBy : null
|
|
}
|
|
|
|
export type MobileViewState = {
|
|
groupMode: MobileGroupMode
|
|
sortMode: MobileSortMode
|
|
hideSleeping: boolean
|
|
hideDefaultBranch: boolean
|
|
alwaysShowDefaultBranch: boolean
|
|
filterRepoIds: string[]
|
|
collapsedGroups: string[]
|
|
workspaceStatuses: readonly WorkspaceStatusDefinition[]
|
|
}
|
|
|
|
// Apply a desktop PersistedUIState onto the local view state, leaving any field
|
|
// the desktop hasn't set untouched (so a partial ui.get doesn't clobber).
|
|
export function applyDesktopViewSettings(
|
|
current: MobileViewState,
|
|
settings: WorkspaceViewSettings
|
|
): MobileViewState {
|
|
const groupMode = groupModeFromDesktop(settings.groupBy)
|
|
const sortMode = sortModeFromDesktop(settings.sortBy)
|
|
// Why: a partially hydrated desktop settings payload may carry an empty
|
|
// status catalog; mobile must keep renderable groups instead of hiding rows.
|
|
const workspaceStatuses = settings.workspaceStatuses
|
|
? coerceMobileWorkspaceStatuses(settings.workspaceStatuses)
|
|
: current.workspaceStatuses
|
|
const next: MobileViewState = {
|
|
groupMode: groupMode ?? current.groupMode,
|
|
sortMode: sortMode ?? current.sortMode,
|
|
hideSleeping: settings.hideSleepingWorkspaces ?? current.hideSleeping,
|
|
hideDefaultBranch: settings.hideDefaultBranchWorkspace ?? current.hideDefaultBranch,
|
|
alwaysShowDefaultBranch:
|
|
settings.alwaysShowDefaultBranchWorkspace ?? current.alwaysShowDefaultBranch,
|
|
filterRepoIds: settings.filterRepoIds ?? current.filterRepoIds,
|
|
collapsedGroups: settings.collapsedGroups ?? current.collapsedGroups,
|
|
workspaceStatuses
|
|
}
|
|
return next
|
|
}
|