Show offline notice in settings related screens (#576)
* refactor: wrap offline notice translations * feat: show offline notice in network screen * feat: show no internet notice in settings screen * feat: show no internet notice in bug report screen * feat: show no internet notice in key packages screen * feat: show no internet notice in relay control screen * docs: update changelog
This commit is contained in:
@@ -20,6 +20,7 @@ import 'package:whitenoise/utils/bubble_grouping.dart';
|
||||
import 'package:whitenoise/utils/metadata.dart';
|
||||
import 'package:whitenoise/widgets/chat_message_bubble.dart';
|
||||
import 'package:whitenoise/widgets/chat_scroll_down_button.dart';
|
||||
import 'package:whitenoise/widgets/offline_system_notice.dart';
|
||||
import 'package:whitenoise/widgets/wn_avatar.dart';
|
||||
import 'package:whitenoise/widgets/wn_button.dart';
|
||||
import 'package:whitenoise/widgets/wn_chat_system_message.dart';
|
||||
@@ -133,12 +134,7 @@ class ChatInviteScreen extends HookConsumerWidget {
|
||||
WnSlate(
|
||||
tag: 'wn-slate-invite-header',
|
||||
systemNotice: isOffline
|
||||
? WnSystemNotice(
|
||||
key: const Key('offline_notice'),
|
||||
title: context.l10n.waitingForInternet,
|
||||
type: WnSystemNoticeType.warning,
|
||||
variant: WnSystemNoticeVariant.expanded,
|
||||
)
|
||||
? const OfflineSystemNotice()
|
||||
: (noticeMessage.value != null
|
||||
? WnSystemNotice(
|
||||
key: ValueKey(noticeMessage.value),
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:whitenoise/theme.dart';
|
||||
import 'package:whitenoise/utils/chat_search.dart';
|
||||
import 'package:whitenoise/widgets/chat_list_header.dart';
|
||||
import 'package:whitenoise/widgets/chat_list_tile.dart';
|
||||
import 'package:whitenoise/widgets/offline_system_notice.dart';
|
||||
import 'package:whitenoise/widgets/wn_button.dart';
|
||||
import 'package:whitenoise/widgets/wn_chat_list.dart';
|
||||
import 'package:whitenoise/widgets/wn_filter_chip.dart';
|
||||
@@ -88,7 +89,7 @@ class ChatListScreen extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
WnSystemNotice? _buildSystemNotice(
|
||||
Widget? _buildSystemNotice(
|
||||
BuildContext context,
|
||||
AppTypography typography,
|
||||
SemanticColors colors, {
|
||||
@@ -99,12 +100,7 @@ class ChatListScreen extends HookConsumerWidget {
|
||||
required VoidCallback onWelcomeDismiss,
|
||||
}) {
|
||||
if (isOffline) {
|
||||
return WnSystemNotice(
|
||||
key: const Key('offline_notice'),
|
||||
title: context.l10n.waitingForInternet,
|
||||
type: WnSystemNoticeType.warning,
|
||||
variant: WnSystemNoticeVariant.expanded,
|
||||
);
|
||||
return const OfflineSystemNotice();
|
||||
}
|
||||
if (updateVersion != null) {
|
||||
return WnSystemNotice(
|
||||
|
||||
@@ -39,6 +39,7 @@ import 'package:whitenoise/widgets/chat_media_upload_preview.dart';
|
||||
import 'package:whitenoise/widgets/chat_message_bubble.dart';
|
||||
import 'package:whitenoise/widgets/chat_message_quote.dart';
|
||||
import 'package:whitenoise/widgets/chat_scroll_down_button.dart';
|
||||
import 'package:whitenoise/widgets/offline_system_notice.dart';
|
||||
import 'package:whitenoise/widgets/wn_button.dart';
|
||||
import 'package:whitenoise/widgets/wn_chat_message_input.dart';
|
||||
import 'package:whitenoise/widgets/wn_icon.dart';
|
||||
@@ -521,12 +522,7 @@ class ChatScreen extends HookConsumerWidget {
|
||||
: null,
|
||||
),
|
||||
systemNotice: isOffline
|
||||
? WnSystemNotice(
|
||||
key: const Key('offline_notice'),
|
||||
title: context.l10n.waitingForInternet,
|
||||
type: WnSystemNoticeType.warning,
|
||||
variant: WnSystemNoticeVariant.expanded,
|
||||
)
|
||||
? const OfflineSystemNotice()
|
||||
: (noticeMessage.value != null
|
||||
? WnSystemNotice(
|
||||
key: ValueKey(noticeMessage.value),
|
||||
|
||||
@@ -5,9 +5,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:whitenoise/hooks/use_key_packages.dart';
|
||||
import 'package:whitenoise/l10n/l10n.dart';
|
||||
import 'package:whitenoise/providers/account_pubkey_provider.dart';
|
||||
import 'package:whitenoise/providers/offline_provider.dart';
|
||||
import 'package:whitenoise/routes.dart';
|
||||
import 'package:whitenoise/src/rust/api/accounts.dart' show FlutterEvent;
|
||||
import 'package:whitenoise/theme.dart';
|
||||
import 'package:whitenoise/widgets/offline_system_notice.dart';
|
||||
import 'package:whitenoise/widgets/wn_button.dart';
|
||||
import 'package:whitenoise/widgets/wn_key_package_card.dart';
|
||||
import 'package:whitenoise/widgets/wn_scroll_edge_effect.dart';
|
||||
@@ -23,6 +25,7 @@ class KeyPackageManagementScreen extends HookConsumerWidget {
|
||||
final colors = context.colors;
|
||||
final typography = context.typographyScaled;
|
||||
final pubkey = ref.watch(accountPubkeyProvider);
|
||||
final isOffline = ref.watch(offlineProvider).value ?? false;
|
||||
final (:state, :fetch, :publish, :delete, :deleteAll) = useKeyPackages(pubkey);
|
||||
|
||||
final noticeMessage = useState<String?>(null);
|
||||
@@ -68,6 +71,7 @@ class KeyPackageManagementScreen extends HookConsumerWidget {
|
||||
Future<void> handleDelete(String id) => handleAction(() => delete(id));
|
||||
|
||||
useEffect(() {
|
||||
if (isOffline) return;
|
||||
fetch().then((result) {
|
||||
if (!context.mounted) return;
|
||||
if (!result.success) {
|
||||
@@ -75,7 +79,7 @@ class KeyPackageManagementScreen extends HookConsumerWidget {
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}, [pubkey]);
|
||||
}, [pubkey, isOffline]);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: colors.backgroundPrimary,
|
||||
@@ -87,14 +91,16 @@ class KeyPackageManagementScreen extends HookConsumerWidget {
|
||||
title: context.l10n.keyPackageManagementTitle,
|
||||
onNavigate: () => Routes.goBack(context),
|
||||
),
|
||||
systemNotice: noticeMessage.value != null
|
||||
? WnSystemNotice(
|
||||
key: ValueKey(noticeMessage.value),
|
||||
title: noticeMessage.value!,
|
||||
type: noticeType.value,
|
||||
onDismiss: dismissNotice,
|
||||
)
|
||||
: null,
|
||||
systemNotice: isOffline
|
||||
? const OfflineSystemNotice()
|
||||
: (noticeMessage.value != null
|
||||
? WnSystemNotice(
|
||||
key: ValueKey(noticeMessage.value),
|
||||
title: noticeMessage.value!,
|
||||
type: noticeType.value,
|
||||
onDismiss: dismissNotice,
|
||||
)
|
||||
: null),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(14.w, 0, 14.w, 14.h),
|
||||
child: Column(
|
||||
@@ -103,6 +109,7 @@ class KeyPackageManagementScreen extends HookConsumerWidget {
|
||||
SizedBox(height: 16.h),
|
||||
_KeyPackageActionButtons(
|
||||
isLoading: state.isLoading,
|
||||
isOffline: isOffline,
|
||||
activeAction: state.activeAction,
|
||||
onPublish: () => handleAction(publish),
|
||||
onFetch: () => handleAction(fetch),
|
||||
@@ -120,7 +127,7 @@ class KeyPackageManagementScreen extends HookConsumerWidget {
|
||||
child: _KeyPackageList(
|
||||
packages: state.packages,
|
||||
onDelete: handleDelete,
|
||||
disabled: state.isLoading,
|
||||
disabled: state.isLoading || isOffline,
|
||||
deletingId: state.deletingId,
|
||||
),
|
||||
),
|
||||
@@ -137,6 +144,7 @@ class KeyPackageManagementScreen extends HookConsumerWidget {
|
||||
class _KeyPackageActionButtons extends StatelessWidget {
|
||||
const _KeyPackageActionButtons({
|
||||
required this.isLoading,
|
||||
required this.isOffline,
|
||||
required this.activeAction,
|
||||
required this.onPublish,
|
||||
required this.onFetch,
|
||||
@@ -144,6 +152,7 @@ class _KeyPackageActionButtons extends StatelessWidget {
|
||||
});
|
||||
|
||||
final bool isLoading;
|
||||
final bool isOffline;
|
||||
final KeyPackageAction? activeAction;
|
||||
final VoidCallback onPublish;
|
||||
final VoidCallback onFetch;
|
||||
@@ -158,7 +167,7 @@ class _KeyPackageActionButtons extends StatelessWidget {
|
||||
WnButton(
|
||||
text: context.l10n.refreshKeyPackages,
|
||||
onPressed: onFetch,
|
||||
disabled: isLoading,
|
||||
disabled: isLoading || isOffline,
|
||||
loading: activeAction == KeyPackageAction.fetch,
|
||||
type: WnButtonType.outline,
|
||||
size: WnButtonSize.medium,
|
||||
@@ -166,14 +175,14 @@ class _KeyPackageActionButtons extends StatelessWidget {
|
||||
WnButton(
|
||||
text: context.l10n.publishNewKeyPackage,
|
||||
onPressed: onPublish,
|
||||
disabled: isLoading,
|
||||
disabled: isLoading || isOffline,
|
||||
loading: activeAction == KeyPackageAction.publish,
|
||||
size: WnButtonSize.medium,
|
||||
),
|
||||
WnButton(
|
||||
text: context.l10n.deleteAllKeyPackages,
|
||||
onPressed: onDeleteAll,
|
||||
disabled: isLoading,
|
||||
disabled: isLoading || isOffline,
|
||||
loading: activeAction == KeyPackageAction.deleteAll,
|
||||
type: WnButtonType.destructive,
|
||||
size: WnButtonSize.medium,
|
||||
|
||||
@@ -8,8 +8,10 @@ import 'package:whitenoise/hooks/use_network_relays.dart';
|
||||
import 'package:whitenoise/hooks/use_system_notice.dart';
|
||||
import 'package:whitenoise/l10n/l10n.dart';
|
||||
import 'package:whitenoise/providers/account_pubkey_provider.dart';
|
||||
import 'package:whitenoise/providers/offline_provider.dart';
|
||||
import 'package:whitenoise/routes.dart';
|
||||
import 'package:whitenoise/theme.dart';
|
||||
import 'package:whitenoise/widgets/offline_system_notice.dart';
|
||||
import 'package:whitenoise/widgets/wn_button.dart';
|
||||
import 'package:whitenoise/widgets/wn_confirmation_slate.dart';
|
||||
import 'package:whitenoise/widgets/wn_icon.dart';
|
||||
@@ -17,7 +19,7 @@ import 'package:whitenoise/widgets/wn_list.dart';
|
||||
import 'package:whitenoise/widgets/wn_list_item.dart';
|
||||
import 'package:whitenoise/widgets/wn_slate.dart';
|
||||
import 'package:whitenoise/widgets/wn_slate_navigation_header.dart';
|
||||
import 'package:whitenoise/widgets/wn_system_notice.dart' show WnSystemNotice;
|
||||
import 'package:whitenoise/widgets/wn_system_notice.dart';
|
||||
import 'package:whitenoise/widgets/wn_tooltip.dart';
|
||||
|
||||
class NetworkScreen extends HookConsumerWidget {
|
||||
@@ -31,6 +33,7 @@ class NetworkScreen extends HookConsumerWidget {
|
||||
final (:state, :fetchAll, :addRelay, :removeRelay, :restoreDefaultRelays) = useNetworkRelays(
|
||||
pubkey,
|
||||
);
|
||||
final isOffline = ref.watch(offlineProvider).value ?? false;
|
||||
final listItemController = useListItemController();
|
||||
final (:noticeMessage, :noticeType, :showErrorNotice, :showSuccessNotice, :dismissNotice) =
|
||||
useSystemNotice();
|
||||
@@ -128,24 +131,28 @@ class NetworkScreen extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
final systemNotice = isOffline
|
||||
? const OfflineSystemNotice()
|
||||
: noticeMessage != null
|
||||
? WnSystemNotice(
|
||||
key: ValueKey(noticeMessage),
|
||||
title: noticeMessage,
|
||||
type: noticeType,
|
||||
onDismiss: dismissNotice,
|
||||
)
|
||||
: null;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: colors.backgroundPrimary,
|
||||
body: SafeArea(
|
||||
child: WnSlate(
|
||||
showTopScrollEffect: true,
|
||||
showBottomScrollEffect: true,
|
||||
systemNotice: systemNotice,
|
||||
header: WnSlateNavigationHeader(
|
||||
title: context.l10n.networkRelaysTitle,
|
||||
onNavigate: () => Routes.goBack(context),
|
||||
),
|
||||
systemNotice: noticeMessage != null
|
||||
? WnSystemNotice(
|
||||
key: ValueKey(noticeMessage),
|
||||
title: noticeMessage,
|
||||
type: noticeType,
|
||||
onDismiss: dismissNotice,
|
||||
)
|
||||
: null,
|
||||
footer: Padding(
|
||||
padding: EdgeInsets.fromLTRB(16.w, 24.h, 16.w, 16.h),
|
||||
child: SizedBox(
|
||||
@@ -155,25 +162,28 @@ class NetworkScreen extends HookConsumerWidget {
|
||||
text: context.l10n.restoreDefaultRelays,
|
||||
size: WnButtonSize.medium,
|
||||
trailingIcon: WnIcons.reset,
|
||||
onPressed: () => WnConfirmationSlate.show(
|
||||
context: context,
|
||||
title: context.l10n.restoreDefaultRelaysConfirmationTitle,
|
||||
message: context.l10n.restoreDefaultRelaysConfirmationMessage,
|
||||
confirmText: context.l10n.restoreDefaultRelays,
|
||||
cancelText: context.l10n.cancel,
|
||||
isDestructive: true,
|
||||
onConfirmAsync: () async {
|
||||
try {
|
||||
await restoreDefaultRelays();
|
||||
return true;
|
||||
} catch (_) {
|
||||
if (context.mounted) {
|
||||
showErrorNotice(context.l10n.restoreDefaultRelaysError);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
),
|
||||
disabled: isOffline,
|
||||
onPressed: isOffline
|
||||
? null
|
||||
: () => WnConfirmationSlate.show(
|
||||
context: context,
|
||||
title: context.l10n.restoreDefaultRelaysConfirmationTitle,
|
||||
message: context.l10n.restoreDefaultRelaysConfirmationMessage,
|
||||
confirmText: context.l10n.restoreDefaultRelays,
|
||||
cancelText: context.l10n.cancel,
|
||||
isDestructive: true,
|
||||
onConfirmAsync: () async {
|
||||
try {
|
||||
await restoreDefaultRelays();
|
||||
return true;
|
||||
} catch (_) {
|
||||
if (context.mounted) {
|
||||
showErrorNotice(context.l10n.restoreDefaultRelaysError);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -216,7 +226,10 @@ class NetworkScreen extends HookConsumerWidget {
|
||||
type: WnButtonType.overlay,
|
||||
size: WnButtonSize.medium,
|
||||
trailingIcon: WnIcons.addLarge,
|
||||
onPressed: () => showAddRelaySheet(RelayCategory.normal),
|
||||
disabled: isOffline,
|
||||
onPressed: isOffline
|
||||
? null
|
||||
: () => showAddRelaySheet(RelayCategory.normal),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -243,7 +256,10 @@ class NetworkScreen extends HookConsumerWidget {
|
||||
type: WnButtonType.overlay,
|
||||
size: WnButtonSize.medium,
|
||||
trailingIcon: WnIcons.addLarge,
|
||||
onPressed: () => showAddRelaySheet(RelayCategory.inbox),
|
||||
disabled: isOffline,
|
||||
onPressed: isOffline
|
||||
? null
|
||||
: () => showAddRelaySheet(RelayCategory.inbox),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -273,7 +289,10 @@ class NetworkScreen extends HookConsumerWidget {
|
||||
type: WnButtonType.overlay,
|
||||
size: WnButtonSize.medium,
|
||||
trailingIcon: WnIcons.addLarge,
|
||||
onPressed: () => showAddRelaySheet(RelayCategory.keyPackage),
|
||||
disabled: isOffline,
|
||||
onPressed: isOffline
|
||||
? null
|
||||
: () => showAddRelaySheet(RelayCategory.keyPackage),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -2,30 +2,45 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:whitenoise/hooks/use_system_notice.dart';
|
||||
import 'package:whitenoise/l10n/l10n.dart';
|
||||
import 'package:whitenoise/providers/offline_provider.dart';
|
||||
import 'package:whitenoise/routes.dart';
|
||||
import 'package:whitenoise/src/rust/api/relays.dart';
|
||||
import 'package:whitenoise/theme.dart';
|
||||
import 'package:whitenoise/widgets/offline_system_notice.dart';
|
||||
import 'package:whitenoise/widgets/wn_button.dart';
|
||||
import 'package:whitenoise/widgets/wn_slate.dart';
|
||||
import 'package:whitenoise/widgets/wn_slate_navigation_header.dart';
|
||||
import 'package:whitenoise/widgets/wn_system_notice.dart';
|
||||
|
||||
final _logger = Logger('RelayControlStateScreen');
|
||||
|
||||
class RelayControlStateScreen extends HookWidget {
|
||||
class RelayControlStateScreen extends HookConsumerWidget {
|
||||
const RelayControlStateScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final colors = context.colors;
|
||||
final typography = context.typographyScaled;
|
||||
final isOffline = ref.watch(offlineProvider).value ?? false;
|
||||
final isLoading = useState(false);
|
||||
final result = useState<String?>(null);
|
||||
final error = useState<String?>(null);
|
||||
|
||||
final (
|
||||
:noticeMessage,
|
||||
:noticeType,
|
||||
:showErrorNotice,
|
||||
:dismissNotice,
|
||||
:showSuccessNotice,
|
||||
) = useSystemNotice();
|
||||
|
||||
Future<void> loadState() async {
|
||||
if (isOffline) return;
|
||||
isLoading.value = true;
|
||||
error.value = null;
|
||||
dismissNotice();
|
||||
|
||||
try {
|
||||
final dump = await debugRelayControlState();
|
||||
@@ -38,7 +53,7 @@ class RelayControlStateScreen extends HookWidget {
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
error.value = context.l10n.relayControlStateLoadError;
|
||||
showErrorNotice(context.l10n.relayControlStateLoadError);
|
||||
result.value = null;
|
||||
} finally {
|
||||
if (context.mounted) {
|
||||
@@ -55,19 +70,16 @@ class RelayControlStateScreen extends HookWidget {
|
||||
|
||||
await Clipboard.setData(ClipboardData(text: dump));
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.rawDebugViewCopied),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
showSuccessNotice(context.l10n.rawDebugViewCopied);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() {
|
||||
loadState();
|
||||
if (!isOffline) {
|
||||
loadState();
|
||||
}
|
||||
return null;
|
||||
}, const []);
|
||||
}, [isOffline]);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: colors.backgroundPrimary,
|
||||
@@ -79,6 +91,16 @@ class RelayControlStateScreen extends HookWidget {
|
||||
title: context.l10n.relayStateTitle,
|
||||
onNavigate: () => Routes.goBack(context),
|
||||
),
|
||||
systemNotice: isOffline
|
||||
? const OfflineSystemNotice()
|
||||
: (noticeMessage != null
|
||||
? WnSystemNotice(
|
||||
key: ValueKey(noticeMessage),
|
||||
title: noticeMessage,
|
||||
type: noticeType,
|
||||
onDismiss: dismissNotice,
|
||||
)
|
||||
: null),
|
||||
child: ListView(
|
||||
padding: EdgeInsets.fromLTRB(14.w, 12.h, 14.w, 14.h),
|
||||
children: [
|
||||
@@ -92,14 +114,6 @@ class RelayControlStateScreen extends HookWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.relayControlStateDumpLabel,
|
||||
style: typography.semiBold10.copyWith(
|
||||
color: colors.backgroundContentSecondary,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
Text(
|
||||
context.l10n.relayControlStateSnapshotDescription,
|
||||
style: typography.medium10.copyWith(
|
||||
@@ -113,34 +127,25 @@ class RelayControlStateScreen extends HookWidget {
|
||||
spacing: 8.w,
|
||||
runSpacing: 8.h,
|
||||
children: [
|
||||
TextButton(
|
||||
WnButton(
|
||||
key: const Key('relay_control_state_refresh_button'),
|
||||
onPressed: isLoading.value ? null : loadState,
|
||||
child: Text(
|
||||
isLoading.value
|
||||
? context.l10n.relayControlStateLoading
|
||||
: context.l10n.relayControlStateRefreshButton,
|
||||
),
|
||||
text: isLoading.value
|
||||
? context.l10n.relayControlStateLoading
|
||||
: context.l10n.relayControlStateRefreshButton,
|
||||
onPressed: isLoading.value || isOffline ? null : loadState,
|
||||
loading: isLoading.value,
|
||||
type: WnButtonType.outline,
|
||||
size: WnButtonSize.small,
|
||||
),
|
||||
TextButton(
|
||||
WnButton(
|
||||
key: const Key('relay_control_state_copy_button'),
|
||||
onPressed: result.value == null ? null : copyDump,
|
||||
child: Text(context.l10n.relayControlStateCopyButton),
|
||||
text: context.l10n.relayControlStateCopyButton,
|
||||
onPressed: (result.value?.trim().isEmpty ?? true) ? null : copyDump,
|
||||
type: WnButtonType.outline,
|
||||
size: WnButtonSize.small,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (error.value != null) ...[
|
||||
SizedBox(height: 6.h),
|
||||
SelectableText(
|
||||
key: const Key('relay_control_state_error'),
|
||||
error.value!,
|
||||
style: typography.medium10.copyWith(
|
||||
color: colors.fillDestructive,
|
||||
fontFamily: 'monospace',
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (result.value != null) ...[
|
||||
SizedBox(height: 6.h),
|
||||
SelectableText(
|
||||
|
||||
@@ -9,11 +9,13 @@ import 'package:whitenoise/hooks/use_system_notice.dart';
|
||||
import 'package:whitenoise/l10n/l10n.dart';
|
||||
import 'package:whitenoise/providers/app_version_provider.dart';
|
||||
import 'package:whitenoise/providers/auth_provider.dart';
|
||||
import 'package:whitenoise/providers/offline_provider.dart';
|
||||
import 'package:whitenoise/routes.dart';
|
||||
import 'package:whitenoise/src/rust/api/bug_report.dart';
|
||||
import 'package:whitenoise/src/rust/api/utils.dart';
|
||||
import 'package:whitenoise/theme.dart';
|
||||
import 'package:whitenoise/widgets/keyboard_dismiss_on_tap.dart';
|
||||
import 'package:whitenoise/widgets/offline_system_notice.dart';
|
||||
import 'package:whitenoise/widgets/wn_button.dart';
|
||||
import 'package:whitenoise/widgets/wn_checkbox.dart';
|
||||
import 'package:whitenoise/widgets/wn_dropdown_selector.dart';
|
||||
@@ -32,6 +34,7 @@ class ReportBugScreen extends HookConsumerWidget {
|
||||
final colors = context.colors;
|
||||
final typography = context.typographyScaled;
|
||||
final l10n = context.l10n;
|
||||
final isOffline = ref.watch(offlineProvider).value ?? false;
|
||||
|
||||
final description = useTextEditingController();
|
||||
final stepsToReproduce = useTextEditingController();
|
||||
@@ -54,6 +57,7 @@ class ReportBugScreen extends HookConsumerWidget {
|
||||
];
|
||||
|
||||
Future<void> handleSend() async {
|
||||
if (isOffline) return;
|
||||
if (description.text.trim().isEmpty) {
|
||||
descriptionError.value = l10n.reportBugWhatWentWrongRequired;
|
||||
return;
|
||||
@@ -103,14 +107,16 @@ class ReportBugScreen extends HookConsumerWidget {
|
||||
title: l10n.reportBug,
|
||||
onNavigate: () => Routes.goBack(context),
|
||||
),
|
||||
systemNotice: notice.noticeMessage != null
|
||||
? WnSystemNotice(
|
||||
key: ValueKey(notice.noticeMessage),
|
||||
title: notice.noticeMessage!,
|
||||
type: notice.noticeType,
|
||||
onDismiss: notice.dismissNotice,
|
||||
)
|
||||
: null,
|
||||
systemNotice: isOffline
|
||||
? const OfflineSystemNotice()
|
||||
: (notice.noticeMessage != null
|
||||
? WnSystemNotice(
|
||||
key: ValueKey(notice.noticeMessage),
|
||||
title: notice.noticeMessage!,
|
||||
type: notice.noticeType,
|
||||
onDismiss: notice.dismissNotice,
|
||||
)
|
||||
: null),
|
||||
child: KeyboardDismissOnTap(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.fromLTRB(14.w, 0, 14.w, 14.h),
|
||||
@@ -161,7 +167,8 @@ class ReportBugScreen extends HookConsumerWidget {
|
||||
child: WnButton(
|
||||
text: l10n.reportBugSend,
|
||||
loading: isSending.value,
|
||||
onPressed: isSending.value ? null : handleSend,
|
||||
disabled: isOffline || isSending.value,
|
||||
onPressed: isOffline || isSending.value ? null : handleSend,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -7,10 +7,12 @@ import 'package:whitenoise/hooks/use_user_metadata.dart';
|
||||
import 'package:whitenoise/l10n/l10n.dart';
|
||||
import 'package:whitenoise/providers/app_version_provider.dart';
|
||||
import 'package:whitenoise/providers/auth_provider.dart';
|
||||
import 'package:whitenoise/providers/offline_provider.dart';
|
||||
import 'package:whitenoise/routes.dart';
|
||||
import 'package:whitenoise/theme.dart';
|
||||
import 'package:whitenoise/utils/formatting.dart';
|
||||
import 'package:whitenoise/utils/metadata.dart';
|
||||
import 'package:whitenoise/widgets/offline_system_notice.dart';
|
||||
import 'package:whitenoise/widgets/wn_avatar.dart';
|
||||
import 'package:whitenoise/widgets/wn_button.dart';
|
||||
import 'package:whitenoise/widgets/wn_icon.dart';
|
||||
@@ -28,6 +30,7 @@ class SettingsScreen extends HookConsumerWidget {
|
||||
final colors = context.colors;
|
||||
final typography = context.typographyScaled;
|
||||
final pubkey = ref.watch(authProvider).value;
|
||||
final isOffline = ref.watch(offlineProvider).value ?? false;
|
||||
final helpState = useSupportChat(accountPubkey: pubkey);
|
||||
final metadataSnapshot = useUserMetadata(context, pubkey);
|
||||
final appVersion = ref.watch(appVersionProvider);
|
||||
@@ -45,6 +48,7 @@ class SettingsScreen extends HookConsumerWidget {
|
||||
child: WnSlate(
|
||||
showTopScrollEffect: true,
|
||||
showBottomScrollEffect: true,
|
||||
systemNotice: isOffline ? const OfflineSystemNotice() : null,
|
||||
header: WnSlateNavigationHeader(
|
||||
title: context.l10n.settings,
|
||||
onNavigate: () => Routes.goBack(context),
|
||||
@@ -174,6 +178,7 @@ class SettingsScreen extends HookConsumerWidget {
|
||||
WnMenu(
|
||||
children: [
|
||||
WnMenuItem(
|
||||
key: const Key('report_bug_menu_item'),
|
||||
icon: WnIcons.flag,
|
||||
label: context.l10n.reportBug,
|
||||
type: WnMenuItemType.secondary,
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:whitenoise/providers/account_pubkey_provider.dart';
|
||||
import 'package:whitenoise/providers/offline_provider.dart';
|
||||
import 'package:whitenoise/routes.dart';
|
||||
import 'package:whitenoise/theme.dart';
|
||||
import 'package:whitenoise/widgets/offline_system_notice.dart';
|
||||
import 'package:whitenoise/widgets/wn_button.dart';
|
||||
import 'package:whitenoise/widgets/wn_icon.dart';
|
||||
import 'package:whitenoise/widgets/wn_slate.dart';
|
||||
@@ -89,12 +90,7 @@ class StartSupportChatScreen extends HookConsumerWidget {
|
||||
onDismiss: dismissNotice,
|
||||
)
|
||||
: isOffline
|
||||
? WnSystemNotice(
|
||||
key: const Key('offline_notice'),
|
||||
title: context.l10n.waitingForInternet,
|
||||
type: WnSystemNoticeType.warning,
|
||||
variant: WnSystemNoticeVariant.expanded,
|
||||
)
|
||||
? const OfflineSystemNotice()
|
||||
: null),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(14.w, 0, 14.w, 14.h),
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:whitenoise/l10n/l10n.dart';
|
||||
import 'package:whitenoise/widgets/wn_system_notice.dart';
|
||||
|
||||
class OfflineSystemNotice extends StatelessWidget {
|
||||
const OfflineSystemNotice({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WnSystemNotice(
|
||||
key: const Key('offline_notice'),
|
||||
title: context.l10n.waitingForInternet,
|
||||
type: WnSystemNoticeType.warning,
|
||||
variant: WnSystemNoticeVariant.expanded,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user