baby-free.openapps.xyz

Source

This app’s code and revision history are public. Download a copy or explore the files below.

Public
The app’s code and revision history are public.
Owned app · rev c84ed8821371
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 381bd6356d4b794ac2a7c1b8
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
← All files
src/lib/prefs.ts
// Device settings: small JSON values in localStorage, shared live between components.
// Units and the night look are per device, so each caregiver sees the log their own way.
import { useSyncExternalStore } from "react";
import { METRIC, IMPERIAL, type Units } from "../../functions/lib/baby";

const listeners = new Map<string, Set<() => void>>();
const cache = new Map<string, unknown>();

export function read<T>(key: string, fallback: T): T {
  if (cache.has(key)) return cache.get(key) as T;
  let value = fallback;
  try {
    const raw = localStorage.getItem(key);
    if (raw != null) value = JSON.parse(raw) as T;
  } catch { /* storage blocked or bad JSON: use the fallback */ }
  cache.set(key, value);
  return value;
}

export function write<T>(key: string, value: T) {
  cache.set(key, value);
  try {
    if (value === undefined) localStorage.removeItem(key);
    else localStorage.setItem(key, JSON.stringify(value));
  } catch { /* storage full or blocked: keep it for this visit */ }
  listeners.get(key)?.forEach((l) => l());
}

export function useStored<T>(key: string, fallback: T): [T, (v: T) => void] {
  const value = useSyncExternalStore(
    (l) => {
      const set = listeners.get(key) ?? new Set();
      set.add(l);
      listeners.set(key, set);
      return () => set.delete(l);
    },
    () => read(key, fallback),
  );
  return [value, (v: T) => write(key, v)];
}

function guessUnits(): Units {
  try {
    const region = new Intl.Locale(navigator.language).maximize().region || "";
    return ["US", "LR", "MM"].includes(region) ? IMPERIAL : METRIC;
  } catch {
    return METRIC;
  }
}

export function useUnits(): [Units, (patch: Partial<Units>) => void] {
  const [stored] = useStored<Partial<Units> | null>("baby:units", null);
  const units = { ...guessUnits(), ...(stored || {}) };
  return [units, (patch) => write("baby:units", { ...units, ...patch })];
}

export type Look = "auto" | "day" | "night";
export const useLook = () => useStored<Look>("baby:look", "auto");

/** The family and baby this device last had open. */
export const useActive = () => useStored<{ family?: string; baby?: Record<string, string> }>("baby:active", {});
export function setActiveFamily(family: string) {
  write("baby:active", { ...read<{ family?: string; baby?: Record<string, string> }>("baby:active", {}), family });
}

/** The name this device logs under, offered when joining or starting a family. */
export const useMyName = () => useStored<string>("baby:name", "");

/** Owner keys for families created on this device, by family id. Kept here only. */
export const ownerKeys = () => read<Record<string, string>>("baby:ownerkeys", {});
export function saveOwnerKey(family: string, key: string) {
  write("baby:ownerkeys", { ...ownerKeys(), [family]: key });
}

Change history

Quality pass: undo on timers, sleep by hour chart, diaper color, fixes
c84ed88213 · 5 hours ago
Bounded source sync
1ed137d95d · 16 hours ago
Bounded source sync
851999f793 · 17 hours ago
Bounded source sync
9623bc231c · 17 hours ago