From ec4b37dbb2ffcbdd77d93e87c4f1947d342651b9 Mon Sep 17 00:00:00 2001 From: Owais Jamil Date: Tue, 17 Mar 2026 13:54:29 -0500 Subject: [PATCH] feat: standardize app bar titles with serif --- docs/tasks/phase-3.md | 16 ++++++++-------- lib/core/router/app_shell.dart | 2 ++ lib/core/theme/typography.dart | 6 +++--- .../feed/presentation/home_feed_screen.dart | 8 +++++--- .../presentation/notifications_screen.dart | 4 +++- .../settings/presentation/settings_screen.dart | 4 +++- .../bloc/notification_bloc_test.dart | 6 +++--- .../data/notification_repository_test.dart | 4 ++-- 8 files changed, 29 insertions(+), 21 deletions(-) diff --git a/docs/tasks/phase-3.md b/docs/tasks/phase-3.md index a411c66..fc07bf2 100644 --- a/docs/tasks/phase-3.md +++ b/docs/tasks/phase-3.md @@ -20,14 +20,14 @@ ## M9 — Notifications -- [ ] Notifications screen with grouped-by-day notification list -- [ ] `NotificationBloc` — events: `NotificationsRequested`, `NotificationsRefreshed`, `NotificationsPageLoaded`, `NotificationsMarkedRead` -- [ ] Fetch notifications via `listNotifications` with cursor pagination -- [ ] Render all notification reasons: like, repost, follow, mention, reply, quote -- [ ] Each notification row: author avatar, reason icon, summary text, optional post preview -- [ ] Unread count badge on nav bar via `getUnreadCount` polling (30s interval) -- [ ] Mark as read via `updateSeen` when notifications screen opens -- [ ] Tap notification to navigate to relevant post or profile +- [x] Notifications screen with grouped-by-day notification list +- [x] `NotificationBloc` — events: `NotificationsRequested`, `NotificationsRefreshed`, `NotificationsPageLoaded`, `NotificationsMarkedRead` +- [x] Fetch notifications via `listNotifications` with cursor pagination +- [x] Render all notification reasons: like, repost, follow, mention, reply, quote +- [x] Each notification row: author avatar, reason icon, summary text, optional post preview +- [x] Unread count badge on nav bar via `getUnreadCount` polling (30s interval) +- [x] Mark as read via `updateSeen` when notifications screen opens +- [x] Tap notification to navigate to relevant post or profile ## M10 — Post & Profile Actions diff --git a/lib/core/router/app_shell.dart b/lib/core/router/app_shell.dart index b1405e7..17f84ca 100644 --- a/lib/core/router/app_shell.dart +++ b/lib/core/router/app_shell.dart @@ -13,11 +13,13 @@ class AppShell extends StatelessWidget { return Scaffold( body: navigationShell, bottomNavigationBar: NavigationBar( + height: 50, backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest, selectedIndex: navigationShell.currentIndex, onDestinationSelected: (index) { navigationShell.goBranch(index, initialLocation: index == navigationShell.currentIndex); }, + indicatorShape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(10)), labelBehavior: NavigationDestinationLabelBehavior.alwaysHide, destinations: _destinations, ), diff --git a/lib/core/theme/typography.dart b/lib/core/theme/typography.dart index 50740b1..34342d5 100644 --- a/lib/core/theme/typography.dart +++ b/lib/core/theme/typography.dart @@ -60,9 +60,9 @@ class AppTypography { headlineLarge: lora(fontSize: 32, fontWeight: FontWeight.w600, color: headlineColor), headlineMedium: lora(fontSize: 28, fontWeight: FontWeight.w600, color: headlineColor), headlineSmall: lora(fontSize: 24, fontWeight: FontWeight.w600, color: headlineColor), - titleLarge: dmSans(fontSize: 22, fontWeight: FontWeight.w600, color: bodyColor), - titleMedium: dmSans(fontSize: 16, fontWeight: FontWeight.w500, color: bodyColor, letterSpacing: 0.15), - titleSmall: jetBrainsMono(fontSize: 14, fontWeight: FontWeight.w500, color: bodyColor, letterSpacing: 0.1), + titleLarge: lora(fontSize: 22, fontWeight: FontWeight.w600, color: bodyColor), + titleMedium: lora(fontSize: 16, fontWeight: FontWeight.w500, color: bodyColor, letterSpacing: 0.15), + titleSmall: lora(fontSize: 14, fontWeight: FontWeight.w500, color: bodyColor, letterSpacing: 0.1), bodyLarge: dmSans(fontSize: 16, fontWeight: FontWeight.w400, color: bodyColor, letterSpacing: 0.5), bodyMedium: dmSans(fontSize: 14, fontWeight: FontWeight.w400, color: bodyColor, letterSpacing: 0.25), bodySmall: jetBrainsMono(fontSize: 12, fontWeight: FontWeight.w400, color: captionColor, letterSpacing: 0.4), diff --git a/lib/features/feed/presentation/home_feed_screen.dart b/lib/features/feed/presentation/home_feed_screen.dart index 6d3283a..7222500 100644 --- a/lib/features/feed/presentation/home_feed_screen.dart +++ b/lib/features/feed/presentation/home_feed_screen.dart @@ -41,7 +41,7 @@ class _HomeFeedScreenState extends State { if (prefsState.status == FeedPreferencesStatus.error) { return Scaffold( - appBar: AppBar(title: const Text('Home')), + appBar: AppBar(title: _title), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -64,7 +64,7 @@ class _HomeFeedScreenState extends State { if (pinnedFeeds.isEmpty) { return Scaffold( - appBar: AppBar(title: const Text('Home')), + appBar: AppBar(title: _title), body: Center( child: Padding( padding: const EdgeInsets.all(24), @@ -92,7 +92,7 @@ class _HomeFeedScreenState extends State { return Scaffold( appBar: AppBar( - title: const Text('Home'), + title: _title, actions: [IconButton(icon: const Icon(Icons.rss_feed), onPressed: () => context.push('/feeds'))], ), body: Column( @@ -147,6 +147,8 @@ class _HomeFeedScreenState extends State { return index >= 0 ? index : 0; } + Widget get _title => Text('Home', style: Theme.of(context).textTheme.titleLarge); + Widget _buildTabBar( BuildContext context, List feeds, diff --git a/lib/features/notifications/presentation/notifications_screen.dart b/lib/features/notifications/presentation/notifications_screen.dart index 688f654..d3d4d24 100644 --- a/lib/features/notifications/presentation/notifications_screen.dart +++ b/lib/features/notifications/presentation/notifications_screen.dart @@ -48,11 +48,13 @@ class _NotificationsScreenState extends State { context.read().refresh(); } + Widget get _title => Text('Notifications', style: Theme.of(context).textTheme.titleMedium); + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Notifications'), + title: _title, actions: [TextButton(onPressed: _markAllRead, child: const Text('Mark All Read'))], ), body: BlocBuilder( diff --git a/lib/features/settings/presentation/settings_screen.dart b/lib/features/settings/presentation/settings_screen.dart index b962f76..8b60e5f 100644 --- a/lib/features/settings/presentation/settings_screen.dart +++ b/lib/features/settings/presentation/settings_screen.dart @@ -13,7 +13,7 @@ class SettingsScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Settings'), + title: _title(context), actions: [ TextButton( onPressed: () { @@ -117,6 +117,8 @@ class SettingsScreen extends StatelessWidget { ); } + Widget _title(BuildContext context) => Text('Settings', style: Theme.of(context).textTheme.titleLarge); + Widget _buildThemeSelector(BuildContext context) { final settingsCubit = context.read(); diff --git a/test/features/notifications/bloc/notification_bloc_test.dart b/test/features/notifications/bloc/notification_bloc_test.dart index 44a113a..ba8177c 100644 --- a/test/features/notifications/bloc/notification_bloc_test.dart +++ b/test/features/notifications/bloc/notification_bloc_test.dart @@ -20,7 +20,7 @@ void main() { final sampleNotification = bsky.Notification( uri: AtUri.parse('at://did:plc:author/app.bsky.feed.post/abc'), cid: 'cid-123', - author: ProfileView(did: 'did:plc:author', handle: 'author.bsky.social'), + author: const ProfileView(did: 'did:plc:author', handle: 'author.bsky.social'), reason: const bsky.NotificationReason.knownValue(data: bsky.KnownNotificationReason.like), record: {r'$type': 'app.bsky.feed.post', 'text': 'Hello world'}, isRead: false, @@ -77,7 +77,7 @@ void main() { final secondNotification = bsky.Notification( uri: AtUri.parse('at://did:plc:author2/app.bsky.feed.post/def'), cid: 'cid-456', - author: ProfileView(did: 'did:plc:author2', handle: 'author2.bsky.social'), + author: const ProfileView(did: 'did:plc:author2', handle: 'author2.bsky.social'), reason: const bsky.NotificationReason.knownValue(data: bsky.KnownNotificationReason.follow), record: {}, isRead: true, @@ -155,7 +155,7 @@ void main() { final newNotification = bsky.Notification( uri: AtUri.parse('at://did:plc:new/app.bsky.feed.post/new'), cid: 'cid-new', - author: ProfileView(did: 'did:plc:new', handle: 'new.bsky.social'), + author: const ProfileView(did: 'did:plc:new', handle: 'new.bsky.social'), reason: const bsky.NotificationReason.knownValue(data: bsky.KnownNotificationReason.repost), record: {}, isRead: false, diff --git a/test/features/notifications/data/notification_repository_test.dart b/test/features/notifications/data/notification_repository_test.dart index 83906e8..2f9f0f8 100644 --- a/test/features/notifications/data/notification_repository_test.dart +++ b/test/features/notifications/data/notification_repository_test.dart @@ -18,7 +18,7 @@ void main() { final sampleNotification = bsky.Notification( uri: AtUri.parse('at://did:plc:author/app.bsky.feed.post/abc'), cid: 'cid-123', - author: ProfileView(did: 'did:plc:author', handle: 'author.bsky.social'), + author: const ProfileView(did: 'did:plc:author', handle: 'author.bsky.social'), reason: const bsky.NotificationReason.knownValue(data: bsky.KnownNotificationReason.like), record: {r'$type': 'app.bsky.feed.post', 'text': 'Hello world'}, isRead: false, @@ -97,7 +97,7 @@ void main() { final notification = bsky.Notification( uri: AtUri.parse('at://did:plc:test/app.bsky.notification/1'), cid: 'cid', - author: ProfileView(did: 'did:plc:test', handle: 'test.bsky.social'), + author: const ProfileView(did: 'did:plc:test', handle: 'test.bsky.social'), reason: const bsky.NotificationReason.knownValue(data: bsky.KnownNotificationReason.follow), record: {}, isRead: true, -- 2.51.2