Skip to content
Open
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: 4 additions & 0 deletions mobile/lib/components/bottom_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:cancerlinc/pages/checklist_page.dart';
import 'package:cancerlinc/pages/home_page.dart';
import 'package:cancerlinc/pages/login_page.dart';
import 'package:cancerlinc/pages/referrals_page.dart';
import 'package:cancerlinc/utils/haptics.dart';

class BottomBar extends StatefulWidget {
const BottomBar({super.key});
Expand Down Expand Up @@ -48,6 +49,7 @@ class BottomBarState extends State<BottomBar> {
),
TextButton(
onPressed: () {
AppHaptics.warning();
Navigator.pop(context);
setState(() {
_checklistHasUnsavedChanges = false;
Expand All @@ -59,6 +61,7 @@ class BottomBarState extends State<BottomBar> {
),
TextButton(
onPressed: () async {
AppHaptics.confirm();
Navigator.pop(context);
await _checklistKey.currentState?.save();
setState(() {
Expand All @@ -74,6 +77,7 @@ class BottomBarState extends State<BottomBar> {
}

Future<void> _logout() async {
AppHaptics.warning();
await FirebaseAuth.instance.signOut();
if (!mounted) return;
Navigator.of(context).pushAndRemoveUntil(
Expand Down
17 changes: 14 additions & 3 deletions mobile/lib/pages/calendar_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:cancerlinc/models/event.dart';
import 'package:cancerlinc/services/event_service.dart';
import 'package:cancerlinc/utils/haptics.dart';

// ─── Design tokens (match checklist page) ──────────────────────────────────────
const _dark = Color(0xFF3F454F);
Expand Down Expand Up @@ -227,13 +228,19 @@ class _CalendarHeader extends StatelessWidget {
? 'Filter'
: 'Filter (${selectedTags.length})',
icon: Icons.filter_list,
onPressed: () => _showFilterSheet(context),
onPressed: () {
AppHaptics.tap();
_showFilterSheet(context);
},
),
const SizedBox(width: 8),
_GreenButton(
label: 'Pick Date',
icon: Icons.calendar_today_outlined,
onPressed: () => _showCalendarPicker(context),
onPressed: () {
AppHaptics.tap();
_showCalendarPicker(context);
},
),
],
),
Expand Down Expand Up @@ -313,6 +320,7 @@ class _CalendarHeader extends StatelessWidget {
backgroundColor: _green,
),
onPressed: () {
AppHaptics.confirm();
onFilterChanged(tempSelected);
Navigator.pop(context);
},
Expand Down Expand Up @@ -545,7 +553,10 @@ class _EventCard extends StatelessWidget {
backgroundColor: _green,
padding: const EdgeInsets.symmetric(vertical: 12),
),
onPressed: () => Navigator.pop(context),
onPressed: () {
AppHaptics.tap();
Navigator.pop(context);
},
child: Text('Close', style: _bold(15)),
),
),
Expand Down
3 changes: 3 additions & 0 deletions mobile/lib/pages/change_password.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:cancerlinc/utils/haptics.dart';

class ChangePasswordPage extends StatefulWidget {
final String code; // verification code passed from forgot_password.dart
Expand Down Expand Up @@ -39,6 +40,7 @@ class _ChangePasswordPageState extends State<ChangePasswordPage> {
void _onSubmit() {
// require that the incoming verification code is not empty
if (widget.code.isEmpty) {
AppHaptics.warning();
// shouldn't happen if caller passed a code, but guard anyway
showDialog<void>(
context: context,
Expand All @@ -57,6 +59,7 @@ class _ChangePasswordPageState extends State<ChangePasswordPage> {
}

if (_formKey.currentState?.validate() ?? false) {
AppHaptics.confirm();
// at this point we'd call the backend to change the password using
// the verification code (widget.code) and the new password.
// for this model, just show a confirmation and pop to login
Expand Down
8 changes: 7 additions & 1 deletion mobile/lib/pages/chat_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:cancerlinc/services/chat_service.dart';
import 'package:cancerlinc/services/notification_service.dart';
import 'package:cancerlinc/components/call_number.dart';
import 'package:cancerlinc/components/search_panel.dart';
import 'package:cancerlinc/utils/haptics.dart';

/// FEATURE FLAG (Ticket M4) — show a non-blocking "pending verification"
/// notice to socially-unverified (non-banned) patients. They can STILL chat;
Expand Down Expand Up @@ -959,7 +960,12 @@ class _InputButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
onTap: onTap == null
? null
: () {
AppHaptics.tap();
onTap!();
},
child: Container(
width: 48,
height: 48,
Expand Down
16 changes: 14 additions & 2 deletions mobile/lib/pages/checklist_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:cancerlinc/models/checklist.dart';
import 'package:cancerlinc/services/checklist_service.dart';
import 'package:cancerlinc/utils/haptics.dart';

// ─── Design tokens ─────────────────────────────────────────────────────────────
const _dark = Color(0xFF3F454F);
Expand Down Expand Up @@ -168,6 +169,7 @@ class ChecklistPageState extends State<ChecklistPage> {
_DarkButton(
label: 'Add',
onPressed: () {
AppHaptics.confirm();
final tempId = 'local_${DateTime.now().millisecondsSinceEpoch}';
setState(() {
_localChecklists.add({
Expand Down Expand Up @@ -206,6 +208,7 @@ class ChecklistPageState extends State<ChecklistPage> {
_DarkButton(
label: 'Delete',
onPressed: () {
AppHaptics.warning();
final i = _localIndexByDocId(docId);
if (i != -1) {
setState(() => _localChecklists[i]['archived'] = true);
Expand Down Expand Up @@ -238,6 +241,7 @@ class ChecklistPageState extends State<ChecklistPage> {
_DarkButton(
label: 'Add',
onPressed: () {
AppHaptics.tap();
final i = _localIndexByDocId(docId);
if (i != -1) {
setState(() {
Expand Down Expand Up @@ -361,7 +365,12 @@ class ChecklistPageState extends State<ChecklistPage> {
),
const SizedBox(width: 8),
GestureDetector(
onTap: canSave ? save : null,
onTap: canSave
? () {
AppHaptics.confirm();
save();
}
: null,
child: Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 12),
Expand Down Expand Up @@ -668,7 +677,10 @@ class ArchivePage extends StatelessWidget {
style: _regular(14, color: _placeholder)),
const SizedBox(height: 14),
GestureDetector(
onTap: () => checklistService.duplicateChecklist(checklist),
onTap: () {
AppHaptics.tap();
checklistService.duplicateChecklist(checklist);
},
child: Container(
height: 40,
decoration: BoxDecoration(
Expand Down
7 changes: 6 additions & 1 deletion mobile/lib/pages/create_account.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:cancerlinc/services/auth.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:cancerlinc/utils/haptics.dart';

class CreateAccountPage extends StatefulWidget {
const CreateAccountPage({super.key});
Expand Down Expand Up @@ -111,6 +112,7 @@ class _CreateAccountPageState extends State<CreateAccountPage> {
);
}
} on FirebaseAuthException catch (e) {
AppHaptics.warning();
setState(() {
_errorMessage = _getFirebaseError(e);
});
Expand Down Expand Up @@ -357,7 +359,10 @@ class _CreateAccountPageState extends State<CreateAccountPage> {
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: _onSignUp,
onPressed: () {
AppHaptics.confirm();
_onSignUp();
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF43474F),
shape: RoundedRectangleBorder(
Expand Down
4 changes: 4 additions & 0 deletions mobile/lib/pages/login_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:cancerlinc/pages/forgot_password.dart';
import 'package:cancerlinc/pages/create_account.dart';
import 'package:cancerlinc/components/bottom_bar.dart';
import 'package:cancerlinc/services/auth.dart';
import 'package:cancerlinc/utils/haptics.dart';

class LoginPage extends StatefulWidget {
const LoginPage({super.key});
Expand Down Expand Up @@ -185,6 +186,7 @@ class LoginPage extends StatefulWidget {
height: 46,
child: ElevatedButton(
onPressed: () async {
AppHaptics.confirm();
try {
final userCredential = await _authService.signIn(
_emailController.text.trim(),
Expand All @@ -202,6 +204,7 @@ class LoginPage extends StatefulWidget {
);
}
} on FirebaseAuthException {
AppHaptics.warning();
setState(() {
_errorMessage = 'Incorrect Email or Password Entered';
});
Expand Down Expand Up @@ -230,6 +233,7 @@ class LoginPage extends StatefulWidget {
height: 46,
child: ElevatedButton(
onPressed: () {
AppHaptics.tap();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => CreateAccountPage()),
Expand Down
13 changes: 10 additions & 3 deletions mobile/lib/pages/verify_email.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:cancerlinc/components/bottom_bar.dart';
import 'package:cancerlinc/utils/haptics.dart';

class VerifyEmail extends StatefulWidget {
final String email;
Expand Down Expand Up @@ -131,8 +132,11 @@ class _VerifyEmailState extends State<VerifyEmail> {
width: double.infinity,
height: 46,
child: OutlinedButton(
onPressed: () =>
FirebaseAuth.instance.currentUser?.sendEmailVerification(),
onPressed: () {
AppHaptics.tap();
FirebaseAuth.instance.currentUser
?.sendEmailVerification();
},
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
overlayColor: Colors.black,
Expand All @@ -147,7 +151,10 @@ class _VerifyEmailState extends State<VerifyEmail> {
width: double.infinity,
height: 46,
child: ElevatedButton(
onPressed: () => Navigator.popUntil(context, (route) => route.isFirst),
onPressed: () {
AppHaptics.tap();
Navigator.popUntil(context, (route) => route.isFirst);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF43474F),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
Expand Down
10 changes: 10 additions & 0 deletions mobile/lib/utils/haptics.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import 'package:flutter/services.dart';

// handles haptic feedback consistently throughout app
class AppHaptics {
static void tap() => HapticFeedback.lightImpact(); // routine buttons
static void confirm() => HapticFeedback.mediumImpact(); // submit/save actions
static void warning() =>
HapticFeedback.heavyImpact(); // destructive/critical actions
static void select() => HapticFeedback.selectionClick(); // nav/tab switches
}