1import type { EnvironmentId, SidebarProjectGroupingMode } from "@t3tools/contracts";
2import { DEFAULT_SIDEBAR_PROJECT_SORT_ORDER } from "@t3tools/contracts";
3import { RegistryContext, useAtomSet, useAtomValue } from "@effect/atom-react";
4import { Atom } from "effect/reactivity";
5import { createContext, createElement, useContext, useState, type PropsWithChildren } from "react";
6
7import type { ThreadListFilters } from "../../persistence/mobile-preferences";
8import { updateMobilePreferencesAtom } from "../../state/preferences";
9import type { HomeProjectSortOrder } from "./homeThreadList";
10
11export interface HomeListOptions {
12 readonly selectedEnvironmentId: EnvironmentId | null;
13 readonly selectedProjectKey: string | null;
14 readonly projectSortOrder: HomeProjectSortOrder;
15}
16
17export interface ResolvedHomeListOptions extends HomeListOptions {
18 readonly projectGroupingMode: SidebarProjectGroupingMode;
19}
20
21function defaultHomeListOptions(filters?: ThreadListFilters): HomeListOptions {
22 return {
23 selectedEnvironmentId: filters?.environmentId ?? null,
24 selectedProjectKey: filters?.projectKey ?? null,
25 projectSortOrder:
26 DEFAULT_SIDEBAR_PROJECT_SORT_ORDER === "manual"
27 ? "updated_at"
28 : DEFAULT_SIDEBAR_PROJECT_SORT_ORDER,
29 };
30}
31
32interface HomeListOptionsContextValue {
33 readonly options: HomeListOptions;
34 readonly updateOptions: (patch: Partial<HomeListOptions>) => void;
35 readonly projectGroupingMode: SidebarProjectGroupingMode;
36}
37
38const HomeListOptionsContext = createContext<HomeListOptionsContextValue | null>(null);
39
40export function HomeListOptionsProvider({
41 children,
42 projectGroupingMode,
43 savedFilters,
44}: PropsWithChildren<{
45 readonly projectGroupingMode: SidebarProjectGroupingMode;
46 readonly savedFilters?: ThreadListFilters | undefined;
47}>) {
48 const registry = useContext(RegistryContext);
49 const [optionsAtom] = useState(() => Atom.make(defaultHomeListOptions(savedFilters)));
50 const options = useAtomValue(optionsAtom);
51 const savePreferences = useAtomSet(updateMobilePreferencesAtom);
52 // Only the scope persists; the sort order is per session. Updates read the
53 // registry, so several in one event each build on the last and save it.
54 const updateOptions = (patch: Partial<HomeListOptions>) => {
55 const current = registry.get(optionsAtom);
56 const next = { ...current, ...patch };
57 registry.set(optionsAtom, next);
58 if (
59 next.selectedEnvironmentId !== current.selectedEnvironmentId ||
60 next.selectedProjectKey !== current.selectedProjectKey
61 ) {
62 savePreferences({
63 threadListFilters: {
64 environmentId: next.selectedEnvironmentId,
65 projectKey: next.selectedProjectKey,
66 },
67 });
68 }
69 };
70 const value = { options, updateOptions, projectGroupingMode };
71 return createElement(HomeListOptionsContext, { value }, children);
72}
73
74export function useHomeListOptions(availableEnvironmentIds: ReadonlySet<EnvironmentId>) {
75 const shared = useContext(HomeListOptionsContext);
76 const [localOptions, setLocalOptions] = useState<HomeListOptions>(defaultHomeListOptions);
77 const options = shared?.options ?? localOptions;
78 const updateOptions =
79 shared?.updateOptions ??
80 ((patch: Partial<HomeListOptions>) => setLocalOptions((current) => ({ ...current, ...patch })));
81 const selectedEnvironmentId =
82 options.selectedEnvironmentId !== null &&
83 availableEnvironmentIds.has(options.selectedEnvironmentId)
84 ? options.selectedEnvironmentId
85 : null;
86 const availableOptions =
87 selectedEnvironmentId === options.selectedEnvironmentId
88 ? options
89 : { ...options, selectedEnvironmentId };
90 const resolvedOptions: ResolvedHomeListOptions = {
91 ...availableOptions,
92 projectGroupingMode: shared?.projectGroupingMode ?? "repository",
93 };
94
95 const setSelectedEnvironmentId = (value: EnvironmentId | null) => {
96 updateOptions({ selectedEnvironmentId: value });
97 };
98 const setSelectedProjectKey = (value: string | null) => {
99 updateOptions({ selectedProjectKey: value });
100 };
101 const setProjectSortOrder = (value: HomeProjectSortOrder) => {
102 updateOptions({ projectSortOrder: value });
103 };
104 return {
105 options: resolvedOptions,
106 setSelectedEnvironmentId,
107 setSelectedProjectKey,
108 setProjectSortOrder,
109 } as const;
110}