From bce96e8966a6cf1c75759653f738d418abfa0c52 Mon Sep 17 00:00:00 2001 From: chehanw Date: Wed, 28 Jan 2026 15:06:53 -0800 Subject: [PATCH 1/4] Fixed issue with scrollables --- homeflow/app/(onboarding)/_layout.tsx | 1 - homeflow/app/(onboarding)/consent.tsx | 21 ++++++++++++------- homeflow/components/ui/icon-symbol.tsx | 2 ++ .../src/components/QuestionnaireForm.tsx | 4 +++- .../components/questions/ChoiceQuestion.tsx | 2 +- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/homeflow/app/(onboarding)/_layout.tsx b/homeflow/app/(onboarding)/_layout.tsx index aff9344..f9f3983 100644 --- a/homeflow/app/(onboarding)/_layout.tsx +++ b/homeflow/app/(onboarding)/_layout.tsx @@ -65,7 +65,6 @@ export default function OnboardingLayout() { name="baseline-survey" options={{ animation: 'slide_from_bottom', - presentation: 'formSheet', }} /> { + setIsSubmitting(true); + + try { + await OnboardingService.goToStep(OnboardingStep.PERMISSIONS); + router.push('/(onboarding)/permissions' as Href); + } finally { + setIsSubmitting(false); + } + }; + return ( @@ -177,8 +189,6 @@ export default function ConsentScreen() { - {/* Spacer for button */} - @@ -203,7 +213,7 @@ export default function ConsentScreen() { @@ -245,6 +255,7 @@ const styles = StyleSheet.create({ }, scrollContent: { padding: Spacing.screenHorizontal, + paddingBottom: Spacing.xl, }, introBox: { borderRadius: 12, @@ -295,10 +306,6 @@ const styles = StyleSheet.create({ textAlign: 'right', }, footer: { - position: 'absolute', - bottom: 0, - left: 0, - right: 0, padding: Spacing.md, paddingBottom: Spacing.lg, borderTopWidth: StyleSheet.hairlineWidth, diff --git a/homeflow/components/ui/icon-symbol.tsx b/homeflow/components/ui/icon-symbol.tsx index 1bd1dd7..f4b56c1 100644 --- a/homeflow/components/ui/icon-symbol.tsx +++ b/homeflow/components/ui/icon-symbol.tsx @@ -28,9 +28,11 @@ const MAPPING = { // Features & Welcome 'heart.fill': 'favorite', 'chart.line.uptrend.xyaxis': 'trending-up', + 'waveform.path.ecg': 'monitor-heart', 'bell.badge.fill': 'notifications', 'sparkles': 'auto-awesome', 'lock.shield.fill': 'security', + 'lock.shield': 'security', 'star.fill': 'star', // Contacts & Communication diff --git a/homeflow/packages/questionnaire/src/components/QuestionnaireForm.tsx b/homeflow/packages/questionnaire/src/components/QuestionnaireForm.tsx index 0fd6d5a..0cabab1 100644 --- a/homeflow/packages/questionnaire/src/components/QuestionnaireForm.tsx +++ b/homeflow/packages/questionnaire/src/components/QuestionnaireForm.tsx @@ -343,7 +343,9 @@ export function QuestionnaireForm({ { padding: theme.spacing.lg }, scrollContentStyle, ]} - keyboardShouldPersistTaps="handled"> + keyboardShouldPersistTaps="handled" + showsVerticalScrollIndicator={true} + bounces={true}> formik.setFieldValue(linkId, value)} accessibilityRole="button" accessibilityLabel={display} From b5a6d3834791f39a5862231823c04848f3acab17 Mon Sep 17 00:00:00 2001 From: chehanw Date: Wed, 28 Jan 2026 19:36:59 -0800 Subject: [PATCH 2/4] Fix TypeScript build errors in tests and ChoiceQuestion Add tsconfig.json for test directory so VS Code resolves Jest and Node types correctly. Remove invalid delayPressIn prop from Pressable in ChoiceQuestion. Co-Authored-By: Claude Opus 4.5 --- homeflow/lib/services/__tests__/tsconfig.json | 12 ++++++++++++ .../src/components/questions/ChoiceQuestion.tsx | 1 - 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 homeflow/lib/services/__tests__/tsconfig.json diff --git a/homeflow/lib/services/__tests__/tsconfig.json b/homeflow/lib/services/__tests__/tsconfig.json new file mode 100644 index 0000000..6251323 --- /dev/null +++ b/homeflow/lib/services/__tests__/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "types": ["jest", "node"] + }, + "include": [ + "./**/*.test.ts", + "../**/*.ts", + "../../../lib/constants.ts" + ], + "exclude": [] +} diff --git a/homeflow/packages/questionnaire/src/components/questions/ChoiceQuestion.tsx b/homeflow/packages/questionnaire/src/components/questions/ChoiceQuestion.tsx index dad6a3b..d2979e8 100644 --- a/homeflow/packages/questionnaire/src/components/questions/ChoiceQuestion.tsx +++ b/homeflow/packages/questionnaire/src/components/questions/ChoiceQuestion.tsx @@ -76,7 +76,6 @@ export function ChoiceQuestion({ item, formik, theme }: ChoiceQuestionProps) { opacity: pressed ? 0.7 : 1, }, ]} - delayPressIn={60} onPress={() => formik.setFieldValue(linkId, value)} accessibilityRole="button" accessibilityLabel={display} From afdbc8de3534e75319d46e5803a194c8336e3ba7 Mon Sep 17 00:00:00 2001 From: chehanw Date: Wed, 28 Jan 2026 20:50:46 -0800 Subject: [PATCH 3/4] Built chatbot functionality using Open API key and amended consent form using IRB --- .DS_Store | Bin 0 -> 6148 bytes homeflow/Throne_Biodesign_Consent_Extract.txt | 42 +++++ homeflow/app/(onboarding)/baseline-survey.tsx | 16 +- homeflow/app/(onboarding)/chat.tsx | 128 +++++++-------- homeflow/app/(onboarding)/complete.tsx | 6 +- homeflow/app/(onboarding)/consent.tsx | 18 +-- homeflow/app/(onboarding)/ineligible.tsx | 20 ++- homeflow/app/(onboarding)/permissions.tsx | 24 ++- homeflow/app/(onboarding)/welcome.tsx | 4 +- .../components/onboarding/ConsentSection.tsx | 44 ++--- homeflow/components/onboarding/DevToolBar.tsx | 150 ++++++++++++++++++ homeflow/components/onboarding/index.ts | 1 + homeflow/lib/consent/consent-document.ts | 142 +++++------------ homeflow/lib/constants.ts | 6 +- .../eligibility-questionnaire.ts | 42 ++--- .../__tests__/onboarding-service.test.ts | 2 - homeflow/lib/services/onboarding-service.ts | 2 - 17 files changed, 362 insertions(+), 285 deletions(-) create mode 100644 .DS_Store create mode 100644 homeflow/Throne_Biodesign_Consent_Extract.txt create mode 100644 homeflow/components/onboarding/DevToolBar.tsx diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..382132b13182cb1c358b87cbf0706a49a5c18950 GIT binary patch literal 6148 zcmeHK%}T>S5Z-O8-BN@e6nb3nTCmlih?h|73mDOZN=-<#!8BX?V-BT|v%Zi|;`2DO zy8(+icoMNQu=!@^XLsj=>r&lj`4KfT4? zl~PGi=zee=N7=Bod#=*CAE(h&C&Xa{A$K=%8mg?X7HK%sxt?)|hG-01?d9^saXQlJ zIjfFbo*s5VKRRBm8e(t%;OuhvoQzZTYRKfkyOte`1-yast*#eunkFiJ0$-6=#1j$& z!~iis3~UYq_6!is%_*5GB?gFrA2NXZgM^0YTFedVtph%IeMWx`5e0O7OCU;%uEpFS zctE&G1vIJLJ~6mS2fMU!uEpG-NoQQo4CC0Di^mJsvx8mgaK>GO^d$y}fprF|rrW~v z{{nuQ(ntP!3iXHqV&I=Kz-t3<(1)Vz+4`+KJZmMi2WTi5m!kp#`r0J`I=GLtwbS?| b>JaBz%njlwXxHh0bP>>m(1#fK1qMC=_AgB- literal 0 HcmV?d00001 diff --git a/homeflow/Throne_Biodesign_Consent_Extract.txt b/homeflow/Throne_Biodesign_Consent_Extract.txt new file mode 100644 index 0000000..226bf7e --- /dev/null +++ b/homeflow/Throne_Biodesign_Consent_Extract.txt @@ -0,0 +1,42 @@ +STANFORD UNIVERSITY +Research Consent Form – Minimal Risk Medical Human Subject Research + +Protocol Title: Home Uroflow Bladder Outlet Study +Protocol Director (PI): Ryan Sun, MD +IRB Number: IRB# ----- + +STUDY OVERVIEW +You are invited to participate in a research study on benign prostatic hyperplasia (BPH) and lower urinary tract symptoms. This study is being conducted at Stanford University to evaluate whether relief of bladder outlet obstruction can improve objective quality of life and activity measures using wearable devices and home uroflow measurement. + +PURPOSE OF THE STUDY +The purpose of this research is to determine whether improvement in bladder outlet obstruction is associated with measurable improvements in daily activity, sleep, and urinary flow patterns. The results may help improve diagnosis and guide treatment of lower urinary tract symptoms. + +STUDY PROCEDURES +If you choose to participate, you will be asked to take part in the following: +- Registration and setup, including study screening, consent, initial surveys, and mobile application setup (approximately 30 minutes). +- Use of a wearable device (such as a smartwatch) that you already own. You will be asked to wear the device daily and allow data collected by the device to be shared with the study team through a Stanford-designed research application. +- Installation and use of a smart toilet–based uroflow measurement device (Throne One) attached to your home toilet. You will be asked to record as many voids as possible during a one-week period. +- Completion of survey questionnaires collecting demographic information, medical history, and urinary symptoms, including follow-up surveys at 1 and 12 weeks after surgery. +- Sharing of medical information from your medical record and from other health systems through Apple Health and related integrations, with your permission. + +RISKS AND DISCOMFORTS +The risks associated with this study include the time required to download, set up, and use the mobile applications and devices. There is a very small risk of inadvertent disclosure of private health information related to your voiding or wearable data. Although safeguards are in place, there is a small risk of data security breaches. + +BENEFITS +You may not receive any direct benefit from participating in this study. The potential benefit is contributing to research that may improve understanding of voiding dysfunction and help develop better diagnostic and treatment tools for future patients. + +PRIVACY AND DATA PROTECTION +Your data will be handled securely and used only for research purposes. Identifiable information may be removed, and de-identified data may be used for future research studies or shared with other investigators without additional consent. All reasonable efforts will be made to protect your privacy. + +COMPENSATION +You will receive $100 for successful completion of the study. + +VOLUNTARY PARTICIPATION +Your participation in this study is voluntary. You may choose not to participate or to withdraw from the study at any time without penalty or loss of benefits to which you are otherwise entitled. Your decision will not affect your medical care. + +CONTACT INFORMATION +If you have questions, concerns, or complaints about this research study, you may contact the Protocol Director: +Ryan Sun, MD +Phone: 713-677-1764 + +You may also contact the Stanford Institutional Review Board (IRB) for independent information about your rights as a research participant. diff --git a/homeflow/app/(onboarding)/baseline-survey.tsx b/homeflow/app/(onboarding)/baseline-survey.tsx index 486771a..84f2ecd 100644 --- a/homeflow/app/(onboarding)/baseline-survey.tsx +++ b/homeflow/app/(onboarding)/baseline-survey.tsx @@ -24,7 +24,7 @@ import { calculateIPSSScore, getIPSSSeverityDescription, } from '@/lib/questionnaires/ipss-questionnaire'; -import { OnboardingProgressBar, ContinueButton } from '@/components/onboarding'; +import { OnboardingProgressBar, ContinueButton, DevToolBar } from '@/components/onboarding'; import { IconSymbol } from '@/components/ui/icon-symbol'; export default function BaselineSurveyScreen() { @@ -100,6 +100,12 @@ export default function BaselineSurveyScreen() { } }; + // Dev-only handler that bypasses the survey + const handleDevContinue = async () => { + await OnboardingService.goToStep(OnboardingStep.COMPLETE); + router.replace('/(onboarding)/complete' as Href); + }; + const getSeverityColor = () => { if (!score) return colors.icon; switch (score.severity) { @@ -190,6 +196,8 @@ export default function BaselineSurveyScreen() { loading={isSubmitting} /> + + ); } @@ -207,18 +215,18 @@ export default function BaselineSurveyScreen() { - {/* TEMPORARY: QuestionnaireForm has its own ScrollView - no dev button here */} - {/* Use "Reset Onboarding" on home screen to test this flow again */} + + ); } diff --git a/homeflow/app/(onboarding)/chat.tsx b/homeflow/app/(onboarding)/chat.tsx index cdbfae6..275e9ec 100644 --- a/homeflow/app/(onboarding)/chat.tsx +++ b/homeflow/app/(onboarding)/chat.tsx @@ -12,6 +12,8 @@ import { StyleSheet, useColorScheme, Animated, + Keyboard, + TouchableWithoutFeedback, } from 'react-native'; import { useRouter, Href } from 'expo-router'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -20,7 +22,7 @@ import { ChatView, ChatProvider } from '@spezivibe/chat'; import { Colors, StanfordColors, Spacing } from '@/constants/theme'; import { OnboardingStep, STUDY_INFO } from '@/lib/constants'; import { OnboardingService } from '@/lib/services/onboarding-service'; -import { OnboardingProgressBar, ContinueButton } from '@/components/onboarding'; +import { OnboardingProgressBar, ContinueButton, DevToolBar } from '@/components/onboarding'; import { IconSymbol } from '@/components/ui/icon-symbol'; /** @@ -66,10 +68,10 @@ const SYSTEM_PROMPT = `You are a friendly research assistant helping to screen a ## Phase 1: Eligibility (Required Criteria) Check these naturally (don't read a checklist): 1. Has iPhone with iOS 15+ (required) -2. Has Apple Watch (required) -3. Has BPH diagnosis OR experiencing urinary symptoms like frequent urination, weak stream, nighttime urination (required) -4. Considering or scheduled for bladder outlet surgery like TURP, HoLEP, GreenLight laser, UroLift, Rezum, Aquablation (required) -5. Willing to use Throne uroflow device at home (optional - explain it's a portable urine flow meter) +2. Has BPH or lower urinary tract symptoms suspected to be caused by BPH - such as frequent urination, weak stream, nighttime urination (required) +3. Planning to undergo a bladder outlet procedure such as TURP, HoLEP, GreenLight laser, UroLift, Rezum, Aquablation (required) + +Note: Apple Watch and Throne uroflow devices will be provided to participants - do NOT ask about these. ## Phase 2: Medical History (if eligible) @@ -131,14 +133,14 @@ When ineligible: [INELIGIBLE] When ALL medical history sections are complete: [HISTORY_COMPLETE] ## Conversation Flow Example -1. Start with eligibility (devices, diagnosis, surgery plans) +1. Start with eligibility (iPhone, BPH diagnosis/symptoms, surgery plans) 2. Transition after eligible: "Great news! You're eligible for the study. [ELIGIBLE] Now I need to collect some medical history. We'll automatically get things like your age and weight from Apple Health, but I need to ask you about medications, conditions, and a few other things..." 3. Work through sections in order: Demographics → Medications → Surgeries → Labs → Conditions → Clinical data → Planned surgery 4. Before finishing, summarize: "Let me confirm what I have..." then list key points 5. End with: "I have everything I need. [HISTORY_COMPLETE] You can tap Continue to proceed." ## Start the Conversation -"Hi! I'm here to help you join the HomeFlow study. This is a research study that tracks urinary symptoms before and after prostate surgery. Let me ask a few quick questions to make sure this study is right for you. +"Hi! I'm here to help you join the HomeFlow study. This is a research study that tracks urinary symptoms before and after bladder outlet surgery. Let me ask a few quick questions to make sure this study is right for you. First - are you using an iPhone with iOS 15 or later?"`; @@ -203,10 +205,8 @@ export default function OnboardingChatScreen() { await OnboardingService.updateData({ eligibility: { hasIPhone: true, - hasAppleWatch: true, hasBPHDiagnosis: true, consideringSurgery: true, - willingToUseThrone: true, isEligible: true, }, }); @@ -247,75 +247,63 @@ export default function OnboardingChatScreen() { + + ); } return ( - - - - - + + + + + + {getPhaseText()} + + + + + + + Starting conversation... + + + } + /> + + {canContinue && ( + - {getPhaseText()} - - - - - - - Starting conversation... + > + + Great! You're ready for the next step. - - } - /> - - {canContinue && ( - - - Great! You're ready for the next step. - - - - )} - - {/* TEMPORARY: Development-only continue button to test other screens */} - {/* TODO: Remove this once eligibility questions are properly set up */} - {!canContinue && ( - - - ⚠️ Temporary: Skip eligibility for testing - - - - )} - + + + )} + + + + ); } diff --git a/homeflow/app/(onboarding)/complete.tsx b/homeflow/app/(onboarding)/complete.tsx index 3767a78..0e074b3 100644 --- a/homeflow/app/(onboarding)/complete.tsx +++ b/homeflow/app/(onboarding)/complete.tsx @@ -16,9 +16,9 @@ import { import { useRouter } from 'expo-router'; import { SafeAreaView } from 'react-native-safe-area-context'; import { Colors, StanfordColors, Spacing } from '@/constants/theme'; -import { STUDY_INFO } from '@/lib/constants'; +import { STUDY_INFO, OnboardingStep } from '@/lib/constants'; import { OnboardingService } from '@/lib/services/onboarding-service'; -import { ContinueButton } from '@/components/onboarding'; +import { ContinueButton, DevToolBar } from '@/components/onboarding'; import { IconSymbol } from '@/components/ui/icon-symbol'; export default function CompleteScreen() { @@ -189,6 +189,8 @@ export default function CompleteScreen() { )} + + ); } diff --git a/homeflow/app/(onboarding)/consent.tsx b/homeflow/app/(onboarding)/consent.tsx index 6b3b6d4..b3a554d 100644 --- a/homeflow/app/(onboarding)/consent.tsx +++ b/homeflow/app/(onboarding)/consent.tsx @@ -30,6 +30,7 @@ import { ConsentSection, ConsentAgreement, ContinueButton, + DevToolBar, } from '@/components/onboarding'; import { IconSymbol } from '@/components/ui/icon-symbol'; @@ -203,22 +204,9 @@ export default function ConsentScreen() { disabled={!canContinue} loading={isSubmitting} /> - - {/* TEMPORARY: Development-only continue button to test other screens */} - {/* TODO: Remove this once ready for production */} - {!canContinue && ( - - - ⚠️ Temporary: Skip consent for testing - - - - )} + + ); } diff --git a/homeflow/app/(onboarding)/ineligible.tsx b/homeflow/app/(onboarding)/ineligible.tsx index 0f0f478..9db356e 100644 --- a/homeflow/app/(onboarding)/ineligible.tsx +++ b/homeflow/app/(onboarding)/ineligible.tsx @@ -14,13 +14,16 @@ import { Animated, Linking, } from 'react-native'; +import { useRouter, Href } from 'expo-router'; import { SafeAreaView } from 'react-native-safe-area-context'; import { Colors, Spacing } from '@/constants/theme'; -import { STUDY_INFO } from '@/lib/constants'; -import { ContinueButton } from '@/components/onboarding'; +import { STUDY_INFO, OnboardingStep } from '@/lib/constants'; +import { OnboardingService } from '@/lib/services/onboarding-service'; +import { ContinueButton, DevToolBar } from '@/components/onboarding'; import { IconSymbol } from '@/components/ui/icon-symbol'; export default function IneligibleScreen() { + const router = useRouter(); const colorScheme = useColorScheme(); const colors = Colors[colorScheme ?? 'light']; @@ -53,6 +56,12 @@ export default function IneligibleScreen() { // For now, we'll just stay on this screen }; + // Dev-only: skip past ineligible to continue testing the flow + const handleDevContinue = async () => { + await OnboardingService.goToStep(OnboardingStep.CONSENT); + router.push('/(onboarding)/consent' as Href); + }; + return ( This study requires: {'\n'}{'\n'}• An iPhone with iOS 15 or later - {'\n'}• An Apple Watch for health monitoring - {'\n'}• A diagnosis of BPH or related symptoms - {'\n'}• Plans for or consideration of bladder outlet surgery + {'\n'}• BPH or lower urinary tract symptoms suspected to be caused by BPH + {'\n'}• Planning to undergo a bladder outlet procedure @@ -118,6 +126,8 @@ export default function IneligibleScreen() { variant="text" /> + + ); } diff --git a/homeflow/app/(onboarding)/permissions.tsx b/homeflow/app/(onboarding)/permissions.tsx index 63753e2..eb5b7b8 100644 --- a/homeflow/app/(onboarding)/permissions.tsx +++ b/homeflow/app/(onboarding)/permissions.tsx @@ -27,6 +27,7 @@ import { PermissionCard, ContinueButton, PermissionStatus, + DevToolBar, } from '@/components/onboarding'; import { IconSymbol } from '@/components/ui/icon-symbol'; @@ -150,6 +151,12 @@ export default function PermissionsScreen() { } }; + // Dev-only handler that bypasses permission requirements + const handleDevContinue = async () => { + await OnboardingService.goToStep(OnboardingStep.BASELINE_SURVEY); + router.push('/(onboarding)/baseline-survey' as Href); + }; + return ( @@ -220,22 +227,9 @@ export default function PermissionsScreen() { disabled={!canContinue} loading={isLoading} /> - - {/* TEMPORARY: Development-only continue button to test other screens */} - {/* TODO: Remove this once ready for production */} - {!canContinue && ( - - - ⚠️ Temporary: Skip permissions for testing - - - - )} + + ); } diff --git a/homeflow/app/(onboarding)/welcome.tsx b/homeflow/app/(onboarding)/welcome.tsx index de84746..788779d 100644 --- a/homeflow/app/(onboarding)/welcome.tsx +++ b/homeflow/app/(onboarding)/welcome.tsx @@ -18,7 +18,7 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import { Colors, StanfordColors, Spacing } from '@/constants/theme'; import { STUDY_INFO, OnboardingStep } from '@/lib/constants'; import { OnboardingService } from '@/lib/services/onboarding-service'; -import { ContinueButton } from '@/components/onboarding'; +import { ContinueButton, DevToolBar } from '@/components/onboarding'; import { IconSymbol } from '@/components/ui/icon-symbol'; export default function WelcomeScreen() { @@ -133,6 +133,8 @@ export default function WelcomeScreen() { + + ); } diff --git a/homeflow/components/onboarding/ConsentSection.tsx b/homeflow/components/onboarding/ConsentSection.tsx index c731f87..aac5c31 100644 --- a/homeflow/components/onboarding/ConsentSection.tsx +++ b/homeflow/components/onboarding/ConsentSection.tsx @@ -12,8 +12,8 @@ import { StyleSheet, TouchableOpacity, Animated, + LayoutAnimation, useColorScheme, - LayoutChangeEvent, } from 'react-native'; import { IconSymbol } from '@/components/ui/icon-symbol'; import { Colors, StanfordColors, Spacing } from '@/constants/theme'; @@ -39,43 +39,30 @@ export function ConsentSection({ const colors = Colors[colorScheme ?? 'light']; const [expanded, setExpanded] = useState(defaultExpanded); - const [contentHeight, setContentHeight] = useState(0); - const animatedHeight = useRef(new Animated.Value(defaultExpanded ? 1 : 0)).current; - - const handleLayout = (event: LayoutChangeEvent) => { - const { height } = event.nativeEvent.layout; - if (height > 0) { - setContentHeight(height); - } - }; + const rotationAnim = useRef(new Animated.Value(defaultExpanded ? 1 : 0)).current; const toggle = () => { - const toValue = expanded ? 0 : 1; - - Animated.spring(animatedHeight, { - toValue, - useNativeDriver: false, - tension: 50, - friction: 10, - }).start(); + LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); if (!expanded && !isRead && onRead) { onRead(); } setExpanded(!expanded); + + Animated.spring(rotationAnim, { + toValue: expanded ? 0 : 1, + useNativeDriver: true, + tension: 50, + friction: 10, + }).start(); }; - const rotateIcon = animatedHeight.interpolate({ + const rotateIcon = rotationAnim.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '180deg'], }); - const maxHeight = animatedHeight.interpolate({ - inputRange: [0, 1], - outputRange: [0, contentHeight || 500], - }); - // Parse markdown-like bold text const renderContent = (text: string) => { const parts = text.split(/(\*\*.*?\*\*)/g); @@ -128,13 +115,13 @@ export function ConsentSection({ - - + {expanded && ( + {renderContent(content)} - + )} ); } @@ -220,9 +207,6 @@ const styles = StyleSheet.create({ fontWeight: '600', flex: 1, }, - contentWrapper: { - overflow: 'hidden', - }, content: { padding: Spacing.md, paddingTop: 0, diff --git a/homeflow/components/onboarding/DevToolBar.tsx b/homeflow/components/onboarding/DevToolBar.tsx new file mode 100644 index 0000000..227bb0e --- /dev/null +++ b/homeflow/components/onboarding/DevToolBar.tsx @@ -0,0 +1,150 @@ +/** + * Dev Tool Bar + * + * Development-only navigation bar for iterating through onboarding screens. + * Provides Reset (red), Prev (yellow), and Continue (green) buttons. + * + * TODO: Remove before production release. + */ + +import React from 'react'; +import { View, TouchableOpacity, Text, StyleSheet } from 'react-native'; +import { useRouter, Href } from 'expo-router'; +import { OnboardingStep, ONBOARDING_FLOW } from '@/lib/constants'; +import { OnboardingService } from '@/lib/services/onboarding-service'; + +const STEP_TO_PATH: Record = { + [OnboardingStep.WELCOME]: '/(onboarding)/welcome', + [OnboardingStep.CHAT]: '/(onboarding)/chat', + [OnboardingStep.CONSENT]: '/(onboarding)/consent', + [OnboardingStep.PERMISSIONS]: '/(onboarding)/permissions', + [OnboardingStep.BASELINE_SURVEY]: '/(onboarding)/baseline-survey', + [OnboardingStep.COMPLETE]: '/(onboarding)/complete', +}; + +interface DevToolBarProps { + currentStep: OnboardingStep; + onContinue: () => void; +} + +export function DevToolBar({ currentStep, onContinue }: DevToolBarProps) { + const router = useRouter(); + + // Only render in development + if (!__DEV__) return null; + + const currentIndex = ONBOARDING_FLOW.indexOf(currentStep); + const hasPrev = currentIndex > 0; + const hasNext = currentIndex < ONBOARDING_FLOW.length - 1; + + const handleReset = async () => { + await OnboardingService.reset(); + await OnboardingService.start(); + if (router.canDismiss()) { + router.dismissAll(); + } + router.replace('/(onboarding)/welcome' as Href); + }; + + const handlePrev = async () => { + if (!hasPrev) return; + const prevStep = ONBOARDING_FLOW[currentIndex - 1]; + await OnboardingService.goToStep(prevStep); + if (router.canDismiss()) { + router.dismissAll(); + } + router.replace(STEP_TO_PATH[prevStep] as Href); + }; + + return ( + + Dev Tools + + + Reset + + + + + Prev + + + + + Continue + + + + ); +} + +const styles = StyleSheet.create({ + container: { + paddingHorizontal: 16, + paddingTop: 8, + paddingBottom: 12, + borderTopWidth: StyleSheet.hairlineWidth, + borderTopColor: 'rgba(0,0,0,0.15)', + backgroundColor: 'rgba(0,0,0,0.03)', + }, + label: { + fontSize: 10, + fontWeight: '700', + color: '#999', + textAlign: 'center', + marginBottom: 6, + textTransform: 'uppercase', + letterSpacing: 1, + }, + buttonRow: { + flexDirection: 'row', + gap: 8, + }, + button: { + flex: 1, + height: 40, + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + }, + resetButton: { + backgroundColor: '#FF3B30', + }, + prevButton: { + backgroundColor: '#FFCC00', + }, + continueButton: { + backgroundColor: '#34C759', + }, + disabledButton: { + opacity: 0.35, + }, + buttonText: { + fontSize: 14, + fontWeight: '600', + color: '#FFFFFF', + }, + darkText: { + color: '#000000', + }, + disabledText: { + color: 'rgba(255,255,255,0.6)', + }, + disabledDarkText: { + color: 'rgba(0,0,0,0.4)', + }, +}); diff --git a/homeflow/components/onboarding/index.ts b/homeflow/components/onboarding/index.ts index 3cb52da..248289e 100644 --- a/homeflow/components/onboarding/index.ts +++ b/homeflow/components/onboarding/index.ts @@ -7,3 +7,4 @@ export { PermissionCard } from './PermissionCard'; export type { PermissionStatus } from './PermissionCard'; export { ConsentSection, ConsentAgreement } from './ConsentSection'; export { ContinueButton } from './ContinueButton'; +export { DevToolBar } from './DevToolBar'; diff --git a/homeflow/lib/consent/consent-document.ts b/homeflow/lib/consent/consent-document.ts index 213e591..f71e785 100644 --- a/homeflow/lib/consent/consent-document.ts +++ b/homeflow/lib/consent/consent-document.ts @@ -51,154 +51,90 @@ export const CONSENT_DOCUMENT: ConsentDocument = { id: 'overview', title: 'Study Overview', required: true, - content: `You are being asked to participate in a research study. This document provides important information about the study, including its purpose, what participation involves, and the risks and benefits. + content: `You are invited to participate in a research study on benign prostatic hyperplasia (BPH) and lower urinary tract symptoms. This study is being conducted at ${STUDY_INFO.institution} to evaluate whether relief of bladder outlet obstruction can improve objective quality of life and activity measures using wearable devices and home uroflow measurement. -**Study Title:** ${STUDY_INFO.name} +**Protocol Title:** ${STUDY_INFO.name} -**Principal Investigator:** ${STUDY_INFO.principalInvestigator} +**Protocol Director (PI):** ${STUDY_INFO.principalInvestigator} -**Institution:** ${STUDY_INFO.institution} - -**IRB Protocol:** ${STUDY_INFO.irbProtocol} - -Please read this information carefully and take as much time as you need. You may ask questions at any time by contacting the research team.`, +**IRB Number:** ${STUDY_INFO.irbProtocol}`, }, { id: 'purpose', title: 'Purpose of the Study', required: true, - content: `The purpose of this research study is to understand how voiding patterns, physical activity, and sleep change before and after bladder outlet surgery for benign prostatic hyperplasia (BPH). - -By collecting data passively using your iPhone, Apple Watch, and optional Throne uroflowmetry device, we hope to: - -- Better understand recovery patterns after BPH surgery -- Identify factors that predict surgical outcomes -- Develop tools to help patients and doctors monitor recovery - -This study will enroll approximately 100 participants over 2 years.`, + content: `The purpose of this research is to determine whether improvement in bladder outlet obstruction is associated with measurable improvements in daily activity, sleep, and urinary flow patterns. The results may help improve diagnosis and guide treatment of lower urinary tract symptoms.`, }, { id: 'procedures', title: 'Study Procedures', required: true, - content: `If you agree to participate, you will be asked to: - -**During Enrollment (Today)** -- Complete eligibility screening -- Provide informed consent (this document) -- Share access to Apple Health data -- Optionally connect your Throne device -- Complete a baseline symptom questionnaire (IPSS) - -**During the Study (6 months)** -- Allow the app to passively collect health data: - - Step count and activity levels - - Sleep duration and patterns - - Heart rate (if available) - - Voiding data from Throne (if connected) -- Complete brief symptom surveys periodically (about 5 minutes each) -- Continue using your devices normally - -**What We Collect** -- Health data from Apple Watch and iPhone -- Uroflow measurements from Throne (optional) -- Survey responses about your symptoms -- Basic demographic information`, + content: `If you choose to participate, you will be asked to take part in the following: + +\u2022 Registration and setup, including study screening, consent, initial surveys, and mobile application setup (approximately 30 minutes). + +\u2022 Use of a wearable device (such as a smartwatch) that you already own. You will be asked to wear the device daily and allow data collected by the device to be shared with the study team through a Stanford-designed research application. + +\u2022 Installation and use of a smart toilet\u2013based uroflow measurement device (Throne One) attached to your home toilet. You will be asked to record as many voids as possible during a one-week period. + +\u2022 Completion of survey questionnaires collecting demographic information, medical history, and urinary symptoms, including follow-up surveys at 1 and 12 weeks after surgery. + +\u2022 Sharing of medical information from your medical record and from other health systems through Apple Health and related integrations, with your permission.`, }, { id: 'risks', title: 'Risks and Discomforts', required: true, - content: `**Minimal Physical Risk** -This study involves no physical interventions. You will continue your normal medical care. - -**Privacy Risk** -There is a risk that your personal health information could be accessed by unauthorized individuals. We take extensive measures to protect your data (see Privacy section). - -**Time and Inconvenience** -Completing surveys takes a small amount of time. The app runs in the background and should not interfere with normal phone use. - -**Emotional Discomfort** -Some participants may feel uncomfortable answering questions about urinary symptoms. You may skip any question you prefer not to answer.`, + content: `The risks associated with this study include the time required to download, set up, and use the mobile applications and devices. There is a very small risk of inadvertent disclosure of private health information related to your voiding or wearable data. Although safeguards are in place, there is a small risk of data security breaches.`, }, { id: 'benefits', title: 'Benefits', required: true, - content: `**Potential Benefits to You** -- Track your symptoms and recovery over time -- Receive personalized insights about your health patterns -- Contribute to research that may help future patients - -**Benefits to Society** -- Improve understanding of BPH surgery outcomes -- Help develop better monitoring tools for patients -- Advance scientific knowledge in urology`, + content: `You may not receive any direct benefit from participating in this study. The potential benefit is contributing to research that may improve understanding of voiding dysfunction and help develop better diagnostic and treatment tools for future patients.`, }, { id: 'privacy', title: 'Privacy and Data Protection', required: true, - content: `**How We Protect Your Data** -- All data is encrypted in transit and at rest -- Data is stored on secure, HIPAA-compliant servers -- Your identity is separated from your health data -- Only authorized researchers can access study data - -**What We Share** -- De-identified data may be shared with other researchers -- We will never sell your data -- We will never share identifiable data without your permission - -**Your Rights** -- You can request a copy of your data at any time -- You can request deletion of your data -- You can withdraw from the study at any time - -**Data Retention** -- Study data will be retained for 7 years after study completion -- After this period, data will be securely destroyed`, + content: `Your data will be handled securely and used only for research purposes. Identifiable information may be removed, and de-identified data may be used for future research studies or shared with other investigators without additional consent. All reasonable efforts will be made to protect your privacy. + +Although safeguards are in place, there is a small risk of data security breaches.`, + }, + { + id: 'hipaa', + title: 'HIPAA Authorization', + required: true, + content: `By participating in this study, you authorize the sharing of medical information from your medical record and from other health systems through Apple Health and related integrations, with your permission. + +Your data will be handled securely and used only for research purposes. Identifiable information may be removed, and de-identified data may be used for future research studies or shared with other investigators without additional consent. All reasonable efforts will be made to protect your privacy. + +Although safeguards are in place, there is a small risk of data security breaches. + +You may choose not to participate or to withdraw from the study at any time without penalty or loss of benefits to which you are otherwise entitled. Your decision will not affect your medical care.`, }, { id: 'compensation', title: 'Compensation', required: false, - content: `There is no direct compensation for participating in this study. However, you will receive: - -- Free access to the HomeFlow app during the study -- Personalized health insights based on your data -- Summary of your symptom trends over time - -If you are injured as a result of being in this study, ${STUDY_INFO.institution} does not have a program to compensate you.`, + content: `You will receive $100 for successful completion of the study.`, }, { id: 'voluntary', title: 'Voluntary Participation', required: true, - content: `Your participation in this study is completely voluntary. - -- You may choose not to participate -- You may withdraw at any time without penalty -- Withdrawing will not affect your medical care -- You may skip any questions you don't want to answer - -To withdraw from the study, contact the research team or use the "Withdraw from Study" option in the app settings.`, + content: `Your participation in this study is voluntary. You may choose not to participate or to withdraw from the study at any time without penalty or loss of benefits to which you are otherwise entitled. Your decision will not affect your medical care.`, }, { id: 'contact', title: 'Contact Information', required: true, - content: `**Questions About the Study** -Contact the research team: -- Email: ${STUDY_INFO.contactEmail} -- Phone: ${STUDY_INFO.contactPhone} + content: `If you have questions, concerns, or complaints about this research study, you may contact the Protocol Director: -**Questions About Your Rights as a Participant** -Contact the ${STUDY_INFO.institution} Institutional Review Board (IRB): -- Protocol Number: ${STUDY_INFO.irbProtocol} +${STUDY_INFO.principalInvestigator} +Phone: ${STUDY_INFO.contactPhone} -**Medical Concerns** -For any medical concerns, contact your healthcare provider directly.`, +You may also contact the ${STUDY_INFO.institution} Institutional Review Board (IRB) for independent information about your rights as a research participant.`, }, ], }; diff --git a/homeflow/lib/constants.ts b/homeflow/lib/constants.ts index 969c02b..11ea8dc 100644 --- a/homeflow/lib/constants.ts +++ b/homeflow/lib/constants.ts @@ -75,8 +75,8 @@ export const CONSENT_VERSION = '1.0.0'; export const STUDY_INFO = { name: 'HomeFlow BPH Study', institution: 'Stanford University', - principalInvestigator: 'Dr. [PI Name]', - irbProtocol: '[IRB Protocol Number]', + principalInvestigator: 'Ryan Sun, MD', + irbProtocol: 'IRB# -----', contactEmail: 'homeflow-study@stanford.edu', - contactPhone: '(650) 123-4567', + contactPhone: '713-677-1764', } as const; diff --git a/homeflow/lib/questionnaires/eligibility-questionnaire.ts b/homeflow/lib/questionnaires/eligibility-questionnaire.ts index 30ff046..148a152 100644 --- a/homeflow/lib/questionnaires/eligibility-questionnaire.ts +++ b/homeflow/lib/questionnaires/eligibility-questionnaire.ts @@ -16,11 +16,8 @@ import { QuestionnaireBuilder } from '@spezivibe/questionnaire'; */ export interface EligibilityCriteria { hasIPhone: boolean; - hasAppleWatch: boolean; hasBPHDiagnosis: boolean; consideringSurgery: boolean; - willingToUseThrone: boolean; - age?: number; } /** @@ -36,21 +33,12 @@ export function checkEligibility(criteria: EligibilityCriteria): { reasons.push('An iPhone with iOS 15 or later is required for the study.'); } - if (!criteria.hasAppleWatch) { - reasons.push('An Apple Watch is required for continuous health monitoring.'); - } - if (!criteria.hasBPHDiagnosis) { - reasons.push('This study is for individuals diagnosed with BPH or experiencing BPH symptoms.'); + reasons.push('This study is for individuals diagnosed with BPH or experiencing lower urinary tract symptoms suspected to be caused by BPH.'); } if (!criteria.consideringSurgery) { - reasons.push('This study focuses on patients scheduled for or considering bladder outlet surgery.'); - } - - // Throne is optional but encouraged - if (!criteria.willingToUseThrone) { - // This is a soft requirement - we can proceed without it + reasons.push('This study focuses on patients planning to undergo a bladder outlet procedure.'); } return { @@ -70,23 +58,12 @@ export const ELIGIBILITY_QUESTIONNAIRE: Questionnaire = new QuestionnaireBuilder .addBoolean('has_iphone', 'Do you have an iPhone with iOS 15 or later?', { required: true, }) - .addBoolean('has_apple_watch', 'Do you have an Apple Watch?', { + .addBoolean('has_bph_diagnosis', 'Do you have BPH or have lower urinary tract symptoms suspected to be caused by BPH?', { required: true, }) - .addBoolean('has_bph_diagnosis', 'Have you been diagnosed with BPH (benign prostatic hyperplasia) or are you experiencing urinary symptoms?', { + .addBoolean('considering_surgery', 'Are you planning to undergo a bladder outlet procedure?', { required: true, }) - .addBoolean('considering_surgery', 'Are you scheduled for or considering bladder outlet surgery (such as TURP, laser therapy, or other BPH procedures)?', { - required: true, - }) - .addBoolean('willing_throne', 'Are you willing to use a Throne uroflowmetry device to track your voiding patterns? (This is optional but recommended)', { - required: true, - }) - .addInteger('age', 'What is your age?', { - required: false, - min: 18, - max: 120, - }) .build(); /** @@ -98,18 +75,17 @@ export const ELIGIBILITY_CHATBOT_PROMPT = `You are a friendly research assistant ## Study Information - Name: HomeFlow BPH Study - Purpose: Track voiding patterns and symptoms before/after bladder outlet surgery -- Requires: iPhone with iOS 15+, Apple Watch -- Focus: Men with BPH who are considering or scheduled for surgery +- Requires: iPhone with iOS 15+ +- Focus: Men with BPH who are planning to undergo a bladder outlet procedure +- Note: Apple Watch and Throne uroflow devices will be provided to participants ## Your Task Ask about eligibility criteria naturally. Don't read a checklist - have a conversation. ## Eligibility Criteria (must collect all): 1. Has iPhone with iOS 15+ (required) -2. Has Apple Watch (required) -3. Has BPH diagnosis OR experiencing urinary symptoms (required) -4. Considering or scheduled for bladder outlet surgery (required) -5. Willing to use Throne device (optional but encouraged) +2. Has BPH or lower urinary tract symptoms suspected to be caused by BPH (required) +3. Planning to undergo a bladder outlet procedure (required) ## Guidelines - Be warm and conversational, not clinical diff --git a/homeflow/lib/services/__tests__/onboarding-service.test.ts b/homeflow/lib/services/__tests__/onboarding-service.test.ts index c22f485..7a66941 100644 --- a/homeflow/lib/services/__tests__/onboarding-service.test.ts +++ b/homeflow/lib/services/__tests__/onboarding-service.test.ts @@ -310,10 +310,8 @@ describe('OnboardingService', () => { await service.updateData({ eligibility: { hasIPhone: true, - hasAppleWatch: true, hasBPHDiagnosis: true, consideringSurgery: true, - willingToUseThrone: true, isEligible: true, }, }); diff --git a/homeflow/lib/services/onboarding-service.ts b/homeflow/lib/services/onboarding-service.ts index 23a3347..0cbf239 100644 --- a/homeflow/lib/services/onboarding-service.ts +++ b/homeflow/lib/services/onboarding-service.ts @@ -15,10 +15,8 @@ export interface OnboardingData { // Eligibility responses (from chatbot) eligibility?: { hasIPhone: boolean; - hasAppleWatch: boolean; hasBPHDiagnosis: boolean; consideringSurgery: boolean; - willingToUseThrone: boolean; isEligible: boolean; }; From 8d0648f4f82334df9d3efd2cc638e2ebec04d1fa Mon Sep 17 00:00:00 2001 From: chehanw Date: Wed, 28 Jan 2026 21:09:51 -0800 Subject: [PATCH 4/4] UI changes to consent form --- homeflow/app/(onboarding)/consent.tsx | 17 +++++++++++++ .../components/onboarding/ConsentSection.tsx | 24 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/homeflow/app/(onboarding)/consent.tsx b/homeflow/app/(onboarding)/consent.tsx index b3a554d..4849b6e 100644 --- a/homeflow/app/(onboarding)/consent.tsx +++ b/homeflow/app/(onboarding)/consent.tsx @@ -13,6 +13,8 @@ import { ScrollView, useColorScheme, TextInput, + KeyboardAvoidingView, + Platform, } from 'react-native'; import { useRouter, Href } from 'expo-router'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -99,11 +101,17 @@ export default function ConsentScreen() { + {/* Introduction */} { + setTimeout(() => { + scrollViewRef.current?.scrollToEnd({ animated: true }); + }, 300); + }} /> Date: {new Date().toLocaleDateString()} @@ -191,6 +204,7 @@ export default function ConsentScreen() { + {!allRequiredRead && ( @@ -238,6 +252,9 @@ const styles = StyleSheet.create({ textAlign: 'center', marginTop: 4, }, + keyboardAvoid: { + flex: 1, + }, scrollView: { flex: 1, }, diff --git a/homeflow/components/onboarding/ConsentSection.tsx b/homeflow/components/onboarding/ConsentSection.tsx index aac5c31..57f6955 100644 --- a/homeflow/components/onboarding/ConsentSection.tsx +++ b/homeflow/components/onboarding/ConsentSection.tsx @@ -120,6 +120,16 @@ export function ConsentSection({ {renderContent(content)} + + + Collapse + + + )} @@ -215,6 +225,20 @@ const styles = StyleSheet.create({ fontSize: 15, lineHeight: 22, }, + collapseButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 6, + marginTop: Spacing.md, + paddingVertical: Spacing.sm, + borderTopWidth: StyleSheet.hairlineWidth, + borderTopColor: 'rgba(0,0,0,0.1)', + }, + collapseButtonText: { + fontSize: 14, + fontWeight: '600', + }, bold: { fontWeight: '600', },