Files
kegel/README.md
marcuspaico 55999b7458
Some checks failed
Build & Release APK / build (push) Has been cancelled
feat: store release setup — EAS config, listing copy, screenshots, new icon
- eas.json production/simulator profiles; iOS plist flags; EAS project linked
- literal versionCode/versionName in build.gradle so EAS can parse it; Gitea CI
  stamps the run number with sed instead of env-driven gradle expressions
- store/: listing copy + keyword rationale, screenshot composer (headless
  Chrome), raw simulator captures, rendered App Store / Play slides, feature
  graphic, privacy policy source, icon generator
- replace Expo placeholder icon with the session-ring mark; regenerate Android
  launcher/splash res and theme colours

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WontRuPqKcod9XK4ZN9MXv
2026-09-07 16:10:27 +10:00

3.2 KiB
Raw Blame History

Kegel

Pelvic floor trainer for sexual performance — a free, local-only take on what Stamina was before it went paid. Expo / React Native, Android + iOS.

How it works

A session is four guided blocks driven by a step timer with haptic cues:

  1. Holds — 8 × squeeze-and-hold. This is the progressive-overload driver: holds start at 5s and grow +1s every 3 completed sessions (capped at 30s). Manual ±1s adjustment in Settings.
  2. Pushes — 5 × reverse kegel (gently bear down). Scales at half the rate of holds, capped at 12s.
  3. Quick squeezes — 10 × 1s burst contractions with 1s release.
  4. Quick pushes — 10 × 1s burst pushes with 1s release.

Progress (session count, difficulty adjustment, completion history) lives in AsyncStorage — no server, no account. The home screen shows your day streak (alive until a full day is missed), best streak, and a two-week activity strip.

Layout

  • src/app/ — expo-router screens: index (home), session (timer), settings
  • src/lib/workout.ts — pure session-plan + progression logic
  • src/lib/streak.ts — pure streak / calendar-day logic (tested)
  • src/lib/store.ts — tiny local store persisted to AsyncStorage
  • android/ — committed prebuild output; CI builds it directly

Development

npm install
npm start            # Expo dev server
npm run typecheck
npm test             # node --test over src/lib/*.test.ts

Release

Android APK (Gitea / Obtainium)

Push to main → Gitea Actions builds a signed APK and publishes a Gitea release — install via Obtainium. android/app/build.gradle keeps literal versionCode/versionName; CI stamps the run number in with sed before building. Keystore: ~/.android-keys/kegel-release.jks (repo secrets KEYSTORE_B64, KEYSTORE_PASSWORD; alias kegel). Beware: expo prebuild -p android (used to regenerate launcher icons from store/icons/make.js) rewrites build.gradle and drops the release signingConfigs block — restore it from git.

App Store / Google Play (EAS)

Store builds run on EAS (project @mrmen23/kegel, profile production, version source remote with auto-increment). The EAS-managed Android keystore differs from the Gitea one, so Play and Obtainium installs can't update over each other.

# Apple: needs an App Store Connect API key for non-interactive auth
export EXPO_ASC_API_KEY_PATH=~/Downloads/AuthKey_L9Z6F5H5B7.p8 EXPO_ASC_KEY_ID=L9Z6F5H5B7 \
       EXPO_ASC_ISSUER_ID=<issuer uuid> EXPO_APPLE_TEAM_ID=HZ6XCKM95R EXPO_APPLE_TEAM_TYPE=INDIVIDUAL
eas build -p ios --profile production --non-interactive
eas submit -p ios --profile production --latest --non-interactive   # creates the ASC record on first run

# Android: needs a Play service-account JSON with release access
eas build -p android --profile production --non-interactive
eas submit -p android --profile production --latest --key <service-account.json> --track internal

Listing copy, keyword rationale and screenshot generation live in store/ (node store/screenshots/make.js renders store/screenshots/raw/*.png into store-sized slides). Privacy policy is served from https://rehbock.xyz/kegel/privacy.html (source in store/web/). iOS dev runs via npx expo run:ios from a Mac.