From d258ba776e2c50cd4e40a7642a68709ffd54df34 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 27 Jan 2025 20:14:02 +0800 Subject: [PATCH] :recycle: Splitting up account page and settings --- assets/translations/en-US.json | 3 + assets/translations/zh-CN.json | 3 + lib/router.dart | 66 ++++++++++--------- lib/screens/account.dart | 78 ++++++++++++----------- lib/screens/account/account_settings.dart | 66 +++++++++++++++++++ lib/types/account.dart | 4 +- lib/types/account.freezed.dart | 10 +-- lib/types/account.g.dart | 4 +- 8 files changed, 160 insertions(+), 74 deletions(-) create mode 100644 lib/screens/account/account_settings.dart diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json index fa0fd0d..8a9ddf4 100644 --- a/assets/translations/en-US.json +++ b/assets/translations/en-US.json @@ -17,6 +17,7 @@ "screenAccountProfileEdit": "Edit Profile", "screenAbuseReport": "Abuse Reports", "screenSettings": "Settings", + "screenAccountSettings": "Account Settings", "screenNews": "News", "screenAlbum": "Album", "screenChat": "Chat", @@ -114,6 +115,8 @@ "accountLogoutConfirm": "You will need to re-enter your account password, even if you have already done so. This is required to login again.", "accountPublishers": "Your publishers", "accountPublishersSubtitle": "Manage your publish identities.", + "accountSettings": "Account Settings", + "accountSettingsSubtitle": "Manage your account and make it yours.", "accountProfileEdit": "Edit your profile", "accountProfileEditSubtitle": "Make your Solarpass account more looks like you.", "accountProfileEditApplied": "Profile modification applied.", diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json index 054209b..af22be4 100644 --- a/assets/translations/zh-CN.json +++ b/assets/translations/zh-CN.json @@ -15,6 +15,7 @@ "screenAccountProfileEdit": "编辑资料", "screenAbuseReport": "滥用检举", "screenSettings": "设置", + "screenAccountSettings": "账号设置", "screenNews": "新闻", "screenAlbum": "相册", "screenChat": "聊天", @@ -98,6 +99,8 @@ "accountLogoutConfirm": "您需要重新输入账号密码,甚至可能需要多步验证来再次登陆。", "accountPublishers": "你的发布者", "accountPublishersSubtitle": "管理你的公共形象。", + "accountSettings": "帐号设置", + "accountSettingsSubtitle": "管理你的帐号并让它更好的服务你。", "accountProfileEdit": "编辑资料", "accountProfileEditSubtitle": "使你的 Solarpass 账户更像你。", "accountProfileEditApplied": "个人资料修改已被应用。", diff --git a/lib/router.dart b/lib/router.dart index b7cca2b..7f7787a 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:surface/screens/abuse_report.dart'; import 'package:surface/screens/account.dart'; +import 'package:surface/screens/account/account_settings.dart'; import 'package:surface/screens/account/profile_page.dart'; import 'package:surface/screens/account/profile_edit.dart'; import 'package:surface/screens/account/publishers/publisher_edit.dart'; @@ -100,6 +101,42 @@ final _appRoutes = [ path: '/account', name: 'account', builder: (context, state) => const AccountScreen(), + routes: [ + GoRoute( + path: '/settings', + name: 'accountSettings', + builder: (context, state) => AccountSettingsScreen(), + ), + GoRoute( + path: '/profile/edit', + name: 'accountProfileEdit', + builder: (context, state) => ProfileEditScreen(), + ), + GoRoute( + path: '/publishers', + name: 'accountPublishers', + builder: (context, state) => PublisherScreen(), + ), + GoRoute( + path: '/publishers/new', + name: 'accountPublisherNew', + builder: (context, state) => AccountPublisherNewScreen(), + ), + GoRoute( + path: '/publishers/edit/:name', + name: 'accountPublisherEdit', + builder: (context, state) => AccountPublisherEditScreen( + name: state.pathParameters['name']!, + ), + ), + GoRoute( + path: '/:name', + name: 'accountProfilePage', + pageBuilder: (context, state) => NoTransitionPage( + child: UserScreen(name: state.pathParameters['name']!), + ), + ), + ] ), GoRoute( path: '/chat', @@ -205,35 +242,6 @@ final _appRoutes = [ name: 'abuseReport', builder: (context, state) => AbuseReportScreen(), ), - GoRoute( - path: '/account/profile/edit', - name: 'accountProfileEdit', - builder: (context, state) => ProfileEditScreen(), - ), - GoRoute( - path: '/account/publishers', - name: 'accountPublishers', - builder: (context, state) => PublisherScreen(), - ), - GoRoute( - path: '/account/publishers/new', - name: 'accountPublisherNew', - builder: (context, state) => AccountPublisherNewScreen(), - ), - GoRoute( - path: '/account/publishers/edit/:name', - name: 'accountPublisherEdit', - builder: (context, state) => AccountPublisherEditScreen( - name: state.pathParameters['name']!, - ), - ), - GoRoute( - path: '/account/:name', - name: 'accountProfilePage', - pageBuilder: (context, state) => NoTransitionPage( - child: UserScreen(name: state.pathParameters['name']!), - ), - ), GoRoute( path: '/settings', name: 'settings', diff --git a/lib/screens/account.dart b/lib/screens/account.dart index 525c893..0ac8adf 100644 --- a/lib/screens/account.dart +++ b/lib/screens/account.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; @@ -13,6 +15,7 @@ import 'package:surface/widgets/account/account_image.dart'; import 'package:surface/widgets/app_bar_leading.dart'; import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart'; +import 'package:surface/widgets/universal_image.dart'; class AccountScreen extends StatelessWidget { const AccountScreen({super.key}); @@ -20,11 +23,39 @@ class AccountScreen extends StatelessWidget { @override Widget build(BuildContext context) { final ua = context.watch(); + final sn = context.read(); return AppScaffold( appBar: AppBar( leading: AutoAppBarLeading(), title: Text("screenAccount").tr(), + flexibleSpace: ua.user != null && ua.user!.banner.isNotEmpty + ? Stack( + fit: StackFit.expand, + children: [ + AutoResizeUniversalImage(sn.getAttachmentUrl(ua.user!.banner), fit: BoxFit.cover), + Positioned( + top: 0, + left: 0, + right: 0, + height: 56 + MediaQuery.of(context).padding.top, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 10, + sigmaY: 10, + ), + child: Container( + color: Colors.black.withOpacity( + clampDouble(10 * 0.1, 0, 0.5), + ), + ), + ), + ), + ), + ], + ) + : null, actions: [ IconButton( icon: const Icon(Symbols.settings, fill: 1), @@ -83,16 +114,6 @@ class _AuthorizedAccountScreen extends StatelessWidget { ); }).padding(all: 20), ).padding(horizontal: 8, top: 16, bottom: 4), - ListTile( - title: Text('accountProfileEdit').tr(), - subtitle: Text('accountProfileEditSubtitle').tr(), - contentPadding: const EdgeInsets.symmetric(horizontal: 24), - leading: const Icon(Symbols.contact_page), - trailing: const Icon(Symbols.chevron_right), - onTap: () { - GoRouter.of(context).pushNamed('accountProfileEdit'); - }, - ), ListTile( title: Text('accountPublishers').tr(), subtitle: Text('accountPublishersSubtitle').tr(), @@ -113,6 +134,16 @@ class _AuthorizedAccountScreen extends StatelessWidget { GoRouter.of(context).pushNamed('abuseReport'); }, ), + ListTile( + title: Text('accountSettings').tr(), + subtitle: Text('accountSettingsSubtitle').tr(), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + leading: const Icon(Symbols.manage_accounts), + trailing: const Icon(Symbols.chevron_right), + onTap: () { + GoRouter.of(context).pushNamed('accountSettings'); + }, + ), ListTile( title: Text('accountLogout').tr(), subtitle: Text('accountLogoutSubtitle').tr(), @@ -134,33 +165,6 @@ class _AuthorizedAccountScreen extends StatelessWidget { await Hive.initFlutter(); }, ), - ListTile( - title: Text('accountDeletion'.tr()), - subtitle: Text('accountDeletionActionDescription'.tr()), - contentPadding: const EdgeInsets.symmetric(horizontal: 24), - leading: const Icon(Symbols.person_cancel), - trailing: const Icon(Symbols.chevron_right), - onTap: () { - context - .showConfirmDialog( - 'accountDeletion'.tr(), - 'accountDeletionDescription'.tr(), - ) - .then((value) { - if (!value || !context.mounted) return; - final sn = context.read(); - sn.client.post('/cgi/id/users/me/deletion').then((value) { - if (context.mounted) { - context.showSnackbar('accountDeletionSubmitted'.tr()); - } - }).catchError((err) { - if (context.mounted) { - context.showErrorDialog(err); - } - }); - }); - }, - ), ], ); } diff --git a/lib/screens/account/account_settings.dart b/lib/screens/account/account_settings.dart new file mode 100644 index 0000000..fb9e680 --- /dev/null +++ b/lib/screens/account/account_settings.dart @@ -0,0 +1,66 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:material_symbols_icons/symbols.dart'; +import 'package:provider/provider.dart'; +import 'package:surface/providers/sn_network.dart'; +import 'package:surface/widgets/dialog.dart'; +import 'package:surface/widgets/navigation/app_scaffold.dart'; + +class AccountSettingsScreen extends StatelessWidget { + const AccountSettingsScreen({super.key}); + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBar: AppBar( + leading: PageBackButton(), + title: Text('screenAccountSettings').tr(), + ), + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + title: Text('accountProfileEdit').tr(), + subtitle: Text('accountProfileEditSubtitle').tr(), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + leading: const Icon(Symbols.contact_page), + trailing: const Icon(Symbols.chevron_right), + onTap: () { + GoRouter.of(context).pushNamed('accountProfileEdit'); + }, + ), + ListTile( + title: Text('accountDeletion'.tr()), + subtitle: Text('accountDeletionActionDescription'.tr()), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + leading: const Icon(Symbols.person_cancel), + trailing: const Icon(Symbols.chevron_right), + onTap: () { + context + .showConfirmDialog( + 'accountDeletion'.tr(), + 'accountDeletionDescription'.tr(), + ) + .then((value) { + if (!value || !context.mounted) return; + final sn = context.read(); + sn.client.post('/cgi/id/users/me/deletion').then((value) { + if (context.mounted) { + context.showSnackbar('accountDeletionSubmitted'.tr()); + } + }).catchError((err) { + if (context.mounted) { + context.showErrorDialog(err); + } + }); + }); + }, + ), + ], + ), + ), + ); + } +} diff --git a/lib/types/account.dart b/lib/types/account.dart index 8d0357e..b6c52bf 100644 --- a/lib/types/account.dart +++ b/lib/types/account.dart @@ -15,8 +15,8 @@ class SnAccount with _$SnAccount { required DateTime? deletedAt, required DateTime? confirmedAt, required List? contacts, - required String avatar, - required String banner, + @Default("") String avatar, + @Default("") String banner, required String description, required String name, required String nick, diff --git a/lib/types/account.freezed.dart b/lib/types/account.freezed.dart index 8f3eefc..e4de56a 100644 --- a/lib/types/account.freezed.dart +++ b/lib/types/account.freezed.dart @@ -367,8 +367,8 @@ class _$SnAccountImpl extends _SnAccount { required this.deletedAt, required this.confirmedAt, required final List? contacts, - required this.avatar, - required this.banner, + this.avatar = "", + this.banner = "", required this.description, required this.name, required this.nick, @@ -410,8 +410,10 @@ class _$SnAccountImpl extends _SnAccount { } @override + @JsonKey() final String avatar; @override + @JsonKey() final String banner; @override final String description; @@ -540,8 +542,8 @@ abstract class _SnAccount extends SnAccount { required final DateTime? deletedAt, required final DateTime? confirmedAt, required final List? contacts, - required final String avatar, - required final String banner, + final String avatar, + final String banner, required final String description, required final String name, required final String nick, diff --git a/lib/types/account.g.dart b/lib/types/account.g.dart index 3e91de3..87ea7fb 100644 --- a/lib/types/account.g.dart +++ b/lib/types/account.g.dart @@ -20,8 +20,8 @@ _$SnAccountImpl _$$SnAccountImplFromJson(Map json) => contacts: (json['contacts'] as List?) ?.map((e) => SnAccountContact.fromJson(e as Map)) .toList(), - avatar: json['avatar'] as String, - banner: json['banner'] as String, + avatar: json['avatar'] as String? ?? "", + banner: json['banner'] as String? ?? "", description: json['description'] as String, name: json['name'] as String, nick: json['nick'] as String,