From d4cbabeb31ed6ae6403ceac0de28fa47e414098d Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 29 May 2024 00:14:41 +0800 Subject: [PATCH] :sparkles: Better DM --- lib/models/account.dart | 4 +- lib/models/call.dart | 4 +- lib/models/channel.dart | 24 ++- lib/models/friendship.dart | 4 +- lib/models/notification.dart | 24 ++- lib/models/personal_page.dart | 8 +- lib/providers/content/channel.dart | 69 +++++++++ lib/router.dart | 2 +- lib/screens/channel/channel_detail.dart | 4 +- lib/screens/channel/channel_organize.dart | 121 ++-------------- lib/screens/contact.dart | 137 +++++++++++++----- lib/screens/realms.dart | 1 - lib/translations.dart | 8 + lib/widgets/account/account_avatar.dart | 15 +- lib/widgets/account/friend_select.dart | 2 +- .../attachments/attachment_publish.dart | 12 +- lib/widgets/posts/post_item.dart | 1 - 17 files changed, 253 insertions(+), 187 deletions(-) diff --git a/lib/models/account.dart b/lib/models/account.dart index da2693e..7ba97da 100644 --- a/lib/models/account.dart +++ b/lib/models/account.dart @@ -15,13 +15,13 @@ class Account { required this.id, required this.createdAt, required this.updatedAt, - this.deletedAt, + required this.deletedAt, required this.name, required this.nick, required this.avatar, required this.banner, required this.description, - this.emailAddress, + required this.emailAddress, this.externalId, }); diff --git a/lib/models/call.dart b/lib/models/call.dart index 0c59648..968c05b 100644 --- a/lib/models/call.dart +++ b/lib/models/call.dart @@ -15,8 +15,8 @@ class Call { required this.id, required this.createdAt, required this.updatedAt, - this.deletedAt, - this.endedAt, + required this.deletedAt, + required this.endedAt, required this.externalId, required this.founderId, required this.channelId, diff --git a/lib/models/channel.dart b/lib/models/channel.dart index 6e2ed56..94a72e5 100644 --- a/lib/models/channel.dart +++ b/lib/models/channel.dart @@ -1,5 +1,3 @@ -import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:solian/models/account.dart'; import 'package:solian/models/realm.dart'; @@ -12,6 +10,7 @@ class Channel { String name; String description; int type; + List? members; Account account; int accountId; Realm? realm; @@ -24,16 +23,17 @@ class Channel { required this.id, required this.createdAt, required this.updatedAt, - this.deletedAt, + required this.deletedAt, required this.alias, required this.name, required this.description, required this.type, + required this.members, required this.account, required this.accountId, required this.isEncrypted, - this.realm, - this.realmId, + required this.realm, + required this.realmId, }); factory Channel.fromJson(Map json) => Channel( @@ -45,6 +45,10 @@ class Channel { name: json['name'], description: json['description'], type: json['type'], + members: json['members'] + ?.map((e) => ChannelMember.fromJson(e)) + .toList() + .cast(), account: Account.fromJson(json['account']), accountId: json['account_id'], realm: json['realm'] != null ? Realm.fromJson(json['realm']) : null, @@ -61,21 +65,13 @@ class Channel { 'name': name, 'description': description, 'type': type, + 'members': members?.map((e) => e.toJson()).toList(), 'account': account.toJson(), 'account_id': accountId, 'realm': realm?.toJson(), 'realm_id': realmId, 'is_encrypted': isEncrypted, }; - - IconData get icon { - switch (type) { - case 1: - return FontAwesomeIcons.userGroup; - default: - return FontAwesomeIcons.hashtag; - } - } } class ChannelMember { diff --git a/lib/models/friendship.dart b/lib/models/friendship.dart index 6737619..dc7541b 100644 --- a/lib/models/friendship.dart +++ b/lib/models/friendship.dart @@ -16,10 +16,10 @@ class Friendship { required this.id, required this.createdAt, required this.updatedAt, - this.deletedAt, + required this.deletedAt, required this.accountId, required this.relatedId, - this.blockedBy, + required this.blockedBy, required this.account, required this.related, required this.status, diff --git a/lib/models/notification.dart b/lib/models/notification.dart index 6734539..4fa12ac 100755 --- a/lib/models/notification.dart +++ b/lib/models/notification.dart @@ -16,25 +16,31 @@ class Notification { required this.id, required this.createdAt, required this.updatedAt, - this.deletedAt, + required this.deletedAt, required this.subject, required this.content, - this.links, + required this.links, required this.isImportant, required this.isRealtime, - this.readAt, - this.senderId, + required this.readAt, + required this.senderId, required this.recipientId, }); factory Notification.fromJson(Map json) => Notification( id: json['id'] ?? 0, - createdAt: json['created_at'] == null ? DateTime.now() : DateTime.parse(json['created_at']), - updatedAt: json['updated_at'] == null ? DateTime.now() : DateTime.parse(json['updated_at']), + createdAt: json['created_at'] == null + ? DateTime.now() + : DateTime.parse(json['created_at']), + updatedAt: json['updated_at'] == null + ? DateTime.now() + : DateTime.parse(json['updated_at']), deletedAt: json['deleted_at'], subject: json['subject'], content: json['content'], - links: json['links'] != null ? List.from(json['links'].map((x) => Link.fromJson(x))) : List.empty(), + links: json['links'] != null + ? List.from(json['links'].map((x) => Link.fromJson(x))) + : List.empty(), isImportant: json['is_important'], isRealtime: json['is_realtime'], readAt: json['read_at'], @@ -49,7 +55,9 @@ class Notification { 'deleted_at': deletedAt, 'subject': subject, 'content': content, - 'links': links != null ? List.from(links!.map((x) => x.toJson())) : List.empty(), + 'links': links != null + ? List.from(links!.map((x) => x.toJson())) + : List.empty(), 'is_important': isImportant, 'is_realtime': isRealtime, 'read_at': readAt, diff --git a/lib/models/personal_page.dart b/lib/models/personal_page.dart index 7938b92..4f72834 100644 --- a/lib/models/personal_page.dart +++ b/lib/models/personal_page.dart @@ -13,11 +13,11 @@ class PersonalPage { required this.id, required this.createdAt, required this.updatedAt, - this.deletedAt, + required this.deletedAt, required this.content, required this.script, required this.style, - this.links, + required this.links, required this.accountId, }); @@ -25,7 +25,9 @@ class PersonalPage { id: json['id'], createdAt: DateTime.parse(json['created_at']), updatedAt: DateTime.parse(json['updated_at']), - deletedAt: json['deleted_at'] != null ? DateTime.parse(json['deleted_at']) : null, + deletedAt: json['deleted_at'] != null + ? DateTime.parse(json['deleted_at']) + : null, content: json['content'], script: json['script'], style: json['style'], diff --git a/lib/providers/content/channel.dart b/lib/providers/content/channel.dart index a86ce33..b927607 100644 --- a/lib/providers/content/channel.dart +++ b/lib/providers/content/channel.dart @@ -1,6 +1,9 @@ +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:solian/providers/auth.dart'; import 'package:solian/services.dart'; +import 'package:solian/widgets/account/friend_select.dart'; +import 'package:uuid/uuid.dart'; class ChannelProvider extends GetxController { Future getChannel(String alias, {String realm = 'global'}) async { @@ -33,4 +36,70 @@ class ChannelProvider extends GetxController { return resp; } + + Future createChannel(String scope, dynamic payload) async { + final AuthProvider auth = Get.find(); + if (!await auth.isAuthorized) throw Exception('unauthorized'); + + final client = GetConnect(maxAuthRetries: 3); + client.httpClient.baseUrl = ServiceFinder.services['messaging']; + client.httpClient.addAuthenticator(auth.requestAuthenticator); + + final resp = await client.post('/api/channels/$scope', payload); + if (resp.statusCode != 200) { + throw Exception(resp.bodyString); + } + + return resp; + } + + Future createDirectChannel( + BuildContext context, String scope) async { + final AuthProvider auth = Get.find(); + if (!await auth.isAuthorized) throw Exception('unauthorized'); + + final related = await showModalBottomSheet( + useRootNavigator: true, + context: context, + builder: (context) => FriendSelect( + title: 'channelOrganizeDirectHint'.tr, + ), + ); + if (related == null) return null; + + final prof = await auth.getProfile(); + final client = GetConnect(maxAuthRetries: 3); + client.httpClient.baseUrl = ServiceFinder.services['messaging']; + client.httpClient.addAuthenticator(auth.requestAuthenticator); + + final resp = await client.post('/api/channels/$scope/dm', { + 'alias': const Uuid().v4().replaceAll('-', '').substring(0, 12), + 'name': 'DM', + 'description': + 'A direct message channel between @${prof.body['name']} and @${related.name}', + 'related_user': related.id, + 'is_encrypted': false, + }); + if (resp.statusCode != 200) { + throw Exception(resp.bodyString); + } + + return resp; + } + + Future updateChannel(String scope, int id, dynamic payload) async { + final AuthProvider auth = Get.find(); + if (!await auth.isAuthorized) throw Exception('unauthorized'); + + final client = GetConnect(maxAuthRetries: 3); + client.httpClient.baseUrl = ServiceFinder.services['messaging']; + client.httpClient.addAuthenticator(auth.requestAuthenticator); + + final resp = await client.put('/api/channels/$scope/$id', payload); + if (resp.statusCode != 200) { + throw Exception(resp.bodyString); + } + + return resp; + } } diff --git a/lib/router.dart b/lib/router.dart index c3dc502..9b5679b 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -20,7 +20,7 @@ abstract class AppRouter { routes: [ ShellRoute( builder: (context, state, child) => - NavShell(state: state, child: child, showAppBar: false), + NavShell(state: state, showAppBar: false, child: child), routes: [ GoRoute( path: '/', diff --git a/lib/screens/channel/channel_detail.dart b/lib/screens/channel/channel_detail.dart index f906824..29960b8 100644 --- a/lib/screens/channel/channel_detail.dart +++ b/lib/screens/channel/channel_detail.dart @@ -96,11 +96,11 @@ class _ChannelDetailScreenState extends State { padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( children: [ - CircleAvatar( + const CircleAvatar( radius: 28, backgroundColor: Colors.teal, child: FaIcon( - widget.channel.icon, + FontAwesomeIcons.hashtag, color: Colors.white, size: 18, ), diff --git a/lib/screens/channel/channel_organize.dart b/lib/screens/channel/channel_organize.dart index 3dc1147..81796e1 100644 --- a/lib/screens/channel/channel_organize.dart +++ b/lib/screens/channel/channel_organize.dart @@ -1,14 +1,12 @@ -import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:get/get.dart'; import 'package:solian/exts.dart'; -import 'package:solian/models/account.dart'; import 'package:solian/models/channel.dart'; import 'package:solian/providers/auth.dart'; +import 'package:solian/providers/content/channel.dart'; import 'package:solian/router.dart'; import 'package:solian/services.dart'; -import 'package:solian/widgets/account/friend_select.dart'; import 'package:solian/widgets/prev_page.dart'; import 'package:uuid/uuid.dart'; @@ -29,11 +27,6 @@ class ChannelOrganizeScreen extends StatefulWidget { } class _ChannelOrganizeScreenState extends State { - static Map channelTypes = { - 0: 'channelTypeCommon'.tr, - 1: 'channelTypeDirect'.tr, - }; - bool _isBusy = false; final _aliasController = TextEditingController(); @@ -41,38 +34,6 @@ class _ChannelOrganizeScreenState extends State { final _descriptionController = TextEditingController(); bool _isEncrypted = false; - int _channelType = 0; - - List _initialMembers = List.empty(growable: true); - - void selectInitialMembers() async { - final input = await showModalBottomSheet( - useRootNavigator: true, - isScrollControlled: true, - context: context, - builder: (context) => FriendSelect( - title: 'channelMember'.tr, - trailingBuilder: (item) { - if (_initialMembers.any((e) => e.id == item.id)) { - return const Icon(Icons.check); - } else { - return null; - } - }, - ), - ); - if (input == null) return; - - setState(() { - if (_initialMembers.any((e) => e.id == input.id)) { - _initialMembers = _initialMembers - .where((e) => e.id != input.id) - .toList(growable: true); - } else { - _initialMembers.add(input as Account); - } - }); - } void applyChannel() async { final AuthProvider auth = Get.find(); @@ -82,6 +43,8 @@ class _ChannelOrganizeScreenState extends State { setState(() => _isBusy = true); + final ChannelProvider provider = Get.find(); + final client = GetConnect(maxAuthRetries: 3); client.httpClient.baseUrl = ServiceFinder.services['messaging']; client.httpClient.addAuthenticator(auth.requestAuthenticator); @@ -92,27 +55,21 @@ class _ChannelOrganizeScreenState extends State { 'name': _nameController.value.text, 'description': _descriptionController.value.text, 'is_encrypted': _isEncrypted, - if (_channelType == 1) - 'members': _initialMembers.map((e) => e.id).toList(), }; - Response resp; - if (widget.edit != null) { - resp = await client.put( - '/api/channels/$scope/${widget.edit!.id}', - payload, - ); - } else if (_channelType == 1) { - resp = await client.post('/api/channels/$scope/dm', payload); - } else { - resp = await client.post('/api/channels/$scope', payload); - } - if (resp.statusCode != 200) { - context.showErrorDialog(resp.bodyString); - } else { - AppRouter.instance.pop(resp.body); + Response? resp; + try { + if (widget.edit != null) { + resp = await provider.updateChannel(scope!, widget.edit!.id, payload); + } else { + resp = await provider.createChannel(scope!, payload); + } + } catch (e) { + context.showErrorDialog(e); } + AppRouter.instance.pop(resp!.body); + setState(() => _isBusy = false); } @@ -127,7 +84,6 @@ class _ChannelOrganizeScreenState extends State { _nameController.text = widget.edit!.name; _descriptionController.text = widget.edit!.description; _isEncrypted = widget.edit!.isEncrypted; - _channelType = widget.edit!.type; } } @@ -227,55 +183,6 @@ class _ChannelOrganizeScreenState extends State { ).paddingSymmetric(horizontal: 16, vertical: 12), ), const Divider(thickness: 0.3), - if (_channelType == 1 && widget.edit == null) - ListTile( - leading: const Icon(Icons.supervisor_account) - .paddingSymmetric(horizontal: 8), - title: Text('channelMember'.tr), - subtitle: _initialMembers.isNotEmpty - ? Text(_initialMembers.map((e) => e.name).join(' ')) - : null, - trailing: const Icon(Icons.chevron_right), - onTap: () => selectInitialMembers(), - ).animate().fadeIn().slideY( - begin: 1, - end: 0, - curve: Curves.fastEaseInToSlowEaseOut, - ), - ListTile( - leading: const Icon(Icons.mode).paddingSymmetric(horizontal: 8), - title: Text('channelType'.tr), - trailing: DropdownButtonHideUnderline( - child: DropdownButton2( - isExpanded: true, - items: channelTypes.entries - .map((item) => DropdownMenuItem( - enabled: widget.edit == null || - item.key == widget.edit?.type, - value: item.key, - child: Text( - item.value, - style: const TextStyle( - fontSize: 14, - ), - ), - )) - .toList(), - value: _channelType, - onChanged: (int? value) { - setState(() => _channelType = value ?? 0); - }, - buttonStyleData: const ButtonStyleData( - padding: EdgeInsets.only(left: 16, right: 1), - height: 40, - width: 140, - ), - menuItemStyleData: const MenuItemStyleData( - height: 40, - ), - ), - ), - ), CheckboxListTile( title: Text('channelEncrypted'.tr), value: _isEncrypted, diff --git a/lib/screens/contact.dart b/lib/screens/contact.dart index 13b582f..bfeca13 100644 --- a/lib/screens/contact.dart +++ b/lib/screens/contact.dart @@ -8,6 +8,7 @@ import 'package:solian/providers/content/channel.dart'; import 'package:solian/router.dart'; import 'package:solian/screens/account/notification.dart'; import 'package:solian/theme.dart'; +import 'package:solian/widgets/account/account_avatar.dart'; import 'package:solian/widgets/account/signin_required_overlay.dart'; class ContactScreen extends StatefulWidget { @@ -19,9 +20,16 @@ class ContactScreen extends StatefulWidget { class _ContactScreenState extends State { bool _isBusy = true; + int? _accountId; final List _channels = List.empty(growable: true); + getProfile() async { + final AuthProvider auth = Get.find(); + final prof = await auth.getProfile(); + _accountId = prof.body['id']; + } + getChannels() async { setState(() => _isBusy = true); @@ -42,6 +50,7 @@ class _ContactScreenState extends State { void initState() { super.initState(); + getProfile(); getChannels(); } @@ -81,17 +90,48 @@ class _ContactScreenState extends State { forceElevated: innerBoxIsScrolled, actions: [ const NotificationButton(), - IconButton( + PopupMenuButton( icon: const Icon(Icons.add_circle), - onPressed: () { - AppRouter.instance - .pushNamed('channelOrganizing') - .then( - (value) { - if (value != null) getChannels(); + itemBuilder: (BuildContext context) => [ + PopupMenuItem( + child: ListTile( + title: Text('channelOrganizeCommon'.tr), + leading: const Icon(Icons.tag), + contentPadding: + const EdgeInsets.symmetric(horizontal: 8), + ), + onTap: () { + AppRouter.instance + .pushNamed('channelOrganizing') + .then( + (value) { + if (value != null) getChannels(); + }, + ); }, - ); - }, + ), + PopupMenuItem( + child: ListTile( + title: Text('channelOrganizeDirect'.tr), + leading: const FaIcon( + FontAwesomeIcons.userGroup, + size: 16, + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 8), + ), + onTap: () { + final ChannelProvider provider = Get.find(); + provider + .createDirectChannel(context, 'global') + .then((resp) { + if (resp != null) { + getChannels(); + } + }); + }, + ), + ], ), SizedBox( width: SolianTheme.isLargeScreen(context) ? 8 : 16, @@ -115,30 +155,7 @@ class _ContactScreenState extends State { itemCount: _channels.length, itemBuilder: (context, index) { final element = _channels[index]; - return ListTile( - leading: CircleAvatar( - backgroundColor: Colors.indigo, - child: FaIcon( - element.icon, - color: Colors.white, - size: 16, - ), - ), - contentPadding: - const EdgeInsets.symmetric(horizontal: 24), - title: Text(element.name), - subtitle: Text(element.description), - onTap: () { - AppRouter.instance.pushNamed( - 'channelChat', - pathParameters: {'alias': element.alias}, - queryParameters: { - if (element.realmId != null) - 'realm': element.realm!.alias, - }, - ); - }, - ); + return buildItem(element); }, ), ), @@ -152,4 +169,58 @@ class _ContactScreenState extends State { ), ); } + + Widget buildItem(Channel element) { + if (element.type == 1) { + final otherside = element.members! + .where((e) => e.account.externalId != _accountId) + .first; + + return ListTile( + leading: AccountAvatar( + content: otherside.account.avatar, + bgColor: Colors.indigo, + feColor: Colors.white, + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + title: Text(otherside.account.name), + subtitle: Text( + 'channelDirectDescription' + .trParams({'username': otherside.account.name}), + ), + onTap: () { + AppRouter.instance.pushNamed( + 'channelChat', + pathParameters: {'alias': element.alias}, + queryParameters: { + if (element.realmId != null) 'realm': element.realm!.alias, + }, + ); + }, + ); + } + + return ListTile( + leading: const CircleAvatar( + backgroundColor: Colors.indigo, + child: FaIcon( + FontAwesomeIcons.hashtag, + color: Colors.white, + size: 16, + ), + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + title: Text(element.name), + subtitle: Text(element.description), + onTap: () { + AppRouter.instance.pushNamed( + 'channelChat', + pathParameters: {'alias': element.alias}, + queryParameters: { + if (element.realmId != null) 'realm': element.realm!.alias, + }, + ); + }, + ); + } } diff --git a/lib/screens/realms.dart b/lib/screens/realms.dart index 0923beb..a7f36fa 100644 --- a/lib/screens/realms.dart +++ b/lib/screens/realms.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; diff --git a/lib/translations.dart b/lib/translations.dart index 90de8a3..65bdf6e 100644 --- a/lib/translations.dart +++ b/lib/translations.dart @@ -105,10 +105,14 @@ class SolianMessages extends Translations { 'realmPublic': 'Public Realm', 'realmCommunity': 'Community Realm', 'channelOrganizing': 'Organize a channel', + 'channelOrganizeCommon': 'Create regular channel', + 'channelOrganizeDirect': 'Create DM', + 'channelOrganizeDirectHint': 'Choose friend to create DM', 'channelEditingNotify': 'You\'re editing channel @channel', 'channelAlias': 'Alias (Identifier)', 'channelName': 'Name', 'channelDescription': 'Description', + 'channelDirectDescription': 'Direct message with @username', 'channelEncrypted': 'Encrypted Channel', 'channelMember': 'Channel member', 'channelMembers': 'Channel members', @@ -222,10 +226,14 @@ class SolianMessages extends Translations { 'realmPublic': '公开领域', 'realmCommunity': '社区领域', 'channelOrganizing': '组织频道', + 'channelOrganizeCommon': '创建普通频道', + 'channelOrganizeDirect': '创建私信频道', + 'channelOrganizeDirectHint': '选择好友来创建私信', 'channelEditingNotify': '你正在编辑频道 @channel', 'channelAlias': '别称(标识符)', 'channelName': '显示名称', 'channelDescription': '频道简介', + 'channelDirectDescription': '与 @username 的私聊', 'channelEncrypted': '加密频道', 'channelMember': '频道成员', 'channelMembers': '频道成员', diff --git a/lib/widgets/account/account_avatar.dart b/lib/widgets/account/account_avatar.dart index a273c34..9e406cf 100644 --- a/lib/widgets/account/account_avatar.dart +++ b/lib/widgets/account/account_avatar.dart @@ -3,11 +3,17 @@ import 'package:solian/services.dart'; class AccountAvatar extends StatelessWidget { final dynamic content; - final Color? color; + final Color? bgColor; + final Color? feColor; final double? radius; - const AccountAvatar( - {super.key, required this.content, this.color, this.radius}); + const AccountAvatar({ + super.key, + required this.content, + this.bgColor, + this.feColor, + this.radius, + }); @override Widget build(BuildContext context) { @@ -22,7 +28,7 @@ class AccountAvatar extends StatelessWidget { return CircleAvatar( key: Key('a$content'), radius: radius, - backgroundColor: color, + backgroundColor: bgColor, backgroundImage: !isEmpty ? NetworkImage( direct @@ -34,6 +40,7 @@ class AccountAvatar extends StatelessWidget { ? Icon( Icons.account_circle, size: radius != null ? radius! * 1.2 : 24, + color: feColor, ) : null, ); diff --git a/lib/widgets/account/friend_select.dart b/lib/widgets/account/friend_select.dart index 75b58ee..401bb88 100644 --- a/lib/widgets/account/friend_select.dart +++ b/lib/widgets/account/friend_select.dart @@ -19,7 +19,7 @@ class FriendSelect extends StatefulWidget { class _FriendSelectState extends State { int _accountId = 0; - List _friends = List.empty(growable: true); + final List _friends = List.empty(growable: true); getFriends() async { final AuthProvider auth = Get.find(); diff --git a/lib/widgets/attachments/attachment_publish.dart b/lib/widgets/attachments/attachment_publish.dart index 66b3d7c..dc1e917 100644 --- a/lib/widgets/attachments/attachment_publish.dart +++ b/lib/widgets/attachments/attachment_publish.dart @@ -56,7 +56,7 @@ class _AttachmentPublishingPopupState extends State { ratio: await calculateFileAspectRatio(file), ); } catch (err) { - this.context.showErrorDialog(err); + context.showErrorDialog(err); } } @@ -79,7 +79,7 @@ class _AttachmentPublishingPopupState extends State { try { await uploadAttachment(file, hash, ratio: ratio); } catch (err) { - this.context.showErrorDialog(err); + context.showErrorDialog(err); } setState(() => _isBusy = false); @@ -102,7 +102,7 @@ class _AttachmentPublishingPopupState extends State { try { await uploadAttachment(file, hash); } catch (err) { - this.context.showErrorDialog(err); + context.showErrorDialog(err); } } @@ -136,7 +136,7 @@ class _AttachmentPublishingPopupState extends State { try { await uploadAttachment(file, hash, ratio: ratio); } catch (err) { - this.context.showErrorDialog(err); + context.showErrorDialog(err); } setState(() => _isBusy = false); @@ -392,7 +392,7 @@ class _AttachmentEditingDialogState extends State { ); return Attachment.fromJson(resp.body); } catch (e) { - this.context.showErrorDialog(e); + context.showErrorDialog(e); return null; } finally { setState(() => _isBusy = false); @@ -406,7 +406,7 @@ class _AttachmentEditingDialogState extends State { await provider.deleteAttachment(widget.item.id); widget.onDelete(); } catch (e) { - this.context.showErrorDialog(e); + context.showErrorDialog(e); } finally { setState(() => _isBusy = false); } diff --git a/lib/widgets/posts/post_item.dart b/lib/widgets/posts/post_item.dart index 1694315..930e792 100644 --- a/lib/widgets/posts/post_item.dart +++ b/lib/widgets/posts/post_item.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';