Chart, ephemeris, reports, daily language, consult, and synastry read birth data only through resolveSubjectBirth. The current person is a request parameter outside Home(). /people replaces the settings chart pane, and deleting someone removes that person's chats and reports.
111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
"use client";
|
|
|
|
import { useSyncExternalStore } from "react";
|
|
|
|
import { fetchAccount, fetchCloudChartLibrary } from "@/lib/home-cloud-sync";
|
|
import { readProfile, upsertSelfChart } from "@/lib/home-profile";
|
|
import type { ChartLibraryRecord } from "@/lib/home-types";
|
|
|
|
const SELF_SUBJECT_ID = "self";
|
|
|
|
let accountId = "";
|
|
let subjectId = SELF_SUBJECT_ID;
|
|
let catalogReady = false;
|
|
const listeners = new Set<() => void>();
|
|
|
|
export function currentSubjectStorageKey(id: string): string {
|
|
return `jyotisha_current_subject:${id}`;
|
|
}
|
|
|
|
export function readCurrentSubjectId(): string {
|
|
return subjectId;
|
|
}
|
|
|
|
export function currentSubjectCatalogReady(): boolean {
|
|
return catalogReady;
|
|
}
|
|
|
|
export function subscribeCurrentSubject(listener: () => void): () => void {
|
|
listeners.add(listener);
|
|
return () => {
|
|
listeners.delete(listener);
|
|
};
|
|
}
|
|
|
|
function emit(): void {
|
|
for (const listener of listeners) listener();
|
|
}
|
|
|
|
export function setCurrentSubject(next: string): void {
|
|
const id = next.trim() || SELF_SUBJECT_ID;
|
|
if (id === subjectId) return;
|
|
subjectId = id;
|
|
if (accountId && typeof localStorage !== "undefined") {
|
|
localStorage.setItem(currentSubjectStorageKey(accountId), id);
|
|
}
|
|
emit();
|
|
}
|
|
|
|
/**
|
|
* `knownOtherIds === null` means the archive has not loaded yet, so a stored
|
|
* id is kept. Once ids are known, an id that is not in the archive falls
|
|
* back to self.
|
|
*/
|
|
export function bindCurrentSubjectAccount(nextAccountId: string, knownOtherIds: readonly string[] | null): void {
|
|
accountId = nextAccountId;
|
|
if (!nextAccountId || typeof localStorage === "undefined") {
|
|
catalogReady = knownOtherIds !== null;
|
|
if (subjectId !== SELF_SUBJECT_ID) {
|
|
subjectId = SELF_SUBJECT_ID;
|
|
emit();
|
|
}
|
|
return;
|
|
}
|
|
localStorage.removeItem(`jyotisha_active_chart:${nextAccountId}`);
|
|
const stored = localStorage.getItem(currentSubjectStorageKey(nextAccountId)) || SELF_SUBJECT_ID;
|
|
const valid = stored === SELF_SUBJECT_ID || knownOtherIds === null || knownOtherIds.includes(stored);
|
|
const next = valid ? stored : SELF_SUBJECT_ID;
|
|
if (!valid) localStorage.setItem(currentSubjectStorageKey(nextAccountId), SELF_SUBJECT_ID);
|
|
catalogReady = knownOtherIds !== null;
|
|
if (next !== subjectId || knownOtherIds !== null) {
|
|
subjectId = next;
|
|
emit();
|
|
}
|
|
}
|
|
|
|
export function useCurrentSubject(): string {
|
|
return useSyncExternalStore(subscribeCurrentSubject, readCurrentSubjectId, () => SELF_SUBJECT_ID);
|
|
}
|
|
|
|
let catalogRequest: Promise<ChartLibraryRecord[]> | null = null;
|
|
let catalogAccountId = "";
|
|
|
|
export function resetSubjectCatalogForTests(): void {
|
|
catalogRequest = null;
|
|
catalogAccountId = "";
|
|
accountId = "";
|
|
subjectId = SELF_SUBJECT_ID;
|
|
catalogReady = false;
|
|
}
|
|
|
|
export function loadSubjectCatalog(force = false): Promise<ChartLibraryRecord[]> {
|
|
if (!force && catalogRequest && catalogAccountId) return catalogRequest;
|
|
catalogRequest = Promise.all([
|
|
fetchAccount(),
|
|
fetchCloudChartLibrary(),
|
|
]).then(([account, others]) => {
|
|
const self = readProfile(account.profile);
|
|
const library = upsertSelfChart(others.filter((record) => record.role === "other"), self);
|
|
catalogAccountId = account.user.id;
|
|
bindCurrentSubjectAccount(
|
|
account.user.id,
|
|
library.filter((record) => record.role === "other").map((record) => record.id),
|
|
);
|
|
return library;
|
|
}).catch((error: unknown) => {
|
|
catalogRequest = null;
|
|
throw error;
|
|
});
|
|
return catalogRequest;
|
|
}
|