lift-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 f9f4e37d6945
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 23e397d8a5d5bf7756362640
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/Start.tsx
// The quick start on the landing page: resume the workout in progress, start an empty one, or open
// a routine or a template. The static HTML in #start shows the same choices before this loads.
import { useActive, useStore, startEmpty } from "./lib/store";
import { workoutFromRoutine, workoutName, fmtSec } from "./lib/model";
import { PROGRAMS, templateHref } from "./lib/templates";
import { navigate } from "./lib/router";
import { A, useNow } from "./ui";
import { useStarter } from "./Workout";
import { Play } from "./icons";

const PICKS = ["Full body", "5x5 A", "Push", "Upper", "Dumbbell full body", "Bodyweight"];

export function Start() {
  const s = useStore();
  const a = useActive();
  const now = useNow(1000);
  const { start, ask } = useStarter();
  const routines = Object.values(s.routines).filter((r) => !r.deleted && r.items.length).sort((x, y) => y.at - x.at).slice(0, 3);
  const templates = PROGRAMS.flatMap((p) => p.routines).filter((t) => PICKS.includes(t.name));

  return (
    <div className="start card">
      {a.workout ? (
        <div className="start-resume">
          <p className="start-title">{a.workout.name}</p>
          <p className="muted small">In progress for {fmtSec((now - a.workout.start) / 1000)}</p>
          <A href="/workout" className="btn btn-lg btn-block">Resume workout</A>
        </div>
      ) : (
        <button type="button" className="btn btn-lg btn-block" onClick={() => { startEmpty(workoutName()); navigate("/workout"); }}><Play /> Start an empty workout</button>
      )}
      {routines.length > 0 && (
        <>
          <p className="start-label">Your routines</p>
          <ul className="start-list">
            {routines.map((r) => (
              <li key={r.id}>
                <button type="button" className="start-item" onClick={() => start(workoutFromRoutine(r, r.id))}>
                  <strong>{r.name}</strong><span>{r.items.length} exercise{r.items.length === 1 ? "" : "s"}</span>
                </button>
              </li>
            ))}
          </ul>
        </>
      )}
      <p className="start-label">Or start from a template</p>
      <ul className="start-list two">
        {templates.map((t) => (
          <li key={t.name}><A href={templateHref(t)} className="start-item"><strong>{t.name}</strong><span>{t.e.split(",").length} exercises</span></A></li>
        ))}
      </ul>
      <p className="hint center">Free. No sign-up, no subscription, no ads.</p>
      {ask}
    </div>
  );
}

Change history

Point OpenApps store links to openapps.free
f9f4e37d69 · 17 hours ago
Add favicon.ico, local share image and openapi.json for links and guides
2796c0063c · 17 hours ago
Bind agent address in policy constants
cf80a3ca66 · 19 hours ago
Add OpenApp contribution strip and OpenApps source links
5e4cc2b9f3 · 19 hours ago
Bounded source sync
ca4a3d3631 · 1 day ago
Bounded source sync
7351063f4d · 1 day ago
Bounded source sync
0ce3238c46 · 1 day ago