From 9d6cf54bf8669cd697ffc51cb15f00685867b57a Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 19 May 2025 23:29:51 +0800 Subject: [PATCH] :lipstick: Optimize styles --- assets/i18n/en-US.json | 8 +++- lib/pods/config.dart | 2 +- lib/screens/account.dart | 84 +++++++++++++++++++++++++---------- lib/screens/auth/tabs.dart | 2 + lib/screens/realm/realms.dart | 7 ++- lib/screens/settings.dart | 42 ++++++++++++++++++ lib/screens/wallet.dart | 40 +++++++++++++++-- lib/screens/wallet.g.dart | 6 +-- 8 files changed, 157 insertions(+), 34 deletions(-) diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index 5ccfc2c..976b3f9 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -1,5 +1,6 @@ { "login": "Login", + "loginDescription": "Existing user? We're welcome you back!", "forgotPassword": "Forgot password", "loginPickFactor": "Pick a factor", "loginMultiFactor": { @@ -19,6 +20,7 @@ "password": "Password", "next": "Next", "createAccount": "Create an Account", + "createAccountDescription": "New to here? We got you covered!", "nickname": "Nickname", "email": "Email", "bio": "Bio", @@ -252,5 +254,9 @@ "leaveChatRoom": "Leave Chat Room", "leaveChatRoomHint": "Are you sure to leave this chat room?", "leaveRealm": "Leave Realm", - "leaveRealmHint": "Are you sure to leave this realm?" + "leaveRealmHint": "Are you sure to leave this realm?", + "walletNotFound": "Wallet not found", + "walletCreateHint": "You don't have a wallet yet. Create one to start using the Solar Network eWallet.", + "walletCreate": "Create a Wallet", + "settingsServerUrl": "Server URL" } diff --git a/lib/pods/config.dart b/lib/pods/config.dart index eed61c0..7eec476 100644 --- a/lib/pods/config.dart +++ b/lib/pods/config.dart @@ -5,7 +5,7 @@ import 'package:shared_preferences/shared_preferences.dart'; const kTokenPairStoreKey = 'dyn_user_tk'; -const kNetworkServerDefault = 'http://localhost:5071'; +const kNetworkServerDefault = 'https://ppa.solian.app'; const kNetworkServerStoreKey = 'app_server_url'; const kAppbarTransparentStoreKey = 'app_bar_transparent'; diff --git a/lib/screens/account.dart b/lib/screens/account.dart index 29a743a..297f963 100644 --- a/lib/screens/account.dart +++ b/lib/screens/account.dart @@ -267,30 +267,66 @@ class _UnauthorizedAccountScreen extends StatelessWidget { Widget build(BuildContext context) { return AppScaffold( appBar: AppBar(title: const Text('Account')), - body: Column( - children: [ - ListTile( - leading: const Icon(Symbols.person_add), - trailing: const Icon(Symbols.chevron_right), - title: Text('createAccount').tr(), - subtitle: Text('New to here? We got you covered!'), - contentPadding: EdgeInsets.symmetric(horizontal: 24), - onTap: () { - context.router.push(CreateAccountRoute()); - }, - ), - ListTile( - leading: const Icon(Symbols.login), - trailing: const Icon(Symbols.chevron_right), - subtitle: Text('Existing user? We\'re welcome you back!'), - contentPadding: EdgeInsets.symmetric(horizontal: 24), - title: Text('login').tr(), - onTap: () { - context.router.push(LoginRoute()); - }, - ), - ], - ), + body: + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 360), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Card( + child: InkWell( + onTap: () { + context.router.push(CreateAccountRoute()); + }, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Icon(Symbols.person_add, size: 48), + const SizedBox(height: 8), + Text('createAccount').tr().bold(), + Text('createAccountDescription').tr(), + ], + ), + ), + ), + ), + ), + const Gap(8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Card( + child: InkWell( + onTap: () { + context.router.push(LoginRoute()); + }, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Icon(Symbols.login, size: 48), + const SizedBox(height: 8), + Text('login').tr().bold(), + Text('loginDescription').tr(), + ], + ), + ), + ), + ), + ), + const Gap(8), + TextButton( + onPressed: () { + context.router.push(SettingsRoute()); + }, + child: Text('appSettings').tr(), + ).center(), + ], + ), + ).center(), ); } } diff --git a/lib/screens/auth/tabs.dart b/lib/screens/auth/tabs.dart index d700f32..d7f9c2c 100644 --- a/lib/screens/auth/tabs.dart +++ b/lib/screens/auth/tabs.dart @@ -146,6 +146,8 @@ class TabsNavigationWidget extends HookConsumerWidget { bottomNavigationBar: !useHorizontalLayout && isTabRoute ? NavigationBar( + height: 56, + labelBehavior: NavigationDestinationLabelBehavior.alwaysHide, selectedIndex: activeIndex, onDestinationSelected: (index) { router.replace(routes[index]); diff --git a/lib/screens/realm/realms.dart b/lib/screens/realm/realms.dart index 4fdc63a..8dd5418 100644 --- a/lib/screens/realm/realms.dart +++ b/lib/screens/realm/realms.dart @@ -16,6 +16,7 @@ import 'package:island/services/file.dart'; import 'package:island/widgets/alert.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/content/cloud_files.dart'; +import 'package:island/widgets/response.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:styled_widget/styled_widget.dart'; @@ -480,7 +481,11 @@ class _RealmInviteSheet extends HookConsumerWidget { }, ), loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stack) => Center(child: Text('Error: $error')), + error: + (error, _) => ResponseErrorWidget( + error: error, + onRetry: () => ref.invalidate(realmInvitesProvider), + ), ), ), ], diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index d222bc2..1798f07 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:styled_widget/styled_widget.dart'; +import 'package:island/pods/config.dart'; @RoutePage() class SettingsScreen extends HookConsumerWidget { @@ -14,6 +15,10 @@ class SettingsScreen extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final serverUrl = ref.watch(serverUrlProvider); + final prefs = ref.watch(sharedPreferencesProvider); + final controller = TextEditingController(text: serverUrl); + return AppScaffold( appBar: AppBar(title: const Text('Settings')), body: SingleChildScrollView( @@ -61,6 +66,43 @@ class SettingsScreen extends HookConsumerWidget { ), ), ), + ListTile( + isThreeLine: true, + minLeadingWidth: 48, + title: Text('settingsServerUrl').tr(), + contentPadding: const EdgeInsets.only(left: 24, right: 17), + leading: const Icon(Symbols.link), + subtitle: Padding( + padding: const EdgeInsets.only(top: 6), + child: TextField( + controller: controller, + decoration: InputDecoration( + hintText: kNetworkServerDefault, + suffixIcon: IconButton( + icon: const Icon(Symbols.restart_alt), + onPressed: () { + controller.text = kNetworkServerDefault; + prefs.setString( + kNetworkServerStoreKey, + kNetworkServerDefault, + ); + ref.invalidate(serverUrlProvider); + }, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + isDense: true, + ), + onSubmitted: (value) { + if (value.isNotEmpty) { + prefs.setString(kNetworkServerStoreKey, value); + ref.invalidate(serverUrlProvider); + } + }, + ), + ), + ), ], ), ), diff --git a/lib/screens/wallet.dart b/lib/screens/wallet.dart index 8b11070..27a8427 100644 --- a/lib/screens/wallet.dart +++ b/lib/screens/wallet.dart @@ -1,10 +1,12 @@ import 'package:auto_route/annotations.dart'; +import 'package:dio/dio.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/wallet.dart'; import 'package:island/pods/network.dart'; import 'package:island/widgets/app_scaffold.dart'; +import 'package:island/widgets/alert.dart'; import 'package:island/widgets/response.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -14,10 +16,17 @@ import 'package:styled_widget/styled_widget.dart'; part 'wallet.g.dart'; @riverpod -Future walletCurrent(Ref ref) async { - final apiClient = ref.watch(apiClientProvider); - final resp = await apiClient.get('/wallets'); - return SnWallet.fromJson(resp.data); +Future walletCurrent(Ref ref) async { + try { + final apiClient = ref.watch(apiClientProvider); + final resp = await apiClient.get('/wallets'); + return SnWallet.fromJson(resp.data); + } catch (err) { + if (err is DioException && err.response?.statusCode == 404) { + return null; + } + rethrow; + } } const Map kCurrencyIconData = { @@ -71,6 +80,16 @@ class WalletScreen extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final wallet = ref.watch(walletCurrentProvider); + Future createWallet() async { + final client = ref.read(apiClientProvider); + try { + await client.post('/wallets'); + ref.invalidate(walletCurrentProvider); + } catch (err) { + showErrorAlert(err); + } + } + String getCurrencyTranslationKey(String currency, {bool isShort = false}) { return 'walletCurrency${isShort ? 'Short' : ''}${currency[0].toUpperCase()}${currency.substring(1).toLowerCase()}'; } @@ -79,6 +98,19 @@ class WalletScreen extends HookConsumerWidget { appBar: AppBar(title: Text('wallet').tr()), body: wallet.when( data: (data) { + if (data == null) { + return Column( + children: [ + Text('walletNotFound').tr(), + Text('walletCreateHint').tr(), + TextButton( + onPressed: createWallet, + child: Text('walletCreate').tr(), + ), + ], + ); + } + return Column( children: [ Column( diff --git a/lib/screens/wallet.g.dart b/lib/screens/wallet.g.dart index 5c2db40..c2f72fa 100644 --- a/lib/screens/wallet.g.dart +++ b/lib/screens/wallet.g.dart @@ -6,11 +6,11 @@ part of 'wallet.dart'; // RiverpodGenerator // ************************************************************************** -String _$walletCurrentHash() => r'9123af148c4a27e079bbe90c7d4e41d08e408a39'; +String _$walletCurrentHash() => r'94e6f3776ce15679d17238e372660c365c9b1028'; /// See also [walletCurrent]. @ProviderFor(walletCurrent) -final walletCurrentProvider = AutoDisposeFutureProvider.internal( +final walletCurrentProvider = AutoDisposeFutureProvider.internal( walletCurrent, name: r'walletCurrentProvider', debugGetCreateSourceHash: @@ -23,7 +23,7 @@ final walletCurrentProvider = AutoDisposeFutureProvider.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef WalletCurrentRef = AutoDisposeFutureProviderRef; +typedef WalletCurrentRef = AutoDisposeFutureProviderRef; String _$transactionListNotifierHash() => r'148ffb0ee9e3be3b92de432f314d8ee2f09e9a24';