Skip to content

Commit 859d844

Browse files
committed
feat: add no relay indicator and heads-up notification in chat list
1 parent 54f9e22 commit 859d844

File tree

6 files changed

+149
-10
lines changed

6 files changed

+149
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Added
11+
- Global No relay indicator
1112

1213
### Changed
1314

assets/pngs/ic_add_chat.png

-83 Bytes
Loading

assets/pngs/ic_off_chat.png

852 Bytes
Loading

lib/ui/contact_list/chat_list_screen.dart

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import 'package:whitenoise/config/providers/group_provider.dart';
1111
import 'package:whitenoise/config/providers/polling_provider.dart';
1212
import 'package:whitenoise/config/providers/profile_provider.dart';
1313
import 'package:whitenoise/config/providers/profile_ready_card_visibility_provider.dart';
14+
import 'package:whitenoise/config/providers/relay_status_provider.dart';
1415
import 'package:whitenoise/config/providers/welcomes_provider.dart';
1516
import 'package:whitenoise/domain/models/chat_list_item.dart';
17+
import 'package:whitenoise/models/relay_status.dart';
1618
import 'package:whitenoise/routing/routes.dart';
1719
import 'package:whitenoise/src/rust/api/welcomes.dart';
1820
import 'package:whitenoise/ui/contact_list/new_chat_bottom_sheet.dart';
@@ -24,6 +26,7 @@ import 'package:whitenoise/ui/core/themes/assets.dart';
2426
import 'package:whitenoise/ui/core/themes/src/extensions.dart';
2527
import 'package:whitenoise/ui/core/ui/wn_app_bar.dart';
2628
import 'package:whitenoise/ui/core/ui/wn_bottom_fade.dart';
29+
import 'package:whitenoise/ui/core/ui/wn_heads_up.dart';
2730
import 'package:whitenoise/ui/core/ui/wn_text_form_field.dart';
2831

2932
class ChatListScreen extends ConsumerStatefulWidget {
@@ -219,6 +222,13 @@ class _ChatListScreenState extends ConsumerState<ChatListScreen> with TickerProv
219222
(item.lastMessage?.content?.toLowerCase().contains(searchLower) ?? false);
220223
}).toList();
221224

225+
final noRelayConnected = ref
226+
.watch(relayStatusProvider)
227+
.relayStatuses
228+
.values
229+
.every(
230+
(status) => status != RelayStatus.connected,
231+
);
222232
return GestureDetector(
223233
onTap: () {
224234
if (_searchFocusNode.hasFocus) {
@@ -251,16 +261,22 @@ class _ChatListScreenState extends ConsumerState<ChatListScreen> with TickerProv
251261
),
252262
actions: [
253263
IconButton(
254-
onPressed: () {
255-
if (_searchFocusNode.hasFocus) {
256-
_searchFocusNode.unfocus();
257-
}
258-
NewChatBottomSheet.show(context);
259-
},
264+
onPressed:
265+
noRelayConnected
266+
? null
267+
: () {
268+
if (_searchFocusNode.hasFocus) {
269+
_searchFocusNode.unfocus();
270+
}
271+
NewChatBottomSheet.show(context);
272+
},
260273
icon: Image.asset(
261-
AssetsPaths.icAddNewChat,
262-
width: 32.w,
263-
height: 32.w,
274+
noRelayConnected ? AssetsPaths.icOffChat : AssetsPaths.icAddNewChat,
275+
width: 21.w,
276+
height: 21.w,
277+
color: context.colors.primaryForeground.withValues(
278+
alpha: noRelayConnected ? 0.5 : 1.0,
279+
),
264280
),
265281
),
266282
Gap(8.w),
@@ -318,7 +334,26 @@ class _ChatListScreenState extends ConsumerState<ChatListScreen> with TickerProv
318334
},
319335
),
320336
),
321-
337+
if (noRelayConnected)
338+
SliverToBoxAdapter(
339+
child:
340+
WnStickyHeadsUp(
341+
title: 'No Relays Connected',
342+
subtitle: 'The app won\'t work until you add at least one.',
343+
action: InkWell(
344+
child: Text(
345+
'Connect Relays',
346+
style: TextStyle(
347+
fontSize: 14.sp,
348+
color: context.colors.primary,
349+
fontWeight: FontWeight.w600,
350+
decoration: TextDecoration.underline,
351+
),
352+
),
353+
onTap: () => context.push(Routes.settingsNetwork),
354+
),
355+
).animate().fadeIn(),
356+
),
322357
if (_isSearchVisible)
323358
SliverPadding(
324359
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 16.h),

lib/ui/core/themes/assets.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class AssetsPaths {
5959
static const String greenBird = '$_pngsDir/green_bird.png';
6060
static const String hands = '$_pngsDir/hands.png';
6161
static const String icAddNewChat = '$_pngsDir/ic_add_chat.png';
62+
static const String icOffChat = '$_pngsDir/ic_off_chat.png';
6263
static const String icNotificationMuted = '$_pngsDir/ic_notification_muted.png';
6364
static const String icCheckmarkSolid = '$_pngsDir/ic_checkmark_solid.png';
6465
static const String icCheckmarkDashed = '$_pngsDir/ic_checkmark_dashed.png';

lib/ui/core/ui/wn_heads_up.dart

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:gap/gap.dart';
3+
import 'package:supa_carbon_icons/supa_carbon_icons.dart';
4+
import 'package:whitenoise/ui/core/themes/src/app_theme.dart';
5+
6+
class WnStickyHeadsUp extends StatelessWidget {
7+
const WnStickyHeadsUp({
8+
super.key,
9+
required this.title,
10+
required this.subtitle,
11+
this.type = WnHeadingType.error,
12+
this.icon,
13+
this.action,
14+
});
15+
final String title;
16+
final String subtitle;
17+
final IconData? icon;
18+
final Widget? action;
19+
final WnHeadingType type;
20+
21+
@override
22+
Widget build(BuildContext context) {
23+
final color = type.color(context);
24+
return Container(
25+
padding: EdgeInsets.all(16.w),
26+
decoration: BoxDecoration(
27+
border: Border(
28+
bottom: BorderSide(
29+
color: color,
30+
width: 1.w,
31+
),
32+
),
33+
),
34+
child: Row(
35+
crossAxisAlignment: CrossAxisAlignment.start,
36+
children: [
37+
Icon(
38+
icon ?? type.icon,
39+
size: 24.w,
40+
color: color,
41+
),
42+
Gap(8.w),
43+
Expanded(
44+
child: Column(
45+
crossAxisAlignment: CrossAxisAlignment.start,
46+
mainAxisAlignment: MainAxisAlignment.center,
47+
children: [
48+
Text(
49+
title,
50+
style: TextStyle(
51+
fontSize: 16.sp,
52+
fontWeight: FontWeight.w600,
53+
color: context.colors.primary,
54+
),
55+
),
56+
Gap(4.h),
57+
Text(
58+
subtitle,
59+
style: TextStyle(
60+
fontSize: 14.sp,
61+
fontWeight: FontWeight.w500,
62+
color: context.colors.mutedForeground,
63+
),
64+
),
65+
Gap(4.h),
66+
if (action != null) action!,
67+
],
68+
),
69+
),
70+
],
71+
),
72+
);
73+
}
74+
}
75+
76+
enum WnHeadingType {
77+
error,
78+
warning,
79+
info;
80+
81+
Color color(BuildContext context) {
82+
switch (this) {
83+
case WnHeadingType.error:
84+
return context.colors.destructive;
85+
case WnHeadingType.warning:
86+
return context.colors.warning;
87+
case WnHeadingType.info:
88+
return context.colors.info;
89+
}
90+
}
91+
92+
IconData get icon {
93+
switch (this) {
94+
case WnHeadingType.error:
95+
return CarbonIcons.error_filled;
96+
case WnHeadingType.warning:
97+
return CarbonIcons.warning_filled;
98+
case WnHeadingType.info:
99+
return CarbonIcons.information;
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)