From 0c2df4533762e9b5690d348fa122ed9b01eed059 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 30 Nov 2024 22:39:49 +0800 Subject: [PATCH] :sparkles: Friend management --- assets/translations/en.json | 32 +- assets/translations/zh.json | 30 +- lib/main.dart | 2 + lib/providers/navigation.dart | 5 + lib/providers/relationship.dart | 34 ++ lib/router.dart | 8 + lib/screens/chat/channel_detail.dart | 6 + lib/screens/friend.dart | 487 +++++++++++++++++++++++++++ lib/types/account.dart | 19 ++ lib/types/account.freezed.dart | 394 ++++++++++++++++++++++ lib/types/account.g.dart | 35 ++ lib/types/chat.freezed.dart | 23 +- lib/types/chat.g.dart | 2 - pubspec.lock | 20 +- 14 files changed, 1060 insertions(+), 37 deletions(-) create mode 100644 lib/providers/relationship.dart create mode 100644 lib/screens/friend.dart diff --git a/assets/translations/en.json b/assets/translations/en.json index dc85760..d2927b6 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -24,6 +24,7 @@ "screenRealmNew": "New Realm", "screenNotification": "Notification", "screenPostSearch": "Search Posts", + "screenFriend": "Friends", "dialogOkay": "Okay", "dialogCancel": "Cancel", "dialogConfirm": "Confirm", @@ -35,6 +36,7 @@ "errorRequestNotFound": "The resource that you looking for is not found.", "errorRequestConnection": "Network connection error, please check your network or the service status.", "errorRequestUnknown": "Unknown request error, maybe you want to take screenshot and report it to us.", + "unknown": "Unknown", "prev": "Previous", "next": "Next", "edit": "Edit", @@ -189,7 +191,7 @@ "channelNotifyLevelAll": "All", "channelNotifyLevelMentioned": "Only Mentioned", "channelNotifyLevelNone": "Muted", - "channelNotifyLevelApplied": "Channel notify level has been applied.", + "channelNotifyLevelApplie": "Channel notify level has been applied.", "fieldChannelProfileNick": "In-Channel Display Name", "fieldChannelProfileNickHint": "The nickname to display in the channel, leave blank to use the account display name.", "fieldRealmAlias": "Realm Alias", @@ -296,5 +298,31 @@ "dailyCheckNegativeHint5Description": "Lost connection at a crucial moment", "dailyCheckNegativeHint6": "Going out", "dailyCheckNegativeHint6Description": "Forgot your umbrella and got caught in the rain", - "happyBirthday": "Happy birthday, {}!" + "happyBirthday": "Happy birthday, {}!", + "friendNew": "Add Friend", + "friendRequests": "Friend Requests", + "friendRequestsDescription": { + "zero": "You have no friend request", + "one": "You have {} friend request", + "other": "You have {} friend requests" + }, + "friendBlocklist": "Blocklist", + "friendBlocklistDescription": { + "zero": "You blocked no one", + "one": "You blocked {} user", + "other": "You blocked {} users" + }, + "friendStatusPending": "Pending", + "friendStatusWaiting": "Waiting", + "friendStatusActive": "Friend", + "friendStatusBlocked": "Blocked", + "friendRequestSent": "Friend request has been sent.", + "fieldFriendRelatedName": "Friend name / account ID", + "friendBlock": "Block", + "friendUnblock": "Unblock", + "friendDeleteAction": "Delete", + "friendDelete": "Delete relation with {}", + "friendDeleteDescription": "Are you sure you want to delete the relation with {}? This operation is irreversible.", + "friendRequestAccept": "Accept", + "friendRequestDecline": "Decline" } diff --git a/assets/translations/zh.json b/assets/translations/zh.json index a01b4dd..e4a7bbe 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -24,6 +24,7 @@ "screenRealmNew": "新建领域", "screenNotification": "通知", "screenPostSearch": "搜索帖子", + "screenFriend": "好友", "dialogOkay": "好的", "dialogCancel": "取消", "dialogConfirm": "确认", @@ -35,6 +36,7 @@ "errorRequestNotFound": "您正查找的资源无法被找到。", "errorRequestConnection": "网络连接错误,请检查您的网络状态或者检查我们的服务状态。", "errorRequestUnknown": "位置请求错误,您可能想将此对话框截图并发送给我们。", + "unknown": "未知", "loading": "加载中…", "prev": "上一步", "next": "下一步", @@ -296,5 +298,31 @@ "dailyCheckNegativeHint5Description": "关键时刻断网", "dailyCheckNegativeHint6": "出门", "dailyCheckNegativeHint6Description": "忘带伞遇上大雨", - "happyBirthday": "生日快乐,{}!" + "happyBirthday": "生日快乐,{}!", + "friendNew": "添加好友", + "friendRequests": "好友请求", + "friendRequestsDescription": { + "zero": "你没有好友请求", + "one": "你有 {} 个好友请求", + "other": "你有 {} 个好友请求" + }, + "friendBlocklist": "屏蔽列表", + "friendBlocklistDescription": { + "zero": "你没有屏蔽任何人", + "one": "你屏蔽了 {} 个用户", + "other": "你屏蔽了 {} 个用户" + }, + "friendStatusPending": "待处理", + "friendStatusWaiting": "等待中", + "friendStatusActive": "正活跃", + "friendStatusBlocked": "已屏蔽", + "friendRequestSent": "好友请求已发送。", + "fieldFriendRelatedName": "好友名 / 账户 ID", + "friendBlock": "屏蔽", + "friendUnblock": "解除屏蔽", + "friendDeleteAction": "遗忘", + "friendDelete": "遗忘跟 {} 的关系", + "friendDeleteDescription": "你确定要遗忘跟 {} 的关系吗?这个操作无法撤销。", + "friendRequestAccept": "接受", + "friendRequestDecline": "拒绝" } diff --git a/lib/main.dart b/lib/main.dart index bf62bd8..45ba465 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,6 +16,7 @@ import 'package:surface/providers/chat_call.dart'; import 'package:surface/providers/navigation.dart'; import 'package:surface/providers/notification.dart'; import 'package:surface/providers/post.dart'; +import 'package:surface/providers/relationship.dart'; import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/theme.dart'; @@ -85,6 +86,7 @@ class SolianApp extends StatelessWidget { Provider(create: (ctx) => SnAttachmentProvider(ctx)), Provider(create: (ctx) => SnPostContentProvider(ctx)), Provider(create: (ctx) => UserDirectoryProvider(ctx)), + Provider(create: (ctx) => SnRelationshipProvider(ctx)), ChangeNotifierProvider(create: (ctx) => UserProvider(ctx)), ChangeNotifierProvider(create: (ctx) => WebSocketProvider(ctx)), ChangeNotifierProvider(create: (ctx) => NotificationProvider(ctx)), diff --git a/lib/providers/navigation.dart b/lib/providers/navigation.dart index dbed59e..34ff219 100644 --- a/lib/providers/navigation.dart +++ b/lib/providers/navigation.dart @@ -63,6 +63,11 @@ class NavigationProvider extends ChangeNotifier { screen: 'album', label: 'screenAlbum', ), + AppNavDestination( + icon: Icon(Symbols.diversity_4, weight: 400, opticalSize: 20), + screen: 'friend', + label: 'screenFriend', + ), AppNavDestination( icon: Icon(Symbols.notifications, weight: 400, opticalSize: 20), screen: 'notification', diff --git a/lib/providers/relationship.dart b/lib/providers/relationship.dart new file mode 100644 index 0000000..c033f1e --- /dev/null +++ b/lib/providers/relationship.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:surface/providers/sn_network.dart'; + +class SnRelationshipProvider { + late final SnNetworkProvider _sn; + + SnRelationshipProvider(BuildContext context) { + _sn = context.read(); + } + + Future updateRelationship( + int relatedId, + int status, + Map permNodes, + ) async { + await _sn.client.put('/cgi/id/users/me/relations/$relatedId', data: { + 'status': status, + 'perm_nodes': permNodes, + }); + } + + Future deleteRelationship(int relatedId) async { + await _sn.client.delete('/cgi/id/users/me/relations/$relatedId'); + } + + Future acceptFriendRequest(int relatedId) async { + await _sn.client.post('/cgi/id/users/me/relations/$relatedId/accept'); + } + + Future declineFriendRequest(int relatedId) async { + await _sn.client.post('/cgi/id/users/me/relations/$relatedId/decline'); + } +} diff --git a/lib/router.dart b/lib/router.dart index f624492..e410b2c 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -15,6 +15,7 @@ import 'package:surface/screens/chat/channel_detail.dart'; import 'package:surface/screens/chat/manage.dart'; import 'package:surface/screens/chat/room.dart'; import 'package:surface/screens/explore.dart'; +import 'package:surface/screens/friend.dart'; import 'package:surface/screens/home.dart'; import 'package:surface/screens/notification.dart'; import 'package:surface/screens/post/post_detail.dart'; @@ -192,6 +193,13 @@ final _appRoutes = [ child: const AlbumScreen(), ), ), + GoRoute( + path: '/friend', + name: 'friend', + pageBuilder: (context, state) => NoTransitionPage( + child: const FriendScreen(), + ), + ), GoRoute( path: '/notification', name: 'notification', diff --git a/lib/screens/chat/channel_detail.dart b/lib/screens/chat/channel_detail.dart index 87ea2cd..54c2fea 100644 --- a/lib/screens/chat/channel_detail.dart +++ b/lib/screens/chat/channel_detail.dart @@ -357,6 +357,12 @@ class _ChannelProfileDetailDialogState _nickController.text = widget.current.nick ?? ''; } + @override + void dispose() { + _nickController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return AlertDialog( diff --git a/lib/screens/friend.dart b/lib/screens/friend.dart new file mode 100644 index 0000000..3883c7e --- /dev/null +++ b/lib/screens/friend.dart @@ -0,0 +1,487 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:material_symbols_icons/symbols.dart'; +import 'package:provider/provider.dart'; +import 'package:styled_widget/styled_widget.dart'; +import 'package:surface/providers/relationship.dart'; +import 'package:surface/providers/sn_network.dart'; +import 'package:surface/types/account.dart'; +import 'package:surface/widgets/account/account_image.dart'; +import 'package:surface/widgets/dialog.dart'; +import 'package:surface/widgets/loading_indicator.dart'; + +const kFriendStatus = { + 0: 'friendStatusPending', + 1: 'friendStatusActive', + 2: 'friendStatusBlocked', + 3: 'friendStatusWaiting', +}; + +class FriendScreen extends StatefulWidget { + const FriendScreen({super.key}); + + @override + State createState() => _FriendScreenState(); +} + +class _FriendScreenState extends State { + bool _isBusy = false; + + List _requests = List.empty(); + List _relations = List.empty(); + List _blocks = List.empty(); + + Future _fetchRelations() async { + setState(() => _isBusy = true); + + try { + final sn = context.read(); + final resp = await sn.client.get('/cgi/id/users/me/relations?status=1'); + _relations = List.from( + resp.data?.map((e) => SnRelationship.fromJson(e)) ?? [], + ); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isBusy = false); + } + } + + Future _fetchRequests() async { + setState(() => _isBusy = true); + + try { + final sn = context.read(); + final resp = await sn.client.get('/cgi/id/users/me/relations?status=0,3'); + _requests = List.from( + resp.data?.map((e) => SnRelationship.fromJson(e)) ?? [], + ); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isBusy = false); + } + } + + Future _fetchBlocks() async { + setState(() => _isBusy = true); + + try { + final sn = context.read(); + final resp = await sn.client.get('/cgi/id/users/me/relations?status=2'); + _blocks = List.from( + resp.data?.map((e) => SnRelationship.fromJson(e)) ?? [], + ); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isBusy = false); + } + } + + bool _isUpdating = false; + + Future _changeRelation(SnRelationship relation, int dstStatus) async { + setState(() => _isUpdating = true); + + try { + final rel = context.read(); + await rel.updateRelationship( + relation.relatedId, + dstStatus, + relation.permNodes, + ); + if (!mounted) return; + _fetchRelations(); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isUpdating = false); + } + } + + Future _deleteRelation(SnRelationship relation) async { + final confirm = await context.showConfirmDialog( + 'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]), + 'friendDeleteDescription'.tr(args: [ + relation.related?.nick ?? 'unknown'.tr(), + ]), + ); + if (!confirm) return; + if (!mounted) return; + + setState(() => _isUpdating = true); + + try { + final rel = context.read(); + await rel.deleteRelationship(relation.relatedId); + if (!mounted) return; + _fetchRelations(); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isUpdating = false); + } + } + + void _showRequests() { + showModalBottomSheet( + context: context, + builder: (context) => _FriendshipPopupWidget(relations: _requests), + ).then((value) { + if (value != null) { + _fetchRequests(); + _fetchRelations(); + } + }); + } + + void _showBlocks() { + showModalBottomSheet( + context: context, + builder: (context) => _FriendshipPopupWidget(relations: _blocks), + ).then((value) { + if (value != null) { + _fetchBlocks(); + _fetchRelations(); + } + }); + } + + @override + void initState() { + super.initState(); + _fetchRelations(); + _fetchRequests(); + _fetchBlocks(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('screenFriend').tr(), + ), + floatingActionButton: FloatingActionButton( + child: const Icon(Symbols.add), + onPressed: () { + showModalBottomSheet( + context: context, + builder: (context) => _NewFriendWidget(), + ); + }, + ), + body: Column( + children: [ + LoadingIndicator(isActive: _isBusy || _isUpdating), + if (_requests.isNotEmpty) + ListTile( + title: Text('friendRequests').tr(), + subtitle: Text( + 'friendRequestsDescription', + ).plural(_requests.length), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + leading: const Icon(Symbols.group_add), + trailing: const Icon(Symbols.chevron_right), + onTap: _showRequests, + ), + if (_blocks.isNotEmpty) + ListTile( + title: Text('friendBlocklist').tr(), + subtitle: Text( + 'friendBlocklistDescription', + ).plural(_blocks.length), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + leading: const Icon(Symbols.block), + trailing: const Icon(Symbols.chevron_right), + onTap: _showBlocks, + ), + if (_requests.isNotEmpty || _blocks.isNotEmpty) + const Divider(height: 1), + Expanded( + child: RefreshIndicator( + onRefresh: () => Future.wait([ + _fetchRelations(), + _fetchRequests(), + ]), + child: ListView.builder( + itemCount: _relations.length, + itemBuilder: (context, index) { + final relation = _relations[index]; + final other = relation.related; + return ListTile( + contentPadding: const EdgeInsets.only(right: 24, left: 16), + leading: AccountImage(content: other?.avatar), + title: Text(other?.nick ?? 'unknown'), + subtitle: Text(other?.nick ?? 'unknown'), + trailing: SizedBox( + height: 48, + width: 120, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + InkWell( + onTap: _isUpdating + ? null + : () => _changeRelation(relation, 2), + child: Text('friendBlock').tr(), + ), + const Gap(8), + InkWell( + onTap: _isUpdating + ? null + : () => _deleteRelation(relation), + child: Text('friendDeleteAction').tr(), + ), + ], + ), + ], + ), + ), + ); + }, + ), + ), + ), + ], + ), + ); + } +} + +class _NewFriendWidget extends StatefulWidget { + const _NewFriendWidget({super.key}); + + @override + State<_NewFriendWidget> createState() => _NewFriendWidgetState(); +} + +class _NewFriendWidgetState extends State<_NewFriendWidget> { + bool _isBusy = false; + + final TextEditingController _relatedController = TextEditingController(); + + Future _sendRequest() async { + if (_relatedController.text.isEmpty) return; + + setState(() => _isBusy = true); + + try { + final sn = context.read(); + await sn.client.post('/cgi/id/users/me/relations', data: { + 'related': _relatedController.text, + }); + if (!mounted) return; + Navigator.pop(context, true); + context.showSnackbar('friendRequestSent'.tr()); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isBusy = false); + } + } + + @override + void dispose() { + super.dispose(); + _relatedController.dispose(); + } + + @override + Widget build(BuildContext context) { + return StyledWidget(Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'friendNew', + style: Theme.of(context).textTheme.titleLarge, + ).tr(), + const Gap(12), + TextField( + controller: _relatedController, + readOnly: _isBusy, + autocorrect: false, + autofocus: true, + textCapitalization: TextCapitalization.none, + decoration: InputDecoration( + labelText: 'fieldFriendRelatedName'.tr(), + suffix: SizedBox( + height: 24, + child: IconButton( + onPressed: _isBusy ? null : () => _sendRequest(), + icon: Icon(Symbols.send), + visualDensity: + const VisualDensity(horizontal: -4, vertical: -4), + padding: EdgeInsets.zero, + ), + ), + ), + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), + ) + ], + )).padding(all: 24); + } +} + +class _FriendshipPopupWidget extends StatefulWidget { + final List relations; + const _FriendshipPopupWidget({super.key, required this.relations}); + + @override + State<_FriendshipPopupWidget> createState() => _FriendshipPopupWidgetState(); +} + +class _FriendshipPopupWidgetState extends State<_FriendshipPopupWidget> { + bool _isBusy = false; + + Future _acceptRequest(SnRelationship relation) async { + setState(() => _isBusy = true); + + try { + final rel = context.read(); + await rel.acceptFriendRequest(relation.relatedId); + if (!mounted) return; + Navigator.pop(context, true); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isBusy = false); + } + } + + Future _declineRequest(SnRelationship relation) async { + setState(() => _isBusy = true); + + try { + final rel = context.read(); + await rel.declineFriendRequest(relation.relatedId); + if (!mounted) return; + Navigator.pop(context, true); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isBusy = false); + } + } + + Future _changeRelation(SnRelationship relation, int dstStatus) async { + setState(() => _isBusy = true); + + try { + final rel = context.read(); + await rel.updateRelationship( + relation.relatedId, + dstStatus, + relation.permNodes, + ); + if (!mounted) return; + Navigator.pop(context, true); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isBusy = false); + } + } + + Future _deleteRelation(SnRelationship relation) async { + final confirm = await context.showConfirmDialog( + 'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]), + 'friendDeleteDescription'.tr(args: [ + relation.related?.nick ?? 'unknown'.tr(), + ]), + ); + if (!confirm) return; + if (!mounted) return; + + setState(() => _isBusy = true); + + try { + final rel = context.read(); + await rel.deleteRelationship(relation.relatedId); + if (!mounted) return; + Navigator.pop(context, true); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isBusy = false); + } + } + + @override + Widget build(BuildContext context) { + return ListView.builder( + itemCount: widget.relations.length, + itemBuilder: (context, index) { + final relation = widget.relations[index]; + final other = relation.related; + return ListTile( + contentPadding: const EdgeInsets.only(right: 24, left: 16), + leading: AccountImage(content: other?.avatar), + title: Text(other?.nick ?? 'unknown'.tr()), + subtitle: Text(other?.nick ?? 'unknown'.tr()), + trailing: SizedBox( + height: 48, + width: 120, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text(kFriendStatus[relation.status] ?? 'unknown') + .tr() + .opacity(0.75), + if (relation.status == 0) + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + InkWell( + onTap: _isBusy ? null : () => _acceptRequest(relation), + child: Text('friendRequestAccept').tr(), + ), + const Gap(8), + InkWell( + onTap: _isBusy ? null : () => _declineRequest(relation), + child: Text('friendRequestDecline').tr(), + ), + ], + ) + else if (relation.status == 2) + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + InkWell( + onTap: + _isBusy ? null : () => _changeRelation(relation, 1), + child: Text('friendUnblock').tr(), + ), + const Gap(8), + InkWell( + onTap: _isBusy ? null : () => _deleteRelation(relation), + child: Text('friendDeleteAction').tr(), + ), + ], + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/types/account.dart b/lib/types/account.dart index cf1f458..afc7c16 100644 --- a/lib/types/account.dart +++ b/lib/types/account.dart @@ -70,3 +70,22 @@ class SnAccountProfile with _$SnAccountProfile { factory SnAccountProfile.fromJson(Map json) => _$SnAccountProfileFromJson(json); } + +@freezed +class SnRelationship with _$SnRelationship { + const factory SnRelationship({ + required int id, + required DateTime createdAt, + required DateTime updatedAt, + required DateTime? deletedAt, + required int accountId, + required int relatedId, + required SnAccount? account, + required SnAccount? related, + required int status, + @Default({}) Map permNodes, + }) = _SnRelationship; + + factory SnRelationship.fromJson(Map json) => + _$SnRelationshipFromJson(json); +} diff --git a/lib/types/account.freezed.dart b/lib/types/account.freezed.dart index 5b70147..bdb1cbd 100644 --- a/lib/types/account.freezed.dart +++ b/lib/types/account.freezed.dart @@ -1271,3 +1271,397 @@ abstract class _SnAccountProfile implements SnAccountProfile { _$$SnAccountProfileImplCopyWith<_$SnAccountProfileImpl> get copyWith => throw _privateConstructorUsedError; } + +SnRelationship _$SnRelationshipFromJson(Map json) { + return _SnRelationship.fromJson(json); +} + +/// @nodoc +mixin _$SnRelationship { + int get id => throw _privateConstructorUsedError; + DateTime get createdAt => throw _privateConstructorUsedError; + DateTime get updatedAt => throw _privateConstructorUsedError; + DateTime? get deletedAt => throw _privateConstructorUsedError; + int get accountId => throw _privateConstructorUsedError; + int get relatedId => throw _privateConstructorUsedError; + SnAccount? get account => throw _privateConstructorUsedError; + SnAccount? get related => throw _privateConstructorUsedError; + int get status => throw _privateConstructorUsedError; + Map get permNodes => throw _privateConstructorUsedError; + + /// Serializes this SnRelationship to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of SnRelationship + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $SnRelationshipCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SnRelationshipCopyWith<$Res> { + factory $SnRelationshipCopyWith( + SnRelationship value, $Res Function(SnRelationship) then) = + _$SnRelationshipCopyWithImpl<$Res, SnRelationship>; + @useResult + $Res call( + {int id, + DateTime createdAt, + DateTime updatedAt, + DateTime? deletedAt, + int accountId, + int relatedId, + SnAccount? account, + SnAccount? related, + int status, + Map permNodes}); + + $SnAccountCopyWith<$Res>? get account; + $SnAccountCopyWith<$Res>? get related; +} + +/// @nodoc +class _$SnRelationshipCopyWithImpl<$Res, $Val extends SnRelationship> + implements $SnRelationshipCopyWith<$Res> { + _$SnRelationshipCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of SnRelationship + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? createdAt = null, + Object? updatedAt = null, + Object? deletedAt = freezed, + Object? accountId = null, + Object? relatedId = null, + Object? account = freezed, + Object? related = freezed, + Object? status = null, + Object? permNodes = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + deletedAt: freezed == deletedAt + ? _value.deletedAt + : deletedAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + accountId: null == accountId + ? _value.accountId + : accountId // ignore: cast_nullable_to_non_nullable + as int, + relatedId: null == relatedId + ? _value.relatedId + : relatedId // ignore: cast_nullable_to_non_nullable + as int, + account: freezed == account + ? _value.account + : account // ignore: cast_nullable_to_non_nullable + as SnAccount?, + related: freezed == related + ? _value.related + : related // ignore: cast_nullable_to_non_nullable + as SnAccount?, + status: null == status + ? _value.status + : status // ignore: cast_nullable_to_non_nullable + as int, + permNodes: null == permNodes + ? _value.permNodes + : permNodes // ignore: cast_nullable_to_non_nullable + as Map, + ) as $Val); + } + + /// Create a copy of SnRelationship + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $SnAccountCopyWith<$Res>? get account { + if (_value.account == null) { + return null; + } + + return $SnAccountCopyWith<$Res>(_value.account!, (value) { + return _then(_value.copyWith(account: value) as $Val); + }); + } + + /// Create a copy of SnRelationship + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $SnAccountCopyWith<$Res>? get related { + if (_value.related == null) { + return null; + } + + return $SnAccountCopyWith<$Res>(_value.related!, (value) { + return _then(_value.copyWith(related: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$SnRelationshipImplCopyWith<$Res> + implements $SnRelationshipCopyWith<$Res> { + factory _$$SnRelationshipImplCopyWith(_$SnRelationshipImpl value, + $Res Function(_$SnRelationshipImpl) then) = + __$$SnRelationshipImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {int id, + DateTime createdAt, + DateTime updatedAt, + DateTime? deletedAt, + int accountId, + int relatedId, + SnAccount? account, + SnAccount? related, + int status, + Map permNodes}); + + @override + $SnAccountCopyWith<$Res>? get account; + @override + $SnAccountCopyWith<$Res>? get related; +} + +/// @nodoc +class __$$SnRelationshipImplCopyWithImpl<$Res> + extends _$SnRelationshipCopyWithImpl<$Res, _$SnRelationshipImpl> + implements _$$SnRelationshipImplCopyWith<$Res> { + __$$SnRelationshipImplCopyWithImpl( + _$SnRelationshipImpl _value, $Res Function(_$SnRelationshipImpl) _then) + : super(_value, _then); + + /// Create a copy of SnRelationship + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? createdAt = null, + Object? updatedAt = null, + Object? deletedAt = freezed, + Object? accountId = null, + Object? relatedId = null, + Object? account = freezed, + Object? related = freezed, + Object? status = null, + Object? permNodes = null, + }) { + return _then(_$SnRelationshipImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + deletedAt: freezed == deletedAt + ? _value.deletedAt + : deletedAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + accountId: null == accountId + ? _value.accountId + : accountId // ignore: cast_nullable_to_non_nullable + as int, + relatedId: null == relatedId + ? _value.relatedId + : relatedId // ignore: cast_nullable_to_non_nullable + as int, + account: freezed == account + ? _value.account + : account // ignore: cast_nullable_to_non_nullable + as SnAccount?, + related: freezed == related + ? _value.related + : related // ignore: cast_nullable_to_non_nullable + as SnAccount?, + status: null == status + ? _value.status + : status // ignore: cast_nullable_to_non_nullable + as int, + permNodes: null == permNodes + ? _value._permNodes + : permNodes // ignore: cast_nullable_to_non_nullable + as Map, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$SnRelationshipImpl implements _SnRelationship { + const _$SnRelationshipImpl( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.deletedAt, + required this.accountId, + required this.relatedId, + required this.account, + required this.related, + required this.status, + final Map permNodes = const {}}) + : _permNodes = permNodes; + + factory _$SnRelationshipImpl.fromJson(Map json) => + _$$SnRelationshipImplFromJson(json); + + @override + final int id; + @override + final DateTime createdAt; + @override + final DateTime updatedAt; + @override + final DateTime? deletedAt; + @override + final int accountId; + @override + final int relatedId; + @override + final SnAccount? account; + @override + final SnAccount? related; + @override + final int status; + final Map _permNodes; + @override + @JsonKey() + Map get permNodes { + if (_permNodes is EqualUnmodifiableMapView) return _permNodes; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_permNodes); + } + + @override + String toString() { + return 'SnRelationship(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, accountId: $accountId, relatedId: $relatedId, account: $account, related: $related, status: $status, permNodes: $permNodes)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SnRelationshipImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.createdAt, createdAt) || + other.createdAt == createdAt) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt) && + (identical(other.deletedAt, deletedAt) || + other.deletedAt == deletedAt) && + (identical(other.accountId, accountId) || + other.accountId == accountId) && + (identical(other.relatedId, relatedId) || + other.relatedId == relatedId) && + (identical(other.account, account) || other.account == account) && + (identical(other.related, related) || other.related == related) && + (identical(other.status, status) || other.status == status) && + const DeepCollectionEquality() + .equals(other._permNodes, _permNodes)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + id, + createdAt, + updatedAt, + deletedAt, + accountId, + relatedId, + account, + related, + status, + const DeepCollectionEquality().hash(_permNodes)); + + /// Create a copy of SnRelationship + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SnRelationshipImplCopyWith<_$SnRelationshipImpl> get copyWith => + __$$SnRelationshipImplCopyWithImpl<_$SnRelationshipImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$SnRelationshipImplToJson( + this, + ); + } +} + +abstract class _SnRelationship implements SnRelationship { + const factory _SnRelationship( + {required final int id, + required final DateTime createdAt, + required final DateTime updatedAt, + required final DateTime? deletedAt, + required final int accountId, + required final int relatedId, + required final SnAccount? account, + required final SnAccount? related, + required final int status, + final Map permNodes}) = _$SnRelationshipImpl; + + factory _SnRelationship.fromJson(Map json) = + _$SnRelationshipImpl.fromJson; + + @override + int get id; + @override + DateTime get createdAt; + @override + DateTime get updatedAt; + @override + DateTime? get deletedAt; + @override + int get accountId; + @override + int get relatedId; + @override + SnAccount? get account; + @override + SnAccount? get related; + @override + int get status; + @override + Map get permNodes; + + /// Create a copy of SnRelationship + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SnRelationshipImplCopyWith<_$SnRelationshipImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/types/account.g.dart b/lib/types/account.g.dart index 04cf2da..90adbe4 100644 --- a/lib/types/account.g.dart +++ b/lib/types/account.g.dart @@ -129,3 +129,38 @@ Map _$$SnAccountProfileImplToJson( 'last_seen_at': instance.lastSeenAt?.toIso8601String(), 'updated_at': instance.updatedAt.toIso8601String(), }; + +_$SnRelationshipImpl _$$SnRelationshipImplFromJson(Map json) => + _$SnRelationshipImpl( + id: (json['id'] as num).toInt(), + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + deletedAt: json['deleted_at'] == null + ? null + : DateTime.parse(json['deleted_at'] as String), + accountId: (json['account_id'] as num).toInt(), + relatedId: (json['related_id'] as num).toInt(), + account: json['account'] == null + ? null + : SnAccount.fromJson(json['account'] as Map), + related: json['related'] == null + ? null + : SnAccount.fromJson(json['related'] as Map), + status: (json['status'] as num).toInt(), + permNodes: json['perm_nodes'] as Map? ?? const {}, + ); + +Map _$$SnRelationshipImplToJson( + _$SnRelationshipImpl instance) => + { + 'id': instance.id, + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + 'deleted_at': instance.deletedAt?.toIso8601String(), + 'account_id': instance.accountId, + 'related_id': instance.relatedId, + 'account': instance.account?.toJson(), + 'related': instance.related?.toJson(), + 'status': instance.status, + 'perm_nodes': instance.permNodes, + }; diff --git a/lib/types/chat.freezed.dart b/lib/types/chat.freezed.dart index f7fdda6..f320b1b 100644 --- a/lib/types/chat.freezed.dart +++ b/lib/types/chat.freezed.dart @@ -37,7 +37,6 @@ mixin _$SnChannel { @HiveField(7) List? get members => throw _privateConstructorUsedError; List? get messages => throw _privateConstructorUsedError; - dynamic get calls => throw _privateConstructorUsedError; @HiveField(8) int get type => throw _privateConstructorUsedError; @HiveField(9) @@ -76,7 +75,6 @@ abstract class $SnChannelCopyWith<$Res> { @HiveField(6) String description, @HiveField(7) List? members, List? messages, - dynamic calls, @HiveField(8) int type, @HiveField(9) int accountId, @HiveField(10) SnRealm? realm, @@ -111,7 +109,6 @@ class _$SnChannelCopyWithImpl<$Res, $Val extends SnChannel> Object? description = null, Object? members = freezed, Object? messages = freezed, - Object? calls = freezed, Object? type = null, Object? accountId = null, Object? realm = freezed, @@ -156,10 +153,6 @@ class _$SnChannelCopyWithImpl<$Res, $Val extends SnChannel> ? _value.messages : messages // ignore: cast_nullable_to_non_nullable as List?, - calls: freezed == calls - ? _value.calls - : calls // ignore: cast_nullable_to_non_nullable - as dynamic, type: null == type ? _value.type : type // ignore: cast_nullable_to_non_nullable @@ -220,7 +213,6 @@ abstract class _$$SnChannelImplCopyWith<$Res> @HiveField(6) String description, @HiveField(7) List? members, List? messages, - dynamic calls, @HiveField(8) int type, @HiveField(9) int accountId, @HiveField(10) SnRealm? realm, @@ -254,7 +246,6 @@ class __$$SnChannelImplCopyWithImpl<$Res> Object? description = null, Object? members = freezed, Object? messages = freezed, - Object? calls = freezed, Object? type = null, Object? accountId = null, Object? realm = freezed, @@ -299,10 +290,6 @@ class __$$SnChannelImplCopyWithImpl<$Res> ? _value._messages : messages // ignore: cast_nullable_to_non_nullable as List?, - calls: freezed == calls - ? _value.calls - : calls // ignore: cast_nullable_to_non_nullable - as dynamic, type: null == type ? _value.type : type // ignore: cast_nullable_to_non_nullable @@ -345,7 +332,6 @@ class _$SnChannelImpl extends _SnChannel { @HiveField(6) required this.description, @HiveField(7) required final List? members, final List? messages, - this.calls, @HiveField(8) required this.type, @HiveField(9) required this.accountId, @HiveField(10) required this.realm, @@ -401,8 +387,6 @@ class _$SnChannelImpl extends _SnChannel { return EqualUnmodifiableListView(value); } - @override - final dynamic calls; @override @HiveField(8) final int type; @@ -424,7 +408,7 @@ class _$SnChannelImpl extends _SnChannel { @override String toString() { - return 'SnChannel(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, alias: $alias, name: $name, description: $description, members: $members, messages: $messages, calls: $calls, type: $type, accountId: $accountId, realm: $realm, realmId: $realmId, isPublic: $isPublic, isCommunity: $isCommunity)'; + return 'SnChannel(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, alias: $alias, name: $name, description: $description, members: $members, messages: $messages, type: $type, accountId: $accountId, realm: $realm, realmId: $realmId, isPublic: $isPublic, isCommunity: $isCommunity)'; } @override @@ -444,7 +428,6 @@ class _$SnChannelImpl extends _SnChannel { other.description == description) && const DeepCollectionEquality().equals(other._members, _members) && const DeepCollectionEquality().equals(other._messages, _messages) && - const DeepCollectionEquality().equals(other.calls, calls) && (identical(other.type, type) || other.type == type) && (identical(other.accountId, accountId) || other.accountId == accountId) && @@ -469,7 +452,6 @@ class _$SnChannelImpl extends _SnChannel { description, const DeepCollectionEquality().hash(_members), const DeepCollectionEquality().hash(_messages), - const DeepCollectionEquality().hash(calls), type, accountId, realm, @@ -504,7 +486,6 @@ abstract class _SnChannel extends SnChannel { @HiveField(6) required final String description, @HiveField(7) required final List? members, final List? messages, - final dynamic calls, @HiveField(8) required final int type, @HiveField(9) required final int accountId, @HiveField(10) required final SnRealm? realm, @@ -543,8 +524,6 @@ abstract class _SnChannel extends SnChannel { @override List? get messages; @override - dynamic get calls; - @override @HiveField(8) int get type; @override diff --git a/lib/types/chat.g.dart b/lib/types/chat.g.dart index a4b875b..d1bd56b 100644 --- a/lib/types/chat.g.dart +++ b/lib/types/chat.g.dart @@ -227,7 +227,6 @@ _$SnChannelImpl _$$SnChannelImplFromJson(Map json) => messages: (json['messages'] as List?) ?.map((e) => SnChatMessage.fromJson(e as Map)) .toList(), - calls: json['calls'], type: (json['type'] as num).toInt(), accountId: (json['account_id'] as num).toInt(), realm: json['realm'] == null @@ -249,7 +248,6 @@ Map _$$SnChannelImplToJson(_$SnChannelImpl instance) => 'description': instance.description, 'members': instance.members, 'messages': instance.messages?.map((e) => e.toJson()).toList(), - 'calls': instance.calls, 'type': instance.type, 'account_id': instance.accountId, 'realm': instance.realm?.toJson(), diff --git a/pubspec.lock b/pubspec.lock index b78499f..069a842 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -670,10 +670,10 @@ packages: dependency: transitive description: name: flutter_webrtc - sha256: fcaee6f28cc1221e804fcba16fbee6f20b0848af20fe11174a0f97c3b9833381 + sha256: "4838217405c42cce422698eacc9c2e17089b9c05322be899c0a725107dcddbdc" url: "https://pub.dev" source: hosted - version: "0.12.2" + version: "0.12.3" freezed: dependency: "direct dev" description: @@ -830,10 +830,10 @@ packages: dependency: transitive description: name: image_picker_android - sha256: "8faba09ba361d4b246dc0a17cb4289b3324c2b9f6db7b3d457ee69106a86bd32" + sha256: fa8141602fde3f7e2f81dbf043613eb44dfa325fa0bcf93c0f142c9f7a2c193e url: "https://pub.dev" source: hosted - version: "0.8.12+17" + version: "0.8.12+18" image_picker_for_web: dependency: transitive description: @@ -974,10 +974,10 @@ packages: dependency: "direct main" description: name: livekit_client - sha256: ad55045435fbf1a106e2da4c9a8d523755ce834db47f6d967beaa58228b21a05 + sha256: "442c228f42a3b757e1a5e526b0816f2b0a93b34625ff29b2c9fb9503ce4e4f3e" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" logging: dependency: transitive description: @@ -1022,10 +1022,10 @@ packages: dependency: "direct main" description: name: material_symbols_icons - sha256: "8a57be605b8bc3fd57005eb776ede61f569214e48834258fb02ab80c7034b82c" + sha256: a783133f87c58e10b1cc19797f7c3192ff9c2bab301c4ade90312d8f2aed01b2 url: "https://pub.dev" source: hosted - version: "4.2800.0" + version: "4.2800.2" media_kit: dependency: "direct main" description: @@ -1899,10 +1899,10 @@ packages: dependency: transitive description: name: win32 - sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2" + sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69" url: "https://pub.dev" source: hosted - version: "5.8.0" + version: "5.9.0" win32_registry: dependency: transitive description: