mirror of
https://github.com/chaos-zhu/easynode.git
synced 2026-10-07 13:38:14 +08:00
feat: login登录优化&通用报错
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../l10n/app_localizations.dart';
|
||||
|
||||
Future<void> runRefreshWithFeedback(
|
||||
BuildContext context,
|
||||
Future<void> Function() refresh,
|
||||
) async {
|
||||
try {
|
||||
await refresh();
|
||||
} catch (error) {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
).trf('common.refreshFailed', [error.toString()]),
|
||||
),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,6 @@ class _LoginPageState extends State<LoginPage> {
|
||||
LoginExpiry _expiry = LoginExpiry.currentDay;
|
||||
bool _savePassword = false;
|
||||
bool _httpRiskAccepted = false;
|
||||
bool _showHttpRiskBanner = false;
|
||||
bool _submitting = false;
|
||||
String? _errorMessage;
|
||||
|
||||
@@ -108,7 +107,11 @@ class _LoginPageState extends State<LoginPage> {
|
||||
|
||||
if (!mounted) return;
|
||||
if (result.requiresHttpRiskConfirmation) {
|
||||
setState(() => _showHttpRiskBanner = true);
|
||||
setState(() => _submitting = false);
|
||||
final accepted = await _confirmHttpRisk();
|
||||
if (!mounted || accepted != true) return;
|
||||
setState(() => _httpRiskAccepted = true);
|
||||
await _submit();
|
||||
return;
|
||||
}
|
||||
if (!result.success || result.session == null) {
|
||||
@@ -121,12 +124,25 @@ class _LoginPageState extends State<LoginPage> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _acceptHttpRiskAndSubmit() async {
|
||||
setState(() {
|
||||
_httpRiskAccepted = true;
|
||||
_showHttpRiskBanner = false;
|
||||
});
|
||||
await _submit();
|
||||
Future<bool?> _confirmHttpRisk() {
|
||||
final l = AppLocalizations.of(context);
|
||||
return showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(l.tr('login.httpRiskTitle')),
|
||||
content: Text(l.tr('login.httpRiskBody')),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: Text(l.tr('common.cancel')),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
child: Text(l.tr('common.continue')),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -170,10 +186,6 @@ class _LoginPageState extends State<LoginPage> {
|
||||
onChanged: (value) =>
|
||||
setState(() => _savePassword = value),
|
||||
),
|
||||
if (_showHttpRiskBanner) ...[
|
||||
const SizedBox(height: 14),
|
||||
_HttpRiskBanner(onConfirm: _acceptHttpRiskAndSubmit),
|
||||
],
|
||||
if (_errorMessage != null) ...[
|
||||
const SizedBox(height: 14),
|
||||
_ErrorBox(message: _errorMessage!),
|
||||
@@ -490,65 +502,6 @@ class _LoginHero extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _HttpRiskBanner extends StatelessWidget {
|
||||
const _HttpRiskBanner({required this.onConfirm});
|
||||
|
||||
final VoidCallback onConfirm;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l = AppLocalizations.of(context);
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFE8BF),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: const Color(0xFFE6A23C)),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Icon(Icons.warning_amber, color: Color(0xFF7A4A00), size: 20),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l.tr('login.httpRiskTitle'),
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF7A4A00),
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
l.tr('login.httpRiskBody'),
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF7A4A00),
|
||||
fontSize: 13,
|
||||
height: 1.35,
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: onConfirm,
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: const Color(0xFF7A4A00),
|
||||
),
|
||||
child: Text(l.tr('common.continue')),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ErrorBox extends StatelessWidget {
|
||||
const _ErrorBox({required this.message});
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../../core/api/api_result.dart';
|
||||
import '../../core/ui/palette.dart';
|
||||
import '../../core/ui/refresh_feedback.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
import '../../state/api_providers.dart';
|
||||
import '../../state/auth_notifier.dart';
|
||||
@@ -55,7 +56,7 @@ class _ServersTabState extends ConsumerState<ServersTab> {
|
||||
}
|
||||
|
||||
Future<void> _refresh() async {
|
||||
await refreshServerSharedData(ref);
|
||||
await runRefreshWithFeedback(context, () => refreshServerSharedData(ref));
|
||||
}
|
||||
|
||||
Future<void> _connect(ServerModel server) async {
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../core/api/api_result.dart';
|
||||
import '../../core/ui/palette.dart';
|
||||
import '../../core/ui/refresh_feedback.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
import '../../state/api_providers.dart';
|
||||
import '../../state/credential_list_notifier.dart';
|
||||
@@ -206,8 +207,12 @@ class _CredentialsPageState extends ConsumerState<CredentialsPage> {
|
||||
),
|
||||
Expanded(
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () =>
|
||||
ref.read(credentialListProvider.notifier).refresh(),
|
||||
onRefresh: () => runRefreshWithFeedback(
|
||||
context,
|
||||
() => ref
|
||||
.read(credentialListProvider.notifier)
|
||||
.refresh(throwOnError: true),
|
||||
),
|
||||
child: async.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (err, _) => _ErrorBody(
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../core/api/api_result.dart';
|
||||
import '../../core/ui/palette.dart';
|
||||
import '../../core/ui/refresh_feedback.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
import '../../state/api_providers.dart';
|
||||
import '../../state/plus_info_notifier.dart';
|
||||
@@ -127,10 +128,12 @@ class _PlusSubscriptionPageState extends ConsumerState<PlusSubscriptionPage> {
|
||||
],
|
||||
),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await ref.read(plusInfoProvider.notifier).refresh();
|
||||
onRefresh: () => runRefreshWithFeedback(context, () async {
|
||||
await ref
|
||||
.read(plusInfoProvider.notifier)
|
||||
.refresh(throwOnError: true);
|
||||
await _loadInitial();
|
||||
},
|
||||
}),
|
||||
child: plusAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (err, _) => _ErrorBody(
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../core/api/api_result.dart';
|
||||
import '../../core/ui/palette.dart';
|
||||
import '../../core/ui/refresh_feedback.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
import '../../state/api_providers.dart';
|
||||
import '../../state/plus_info_notifier.dart';
|
||||
@@ -247,7 +248,12 @@ class _ProxyPageState extends ConsumerState<ProxyPage> {
|
||||
),
|
||||
Expanded(
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () => ref.read(proxyListProvider.notifier).refresh(),
|
||||
onRefresh: () => runRefreshWithFeedback(
|
||||
context,
|
||||
() => ref
|
||||
.read(proxyListProvider.notifier)
|
||||
.refresh(throwOnError: true),
|
||||
),
|
||||
child: async.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (err, _) => _ErrorBody(
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../core/api/api_result.dart';
|
||||
import '../../core/ui/palette.dart';
|
||||
import '../../core/ui/refresh_feedback.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
import '../../state/api_providers.dart';
|
||||
import '../../state/auth_notifier.dart';
|
||||
@@ -212,7 +213,11 @@ class _SessionsPageState extends ConsumerState<SessionsPage> {
|
||||
],
|
||||
),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () => ref.read(loginLogProvider.notifier).refresh(),
|
||||
onRefresh: () => runRefreshWithFeedback(
|
||||
context,
|
||||
() =>
|
||||
ref.read(loginLogProvider.notifier).refresh(throwOnError: true),
|
||||
),
|
||||
child: logAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (err, _) => _ErrorBody(
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../core/api/api_result.dart';
|
||||
import '../../core/ui/refresh_feedback.dart';
|
||||
import '../../features/scripts/script_form_page.dart';
|
||||
import '../../features/scripts/script_group_model.dart';
|
||||
import '../../features/scripts/script_groups_page.dart';
|
||||
@@ -39,10 +40,15 @@ class _ScriptsTabState extends ConsumerState<ScriptsTab> {
|
||||
}
|
||||
|
||||
Future<void> _refresh() async {
|
||||
await Future.wait([
|
||||
ref.read(scriptListProvider.notifier).refresh(),
|
||||
ref.read(scriptGroupListProvider.notifier).refresh(),
|
||||
]);
|
||||
await runRefreshWithFeedback(
|
||||
context,
|
||||
() => Future.wait([
|
||||
ref.read(scriptListProvider.notifier).refresh(throwOnError: true),
|
||||
ref
|
||||
.read(scriptGroupListProvider.notifier)
|
||||
.refresh(throwOnError: true),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _openForm({ScriptModel? script}) async {
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../core/ui/palette.dart';
|
||||
import '../../core/ui/refresh_feedback.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
import '../../state/auth_notifier.dart';
|
||||
import '../../state/credential_list_notifier.dart';
|
||||
@@ -184,14 +185,14 @@ class SettingsTab extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _refresh(WidgetRef ref) async {
|
||||
await Future.wait([
|
||||
ref.read(hostListProvider.notifier).refresh(),
|
||||
ref.read(credentialListProvider.notifier).refresh(),
|
||||
ref.read(scriptListProvider.notifier).refresh(),
|
||||
ref.read(plusInfoProvider.notifier).refresh(),
|
||||
ref.read(plusDiscountProvider.notifier).refresh(),
|
||||
]);
|
||||
Future<void> _refresh(BuildContext context, WidgetRef ref) async {
|
||||
await runRefreshWithFeedback(context, () => Future.wait([
|
||||
ref.read(hostListProvider.notifier).refresh(throwOnError: true),
|
||||
ref.read(credentialListProvider.notifier).refresh(throwOnError: true),
|
||||
ref.read(scriptListProvider.notifier).refresh(throwOnError: true),
|
||||
ref.read(plusInfoProvider.notifier).refresh(throwOnError: true),
|
||||
ref.read(plusDiscountProvider.notifier).refresh(throwOnError: true),
|
||||
]));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -218,7 +219,7 @@ class SettingsTab extends ConsumerWidget {
|
||||
body: SafeArea(
|
||||
bottom: false,
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () => _refresh(ref),
|
||||
onRefresh: () => _refresh(context, ref),
|
||||
child: CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../../core/api/api_result.dart';
|
||||
import '../../core/ui/refresh_feedback.dart';
|
||||
import '../../features/servers/server_model.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
import '../../state/api_providers.dart';
|
||||
@@ -30,7 +31,10 @@ class SftpTab extends ConsumerStatefulWidget {
|
||||
class _SftpTabState extends ConsumerState<SftpTab> {
|
||||
bool _connecting = false;
|
||||
|
||||
Future<void> _refresh() => ref.read(hostListProvider.notifier).refresh();
|
||||
Future<void> _refresh() => runRefreshWithFeedback(
|
||||
context,
|
||||
() => ref.read(hostListProvider.notifier).refresh(throwOnError: true),
|
||||
);
|
||||
|
||||
Future<void> _openServerPicker() async {
|
||||
final manager = ref.read(sftpSessionManagerProvider);
|
||||
|
||||
@@ -24,6 +24,7 @@ const Map<String, String> stringsEn = {
|
||||
'common.exitApp': 'Exit',
|
||||
'common.exitAppTitle': 'Exit app?',
|
||||
'common.exitAppBody': 'Are you sure you want to exit the app?',
|
||||
'common.refreshFailed': 'Refresh failed: {0}',
|
||||
|
||||
// Login page
|
||||
'login.serverAddress': 'Server address',
|
||||
|
||||
@@ -24,6 +24,8 @@ const Map<String, String> stringsZh = {
|
||||
'common.exitAppTitle': '退出 App?',
|
||||
'common.exitAppBody': '确认退出 App?',
|
||||
|
||||
'common.refreshFailed': '刷新失败:{0}',
|
||||
|
||||
// Login
|
||||
'login.serverAddress': '服务端地址',
|
||||
'login.serverAddressHint': 'https://example.com',
|
||||
|
||||
@@ -13,13 +13,18 @@ class CredentialListNotifier
|
||||
return _fetch(ref.watch(apiClientProvider));
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
Future<void> refresh({bool throwOnError = false}) async {
|
||||
final previous = state.valueOrNull;
|
||||
if (previous == null) state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(
|
||||
() => _fetch(ref.read(apiClientProvider)),
|
||||
);
|
||||
if (state.hasError && previous != null) state = AsyncData(previous);
|
||||
try {
|
||||
state = AsyncData(await _fetch(ref.read(apiClientProvider)));
|
||||
} catch (error, stackTrace) {
|
||||
state = previous == null
|
||||
? AsyncError(error, stackTrace)
|
||||
: AsyncData(previous);
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ServerCredentialModel>> _fetch(ApiClient api) async {
|
||||
|
||||
@@ -18,21 +18,24 @@ class GroupListNotifier extends AsyncNotifier<List<ServerGroupModel>> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
Future<void> refresh({bool throwOnError = false}) async {
|
||||
final previous = state.valueOrNull;
|
||||
if (previous == null) {
|
||||
state = const AsyncLoading();
|
||||
}
|
||||
state = await AsyncValue.guard(() async {
|
||||
try {
|
||||
return await ref.read(serverRepositoryProvider).fetchGroups();
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
rethrow;
|
||||
}
|
||||
});
|
||||
if (state.hasError && previous != null) {
|
||||
state = AsyncData(previous);
|
||||
try {
|
||||
final groups = await ref.read(serverRepositoryProvider).fetchGroups();
|
||||
state = AsyncData(groups);
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
} catch (error, stackTrace) {
|
||||
state = previous == null
|
||||
? AsyncError(error, stackTrace)
|
||||
: AsyncData(previous);
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,21 +20,24 @@ class HostListNotifier extends AsyncNotifier<List<ServerModel>> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
Future<void> refresh({bool throwOnError = false}) async {
|
||||
final previous = state.valueOrNull;
|
||||
if (previous == null) {
|
||||
state = const AsyncLoading();
|
||||
}
|
||||
state = await AsyncValue.guard(() async {
|
||||
try {
|
||||
return await ref.read(serverRepositoryProvider).fetchHosts();
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
rethrow;
|
||||
}
|
||||
});
|
||||
if (state.hasError && previous != null) {
|
||||
state = AsyncData(previous);
|
||||
try {
|
||||
final hosts = await ref.read(serverRepositoryProvider).fetchHosts();
|
||||
state = AsyncData(hosts);
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
} catch (error, stackTrace) {
|
||||
state = previous == null
|
||||
? AsyncError(error, stackTrace)
|
||||
: AsyncData(previous);
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,23 @@ class LoginLogNotifier extends AsyncNotifier<LoginLogData> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
Future<void> refresh({bool throwOnError = false}) async {
|
||||
final previous = state.valueOrNull;
|
||||
if (previous == null) state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(() async {
|
||||
try {
|
||||
return await ref.read(settingsRepositoryProvider).getLoginLog();
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
rethrow;
|
||||
}
|
||||
});
|
||||
if (state.hasError && previous != null) state = AsyncData(previous);
|
||||
try {
|
||||
final log = await ref.read(settingsRepositoryProvider).getLoginLog();
|
||||
state = AsyncData(log);
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
} catch (error, stackTrace) {
|
||||
state = previous == null
|
||||
? AsyncError(error, stackTrace)
|
||||
: AsyncData(previous);
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,19 @@ class PlusDiscountNotifier extends AsyncNotifier<PlusDiscount> {
|
||||
return ref.watch(settingsRepositoryProvider).getPlusDiscount();
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
Future<void> refresh({bool throwOnError = false}) async {
|
||||
final previous = state.valueOrNull;
|
||||
state = await AsyncValue.guard(
|
||||
() => ref.read(settingsRepositoryProvider).getPlusDiscount(),
|
||||
);
|
||||
if (state.hasError && previous != null) state = AsyncData(previous);
|
||||
try {
|
||||
state = AsyncData(
|
||||
await ref.read(settingsRepositoryProvider).getPlusDiscount(),
|
||||
);
|
||||
} catch (error, stackTrace) {
|
||||
state = previous == null
|
||||
? AsyncError(error, stackTrace)
|
||||
: AsyncData(previous);
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,20 +17,23 @@ class PlusInfoNotifier extends AsyncNotifier<PlusInfo> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
Future<void> refresh({bool throwOnError = false}) async {
|
||||
final previous = state.valueOrNull;
|
||||
if (previous == null) state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(
|
||||
() async {
|
||||
try {
|
||||
return await ref.read(settingsRepositoryProvider).getPlusInfo();
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
rethrow;
|
||||
}
|
||||
},
|
||||
);
|
||||
if (state.hasError && previous != null) state = AsyncData(previous);
|
||||
try {
|
||||
final plusInfo = await ref.read(settingsRepositoryProvider).getPlusInfo();
|
||||
state = AsyncData(plusInfo);
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
} catch (error, stackTrace) {
|
||||
state = previous == null
|
||||
? AsyncError(error, stackTrace)
|
||||
: AsyncData(previous);
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,13 +12,18 @@ class ProxyListNotifier extends AsyncNotifier<List<ServerProxyModel>> {
|
||||
return _fetch(ref.watch(apiClientProvider));
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
Future<void> refresh({bool throwOnError = false}) async {
|
||||
final previous = state.valueOrNull;
|
||||
if (previous == null) state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(
|
||||
() => _fetch(ref.read(apiClientProvider)),
|
||||
);
|
||||
if (state.hasError && previous != null) state = AsyncData(previous);
|
||||
try {
|
||||
state = AsyncData(await _fetch(ref.read(apiClientProvider)));
|
||||
} catch (error, stackTrace) {
|
||||
state = previous == null
|
||||
? AsyncError(error, stackTrace)
|
||||
: AsyncData(previous);
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ServerProxyModel>> _fetch(ApiClient api) async {
|
||||
|
||||
@@ -20,21 +20,24 @@ class ScriptGroupListNotifier extends AsyncNotifier<List<ScriptGroupModel>> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
Future<void> refresh({bool throwOnError = false}) async {
|
||||
final previous = state.valueOrNull;
|
||||
if (previous == null) {
|
||||
state = const AsyncLoading();
|
||||
}
|
||||
state = await AsyncValue.guard(() async {
|
||||
try {
|
||||
return await ref.read(scriptRepositoryProvider).fetchGroups();
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
rethrow;
|
||||
}
|
||||
});
|
||||
if (state.hasError && previous != null) {
|
||||
state = AsyncData(previous);
|
||||
try {
|
||||
final groups = await ref.read(scriptRepositoryProvider).fetchGroups();
|
||||
state = AsyncData(groups);
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
} catch (error, stackTrace) {
|
||||
state = previous == null
|
||||
? AsyncError(error, stackTrace)
|
||||
: AsyncData(previous);
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,21 +21,24 @@ class ScriptListNotifier extends AsyncNotifier<List<ScriptModel>> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
Future<void> refresh({bool throwOnError = false}) async {
|
||||
final previous = state.valueOrNull;
|
||||
if (previous == null) {
|
||||
state = const AsyncLoading();
|
||||
}
|
||||
state = await AsyncValue.guard(() async {
|
||||
try {
|
||||
return await ref.read(scriptRepositoryProvider).fetchScripts();
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
rethrow;
|
||||
}
|
||||
});
|
||||
if (state.hasError && previous != null) {
|
||||
state = AsyncData(previous);
|
||||
try {
|
||||
final scripts = await ref.read(scriptRepositoryProvider).fetchScripts();
|
||||
state = AsyncData(scripts);
|
||||
} on UnauthorizedFailure {
|
||||
await ref.read(authProvider.notifier).signOut();
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
} catch (error, stackTrace) {
|
||||
state = previous == null
|
||||
? AsyncError(error, stackTrace)
|
||||
: AsyncData(previous);
|
||||
if (!throwOnError) return;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import 'proxy_list_notifier.dart';
|
||||
|
||||
Future<void> refreshServerSharedData(WidgetRef ref) async {
|
||||
await Future.wait([
|
||||
ref.read(groupListProvider.notifier).refresh(),
|
||||
ref.read(hostListProvider.notifier).refresh(),
|
||||
ref.read(credentialListProvider.notifier).refresh(),
|
||||
ref.read(proxyListProvider.notifier).refresh(),
|
||||
ref.read(groupListProvider.notifier).refresh(throwOnError: true),
|
||||
ref.read(hostListProvider.notifier).refresh(throwOnError: true),
|
||||
ref.read(credentialListProvider.notifier).refresh(throwOnError: true),
|
||||
ref.read(proxyListProvider.notifier).refresh(throwOnError: true),
|
||||
]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user