monitor-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 11d38ef641b0
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 9383f719c9b37ff76411858f
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/Parent.tsx
// The parent's start: pair with a station by code or QR code, or open one paired before.
import { useEffect, useRef, useState, type FormEvent } from "react";
import { navigate } from "./lib/router";
import { deviceName, pairings } from "./lib/store";
import { joinRoom, resolveCode } from "./pair";
import { TopBar } from "./Baby";
import { Icon, Sheet, useNightChrome } from "./ui";

const ROOM_RE = /^[A-Za-z0-9]{20}$/;

/** The room id from a pairing QR code or link (https://.../pair#<room>). */
function roomFromLink(text: string): string | null {
  try {
    const hash = new URL(text, location.origin).hash.slice(1);
    return ROOM_RE.test(hash) ? hash : null;
  } catch {
    return null;
  }
}

function DeviceName({ value, onChange }: { value: string; onChange: (v: string) => void }) {
  const [editing, setEditing] = useState(false);
  if (editing) {
    return (
      <label className="field">
        <span>Name this device</span>
        <input className="input" autoFocus value={value} maxLength={40} onChange={(e) => onChange(e.target.value)} onBlur={() => setEditing(false)} />
        <small>The baby station shows this name in its list of parents.</small>
      </label>
    );
  }
  return (
    <p className="night-muted small">The baby station will list this device as <strong className="night-strong">{value || "Parent device"}</strong>. <button className="link-btn" type="button" onClick={() => setEditing(true)}>Change</button></p>
  );
}

export function ParentPage() {
  useNightChrome();
  const [code, setCode] = useState("");
  const [name, setName] = useState(deviceName.get);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [scanning, setScanning] = useState(false);
  const list = pairings.list();
  const canScan = typeof window !== "undefined" && "BarcodeDetector" in window;

  const pair = async (room: string) => {
    const label = name.trim().slice(0, 40) || "Parent device";
    deviceName.set(label);
    const p = await joinRoom(room, label);
    navigate(`/watch/${p.key}`);
  };

  const submit = async (value: string) => {
    const digits = value.replace(/\D/g, "");
    if (digits.length !== 6) return setError("The code has 6 digits.");
    setBusy(true);
    setError(null);
    try {
      await pair(await resolveCode(digits));
    } catch (err) {
      setError((err as Error).message);
      setBusy(false);
    }
  };

  const onScan = async (text: string) => {
    setScanning(false);
    const room = roomFromLink(text);
    if (!room) return setError("That QR code is not a Monitor pairing code.");
    setBusy(true);
    try {
      await pair(room);
    } catch (err) {
      setError((err as Error).message);
      setBusy(false);
    }
  };

  return (
    <div className="night">
      <div className="night-page">
        <TopBar title="Parent" />
        <h1 className="night-h1">Pair with the baby station</h1>
        <form className="pair-form" onSubmit={(e: FormEvent) => { e.preventDefault(); void submit(code); }} noValidate>
          <label className="field">
            <span>Code shown on the baby station</span>
            <input
              className="input code-input" inputMode="numeric" autoComplete="one-time-code" pattern="[0-9 ]*" maxLength={7}
              placeholder="000 000" value={code} disabled={busy} autoFocus={!list.length}
              onChange={(e) => {
                const v = e.target.value.replace(/[^\d ]/g, "");
                setCode(v);
                setError(null);
                if (v.replace(/\D/g, "").length === 6) void submit(v);
              }}
            />
          </label>
          {error && <p className="notice notice-error" role="alert">{error}</p>}
          <button className="btn btn-xl btn-block" type="submit" disabled={busy}>{busy ? "Pairing..." : "Pair"}</button>
          {canScan ? (
            <button className="btn btn-xl btn-block btn-line" type="button" disabled={busy} onClick={() => setScanning(true)}><Icon name="qr" size={22} /> Scan the QR code</button>
          ) : (
            <p className="night-muted small">Or point this phone's camera app at the QR code on the baby station.</p>
          )}
          <DeviceName value={name} onChange={setName} />
        </form>

        {list.length > 0 && (
          <section className="panel-night">
            <h2 className="night-h2">Paired before</h2>
            <ul className="again-list">
              {list.map((p) => (
                <li key={p.key}>
                  <a className="again-item" href={`/watch/${p.key}`} onClick={(e) => { e.preventDefault(); navigate(`/watch/${p.key}`); }}>
                    <span className="again-name">{p.name}</span>
                    <span className="again-go">Watch</span>
                  </a>
                </li>
              ))}
            </ul>
          </section>
        )}
      </div>
      {scanning && <Scanner onResult={(t) => void onScan(t)} onClose={() => setScanning(false)} />}
    </div>
  );
}

interface Detector { detect(source: CanvasImageSource): Promise<{ rawValue: string }[]> }

function Scanner({ onResult, onClose }: { onResult: (text: string) => void; onClose: () => void }) {
  const video = useRef<HTMLVideoElement>(null);
  const result = useRef(onResult);
  result.current = onResult;
  const [error, setError] = useState<string | null>(null);
  useEffect(() => {
    let stream: MediaStream | null = null;
    let timer: number | undefined;
    let done = false;
    (async () => {
      try {
        stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } });
        if (done) return stream.getTracks().forEach((t) => t.stop());
        video.current!.srcObject = stream;
        await video.current!.play();
        const Ctor = (window as unknown as { BarcodeDetector: new (o: { formats: string[] }) => Detector }).BarcodeDetector;
        const detector = new Ctor({ formats: ["qr_code"] });
        const look = async () => {
          if (done) return;
          const found = await detector.detect(video.current!).catch(() => []);
          if (found.length && !done) { done = true; result.current(found[0].rawValue); return; }
          timer = window.setTimeout(look, 250);
        };
        void look();
      } catch {
        setError("Could not open the camera. Allow it, or type the code instead.");
      }
    })();
    return () => { done = true; clearTimeout(timer); stream?.getTracks().forEach((t) => t.stop()); };
  }, []);
  return (
    <Sheet title="Scan the QR code" onClose={onClose}>
      {error ? <p className="notice notice-error">{error}</p> : (
        <div className="scanner"><video ref={video} muted playsInline /><span className="scanner-frame" aria-hidden="true" /></div>
      )}
      <p className="night-muted small center">Point the camera at the QR code on the baby station.</p>
    </Sheet>
  );
}

/** Opened from a scanned QR code: pairs with the station named in the link's fragment. */
export function PairLinkPage() {
  useNightChrome();
  const [room] = useState(() => roomFromLink(location.href));
  const [name, setName] = useState(deviceName.get);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(room ? null : "This pairing link is incomplete. Scan the QR code again, or type the code instead.");

  useEffect(() => { history.replaceState(null, "", "/pair"); }, []);

  const pair = async () => {
    if (!room) return;
    setBusy(true);
    setError(null);
    try {
      const label = name.trim().slice(0, 40) || "Parent device";
      deviceName.set(label);
      const p = await joinRoom(room, label);
      navigate(`/watch/${p.key}`);
    } catch (err) {
      setError((err as Error).message);
      setBusy(false);
    }
  };

  return (
    <div className="night">
      <div className="night-page">
        <TopBar title="Parent" />
        <h1 className="night-h1">Watch this baby station?</h1>
        <p className="night-lede">This device will see and hear the baby station whenever it is on. You can remove it later from the station.</p>
        {error && <p className="notice notice-error" role="alert">{error}</p>}
        {room ? (
          <div className="pair-form">
            <button className="btn btn-xl btn-block" type="button" disabled={busy} onClick={() => void pair()}>{busy ? "Pairing..." : "Pair and watch"}</button>
            <DeviceName value={name} onChange={setName} />
          </div>
        ) : (
          <button className="btn btn-xl btn-block" type="button" onClick={() => navigate("/parent")}>Type the code instead</button>
        )}
      </div>
    </div>
  );
}

Change history

Move store links to openapps.free
11d38ef641 · 17 hours ago
Release with preview
41c8af3ee5 · 19 hours ago
Bind agent address in policy
5b2d792529 · 19 hours ago
Use OpenApps service URLs and add contribution strip (retry)
be4477dcb3 · 19 hours ago
Bounded source sync
5c9ec00c29 · 2 days ago
Bounded source sync
b33aa231f4 · 2 days ago
Bounded source sync
6cb67b3c8e · 2 days ago
Bounded source sync
e054acaa29 · 2 days ago