|
1 | 1 | import { atom } from 'jotai' |
| 2 | +import { atomWithQuery } from 'jotai-tanstack-query' |
2 | 3 |
|
3 | 4 | import { urlArgsAtom, urlArgsStableAtom } from './url-atoms' |
4 | 5 |
|
5 | | -const DEFAULT_PROJECT = 'MathlibDemo' |
| 6 | +/* An example a project provides */ |
| 7 | +export interface Example { |
| 8 | + file: string |
| 9 | + name: string |
| 10 | +} |
| 11 | + |
| 12 | +/* A project's user configuration */ |
| 13 | +export type ProjectConfig = { |
| 14 | + name: string |
| 15 | + hidden: boolean |
| 16 | + default: boolean |
| 17 | + examples: Example[] |
| 18 | +} |
| 19 | + |
| 20 | +/* A project */ |
| 21 | +export type Project = { |
| 22 | + folder: string |
| 23 | + config: ProjectConfig |
| 24 | +} |
| 25 | + |
| 26 | +const projectsQueryAtom = atomWithQuery<Project[]>((get) => ({ |
| 27 | + queryKey: ['projects'], |
| 28 | + queryFn: async () => { |
| 29 | + const res = await fetch(`/api/projects`) |
| 30 | + return res.json() |
| 31 | + }, |
| 32 | +})) |
| 33 | + |
| 34 | +/** Sort alphabetically while the `default` project always comes first */ |
| 35 | +function sortProjects(p: Project, q: Project): number { |
| 36 | + if (p.config.default) return -1 |
| 37 | + if (q.config.default) return 1 |
| 38 | + return p.config.name.localeCompare(q.config.name) |
| 39 | +} |
| 40 | + |
| 41 | +/** All available projects */ |
| 42 | +export const projectsAtom = atom((get) => { |
| 43 | + const query = get(projectsQueryAtom) |
| 44 | + return { ...query, data: query.data?.sort(sortProjects) ?? [] } |
| 45 | +}) |
| 46 | + |
| 47 | +export const defaultProjectFolderAtom = atom((get) => { |
| 48 | + const projects = get(projectsAtom).data |
| 49 | + if (projects.length === 0) return 'MathlibDemo' // TODO: is this correct? |
| 50 | + const defaultProjects = projects.filter((it) => it.config.default) |
| 51 | + |
| 52 | + if (defaultProjects.length === 0) { |
| 53 | + console.warn(`Expected exactly one default project, but found none.`) |
| 54 | + return projects[0].folder |
| 55 | + } |
| 56 | + if (defaultProjects.length > 1) { |
| 57 | + console.error(`Expected exactly one default project, but found ${defaultProjects.length}`) |
| 58 | + } |
| 59 | + return defaultProjects[0].folder |
| 60 | +}) |
6 | 61 |
|
7 | 62 | /** The currently selected project */ |
8 | 63 | export const projectAtom = atom( |
9 | 64 | (get) => { |
10 | 65 | const urlArgs = get(urlArgsStableAtom) |
11 | | - return urlArgs.project ?? DEFAULT_PROJECT |
| 66 | + return urlArgs.project ?? get(defaultProjectFolderAtom) |
12 | 67 | }, |
13 | 68 | (get, set, project: string) => { |
14 | 69 | const urlArgs = get(urlArgsStableAtom) |
15 | | - set(urlArgsAtom, { ...urlArgs, project: project !== DEFAULT_PROJECT ? project : undefined }) |
| 70 | + set(urlArgsAtom, { |
| 71 | + ...urlArgs, |
| 72 | + project: project !== get(defaultProjectFolderAtom) ? project : undefined, |
| 73 | + }) |
16 | 74 | }, |
17 | 75 | ) |
0 commit comments