diff --git a/homeflow/app/(tabs)/index.tsx b/homeflow/app/(tabs)/index.tsx
index 1fb63b5..89b38be 100644
--- a/homeflow/app/(tabs)/index.tsx
+++ b/homeflow/app/(tabs)/index.tsx
@@ -1,13 +1,27 @@
-import React, { useState } from 'react';
+/**
+ * Home Screen — StreamSync Wellness Dashboard
+ *
+ * Displays:
+ * - Throne Science uroflow module (7-day chart with metric toggle)
+ * - Apple HealthKit module (activity rings, sleep, vitals)
+ * - Surgery date & study timeline
+ * - Watch wear reminder
+ */
+
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
+ ActivityIndicator,
+ Dimensions,
+ Modal,
+ Platform,
+ Pressable,
+ RefreshControl,
+ ScrollView,
StyleSheet,
- View,
Text,
- ScrollView,
TouchableOpacity,
+ View,
Alert,
- Modal,
- Pressable,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { router } from 'expo-router';
@@ -18,15 +32,491 @@ import { useSurgeryDate } from '@/hooks/use-surgery-date';
import { useWatchUsage } from '@/hooks/use-watch-usage';
import { SurgeryCompleteModal } from '@/components/home/SurgeryCompleteModal';
import { useAppTheme } from '@/lib/theme/ThemeContext';
+import { useHealthSummary } from '@/hooks/use-health-summary';
+import {
+ fetchSessions,
+ fetchMetricsBatch,
+ type ThroneSession,
+ type ThroneMetric,
+} from '@/src/services/throneFirestore';
+import {
+ parseSessionWithMetrics,
+ type ParsedVoidSession,
+} from '@/src/data/parseVoidingSession';
+import {
+ filterByRange,
+ bucketSeries,
+ type BucketPoint,
+} from '@/src/data/voidingAggregation';
+import {
+ METRIC_LABELS,
+ METRIC_UNITS,
+ METRIC_KEYS,
+ type VoidMetricKey,
+} from '@/src/data/voidingFieldMap';
+
+// ─── Constants ────────────────────────────────────────────────────────────────
+
+const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
+
+const MOVE_GOAL_KCAL = 500;
+const EXERCISE_GOAL_MIN = 30;
+const STEPS_GOAL = 10_000;
+
+// ─── Activity Ring Component ──────────────────────────────────────────────────
+
+/**
+ * Draws a single circular progress ring using the two-half-disc technique.
+ * Works without any SVG library.
+ */
+function CircleRing({
+ pct,
+ color,
+ size,
+ strokeWidth,
+ bgColor,
+}: {
+ pct: number;
+ color: string;
+ size: number;
+ strokeWidth: number;
+ bgColor: string;
+}) {
+ const r = Math.max(0, Math.min(1, isNaN(pct) ? 0 : pct));
+ const half = size / 2;
+ const innerSize = size - strokeWidth * 2;
+
+ const rightDeg = r <= 0.5 ? -180 + r * 360 : 0;
+ const leftDeg = r <= 0.5 ? -180 : -180 + (r - 0.5) * 360;
+
+ return (
+
+ {/* Gray track disc */}
+
+
+ {/* Right half fill */}
+
+
+
+
+ {/* Left half fill — only shown past 50% */}
+ {r > 0.5 && (
+
+
+
+ )}
+
+ {/* Inner cutout to create ring appearance */}
+
+
+ );
+}
+
+/** Three concentric activity rings (Move / Exercise / Steps). */
+function ActivityRings({
+ movePct,
+ exercisePct,
+ stepsPct,
+ cardColor,
+}: {
+ movePct: number;
+ exercisePct: number;
+ stepsPct: number;
+ cardColor: string;
+}) {
+ const STROKE = 10;
+ const GAP = 3;
+ const OUTER = 86;
+ const MIDDLE = OUTER - STROKE * 2 - GAP * 2;
+ const INNER = MIDDLE - STROKE * 2 - GAP * 2;
+
+ const middleOffset = STROKE + GAP;
+ const innerOffset = STROKE * 2 + GAP * 2;
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
+
+// ─── Mini bar chart ───────────────────────────────────────────────────────────
+
+function MiniBarChart({
+ data,
+ accentColor,
+ c,
+}: {
+ data: BucketPoint[];
+ accentColor: string;
+ c: ReturnType['theme']['colors'];
+}) {
+ const CHART_HEIGHT = 80;
+ const screenW = Dimensions.get('window').width;
+ const chartW = screenW - 32 * 2 - 16 * 2 - 36; // screen - screenPad*2 - cardPad*2 - yAxis
+
+ if (data.length === 0) {
+ return (
+
+
+ No data this week
+
+
+ );
+ }
+
+ const maxVal = Math.max(...data.map((d) => d.value), 0.01);
+ const barW = Math.max(6, Math.floor((chartW - (data.length - 1) * 4) / data.length));
+
+ return (
+
+
+ {/* Y-axis */}
+
+
+ {maxVal.toFixed(1)}
+
+ 0
+
+
+ {/* Bars */}
+
+
+
+
+
+ {data.map((pt, i) => (
+
+
+
+ ))}
+
+
+
+
+ {/* X labels */}
+
+ {data.map((pt, i) => {
+ const show =
+ data.length <= 7 ||
+ i === 0 ||
+ i === data.length - 1 ||
+ i % Math.ceil(data.length / 5) === 0;
+ return (
+
+ {show && (
+
+ {pt.label}
+
+ )}
+
+ );
+ })}
+
+
+ );
+}
+
+const miniChartStyles = StyleSheet.create({
+ chartRow: { flexDirection: 'row', alignItems: 'stretch' },
+ yAxis: { width: 36, justifyContent: 'space-between', paddingRight: 6, alignItems: 'flex-end' },
+ axisLabel: { fontSize: 9 },
+ barArea: { flex: 1, position: 'relative' },
+ gridLine: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ },
+ barRow: { flex: 1, flexDirection: 'row', alignItems: 'flex-end', gap: 4 },
+ barWrapper: { flex: 1, alignItems: 'center', justifyContent: 'flex-end', height: '100%' },
+ xRow: { flexDirection: 'row', marginTop: 4 },
+ xLabel: { fontSize: 9 },
+ empty: { justifyContent: 'center', alignItems: 'center' },
+ emptyText: { fontSize: 13 },
+});
+
+// ─── Metric pill row ──────────────────────────────────────────────────────────
+
+function MetricPills({
+ selected,
+ onSelect,
+ c,
+}: {
+ selected: VoidMetricKey;
+ onSelect: (k: VoidMetricKey) => void;
+ c: ReturnType['theme']['colors'];
+}) {
+ return (
+
+ {METRIC_KEYS.map((key) => {
+ const active = key === selected;
+ return (
+ onSelect(key)}
+ style={[
+ pillStyles.pill,
+ { backgroundColor: active ? c.accent : c.secondaryFill },
+ ]}
+ >
+
+ {METRIC_LABELS[key]}
+
+
+ );
+ })}
+
+ );
+}
+
+const pillStyles = StyleSheet.create({
+ row: { flexDirection: 'row', flexWrap: 'wrap', gap: 6 },
+ pill: { paddingHorizontal: 12, paddingVertical: 5, borderRadius: 20 },
+ text: { fontSize: 12, fontWeight: '600' },
+});
+
+// ─── Vitals row ───────────────────────────────────────────────────────────────
+
+function VitalRow({
+ icon,
+ label,
+ value,
+ color,
+ c,
+ isLast,
+}: {
+ icon: string;
+ label: string;
+ value: string;
+ color: string;
+ c: ReturnType['theme']['colors'];
+ isLast?: boolean;
+}) {
+ return (
+ <>
+
+
+
+
+ {label}
+ {value}
+
+ {!isLast && }
+ >
+ );
+}
+
+const vitalStyles = StyleSheet.create({
+ row: { flexDirection: 'row', alignItems: 'center', paddingVertical: 10, gap: 10 },
+ iconBox: { width: 30, height: 30, borderRadius: 8, alignItems: 'center', justifyContent: 'center' },
+ label: { flex: 1, fontSize: 15 },
+ value: { fontSize: 15, fontWeight: '600' },
+ divider: { height: StyleSheet.hairlineWidth, marginLeft: 40 },
+});
+
+// ─── Helpers ──────────────────────────────────────────────────────────────────
+
+function formatMetricSummary(sessions: ParsedVoidSession[], metric: VoidMetricKey): string {
+ const vals = sessions.map((s) => s[metric]).filter((v): v is number => v !== null);
+ if (vals.length === 0) return '—';
+ const avg = vals.reduce((a, b) => a + b, 0) / vals.length;
+ if (metric === 'durationSeconds') {
+ const sec = Math.round(avg);
+ if (sec >= 60) return `${Math.floor(sec / 60)}m ${sec % 60}s`;
+ return `${sec}s`;
+ }
+ return avg.toFixed(1);
+}
+
+function daysUntil(dateStr: string): string {
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+ const target = new Date(dateStr + 'T00:00:00');
+ const diff = Math.ceil((target.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
+ if (diff === 0) return 'Scheduled for today';
+ if (diff === 1) return 'Tomorrow';
+ if (diff < 0) return '';
+ return `${diff} days from now`;
+}
+
+// ─── Main Screen ──────────────────────────────────────────────────────────────
export default function HomeScreen() {
const { theme } = useAppTheme();
- const { isDark, colors: t } = theme;
+ const { colors: t } = theme;
const surgery = useSurgeryDate();
const watch = useWatchUsage();
+
const [showSurgeryModal, setShowSurgeryModal] = useState(false);
const [showDevSheet, setShowDevSheet] = useState(false);
const [watchDismissed, setWatchDismissed] = useState(false);
+ const [refreshKey, setRefreshKey] = useState(0);
+
+ // ─── Throne data ───────────────────────────────────────────────────────────
+
+ const [sessions, setSessions] = useState([]);
+ const [throneLoading, setThroneLoading] = useState(true);
+ const [selectedMetric, setSelectedMetric] = useState('avgFlowRate');
+
+ useEffect(() => {
+ let cancelled = false;
+
+ async function loadThroneData() {
+ setThroneLoading(true);
+ try {
+ const since = new Date(Date.now() - WEEK_MS);
+ // Match voiding.tsx: no userId filter (Firestore userId field may differ from auth uid)
+ const raw: ThroneSession[] = await fetchSessions({ startDate: since });
+ if (cancelled) return;
+
+ const ids = raw.map((s) => s.id);
+ const allMetrics: ThroneMetric[] = await fetchMetricsBatch(ids);
+ if (cancelled) return;
+
+ // Build sessionId → ThroneMetric[] map (same as voiding.tsx)
+ const metricsMap = new Map();
+ for (const m of allMetrics) {
+ const arr = metricsMap.get(m.sessionId);
+ if (arr) arr.push(m);
+ else metricsMap.set(m.sessionId, [m]);
+ }
+
+ // Keep all sessions (no status filter) — same as voiding.tsx
+ const parsed: ParsedVoidSession[] = raw
+ .map((s) => parseSessionWithMetrics(s, metricsMap.get(s.id) ?? []));
+
+ if (!cancelled) setSessions(parsed);
+ } catch {
+ // silent — empty state shows
+ } finally {
+ if (!cancelled) setThroneLoading(false);
+ }
+ }
+
+ loadThroneData();
+ return () => { cancelled = true; };
+ }, [refreshKey]);
+
+ const weekSessions = useMemo(
+ () => filterByRange(sessions, '1w'),
+ [sessions],
+ );
+
+ const chartData = useMemo(
+ () => bucketSeries(weekSessions, '1w', selectedMetric),
+ [weekSessions, selectedMetric],
+ );
+
+ const metricAvg = useMemo(
+ () => formatMetricSummary(weekSessions, selectedMetric),
+ [weekSessions, selectedMetric],
+ );
+
+ // ─── HealthKit data ────────────────────────────────────────────────────────
+
+ const { summary: health, isLoading: healthLoading, refresh: refreshHealth } = useHealthSummary();
+
+ const activity = health?.activity ?? null;
+ const sleep = health?.raw.sleep ?? null;
+ const vitals = health?.raw.vitals ?? null;
+
+ const movePct = activity ? Math.min(1, activity.energyBurned / MOVE_GOAL_KCAL) : 0;
+ const exercisePct = activity ? Math.min(1, activity.activeMinutes / EXERCISE_GOAL_MIN) : 0;
+ const stepsPct = activity ? Math.min(1, activity.steps / STEPS_GOAL) : 0;
+
+ const sleepLabel = sleep
+ ? `${Math.floor(sleep.totalAsleepMinutes / 60)}h ${sleep.totalAsleepMinutes % 60}m`
+ : '—';
+
+ const hrLabel = vitals?.heartRate?.average
+ ? `${Math.round(vitals.heartRate.average)} bpm`
+ : vitals?.restingHeartRate
+ ? `${Math.round(vitals.restingHeartRate)} bpm`
+ : '—';
+
+ const spo2Label = vitals?.oxygenSaturation
+ ? `${Math.round(vitals.oxygenSaturation * 100)}%`
+ : '—';
+
+ const respLabel = vitals?.respiratoryRate
+ ? `${Math.round(vitals.respiratoryRate)} brpm`
+ : '—';
+
+ // ─── Refresh ───────────────────────────────────────────────────────────────
+
+ const onRefresh = useCallback(() => {
+ setRefreshKey((k) => k + 1);
+ refreshHealth();
+ }, [refreshHealth]);
const handleResetOnboarding = () => {
Alert.alert(
@@ -41,8 +531,7 @@ export default function HomeScreen() {
try {
await OnboardingService.reset();
notifyOnboardingComplete();
- } catch (error) {
- console.error('Error resetting onboarding:', error);
+ } catch {
Alert.alert('Error', 'Failed to reset onboarding');
}
},
@@ -53,14 +542,23 @@ export default function HomeScreen() {
const showWatchReminder = !watch.isLoading && !watch.watchWornRecently && !watchDismissed;
+ // ─── Render ────────────────────────────────────────────────────────────────
+
return (
+ }
>
- {/* Header — Apple Health style: date above, large title below */}
+ {/* Header */}
@@ -89,13 +587,159 @@ export default function HomeScreen() {
)}
- {/* Surgery Date Card */}
+ {/* ─── Throne Science Module ─────────────────────────────────────── */}
+
+
+
+
+
+
+
+
+ Throne Science
+
+
+ Uroflow · Past 7 days
+
+
+
+
+
+ {/* Metric toggles */}
+
+
+ {/* Summary stat */}
+
+
+ {throneLoading ? '—' : metricAvg}
+
+
+ {' '}{METRIC_UNITS[selectedMetric]}
+
+
+ {' '}avg {METRIC_LABELS[selectedMetric].toLowerCase()}
+
+
+
+ {/* Bar chart */}
+ {throneLoading ? (
+
+
+
+ ) : (
+
+ )}
+
+
+ {/* ─── Apple HealthKit Module ────────────────────────────────────── */}
+
+
+
+
+
+
+
+
+ Apple Health
+
+
+ {"Today's summary"}
+
+
+
+
+
+ {healthLoading ? (
+
+
+
+ ) : Platform.OS !== 'ios' ? (
+
+ Apple Health is only available on iOS.
+
+ ) : (
+ <>
+ {/* Activity rings + stats */}
+
+
+
+
+
+
+
+ Move
+
+
+ {activity ? `${Math.round(activity.energyBurned)} kcal` : '—'}
+
+
+
+
+
+ Exercise
+
+
+ {activity ? `${activity.activeMinutes} min` : '—'}
+
+
+
+
+
+ Steps
+
+
+ {activity ? activity.steps.toLocaleString() : '—'}
+
+
+
+
+
+
+
+ {/* Sleep, Heart Rate, SpO2, Respiratory */}
+
+
+
+
+ >
+ )}
+
+
+ {/* ─── Surgery Date Card ─────────────────────────────────────────── */}
-
- Surgery date
-
+ Surgery date
{surgery.isPlaceholder && __DEV__ && (
@@ -125,14 +769,12 @@ export default function HomeScreen() {
)}
- {/* Study Timeline Card */}
+ {/* Study Timeline */}
{!surgery.isLoading && (
-
- Study timeline
-
+ Study timeline
{surgery.isPlaceholder && __DEV__ && (
@@ -143,18 +785,14 @@ export default function HomeScreen() {
-
- Start
-
+ Start
{surgery.studyStartLabel}
-
- End
-
+ End
{surgery.studyEndLabel}
@@ -163,7 +801,7 @@ export default function HomeScreen() {
)}
- {/* Watch Reminder */}
+ {/* Watch reminder */}
{showWatchReminder && (
@@ -196,7 +834,7 @@ export default function HomeScreen() {
)}
- {/* Recovery Instructions card — shown after surgery */}
+ {/* Recovery card */}
{surgery.hasPassed && (
Recovery plan
-
+
-
- Your Recovery Plan
-
+ Your Recovery Plan
Stanford discharge instructions · tap to review
)}
- {/* Study info */}
-
-
- Your Study
-
-
- Track your BPH symptoms, voiding patterns, and recovery progress
- throughout the study. Your daily data helps your care team
- understand your health patterns.
-
-
-
@@ -252,9 +881,7 @@ export default function HomeScreen() {
onPress={() => {}}
>
-
- Developer Tools
-
+ Developer Tools
setShowDevSheet(false)}
activeOpacity={0.7}
>
-
- Close
-
+ Close
@@ -301,57 +926,24 @@ export default function HomeScreen() {
);
}
-function daysUntil(dateStr: string): string {
- const today = new Date();
- today.setHours(0, 0, 0, 0);
- const target = new Date(dateStr + 'T00:00:00');
- const diff = Math.ceil((target.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
- if (diff === 0) return 'Scheduled for today';
- if (diff === 1) return 'Tomorrow';
- if (diff < 0) return '';
- return `${diff} days from now`;
-}
+// ─── Styles ───────────────────────────────────────────────────────────────────
const styles = StyleSheet.create({
- container: {
- flex: 1,
- },
- scrollView: {
- flex: 1,
- },
- scrollContent: {
- paddingHorizontal: 16,
- paddingTop: 8,
- },
+ container: { flex: 1 },
+ scrollView: { flex: 1 },
+ scrollContent: { paddingHorizontal: 16, paddingTop: 8 },
- // Header — matches Apple Health large-title pattern
+ // Header
headerRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-start',
marginBottom: 20,
},
- headerText: {
- flex: 1,
- },
- dateLabel: {
- fontSize: 13,
- fontWeight: '400',
- marginBottom: 2,
- },
- greetingNormal: {
- fontSize: 34,
- fontWeight: '700',
- letterSpacing: 0.37,
- },
- greeting: {
- fontSize: 34,
- fontWeight: '700',
- letterSpacing: 0.37,
- fontStyle: 'italic',
- },
-
- // Dev pill
+ headerText: { flex: 1 },
+ dateLabel: { fontSize: 13, fontWeight: '400', marginBottom: 2 },
+ greetingNormal: { fontSize: 34, fontWeight: '700', letterSpacing: 0.37 },
+ greeting: { fontSize: 34, fontWeight: '700', letterSpacing: 0.37, fontStyle: 'italic' },
devPill: {
flexDirection: 'row',
alignItems: 'center',
@@ -361,102 +953,73 @@ const styles = StyleSheet.create({
borderRadius: 14,
marginTop: 20,
},
- devPillText: {
- fontSize: 12,
- fontWeight: '500',
- },
+ devPillText: { fontSize: 12, fontWeight: '500' },
- // Cards — iOS grouped-style
- card: {
- borderRadius: 12,
+ // Module cards (Throne + HealthKit)
+ moduleCard: {
+ borderRadius: 16,
padding: 16,
marginBottom: 12,
+ gap: 12,
},
- cardHeader: {
+ moduleHeader: {
flexDirection: 'row',
alignItems: 'center',
- gap: 6,
- marginBottom: 8,
+ justifyContent: 'space-between',
},
- cardLabel: {
- fontSize: 13,
- fontWeight: '600',
- letterSpacing: 0.2,
+ moduleHeaderLeft: { flexDirection: 'row', alignItems: 'center', gap: 10 },
+ moduleIconBox: {
+ width: 36,
+ height: 36,
+ borderRadius: 10,
+ alignItems: 'center',
+ justifyContent: 'center',
},
+ moduleTitle: { fontSize: 17, fontWeight: '700' },
+ moduleSubtitle: { fontSize: 12, marginTop: 1 },
+
+ // Metric summary
+ metricSummaryRow: { flexDirection: 'row', alignItems: 'baseline' },
+ metricSummaryValue: { fontSize: 28, fontWeight: '700' },
+ metricSummaryUnit: { fontSize: 15, fontWeight: '500' },
+ metricSummaryLabel: { fontSize: 13 },
+
+ // Loading
+ loadingRow: { height: 80, alignItems: 'center', justifyContent: 'center' },
+ platformNote: { fontSize: 14, fontStyle: 'italic', textAlign: 'center', paddingVertical: 12 },
+
+ // Activity rings section
+ activityRow: { flexDirection: 'row', alignItems: 'center', gap: 20 },
+ activityStats: { flex: 1, gap: 10 },
+ activityStatRow: { flexDirection: 'row', alignItems: 'center', gap: 8 },
+ ringDot: { width: 8, height: 8, borderRadius: 4 },
+ activityStatLabel: { flex: 1, fontSize: 13 },
+ activityStatValue: { fontSize: 13, fontWeight: '600' },
+ sectionDivider: { height: StyleSheet.hairlineWidth, marginVertical: 4 },
+
+ // Legacy cards (surgery, timeline, etc.)
+ card: { borderRadius: 12, padding: 16, marginBottom: 12 },
+ cardHeader: { flexDirection: 'row', alignItems: 'center', gap: 6, marginBottom: 8 },
+ cardLabel: { fontSize: 13, fontWeight: '600', letterSpacing: 0.2 },
accentBorder: {
borderLeftWidth: 3,
borderTopLeftRadius: 12,
borderBottomLeftRadius: 12,
},
- cardValue: {
- fontSize: 22,
- fontWeight: '700',
- letterSpacing: 0.35,
- },
- cardSubtext: {
- fontSize: 15,
- fontWeight: '400',
- marginTop: 4,
- },
- placeholderBadge: {
- paddingHorizontal: 8,
- paddingVertical: 2,
- borderRadius: 6,
- marginLeft: 'auto',
- },
- placeholderBadgeText: {
- fontSize: 11,
- fontWeight: '500',
- },
-
- // Study timeline
- timelineRow: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- timelineItem: {
- flex: 1,
- },
- timelineLabel: {
- fontSize: 13,
- fontWeight: '400',
- marginBottom: 2,
- },
- timelineValue: {
- fontSize: 17,
- fontWeight: '600',
- },
- timelineDivider: {
- width: StyleSheet.hairlineWidth,
- height: 32,
- marginHorizontal: 16,
- },
-
- // Watch reminder
- reminderCard: {
- borderRadius: 12,
- padding: 16,
- marginBottom: 12,
- },
- reminderContent: {
- flexDirection: 'row',
- alignItems: 'flex-start',
- gap: 12,
- },
- reminderTextContainer: {
- flex: 1,
- },
- reminderTitle: {
- fontSize: 15,
- fontWeight: '600',
- marginBottom: 2,
- },
- reminderBody: {
- fontSize: 13,
- lineHeight: 18,
- },
-
- // All set
+ cardValue: { fontSize: 22, fontWeight: '700', letterSpacing: 0.35 },
+ cardSubtext: { fontSize: 15, fontWeight: '400', marginTop: 4 },
+ placeholderBadge: { paddingHorizontal: 8, paddingVertical: 2, borderRadius: 6, marginLeft: 'auto' },
+ placeholderBadgeText: { fontSize: 11, fontWeight: '500' },
+ timelineRow: { flexDirection: 'row', alignItems: 'center' },
+ timelineItem: { flex: 1 },
+ timelineLabel: { fontSize: 13, fontWeight: '400', marginBottom: 2 },
+ timelineValue: { fontSize: 17, fontWeight: '600' },
+ timelineDivider: { width: StyleSheet.hairlineWidth, height: 32, marginHorizontal: 16 },
+ reminderCard: { borderRadius: 12, padding: 16, marginBottom: 12 },
+ reminderContent: { flexDirection: 'row', alignItems: 'flex-start', gap: 12 },
+ reminderTextContainer: { flex: 1 },
+ reminderTitle: { fontSize: 15, fontWeight: '600', marginBottom: 2 },
+ reminderBody: { fontSize: 13, lineHeight: 18 },
allSetCard: {
flexDirection: 'row',
alignItems: 'center',
@@ -465,34 +1028,11 @@ const styles = StyleSheet.create({
padding: 16,
marginBottom: 12,
},
- allSetText: {
- fontSize: 15,
- fontWeight: '400',
- },
-
- // Study section
- sectionTitle: {
- fontSize: 17,
- fontWeight: '600',
- marginBottom: 6,
- },
- studyBody: {
- fontSize: 15,
- lineHeight: 22,
- },
+ allSetText: { fontSize: 15, fontWeight: '400' },
- // Dev tools sheet
- sheetOverlay: {
- flex: 1,
- justifyContent: 'flex-end',
- backgroundColor: 'rgba(0,0,0,0.3)',
- },
- sheetContent: {
- borderTopLeftRadius: 14,
- borderTopRightRadius: 14,
- padding: 20,
- paddingBottom: 40,
- },
+ // Dev sheet
+ sheetOverlay: { flex: 1, justifyContent: 'flex-end', backgroundColor: 'rgba(0,0,0,0.3)' },
+ sheetContent: { borderTopLeftRadius: 14, borderTopRightRadius: 14, padding: 20, paddingBottom: 40 },
sheetHandle: {
width: 36,
height: 5,
@@ -516,17 +1056,7 @@ const styles = StyleSheet.create({
borderRadius: 10,
marginBottom: 8,
},
- sheetButtonText: {
- fontSize: 17,
- fontWeight: '400',
- },
- sheetCancel: {
- alignItems: 'center',
- padding: 14,
- marginTop: 4,
- },
- sheetCancelText: {
- fontSize: 17,
- fontWeight: '600',
- },
+ sheetButtonText: { fontSize: 17, fontWeight: '400' },
+ sheetCancel: { alignItems: 'center', padding: 14, marginTop: 4 },
+ sheetCancelText: { fontSize: 17, fontWeight: '600' },
});