♻️ Splitting up account page and settings

This commit is contained in:
LittleSheep 2025-01-27 20:14:02 +08:00
parent 0dcfcaad56
commit d258ba776e
8 changed files with 160 additions and 74 deletions

View File

@ -17,6 +17,7 @@
"screenAccountProfileEdit": "Edit Profile", "screenAccountProfileEdit": "Edit Profile",
"screenAbuseReport": "Abuse Reports", "screenAbuseReport": "Abuse Reports",
"screenSettings": "Settings", "screenSettings": "Settings",
"screenAccountSettings": "Account Settings",
"screenNews": "News", "screenNews": "News",
"screenAlbum": "Album", "screenAlbum": "Album",
"screenChat": "Chat", "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.", "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", "accountPublishers": "Your publishers",
"accountPublishersSubtitle": "Manage your publish identities.", "accountPublishersSubtitle": "Manage your publish identities.",
"accountSettings": "Account Settings",
"accountSettingsSubtitle": "Manage your account and make it yours.",
"accountProfileEdit": "Edit your profile", "accountProfileEdit": "Edit your profile",
"accountProfileEditSubtitle": "Make your Solarpass account more looks like you.", "accountProfileEditSubtitle": "Make your Solarpass account more looks like you.",
"accountProfileEditApplied": "Profile modification applied.", "accountProfileEditApplied": "Profile modification applied.",

View File

@ -15,6 +15,7 @@
"screenAccountProfileEdit": "编辑资料", "screenAccountProfileEdit": "编辑资料",
"screenAbuseReport": "滥用检举", "screenAbuseReport": "滥用检举",
"screenSettings": "设置", "screenSettings": "设置",
"screenAccountSettings": "账号设置",
"screenNews": "新闻", "screenNews": "新闻",
"screenAlbum": "相册", "screenAlbum": "相册",
"screenChat": "聊天", "screenChat": "聊天",
@ -98,6 +99,8 @@
"accountLogoutConfirm": "您需要重新输入账号密码,甚至可能需要多步验证来再次登陆。", "accountLogoutConfirm": "您需要重新输入账号密码,甚至可能需要多步验证来再次登陆。",
"accountPublishers": "你的发布者", "accountPublishers": "你的发布者",
"accountPublishersSubtitle": "管理你的公共形象。", "accountPublishersSubtitle": "管理你的公共形象。",
"accountSettings": "帐号设置",
"accountSettingsSubtitle": "管理你的帐号并让它更好的服务你。",
"accountProfileEdit": "编辑资料", "accountProfileEdit": "编辑资料",
"accountProfileEditSubtitle": "使你的 Solarpass 账户更像你。", "accountProfileEditSubtitle": "使你的 Solarpass 账户更像你。",
"accountProfileEditApplied": "个人资料修改已被应用。", "accountProfileEditApplied": "个人资料修改已被应用。",

View File

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:surface/screens/abuse_report.dart'; import 'package:surface/screens/abuse_report.dart';
import 'package:surface/screens/account.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_page.dart';
import 'package:surface/screens/account/profile_edit.dart'; import 'package:surface/screens/account/profile_edit.dart';
import 'package:surface/screens/account/publishers/publisher_edit.dart'; import 'package:surface/screens/account/publishers/publisher_edit.dart';
@ -100,6 +101,42 @@ final _appRoutes = [
path: '/account', path: '/account',
name: 'account', name: 'account',
builder: (context, state) => const AccountScreen(), 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( GoRoute(
path: '/chat', path: '/chat',
@ -205,35 +242,6 @@ final _appRoutes = [
name: 'abuseReport', name: 'abuseReport',
builder: (context, state) => AbuseReportScreen(), 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( GoRoute(
path: '/settings', path: '/settings',
name: 'settings', name: 'settings',

View File

@ -1,3 +1,5 @@
import 'dart:ui';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.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/app_bar_leading.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:surface/widgets/universal_image.dart';
class AccountScreen extends StatelessWidget { class AccountScreen extends StatelessWidget {
const AccountScreen({super.key}); const AccountScreen({super.key});
@ -20,11 +23,39 @@ class AccountScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ua = context.watch<UserProvider>(); final ua = context.watch<UserProvider>();
final sn = context.read<SnNetworkProvider>();
return AppScaffold( return AppScaffold(
appBar: AppBar( appBar: AppBar(
leading: AutoAppBarLeading(), leading: AutoAppBarLeading(),
title: Text("screenAccount").tr(), 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: [ actions: [
IconButton( IconButton(
icon: const Icon(Symbols.settings, fill: 1), icon: const Icon(Symbols.settings, fill: 1),
@ -83,16 +114,6 @@ class _AuthorizedAccountScreen extends StatelessWidget {
); );
}).padding(all: 20), }).padding(all: 20),
).padding(horizontal: 8, top: 16, bottom: 4), ).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( ListTile(
title: Text('accountPublishers').tr(), title: Text('accountPublishers').tr(),
subtitle: Text('accountPublishersSubtitle').tr(), subtitle: Text('accountPublishersSubtitle').tr(),
@ -113,6 +134,16 @@ class _AuthorizedAccountScreen extends StatelessWidget {
GoRouter.of(context).pushNamed('abuseReport'); 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( ListTile(
title: Text('accountLogout').tr(), title: Text('accountLogout').tr(),
subtitle: Text('accountLogoutSubtitle').tr(), subtitle: Text('accountLogoutSubtitle').tr(),
@ -134,33 +165,6 @@ class _AuthorizedAccountScreen extends StatelessWidget {
await Hive.initFlutter(); 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<SnNetworkProvider>();
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);
}
});
});
},
),
], ],
); );
} }

View File

@ -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<SnNetworkProvider>();
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);
}
});
});
},
),
],
),
),
);
}
}

View File

@ -15,8 +15,8 @@ class SnAccount with _$SnAccount {
required DateTime? deletedAt, required DateTime? deletedAt,
required DateTime? confirmedAt, required DateTime? confirmedAt,
required List<SnAccountContact>? contacts, required List<SnAccountContact>? contacts,
required String avatar, @Default("") String avatar,
required String banner, @Default("") String banner,
required String description, required String description,
required String name, required String name,
required String nick, required String nick,

View File

@ -367,8 +367,8 @@ class _$SnAccountImpl extends _SnAccount {
required this.deletedAt, required this.deletedAt,
required this.confirmedAt, required this.confirmedAt,
required final List<SnAccountContact>? contacts, required final List<SnAccountContact>? contacts,
required this.avatar, this.avatar = "",
required this.banner, this.banner = "",
required this.description, required this.description,
required this.name, required this.name,
required this.nick, required this.nick,
@ -410,8 +410,10 @@ class _$SnAccountImpl extends _SnAccount {
} }
@override @override
@JsonKey()
final String avatar; final String avatar;
@override @override
@JsonKey()
final String banner; final String banner;
@override @override
final String description; final String description;
@ -540,8 +542,8 @@ abstract class _SnAccount extends SnAccount {
required final DateTime? deletedAt, required final DateTime? deletedAt,
required final DateTime? confirmedAt, required final DateTime? confirmedAt,
required final List<SnAccountContact>? contacts, required final List<SnAccountContact>? contacts,
required final String avatar, final String avatar,
required final String banner, final String banner,
required final String description, required final String description,
required final String name, required final String name,
required final String nick, required final String nick,

View File

@ -20,8 +20,8 @@ _$SnAccountImpl _$$SnAccountImplFromJson(Map<String, dynamic> json) =>
contacts: (json['contacts'] as List<dynamic>?) contacts: (json['contacts'] as List<dynamic>?)
?.map((e) => SnAccountContact.fromJson(e as Map<String, dynamic>)) ?.map((e) => SnAccountContact.fromJson(e as Map<String, dynamic>))
.toList(), .toList(),
avatar: json['avatar'] as String, avatar: json['avatar'] as String? ?? "",
banner: json['banner'] as String, banner: json['banner'] as String? ?? "",
description: json['description'] as String, description: json['description'] as String,
name: json['name'] as String, name: json['name'] as String,
nick: json['nick'] as String, nick: json['nick'] as String,