Milestone 0/1: shell, calendar/psalter/hours scaffold, Prime
Deploy / deploy (push) Failing after 1m8s
Deploy / deploy (push) Failing after 1m8s
Client-side-first PWA (Vite/TS, no backend) per the approved plan: day- navigable shell listing all 8 hours, Prime fully resolves via the calendar -> psalter -> ordo pipeline, the other 7 hours are registered but flagged not-implemented. Sanctoral/temporal calendar data uses a day -> id indirection layer (saints, easter-offsets, fixed-date-calendar) so reassigning a feast to a different day is a data edit, not a code change. Docker (Caddy-serving-static) + Gitea CI workflow scaffolded to match the eec/drip/bookshop operational pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import type { HourId } from './hours/types';
|
||||
import type { LanguageCode } from './psalter/types';
|
||||
|
||||
export interface AppState {
|
||||
date: string; // ISO YYYY-MM-DD
|
||||
selectedHour: HourId;
|
||||
/** Exactly one or two entries — see plan: one-language / two-language layout only. */
|
||||
languages: [LanguageCode] | [LanguageCode, LanguageCode];
|
||||
}
|
||||
|
||||
export function todayIso(): string {
|
||||
const d = new Date();
|
||||
const yyyy = d.getFullYear();
|
||||
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(d.getDate()).padStart(2, '0');
|
||||
return `${yyyy}-${mm}-${dd}`;
|
||||
}
|
||||
|
||||
const state: AppState = {
|
||||
date: todayIso(),
|
||||
selectedHour: 'prime',
|
||||
languages: ['en'],
|
||||
};
|
||||
|
||||
type Listener = (state: Readonly<AppState>) => void;
|
||||
const listeners = new Set<Listener>();
|
||||
|
||||
export function getState(): Readonly<AppState> {
|
||||
return state;
|
||||
}
|
||||
|
||||
export function subscribe(listener: Listener): () => void {
|
||||
listeners.add(listener);
|
||||
return () => listeners.delete(listener);
|
||||
}
|
||||
|
||||
function notify(): void {
|
||||
for (const listener of listeners) listener(state);
|
||||
}
|
||||
|
||||
export function setDate(date: string): void {
|
||||
state.date = date;
|
||||
notify();
|
||||
}
|
||||
|
||||
export function goToToday(): void {
|
||||
setDate(todayIso());
|
||||
}
|
||||
|
||||
export function shiftDate(days: number): void {
|
||||
const d = new Date(`${state.date}T00:00:00Z`);
|
||||
d.setUTCDate(d.getUTCDate() + days);
|
||||
setDate(d.toISOString().slice(0, 10));
|
||||
}
|
||||
|
||||
export function setSelectedHour(hourId: HourId): void {
|
||||
state.selectedHour = hourId;
|
||||
notify();
|
||||
}
|
||||
|
||||
export function setLanguages(languages: AppState['languages']): void {
|
||||
state.languages = languages;
|
||||
notify();
|
||||
}
|
||||
Reference in New Issue
Block a user