Files
tolaria/apps/mobile/src/mobileSidebarNavigation.ts
2026-05-05 20:29:56 +02:00

178 lines
4.7 KiB
TypeScript

import type { IconName } from './NamedIcon'
import type { MobileNote } from './mobileNoteProjection'
import { evaluateMobileView, mobileViewDefinitions } from './mobileViewFilters'
export type MobileSidebarSelection =
| { kind: 'library'; id: 'all' | 'archive' | 'favorites' | 'inbox' }
| { kind: 'type'; type: string }
| { kind: 'view'; id: string }
export type MobileSidebarItem = {
count: number
icon: IconName
label: string
selection: MobileSidebarSelection
}
export type MobileSidebarSection = {
items: MobileSidebarItem[]
title: string
}
export const defaultMobileSidebarSelection: MobileSidebarSelection = { kind: 'library', id: 'inbox' }
export function createMobileSidebarSections(notes: MobileNote[]): MobileSidebarSection[] {
return [
{
title: 'Library',
items: [
libraryItem({ count: inboxNotes(notes).length, icon: 'tray', id: 'inbox', label: 'Inbox' }),
libraryItem({ count: activeNotes(notes).length, icon: 'file-text', id: 'all', label: 'All Notes' }),
libraryItem({ count: archivedNotes(notes).length, icon: 'archive', id: 'archive', label: 'Archive' }),
libraryItem({ count: favoriteNotes(notes).length, icon: 'star', id: 'favorites', label: 'Favorites' }),
],
},
{
title: 'Views',
items: mobileViewDefinitions.map((view) => ({
count: evaluateMobileView({ notes: activeNotes(notes), view }).length,
icon: view.icon as IconName,
label: view.name,
selection: { kind: 'view', id: view.id },
})),
},
{
title: 'Types',
items: noteTypes(notes).map((type) => ({
count: activeNotes(notes).filter((note) => note.type === type).length,
icon: iconForType(type),
label: pluralTypeLabel(type),
selection: { kind: 'type', type },
})),
},
]
}
export function filterNotesForSidebarSelection({
notes,
selection,
}: {
notes: MobileNote[]
selection: MobileSidebarSelection
}) {
if (selection.kind === 'type') {
return activeNotes(notes).filter((note) => note.type === selection.type)
}
if (selection.kind === 'view') {
const view = mobileViewDefinitions.find((definition) => definition.id === selection.id)
return view ? evaluateMobileView({ notes: activeNotes(notes), view }) : []
}
switch (selection.id) {
case 'all':
return activeNotes(notes)
case 'archive':
return archivedNotes(notes)
case 'favorites':
return favoriteNotes(notes)
default:
return inboxNotes(notes)
}
}
export function mobileSidebarTitle(selection: MobileSidebarSelection) {
if (selection.kind === 'type') {
return pluralTypeLabel(selection.type)
}
if (selection.kind === 'view') {
return mobileViewDefinitions.find((definition) => definition.id === selection.id)?.name ?? 'View'
}
return libraryTitle(selection.id)
}
export function isMobileSidebarSelectionActive({
candidate,
current,
}: {
candidate: MobileSidebarSelection
current: MobileSidebarSelection
}) {
return candidate.kind === current.kind
&& (candidate.kind === 'type' ? candidate.type === (current as { type: string }).type : candidate.id === (current as { id: string }).id)
}
function libraryItem({
count,
icon,
id,
label,
}: {
count: number
icon: IconName
id: 'all' | 'archive' | 'favorites' | 'inbox'
label: string
}): MobileSidebarItem {
return {
count,
icon,
label,
selection: { kind: 'library', id },
}
}
function inboxNotes(notes: MobileNote[]) {
return activeNotes(notes).filter((note) => !note.status || note.status === 'Draft')
}
function activeNotes(notes: MobileNote[]) {
return notes.filter((note) => !note.archived)
}
function archivedNotes(notes: MobileNote[]) {
return notes.filter((note) => note.archived)
}
function favoriteNotes(notes: MobileNote[]) {
return activeNotes(notes)
.filter((note) => note.favorite)
.sort((left, right) => (left.favoriteIndex ?? Number.MAX_SAFE_INTEGER) - (right.favoriteIndex ?? Number.MAX_SAFE_INTEGER))
}
function noteTypes(notes: MobileNote[]) {
return [...new Set(activeNotes(notes).map((note) => note.type))].sort()
}
function libraryTitle(id: 'all' | 'archive' | 'favorites' | 'inbox') {
if (id === 'all') return 'All Notes'
if (id === 'archive') return 'Archive'
if (id === 'favorites') return 'Favorites'
return 'Inbox'
}
function pluralTypeLabel(type: string) {
return type.endsWith('s') ? type : `${type}s`
}
function iconForType(type: string): IconName {
if (type === 'Essay') {
return 'pen-nib'
}
if (type === 'Project') {
return 'wrench'
}
if (type === 'Resource') {
return 'books'
}
if (type === 'Release Note') {
return 'flag'
}
return 'file-text'
}