subs-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 1ae4feb2bebf
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 5af1a249bbbef1db1b4c5ec8
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/Add.tsx
// Adding a subscription: the editor, plus one-tap picks for the most common ones. A link can fill it
// in (/add?name=Netflix&price=15.49&cycle=monthly), and nothing is saved until the person taps Add.
import { useMemo, useState } from "react";
import { useStore } from "./lib/store";
import { blank } from "./lib/subs";
import { isDayKey } from "./lib/dates";
import { navigate } from "./lib/router";
import { Editor, draftFromQuery } from "./Editor";
import { QuickPicks } from "./Start";

export function AddPage({ query }: { query: URLSearchParams }) {
  const { currency } = useStore();
  const [round, setRound] = useState(0);
  const [picked, setPicked] = useState<string | null>(null);
  const fromLink = useMemo(() => draftFromQuery(query, currency), [query, currency]);
  const initial = useMemo(() => {
    if (picked) return draftFromQuery(new URLSearchParams({ name: picked }), currency)!;
    return round === 0 && fromLink ? fromLink : blank();
  }, [round, picked, fromLink, currency]);
  return (
    <div className="page add">
      <header className="page-head">
        <div><p className="eyebrow">Subs</p><h1 className="h1">Add a subscription</h1></div>
      </header>
      {fromLink && round === 0 && !picked && <p className="notice">Filled in from a link. Check the price and date, then tap Add subscription.</p>}
      <div className="add-grid">
        <section className="card pad">
          <Editor key={`${round}-${picked}`} initial={initial} mode="add" fixedDate={round === 0 && Boolean(fromLink) && isDayKey(query.get("next") || "")} onDone={(_, again) => {
            if (again) { setPicked(null); setRound((r) => r + 1); window.scrollTo(0, 0); }
            else navigate("/home");
          }} />
        </section>
        <aside className="stack add-side">
          <h2 className="section-label">Common ones</h2>
          <QuickPicks onPick={(name) => { setPicked(name); setRound((r) => r + 1); window.scrollTo(0, 0); }} />
          <p className="hint">Not sure what you pay for? Look through your last two card statements, and the subscription list in your phone's app store settings.</p>
        </aside>
      </div>
    </div>
  );
}

Change history

Point store links to openapps.free
1ae4feb2be · 17 hours ago
Bind agent address in policy
99bbab3f0c · 19 hours ago
Use OpenApps service URLs and add contribution strip
781c3c23e5 · 19 hours ago
Bounded source sync
048e3257a5 · 1 day ago
Bounded source sync
7a358a3be6 · 1 day ago
Bounded source sync
0c833fcb0e · 1 day ago