Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Or scan the QR code with **Expo Go** on your phone.

```
assets/exercises.json # the free-tier bundle (400 exercises), imported as a module
assets/images/flat/*.webp # 745 flat webp (start/peak pairs + single-pose "main")
assets/images/flat/*.webp # 741 flat webp (start/peak pairs + single-pose "main")
assets/images/muscles/*.webp # 27 muscle icons
assets/images/equipment/*.webp # 46 equipment icons
assets/images/samples/*.webp # 1 paid-tier looping animation (Standard-tier preview)
Expand All @@ -43,7 +43,7 @@ LICENSE # MIT for the example code

React Native's Metro bundler resolves `require()` of static assets at build
time. Dynamic paths like `require('./assets/' + name)` won't work. We generate
`lib/images.ts` once with one explicit `require()` per file (745 flat + samples
`lib/images.ts` once with one explicit `require()` per file (741 flat + samples
+ icons), so `getImage(slug, variant)` returns a usable module ID at runtime.

The paid-tier preview gallery is **derived** from the animation files in
Expand Down
4 changes: 3 additions & 1 deletion app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export default function RootLayout() {
headerShadowVisible: false,
contentStyle: { backgroundColor: t.bg },
}}
/>
>
<Stack.Screen name="index" options={{ headerShown: false }} />
</Stack>
</SafeAreaProvider>
</GestureHandlerRootView>
);
Expand Down
14 changes: 12 additions & 2 deletions app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export default function ListScreen() {
)}
ListFooterComponent={
<>
<SampleGallery locale={locale} />
<View style={[styles.footer, { borderTopColor: t.border }]}>
<Text style={[styles.footerText, { color: t.muted }]}>
Exercise data by RepDB (repdb.co) — free tier, attribution required.
Expand All @@ -60,6 +59,9 @@ export default function ListScreen() {
</>
}
/>
<View pointerEvents="box-none" style={styles.promoOverlay}>
<SampleGallery locale={locale} />
</View>
</SafeAreaView>
);
}
Expand All @@ -75,7 +77,15 @@ const styles = StyleSheet.create({
},
title: { fontSize: 24, fontWeight: '700', letterSpacing: -0.3 },
subtitle: { fontSize: 13, marginTop: 4 },
list: { paddingHorizontal: 16, paddingTop: 8, paddingBottom: 16 },
list: { paddingHorizontal: 16, paddingTop: 8, paddingBottom: 180 },
promoOverlay: {
position: 'absolute',
left: 16,
right: 16,
bottom: 16,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the floating preview above the bottom safe area

On devices with a nonzero bottom inset, such as gesture-navigation iPhones and edge-to-edge Android devices, the surrounding SafeAreaView explicitly excludes the bottom edge, so positioning this card at bottom: 16 places part of it inside the home-indicator/navigation area. Offset the overlay using the bottom safe-area inset (and account for the resulting height in the list padding) so the preview remains fully visible and usable.

Useful? React with 👍 / 👎.

zIndex: 10,
alignItems: 'flex-end',
},
footer: {
paddingTop: 16,
marginTop: 16,
Expand Down
30 changes: 16 additions & 14 deletions assets/exercises.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schema_version": 3,
"generated_at": "2026-07-16T19:41:37Z",
"generated_at": "2026-07-27T13:53:50Z",
"locales": [
"de",
"en",
Expand Down Expand Up @@ -12294,16 +12294,16 @@
"dumbbell pistol squat"
],
"instructions_en": [
"Stand tall, holding a dumbbell in front of your chest with both hands or in one hand on the same side as the working leg.",
"Stand tall, holding a dumbbell in each hand with your arms hanging at your sides.",
"Lift one leg straight out in front of you, keeping it extended throughout the movement.",
"Lower your body by bending the knee of your standing leg, keeping your chest up and back straight.",
"Descend until your glutes are close to your heel or your thigh is parallel to the floor, maintaining balance.",
"Push through your heel to return to the starting position, extending your standing leg fully.",
"Complete all reps on one side, then switch legs and repeat for the other side per set."
],
"tips_en": [
"Start with bodyweight pistol squats to master the movement pattern before adding a dumbbell.",
"Use a counterweight (dumbbell) held out in front to help maintain balance during the descent.",
"Start with bodyweight pistol squats to master the movement pattern before adding dumbbells.",
"Let the dumbbells hang straight down at your sides and keep your chest up to stay balanced over the working foot.",
"Focus on controlling the eccentric (lowering) phase to improve stability and strength.",
"Elevate your heel slightly on a small plate if ankle mobility is a limiting factor."
],
Expand All @@ -12318,34 +12318,34 @@
"name_de": "Kurzhantel-Pistol-Squat",
"description_de": "Der Kurzhantel-Pistol-Squat ist eine fortgeschrittene einbeinige Kniebeugen-Variante, die erhebliche Unterkörperkraft, Gleichgewicht und Beweglichkeit aufbaut, hauptsächlich für Quadrizeps und Gesäßmuskulatur.",
"instructions_de": [
"Stehe aufrecht und halte eine Kurzhantel mit beiden Händen vor der Brust oder in einer Hand auf der Seite des Arbeitsbeins.",
"Stehe aufrecht und halte in jeder Hand eine Kurzhantel, die Arme hängen seitlich am Körper.",
"Hebe ein Bein gerade nach vorne an und halte es während der gesamten Bewegung gestreckt.",
"Senke deinen Körper, indem du das Knie deines Standbeins beugst, Brust oben und Rücken gerade halten.",
"Gehe so tief, bis dein Gesäß nahe an deiner Ferse ist oder dein Oberschenkel parallel zum Boden ist, und halte das Gleichgewicht.",
"Drücke dich durch die Ferse zurück in die Ausgangsposition und strecke dein Standbein vollständig.",
"Absolviere alle Wiederholungen auf einer Seite, wechsle dann das Bein und wiederhole pro Satz für die andere Seite."
],
"tips_de": [
"Beginne mit Pistol-Squats ohne Gewicht, um das Bewegungsmuster zu meistern, bevor du eine Kurzhantel hinzufügst.",
"Nutze ein Gegengewicht (Kurzhantel), das du nach vorne hältst, um das Gleichgewicht während der Abwärtsbewegung zu halten.",
"Konzentriere dich auf die Kontrolle der exzentrischen (senkenden) Phase, um Stabilität und Kraft zu verbessern.",
"Erhöhe deine Ferse leicht auf einer kleinen Platte, wenn die Sprunggelenksbeweglichkeit ein limitierender Faktor ist."
"Beginne mit Pistol Squats ohne Gewicht, um das Bewegungsmuster zu beherrschen, bevor du Kurzhanteln hinzunimmst.",
"Lass die Kurzhanteln seitlich gerade nach unten hängen und halte die Brust aufrecht, um über dem Arbeitsfuß im Gleichgewicht zu bleiben.",
"Konzentriere dich auf die kontrollierte exzentrische (absenkende) Phase, um Stabilität und Kraft zu verbessern.",
"Erhöhe die Ferse leicht auf einer kleinen Scheibe, falls die Sprunggelenksbeweglichkeit ein limitierender Faktor ist."
],
"name_es": "Sentadilla Pistola con Mancuerna",
"description_es": "La Sentadilla Pistola con Mancuerna es una variación avanzada de sentadilla a una pierna que desarrolla fuerza significativa en la parte inferior del cuerpo, equilibrio y movilidad, apuntando principalmente a los cuádriceps y glúteos.",
"instructions_es": [
"Ponte de pie, sosteniendo una mancuerna frente a tu pecho con ambas manos o en una mano del mismo lado que la pierna que trabaja.",
"Ponte de pie, sosteniendo una mancuerna en cada mano con los brazos colgando a los lados.",
"Levanta una pierna recta hacia adelante, manteniéndola extendida durante todo el movimiento.",
"Baja tu cuerpo doblando la rodilla de tu pierna de apoyo, manteniendo el pecho erguido y la espalda recta.",
"Desciende hasta que tus glúteos estén cerca de tu talón o tu muslo esté paralelo al suelo, manteniendo el equilibrio.",
"Empuja a través de tu talón para volver a la posición inicial, extendiendo completamente tu pierna de apoyo.",
"Completa todas las repeticiones de un lado, luego cambia de pierna y repite para el otro lado por serie."
],
"tips_es": [
"Comienza con sentadillas pistola sin peso corporal para dominar el patrón de movimiento antes de añadir una mancuerna.",
"Usa un contrapeso (mancuerna) sostenido al frente para ayudar a mantener el equilibrio durante el descenso.",
"Concéntrate en controlar la fase excéntrica (bajada) para mejorar la estabilidad y la fuerza.",
"Eleva ligeramente tu talón sobre un pequeño disco si la movilidad del tobillo es un factor limitante."
"Empieza con sentadillas pistola sin peso para dominar el patrón de movimiento antes de añadir mancuernas.",
"Deja que las mancuernas cuelguen rectas a los lados y mantén el pecho erguido para conservar el equilibrio sobre el pie que trabaja.",
"Concéntrate en controlar la fase excéntrica (de descenso) para mejorar la estabilidad y la fuerza.",
"Eleva ligeramente el talón sobre un disco pequeño si la movilidad del tobillo es un factor limitante."
],
"images": {
"flat": [
Expand Down Expand Up @@ -13782,6 +13782,7 @@
"forearm_flexors",
"trapezius"
],
"image_alias": "romanian-deadlift",
"name_de": "SZ-Stange Romanian Deadlift",
"description_de": "Ein Romanian Deadlift mit der SZ-Stange. Der angewinkelte Griff schont die Handgelenke und eignet sich für leichtere Hinge-Arbeit oder handgelenkempfindliche Athleten.",
"instructions_de": [
Expand Down Expand Up @@ -28517,6 +28518,7 @@
"secondary_muscles": [
"soleus"
],
"image_alias": "machine-calf-raise",
"name_de": "Stehende Wadenhebeübung",
"description_de": "Eine im Stand ausgeführte Wadenübung, die den Gastrocnemius durch einen vollen Bewegungsumfang kräftigt.",
"instructions_de": [
Expand Down
Binary file modified assets/images/flat/ab-wheel-rollout-peak.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/flat/ab-wheel-rollout-start.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/flat/bench-leg-pull-in-peak.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/flat/bench-leg-pull-in-start.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file modified assets/images/flat/pull-up-peak.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/flat/pull-up-start.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/images/flat/standing-calf-raise-peak.webp
Binary file not shown.
Binary file removed assets/images/flat/standing-calf-raise-start.webp
Binary file not shown.
7 changes: 7 additions & 0 deletions assets/images/samples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# premium-samples/

A Standard-tier exercise — bent-over-db-row — at full paid-tier quality:
classic start/peak stills plus the looping transparent-background animation,
the same clip shown on https://repdb.co. The Standard tier adds animations
like this for most exercises. Current tiers and pricing:
https://repdb.co/pricing
Binary file added assets/images/samples/bent-over-db-row-peak.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/samples/bent-over-db-row-start.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 50 additions & 28 deletions components/SampleGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,41 @@ export function SampleGallery({ locale }: { locale: Locale }) {
if (SAMPLE_SLUGS.length === 0) return null;

return (
<View style={[styles.section, { borderColor: t.border }]}>
<View style={styles.headerRow}>
<Text style={[styles.title, { color: t.text }]}>Paid tier preview</Text>
<View style={[styles.badge, { backgroundColor: t.accentSoft, borderColor: t.accent }]}>
<Text style={[styles.badgeText, { color: t.accent }]}>
STANDARD TIER PREVIEW
</Text>
<View
style={[
styles.section,
{
backgroundColor: t.surface,
borderColor: t.borderStrong,
shadowColor: t.text,
},
]}
>
<View style={styles.copy}>
<View style={styles.headerRow}>
<Text style={[styles.title, { color: t.text }]}>Paid tier preview</Text>
<View
style={[styles.badge, { backgroundColor: t.accentSoft, borderColor: t.accent }]}
>
<Text style={[styles.badgeText, { color: t.accent }]}>STANDARD TIER</Text>
</View>
</View>
</View>

<Text style={[styles.note, { color: t.muted }]}>
The Standard tier adds looping animations like these for most exercises — the
clips shown on repdb.co.{' '}
<Text
style={{ color: t.accent, fontWeight: '600' }}
onPress={() => Linking.openURL('https://repdb.co/pricing')}
>
See pricing →
<Text style={[styles.note, { color: t.muted }]}>
Looping animations for most exercises.{' '}
<Text
style={{ color: t.accent, fontWeight: '600' }}
onPress={() => Linking.openURL('https://repdb.co/pricing')}
>
See pricing →
</Text>
</Text>
</Text>
</View>

<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={styles.scroller}
contentContainerStyle={styles.row}
>
{SAMPLE_SLUGS.map((slug) => {
Expand Down Expand Up @@ -74,11 +85,21 @@ function prettySlug(slug: string): string {

const styles = StyleSheet.create({
section: {
marginTop: 16,
paddingTop: 16,
borderTopWidth: StyleSheet.hairlineWidth,
gap: 10,
width: '100%',
maxWidth: 620,
minHeight: 118,
padding: 12,
borderWidth: 1,
borderRadius: 16,
flexDirection: 'row',
alignItems: 'center',
gap: 12,
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.22,
shadowRadius: 20,
elevation: 12,
},
copy: { flex: 1, minWidth: 0, gap: 7 },
headerRow: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', gap: 8 },
title: { fontSize: 16, fontWeight: '700', letterSpacing: -0.2 },
badge: {
Expand All @@ -89,15 +110,16 @@ const styles = StyleSheet.create({
},
badgeText: { fontSize: 9.5, fontWeight: '700', letterSpacing: 0.5 },
note: { fontSize: 12, lineHeight: 17 },
row: { gap: 12, paddingVertical: 2, paddingRight: 4 },
tile: { width: 132, gap: 6 },
scroller: { flexGrow: 0, flexShrink: 1, maxWidth: 204 },
row: { gap: 10 },
tile: { width: 92, gap: 4 },
frame: {
width: 132,
height: 132,
borderRadius: 12,
width: 92,
height: 92,
borderRadius: 10,
borderWidth: StyleSheet.hairlineWidth,
overflow: 'hidden',
},
image: { width: '100%', height: '100%', padding: 10 },
tileLabel: { fontSize: 12, fontWeight: '500', textAlign: 'center' },
image: { width: '100%', height: '100%', padding: 7 },
tileLabel: { fontSize: 10.5, fontWeight: '500', textAlign: 'center' },
});
3 changes: 3 additions & 0 deletions components/StartPeakViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ export function StartPeakViewer({ slug, alt, variants, interval = 1600 }: Props)

const styles = StyleSheet.create({
frame: {
width: '100%',
maxWidth: 560,
alignSelf: 'center',
aspectRatio: 4 / 3,
borderRadius: 12,
borderWidth: StyleSheet.hairlineWidth,
Expand Down
4 changes: 0 additions & 4 deletions lib/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,6 @@ export const IMAGES: Record<string, number> = {
'ez-bar-reverse-curl-start': require('../assets/images/flat/ez-bar-reverse-curl-start.webp'),
'ez-bar-reverse-grip-row-peak': require('../assets/images/flat/ez-bar-reverse-grip-row-peak.webp'),
'ez-bar-reverse-grip-row-start': require('../assets/images/flat/ez-bar-reverse-grip-row-start.webp'),
'ez-bar-romanian-deadlift-peak': require('../assets/images/flat/ez-bar-romanian-deadlift-peak.webp'),
'ez-bar-romanian-deadlift-start': require('../assets/images/flat/ez-bar-romanian-deadlift-start.webp'),
'ez-bar-shrug-peak': require('../assets/images/flat/ez-bar-shrug-peak.webp'),
'ez-bar-shrug-start': require('../assets/images/flat/ez-bar-shrug-start.webp'),
'ez-bar-spider-curl-peak': require('../assets/images/flat/ez-bar-spider-curl-peak.webp'),
Expand Down Expand Up @@ -683,8 +681,6 @@ export const IMAGES: Record<string, number> = {
'stability-ball-knee-tuck-start': require('../assets/images/flat/stability-ball-knee-tuck-start.webp'),
'stability-ball-push-up-peak': require('../assets/images/flat/stability-ball-push-up-peak.webp'),
'stability-ball-push-up-start': require('../assets/images/flat/stability-ball-push-up-start.webp'),
'standing-calf-raise-peak': require('../assets/images/flat/standing-calf-raise-peak.webp'),
'standing-calf-raise-start': require('../assets/images/flat/standing-calf-raise-start.webp'),
'step-ups-peak': require('../assets/images/flat/step-ups-peak.webp'),
'step-ups-start': require('../assets/images/flat/step-ups-start.webp'),
'stiff-leg-deadlift-peak': require('../assets/images/flat/stiff-leg-deadlift-peak.webp'),
Expand Down
Loading
Loading