From df6f2af756e008896132f32884cb21ed6f9a574e Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Fri, 29 Nov 2024 00:01:41 +0800 Subject: [PATCH] :sparkles: Leave channel --- assets/translations/en.json | 4 + assets/translations/zh.json | 4 + lib/screens/chat/channel_detail.dart | 186 ++++++++++++++++----------- 3 files changed, 121 insertions(+), 73 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 007337f..b72a792 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -179,6 +179,10 @@ "channelEditDescription": "Change the basic information of the channel, metadata, etc.", "channelActionDelete": "Delete Channel", "channelActionDeleteDescription": "Delete the entire channel, and also delete messages in the channel.", + "channelLeave": "Leave Channel {}", + "channelLeaveDescription": "Leave this channel, but the messages in the channel will not be removed.", + "channelActionLeave": "Leave Channel", + "channelActionLeaveDescription": "Delete your profile in this channel.", "fieldRealmAlias": "Realm Alias", "fieldRealmAliasHint": "The unique realm alias within the site, used to represent the realm in URL, leave blank to auto generate. Should be URL-Safe.", "fieldRealmName": "Name", diff --git a/assets/translations/zh.json b/assets/translations/zh.json index 73b639f..a4cb6fe 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -179,6 +179,10 @@ "channelEditDescription": "更改频道基本信息,元数据等。", "channelActionDelete": "删除频道", "channelActionDeleteDescription": "删除整个频道,并且删除频道里的所有信息。", + "channelLeave": "退出频道 {}", + "channelLeaveDescription": "退出该频道,但是你频道内的信息不会被移除。", + "channelActionLeave": "退出频道", + "channelActionLeaveDescription": "删除你在这个频道的身份。", "fieldRealmAlias": "领域别名", "fieldRealmAliasHint": "全站范围内唯一的领域别名,用于在 URL 中表示该领域,留空则自动生成。应遵循 URL-Safe 的原则。", "fieldRealmName": "名称", diff --git a/lib/screens/chat/channel_detail.dart b/lib/screens/chat/channel_detail.dart index 69aed08..bed7a0a 100644 --- a/lib/screens/chat/channel_detail.dart +++ b/lib/screens/chat/channel_detail.dart @@ -8,6 +8,7 @@ import 'package:styled_widget/styled_widget.dart'; import 'package:surface/providers/channel.dart'; import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/user_directory.dart'; +import 'package:surface/providers/userinfo.dart'; import 'package:surface/types/chat.dart'; import 'package:surface/widgets/account/account_image.dart'; import 'package:surface/widgets/dialog.dart'; @@ -86,6 +87,27 @@ class _ChannelDetailScreenState extends State { } } + Future _leaveChannel() async { + final confirm = await context.showConfirmDialog( + 'channelLeave'.tr(args: [_channel!.name]), + 'channelLeaveDescription'.tr(), + ); + if (!confirm) return; + if (!mounted) return; + + try { + final sn = context.read(); + await sn.client.delete( + '/cgi/im/channels/${_channel!.realm?.alias ?? 'global'}/${_channel!.id}/members/me', + ); + if (!mounted) return; + Navigator.pop(context, false); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } + } + @override void initState() { super.initState(); @@ -97,94 +119,112 @@ class _ChannelDetailScreenState extends State { @override Widget build(BuildContext context) { final ud = context.read(); + final ua = context.read(); + + final isOwned = ua.isAuthorized && _channel?.accountId == ua.user?.id; + return Scaffold( appBar: AppBar( title: _channel != null ? Text(_channel!.name) : Text('loading').tr(), ), - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LoadingIndicator(isActive: _isBusy), - const Gap(24), - if (_channel != null) + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LoadingIndicator(isActive: _isBusy), + const Gap(24), + if (_channel != null) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _channel!.name, + style: Theme.of(context).textTheme.titleMedium, + ), + Text( + _channel!.description, + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ).padding(horizontal: 24), + const Gap(16), + const Divider(), + const Gap(12), + if (_profile != null) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('channelDetailPersonalRegion') + .bold() + .fontSize(17) + .tr() + .padding(horizontal: 20, bottom: 4), + ListTile( + leading: AccountImage( + content: + ud.getAccountFromCache(_profile!.accountId)?.avatar, + radius: 18, + ), + trailing: const Icon(Symbols.chevron_right), + title: Text('channelEditProfile').tr(), + subtitle: Text( + (_profile?.nick?.isEmpty ?? true) + ? ud.getAccountFromCache(_profile!.accountId)!.nick + : _profile!.nick!, + ), + contentPadding: const EdgeInsets.only(left: 20, right: 20), + onTap: () {}, + ), + if (!isOwned) + ListTile( + leading: const Icon(Symbols.exit_to_app), + trailing: const Icon(Symbols.chevron_right), + title: Text('channelActionLeave').tr(), + subtitle: Text('channelActionLeaveDescription').tr(), + contentPadding: + const EdgeInsets.symmetric(horizontal: 24), + onTap: _leaveChannel, + ), + ], + ).padding(bottom: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - _channel!.name, - style: Theme.of(context).textTheme.titleMedium, - ), - Text( - _channel!.description, - style: Theme.of(context).textTheme.bodyMedium, - ), - ], - ).padding(horizontal: 24), - const Gap(16), - const Divider(), - if (_profile != null) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('channelDetailPersonalRegion') + Text('channelDetailAdminRegion') .bold() .fontSize(17) .tr() .padding(horizontal: 20, bottom: 4), ListTile( - leading: AccountImage( - content: - ud.getAccountFromCache(_profile!.accountId)?.avatar, - radius: 18, - ), + leading: const Icon(Symbols.edit), trailing: const Icon(Symbols.chevron_right), - title: Text('channelEditProfile').tr(), - subtitle: Text( - (_profile?.nick?.isEmpty ?? true) - ? ud.getAccountFromCache(_profile!.accountId)!.nick - : _profile!.nick!, - ), - contentPadding: const EdgeInsets.only(left: 20, right: 20), - onTap: () {}, + title: Text('channelEdit').tr(), + subtitle: Text('channelEditDescription').tr(), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + onTap: () { + GoRouter.of(context).pushNamed( + 'chatManage', + queryParameters: {'editing': _channel!.keyPath}, + ).then((value) { + if (value != null && context.mounted) { + Navigator.pop(context, value); + } + }); + }, ), + if (isOwned) + ListTile( + leading: const Icon(Symbols.delete), + trailing: const Icon(Symbols.chevron_right), + title: Text('channelActionDelete').tr(), + subtitle: Text('channelActionDeleteDescription').tr(), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + onTap: _deleteChannel, + ), ], - ).padding(bottom: 16), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('channelDetailAdminRegion') - .bold() - .fontSize(17) - .tr() - .padding(horizontal: 20, bottom: 4), - ListTile( - leading: const Icon(Symbols.edit), - trailing: const Icon(Symbols.chevron_right), - title: Text('channelEdit').tr(), - subtitle: Text('channelEditDescription').tr(), - contentPadding: const EdgeInsets.symmetric(horizontal: 24), - onTap: () { - GoRouter.of(context).pushNamed( - 'chatManage', - queryParameters: {'editing': _channel!.keyPath}, - ).then((value) { - if (value != null && context.mounted) { - Navigator.pop(context, value); - } - }); - }, - ), - ListTile( - leading: const Icon(Symbols.delete), - trailing: const Icon(Symbols.chevron_right), - title: Text('channelActionDelete').tr(), - subtitle: Text('channelActionDeleteDescription').tr(), - contentPadding: const EdgeInsets.symmetric(horizontal: 24), - onTap: _deleteChannel, - ), - ], - ), - ], + ), + ], + ), ), ); }