♻️ Move the post editor mode into editor itself
This commit is contained in:
		| @@ -153,6 +153,11 @@ | |||||||
|   "publisherRunBy": "Run by {}", |   "publisherRunBy": "Run by {}", | ||||||
|   "fieldPublisherBelongToRealm": "Belongs to", |   "fieldPublisherBelongToRealm": "Belongs to", | ||||||
|   "fieldPublisherBelongToRealmUnset": "Unset Publisher Belongs to Realm", |   "fieldPublisherBelongToRealmUnset": "Unset Publisher Belongs to Realm", | ||||||
|  |   "writePost": "Compose", | ||||||
|  |   "postTypeStory": "Story", | ||||||
|  |   "postTypeArticle": "Article", | ||||||
|  |   "postTypeQuestion": "Question", | ||||||
|  |   "postTypeVideo": "Video", | ||||||
|   "writePostTypeStory": "Post a story", |   "writePostTypeStory": "Post a story", | ||||||
|   "writePostTypeArticle": "Write an article", |   "writePostTypeArticle": "Write an article", | ||||||
|   "writePostTypeQuestion": "Ask a question", |   "writePostTypeQuestion": "Ask a question", | ||||||
|   | |||||||
| @@ -137,6 +137,11 @@ | |||||||
|   "publisherRunBy": "由 {} 管理", |   "publisherRunBy": "由 {} 管理", | ||||||
|   "fieldPublisherBelongToRealm": "所属领域", |   "fieldPublisherBelongToRealm": "所属领域", | ||||||
|   "fieldPublisherBelongToRealmUnset": "未设置发布者所属领域", |   "fieldPublisherBelongToRealmUnset": "未设置发布者所属领域", | ||||||
|  |   "writePost": "撰写", | ||||||
|  |   "postTypeStory": "动态", | ||||||
|  |   "postTypeArticle": "文章", | ||||||
|  |   "postTypeQuestion": "问题", | ||||||
|  |   "postTypeVideo": "视频", | ||||||
|   "writePostTypeStory": "发动态", |   "writePostTypeStory": "发动态", | ||||||
|   "writePostTypeArticle": "写文章", |   "writePostTypeArticle": "写文章", | ||||||
|   "writePostTypeQuestion": "提问题", |   "writePostTypeQuestion": "提问题", | ||||||
|   | |||||||
| @@ -71,7 +71,8 @@ class PostWriteMedia { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   PostWriteMedia.fromBytes(this.raw, this.name, this.type, {this.attachment, this.file}); |   PostWriteMedia.fromBytes(this.raw, this.name, this.type, | ||||||
|  |       {this.attachment, this.file}); | ||||||
|  |  | ||||||
|   bool get isEmpty => attachment == null && file == null && raw == null; |   bool get isEmpty => attachment == null && file == null && raw == null; | ||||||
|  |  | ||||||
| @@ -105,7 +106,8 @@ class PostWriteMedia { | |||||||
|   }) { |   }) { | ||||||
|     if (attachment != null) { |     if (attachment != null) { | ||||||
|       final sn = context.read<SnNetworkProvider>(); |       final sn = context.read<SnNetworkProvider>(); | ||||||
|       final ImageProvider provider = UniversalImage.provider(sn.getAttachmentUrl(attachment!.rid)); |       final ImageProvider provider = | ||||||
|  |           UniversalImage.provider(sn.getAttachmentUrl(attachment!.rid)); | ||||||
|       if (width != null && height != null && !kIsWeb) { |       if (width != null && height != null && !kIsWeb) { | ||||||
|         return ResizeImage( |         return ResizeImage( | ||||||
|           provider, |           provider, | ||||||
| @@ -116,7 +118,8 @@ class PostWriteMedia { | |||||||
|       } |       } | ||||||
|       return provider; |       return provider; | ||||||
|     } else if (file != null) { |     } else if (file != null) { | ||||||
|       final ImageProvider provider = kIsWeb ? NetworkImage(file!.path) : FileImage(File(file!.path)); |       final ImageProvider provider = | ||||||
|  |           kIsWeb ? NetworkImage(file!.path) : FileImage(File(file!.path)); | ||||||
|       if (width != null && height != null) { |       if (width != null && height != null) { | ||||||
|         return ResizeImage( |         return ResizeImage( | ||||||
|           provider, |           provider, | ||||||
| @@ -159,11 +162,14 @@ class PostWriteController extends ChangeNotifier { | |||||||
|   final TextEditingController aliasController = TextEditingController(); |   final TextEditingController aliasController = TextEditingController(); | ||||||
|   final TextEditingController rewardController = TextEditingController(); |   final TextEditingController rewardController = TextEditingController(); | ||||||
|  |  | ||||||
|   ContentInsertionConfiguration get contentInsertionConfiguration => ContentInsertionConfiguration( |   ContentInsertionConfiguration get contentInsertionConfiguration => | ||||||
|  |       ContentInsertionConfiguration( | ||||||
|         onContentInserted: (KeyboardInsertedContent content) { |         onContentInserted: (KeyboardInsertedContent content) { | ||||||
|           if (content.hasData) { |           if (content.hasData) { | ||||||
|             addAttachments( |             addAttachments([ | ||||||
|                 [PostWriteMedia.fromBytes(content.data!, 'attachmentInsertedImage'.tr(), SnMediaType.image)]); |               PostWriteMedia.fromBytes(content.data!, | ||||||
|  |                   'attachmentInsertedImage'.tr(), SnMediaType.image) | ||||||
|  |             ]); | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|       ); |       ); | ||||||
| @@ -193,7 +199,8 @@ class PostWriteController extends ChangeNotifier { | |||||||
|  |  | ||||||
|   String get description => descriptionController.text; |   String get description => descriptionController.text; | ||||||
|  |  | ||||||
|   bool get isRelatedNull => ![editingPost, repostingPost, replyingPost].any((ele) => ele != null); |   bool get isRelatedNull => | ||||||
|  |       ![editingPost, repostingPost, replyingPost].any((ele) => ele != null); | ||||||
|  |  | ||||||
|   bool isLoading = false, isBusy = false; |   bool isLoading = false, isBusy = false; | ||||||
|   double? progress; |   double? progress; | ||||||
| @@ -237,14 +244,18 @@ class PostWriteController extends ChangeNotifier { | |||||||
|         publishedAt = post.publishedAt; |         publishedAt = post.publishedAt; | ||||||
|         publishedUntil = post.publishedUntil; |         publishedUntil = post.publishedUntil; | ||||||
|         visibleUsers = List.from(post.visibleUsersList ?? [], growable: true); |         visibleUsers = List.from(post.visibleUsersList ?? [], growable: true); | ||||||
|         invisibleUsers = List.from(post.invisibleUsersList ?? [], growable: true); |         invisibleUsers = | ||||||
|  |             List.from(post.invisibleUsersList ?? [], growable: true); | ||||||
|         visibility = post.visibility; |         visibility = post.visibility; | ||||||
|         tags = List.from(post.tags.map((ele) => ele.alias), growable: true); |         tags = List.from(post.tags.map((ele) => ele.alias), growable: true); | ||||||
|         categories = List.from(post.categories.map((ele) => ele.alias), growable: true); |         categories = | ||||||
|         attachments.addAll(post.preload?.attachments?.map((ele) => PostWriteMedia(ele)) ?? []); |             List.from(post.categories.map((ele) => ele.alias), growable: true); | ||||||
|  |         attachments.addAll( | ||||||
|  |             post.preload?.attachments?.map((ele) => PostWriteMedia(ele)) ?? []); | ||||||
|         poll = post.preload?.poll; |         poll = post.preload?.poll; | ||||||
|  |  | ||||||
|         if (post.preload?.thumbnail != null && (post.preload?.thumbnail?.rid.isNotEmpty ?? false)) { |         if (post.preload?.thumbnail != null && | ||||||
|  |             (post.preload?.thumbnail?.rid.isNotEmpty ?? false)) { | ||||||
|           thumbnail = PostWriteMedia(post.preload!.thumbnail); |           thumbnail = PostWriteMedia(post.preload!.thumbnail); | ||||||
|         } |         } | ||||||
|         if (post.preload?.realm != null) { |         if (post.preload?.realm != null) { | ||||||
| @@ -272,7 +283,8 @@ class PostWriteController extends ChangeNotifier { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<SnAttachment> _uploadAttachment(BuildContext context, PostWriteMedia media, |   Future<SnAttachment> _uploadAttachment( | ||||||
|  |       BuildContext context, PostWriteMedia media, | ||||||
|       {bool isCompressed = false}) async { |       {bool isCompressed = false}) async { | ||||||
|     final attach = context.read<SnAttachmentProvider>(); |     final attach = context.read<SnAttachmentProvider>(); | ||||||
|  |  | ||||||
| @@ -281,7 +293,9 @@ class PostWriteController extends ChangeNotifier { | |||||||
|       media.name, |       media.name, | ||||||
|       'interactive', |       'interactive', | ||||||
|       null, |       null, | ||||||
|       mimetype: media.raw != null && media.type == SnMediaType.image ? 'image/png' : null, |       mimetype: media.raw != null && media.type == SnMediaType.image | ||||||
|  |           ? 'image/png' | ||||||
|  |           : null, | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     var item = await attach.chunkedUploadParts( |     var item = await attach.chunkedUploadParts( | ||||||
| @@ -297,9 +311,11 @@ class PostWriteController extends ChangeNotifier { | |||||||
|  |  | ||||||
|     if (media.type == SnMediaType.video && !isCompressed && context.mounted) { |     if (media.type == SnMediaType.video && !isCompressed && context.mounted) { | ||||||
|       try { |       try { | ||||||
|         final compressedAttachment = await _tryCompressVideoCopy(context, media); |         final compressedAttachment = | ||||||
|  |             await _tryCompressVideoCopy(context, media); | ||||||
|         if (compressedAttachment != null) { |         if (compressedAttachment != null) { | ||||||
|           item = await attach.updateOne(item, compressedId: compressedAttachment.id); |           item = await attach.updateOne(item, | ||||||
|  |               compressedId: compressedAttachment.id); | ||||||
|         } |         } | ||||||
|       } catch (err) { |       } catch (err) { | ||||||
|         if (context.mounted) context.showErrorDialog(err); |         if (context.mounted) context.showErrorDialog(err); | ||||||
| @@ -309,8 +325,10 @@ class PostWriteController extends ChangeNotifier { | |||||||
|     return item; |     return item; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<SnAttachment?> _tryCompressVideoCopy(BuildContext context, PostWriteMedia media) async { |   Future<SnAttachment?> _tryCompressVideoCopy( | ||||||
|     if (kIsWeb || !(Platform.isAndroid || Platform.isIOS || Platform.isMacOS)) return null; |       BuildContext context, PostWriteMedia media) async { | ||||||
|  |     if (kIsWeb || !(Platform.isAndroid || Platform.isIOS || Platform.isMacOS)) | ||||||
|  |       return null; | ||||||
|     if (media.type != SnMediaType.video) return null; |     if (media.type != SnMediaType.video) return null; | ||||||
|     if (media.file == null) return null; |     if (media.file == null) return null; | ||||||
|     if (VideoCompress.isCompressing) return null; |     if (VideoCompress.isCompressing) return null; | ||||||
| @@ -334,7 +352,8 @@ class PostWriteController extends ChangeNotifier { | |||||||
|     if (!context.mounted) return null; |     if (!context.mounted) return null; | ||||||
|  |  | ||||||
|     final compressedMedia = PostWriteMedia.fromFile(XFile(mediaInfo.path!)); |     final compressedMedia = PostWriteMedia.fromFile(XFile(mediaInfo.path!)); | ||||||
|     final compressedAttachment = await _uploadAttachment(context, compressedMedia, isCompressed: true); |     final compressedAttachment = | ||||||
|  |         await _uploadAttachment(context, compressedMedia, isCompressed: true); | ||||||
|  |  | ||||||
|     return compressedAttachment; |     return compressedAttachment; | ||||||
|   } |   } | ||||||
| @@ -370,18 +389,25 @@ class PostWriteController extends ChangeNotifier { | |||||||
|           'content': contentController.text, |           'content': contentController.text, | ||||||
|           if (aliasController.text.isNotEmpty) 'alias': aliasController.text, |           if (aliasController.text.isNotEmpty) 'alias': aliasController.text, | ||||||
|           if (titleController.text.isNotEmpty) 'title': titleController.text, |           if (titleController.text.isNotEmpty) 'title': titleController.text, | ||||||
|           if (descriptionController.text.isNotEmpty) 'description': descriptionController.text, |           if (descriptionController.text.isNotEmpty) | ||||||
|  |             'description': descriptionController.text, | ||||||
|           if (rewardController.text.isNotEmpty) 'reward': rewardController.text, |           if (rewardController.text.isNotEmpty) 'reward': rewardController.text, | ||||||
|           if (thumbnail != null && thumbnail!.attachment != null) 'thumbnail': thumbnail!.attachment!.toJson(), |           if (thumbnail != null && thumbnail!.attachment != null) | ||||||
|           'attachments': |             'thumbnail': thumbnail!.attachment!.toJson(), | ||||||
|               attachments.where((e) => e.attachment != null).map((e) => e.attachment!.toJson()).toList(growable: true), |           'attachments': attachments | ||||||
|  |               .where((e) => e.attachment != null) | ||||||
|  |               .map((e) => e.attachment!.toJson()) | ||||||
|  |               .toList(growable: true), | ||||||
|           'tags': tags.map((ele) => {'alias': ele}).toList(growable: true), |           'tags': tags.map((ele) => {'alias': ele}).toList(growable: true), | ||||||
|           'categories': categories.map((ele) => {'alias': ele}).toList(growable: true), |           'categories': | ||||||
|  |               categories.map((ele) => {'alias': ele}).toList(growable: true), | ||||||
|           'visibility': visibility, |           'visibility': visibility, | ||||||
|           'visible_users_list': visibleUsers, |           'visible_users_list': visibleUsers, | ||||||
|           'invisible_users_list': invisibleUsers, |           'invisible_users_list': invisibleUsers, | ||||||
|           if (publishedAt != null) 'published_at': publishedAt!.toUtc().toIso8601String(), |           if (publishedAt != null) | ||||||
|           if (publishedUntil != null) 'published_until': publishedAt!.toUtc().toIso8601String(), |             'published_at': publishedAt!.toUtc().toIso8601String(), | ||||||
|  |           if (publishedUntil != null) | ||||||
|  |             'published_until': publishedAt!.toUtc().toIso8601String(), | ||||||
|           if (replyingPost != null) 'reply_to': replyingPost!.toJson(), |           if (replyingPost != null) 'reply_to': replyingPost!.toJson(), | ||||||
|           if (repostingPost != null) 'repost_to': repostingPost!.toJson(), |           if (repostingPost != null) 'repost_to': repostingPost!.toJson(), | ||||||
|           if (poll != null) 'poll': poll!.toJson(), |           if (poll != null) 'poll': poll!.toJson(), | ||||||
| @@ -391,6 +417,12 @@ class PostWriteController extends ChangeNotifier { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   bool get isNotEmpty => | ||||||
|  |       title.isNotEmpty || | ||||||
|  |       description.isNotEmpty || | ||||||
|  |       contentController.text.isNotEmpty || | ||||||
|  |       attachments.isNotEmpty; | ||||||
|  |  | ||||||
|   bool temporaryRestored = false; |   bool temporaryRestored = false; | ||||||
|  |  | ||||||
|   void _temporaryLoad() { |   void _temporaryLoad() { | ||||||
| @@ -403,18 +435,24 @@ class PostWriteController extends ChangeNotifier { | |||||||
|       titleController.text = data['title'] ?? ''; |       titleController.text = data['title'] ?? ''; | ||||||
|       descriptionController.text = data['description'] ?? ''; |       descriptionController.text = data['description'] ?? ''; | ||||||
|       rewardController.text = data['reward']?.toString() ?? ''; |       rewardController.text = data['reward']?.toString() ?? ''; | ||||||
|       if (data['thumbnail'] != null) thumbnail = PostWriteMedia(SnAttachment.fromJson(data['thumbnail'])); |       if (data['thumbnail'] != null) | ||||||
|       attachments |         thumbnail = PostWriteMedia(SnAttachment.fromJson(data['thumbnail'])); | ||||||
|           .addAll(data['attachments'].map((ele) => PostWriteMedia(SnAttachment.fromJson(ele))).cast<PostWriteMedia>()); |       attachments.addAll(data['attachments'] | ||||||
|  |           .map((ele) => PostWriteMedia(SnAttachment.fromJson(ele))) | ||||||
|  |           .cast<PostWriteMedia>()); | ||||||
|       tags = List.from(data['tags'].map((ele) => ele['alias'])); |       tags = List.from(data['tags'].map((ele) => ele['alias'])); | ||||||
|       categories = List.from(data['categories'].map((ele) => ele['alias'])); |       categories = List.from(data['categories'].map((ele) => ele['alias'])); | ||||||
|       visibility = data['visibility']; |       visibility = data['visibility']; | ||||||
|       visibleUsers = List.from(data['visible_users_list'] ?? []); |       visibleUsers = List.from(data['visible_users_list'] ?? []); | ||||||
|       invisibleUsers = List.from(data['invisible_users_list'] ?? []); |       invisibleUsers = List.from(data['invisible_users_list'] ?? []); | ||||||
|       if (data['published_at'] != null) publishedAt = DateTime.tryParse(data['published_at'])?.toLocal(); |       if (data['published_at'] != null) | ||||||
|       if (data['published_until'] != null) publishedUntil = DateTime.tryParse(data['published_until'])?.toLocal(); |         publishedAt = DateTime.tryParse(data['published_at'])?.toLocal(); | ||||||
|       replyingPost = data['reply_to'] != null ? SnPost.fromJson(data['reply_to']) : null; |       if (data['published_until'] != null) | ||||||
|       repostingPost = data['repost_to'] != null ? SnPost.fromJson(data['repost_to']) : null; |         publishedUntil = DateTime.tryParse(data['published_until'])?.toLocal(); | ||||||
|  |       replyingPost = | ||||||
|  |           data['reply_to'] != null ? SnPost.fromJson(data['reply_to']) : null; | ||||||
|  |       repostingPost = | ||||||
|  |           data['repost_to'] != null ? SnPost.fromJson(data['repost_to']) : null; | ||||||
|       poll = data['poll'] != null ? SnPoll.fromJson(data['poll']) : null; |       poll = data['poll'] != null ? SnPoll.fromJson(data['poll']) : null; | ||||||
|       realm = data['realm'] != null ? SnRealm.fromJson(data['realm']) : null; |       realm = data['realm'] != null ? SnRealm.fromJson(data['realm']) : null; | ||||||
|       temporaryRestored = true; |       temporaryRestored = true; | ||||||
| @@ -463,7 +501,9 @@ class PostWriteController extends ChangeNotifier { | |||||||
|           media.name, |           media.name, | ||||||
|           'interactive', |           'interactive', | ||||||
|           null, |           null, | ||||||
|           mimetype: media.raw != null && media.type == SnMediaType.image ? 'image/png' : null, |           mimetype: media.raw != null && media.type == SnMediaType.image | ||||||
|  |               ? 'image/png' | ||||||
|  |               : null, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         var item = await attach.chunkedUploadParts( |         var item = await attach.chunkedUploadParts( | ||||||
| @@ -472,16 +512,20 @@ class PostWriteController extends ChangeNotifier { | |||||||
|           place.$2, |           place.$2, | ||||||
|           onProgress: (value) { |           onProgress: (value) { | ||||||
|             // Calculate overall progress for attachments |             // Calculate overall progress for attachments | ||||||
|             progress = math.max(((i + value) / attachments.length) * kAttachmentProgressWeight, value); |             progress = math.max( | ||||||
|  |                 ((i + value) / attachments.length) * kAttachmentProgressWeight, | ||||||
|  |                 value); | ||||||
|             notifyListeners(); |             notifyListeners(); | ||||||
|           }, |           }, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|           if (context.mounted) { |           if (context.mounted) { | ||||||
|             final compressedAttachment = await _tryCompressVideoCopy(context, media); |             final compressedAttachment = | ||||||
|  |                 await _tryCompressVideoCopy(context, media); | ||||||
|             if (compressedAttachment != null) { |             if (compressedAttachment != null) { | ||||||
|               item = await attach.updateOne(item, compressedId: compressedAttachment.id); |               item = await attach.updateOne(item, | ||||||
|  |                   compressedId: compressedAttachment.id); | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         } catch (err) { |         } catch (err) { | ||||||
| @@ -518,16 +562,23 @@ class PostWriteController extends ChangeNotifier { | |||||||
|           'content': contentController.text, |           'content': contentController.text, | ||||||
|           if (aliasController.text.isNotEmpty) 'alias': aliasController.text, |           if (aliasController.text.isNotEmpty) 'alias': aliasController.text, | ||||||
|           if (titleController.text.isNotEmpty) 'title': titleController.text, |           if (titleController.text.isNotEmpty) 'title': titleController.text, | ||||||
|           if (descriptionController.text.isNotEmpty) 'description': descriptionController.text, |           if (descriptionController.text.isNotEmpty) | ||||||
|           if (thumbnail != null && thumbnail!.attachment != null) 'thumbnail': thumbnail!.attachment!.rid, |             'description': descriptionController.text, | ||||||
|           'attachments': attachments.where((e) => e.attachment != null).map((e) => e.attachment!.rid).toList(), |           if (thumbnail != null && thumbnail!.attachment != null) | ||||||
|  |             'thumbnail': thumbnail!.attachment!.rid, | ||||||
|  |           'attachments': attachments | ||||||
|  |               .where((e) => e.attachment != null) | ||||||
|  |               .map((e) => e.attachment!.rid) | ||||||
|  |               .toList(), | ||||||
|           'tags': tags.map((ele) => {'alias': ele}).toList(), |           'tags': tags.map((ele) => {'alias': ele}).toList(), | ||||||
|           'categories': categories.map((ele) => {'alias': ele}).toList(), |           'categories': categories.map((ele) => {'alias': ele}).toList(), | ||||||
|           'visibility': visibility, |           'visibility': visibility, | ||||||
|           'visible_users_list': visibleUsers, |           'visible_users_list': visibleUsers, | ||||||
|           'invisible_users_list': invisibleUsers, |           'invisible_users_list': invisibleUsers, | ||||||
|           if (publishedAt != null) 'published_at': publishedAt!.toUtc().toIso8601String(), |           if (publishedAt != null) | ||||||
|           if (publishedUntil != null) 'published_until': publishedAt!.toUtc().toIso8601String(), |             'published_at': publishedAt!.toUtc().toIso8601String(), | ||||||
|  |           if (publishedUntil != null) | ||||||
|  |             'published_until': publishedAt!.toUtc().toIso8601String(), | ||||||
|           if (replyingPost != null) 'reply_to': replyingPost!.id, |           if (replyingPost != null) 'reply_to': replyingPost!.id, | ||||||
|           if (repostingPost != null) 'repost_to': repostingPost!.id, |           if (repostingPost != null) 'repost_to': repostingPost!.id, | ||||||
|           if (reward != null) 'reward': reward, |           if (reward != null) 'reward': reward, | ||||||
| @@ -536,11 +587,14 @@ class PostWriteController extends ChangeNotifier { | |||||||
|           if (realm != null) 'realm': realm!.id, |           if (realm != null) 'realm': realm!.id, | ||||||
|         }, |         }, | ||||||
|         onSendProgress: (count, total) { |         onSendProgress: (count, total) { | ||||||
|           progress = baseProgressVal + (count / total) * (kPostingProgressWeight / 2); |           progress = | ||||||
|  |               baseProgressVal + (count / total) * (kPostingProgressWeight / 2); | ||||||
|           notifyListeners(); |           notifyListeners(); | ||||||
|         }, |         }, | ||||||
|         onReceiveProgress: (count, total) { |         onReceiveProgress: (count, total) { | ||||||
|           progress = baseProgressVal + (kPostingProgressWeight / 2) + (count / total) * (kPostingProgressWeight / 2); |           progress = baseProgressVal + | ||||||
|  |               (kPostingProgressWeight / 2) + | ||||||
|  |               (count / total) * (kPostingProgressWeight / 2); | ||||||
|           notifyListeners(); |           notifyListeners(); | ||||||
|         }, |         }, | ||||||
|         options: Options( |         options: Options( | ||||||
| @@ -683,7 +737,8 @@ class PostWriteController extends ChangeNotifier { | |||||||
|     repostingPost = null; |     repostingPost = null; | ||||||
|     mode = kTitleMap.keys.first; |     mode = kTitleMap.keys.first; | ||||||
|     temporaryRestored = false; |     temporaryRestored = false; | ||||||
|     SharedPreferences.getInstance().then((prefs) => prefs.remove(kTemporaryStorageKey)); |     SharedPreferences.getInstance() | ||||||
|  |         .then((prefs) => prefs.remove(kTemporaryStorageKey)); | ||||||
|     notifyListeners(); |     notifyListeners(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -66,10 +66,9 @@ final _appRoutes = [ | |||||||
|     builder: (context, state) => const ExploreScreen(), |     builder: (context, state) => const ExploreScreen(), | ||||||
|     routes: [ |     routes: [ | ||||||
|       GoRoute( |       GoRoute( | ||||||
|         path: '/write/:mode', |         path: '/write', | ||||||
|         name: 'postEditor', |         name: 'postEditor', | ||||||
|         builder: (context, state) => PostEditorScreen( |         builder: (context, state) => PostEditorScreen( | ||||||
|           mode: state.pathParameters['mode']!, |  | ||||||
|           postEditId: int.tryParse( |           postEditId: int.tryParse( | ||||||
|             state.uri.queryParameters['editing'] ?? '', |             state.uri.queryParameters['editing'] ?? '', | ||||||
|           ), |           ), | ||||||
|   | |||||||
| @@ -204,7 +204,6 @@ class _ChatScreenState extends State<ChatScreen> { | |||||||
|               Theme.of(context).floatingActionButtonTheme.foregroundColor, |               Theme.of(context).floatingActionButtonTheme.foregroundColor, | ||||||
|           backgroundColor: |           backgroundColor: | ||||||
|               Theme.of(context).floatingActionButtonTheme.backgroundColor, |               Theme.of(context).floatingActionButtonTheme.backgroundColor, | ||||||
|           shape: const CircleBorder(), |  | ||||||
|         ), |         ), | ||||||
|         closeButtonBuilder: DefaultFloatingActionButtonBuilder( |         closeButtonBuilder: DefaultFloatingActionButtonBuilder( | ||||||
|           child: const Icon(Symbols.close, size: 28), |           child: const Icon(Symbols.close, size: 28), | ||||||
| @@ -213,7 +212,6 @@ class _ChatScreenState extends State<ChatScreen> { | |||||||
|               Theme.of(context).floatingActionButtonTheme.foregroundColor, |               Theme.of(context).floatingActionButtonTheme.foregroundColor, | ||||||
|           backgroundColor: |           backgroundColor: | ||||||
|               Theme.of(context).floatingActionButtonTheme.backgroundColor, |               Theme.of(context).floatingActionButtonTheme.backgroundColor, | ||||||
|           shape: const CircleBorder(), |  | ||||||
|         ), |         ), | ||||||
|         children: [ |         children: [ | ||||||
|           Row( |           Row( | ||||||
|   | |||||||
| @@ -111,7 +111,6 @@ class _ExploreScreenState extends State<ExploreScreen> | |||||||
|               Theme.of(context).floatingActionButtonTheme.foregroundColor, |               Theme.of(context).floatingActionButtonTheme.foregroundColor, | ||||||
|           backgroundColor: |           backgroundColor: | ||||||
|               Theme.of(context).floatingActionButtonTheme.backgroundColor, |               Theme.of(context).floatingActionButtonTheme.backgroundColor, | ||||||
|           shape: const CircleBorder(), |  | ||||||
|         ), |         ), | ||||||
|         closeButtonBuilder: DefaultFloatingActionButtonBuilder( |         closeButtonBuilder: DefaultFloatingActionButtonBuilder( | ||||||
|           child: const Icon(Symbols.close, size: 28), |           child: const Icon(Symbols.close, size: 28), | ||||||
| @@ -120,90 +119,24 @@ class _ExploreScreenState extends State<ExploreScreen> | |||||||
|               Theme.of(context).floatingActionButtonTheme.foregroundColor, |               Theme.of(context).floatingActionButtonTheme.foregroundColor, | ||||||
|           backgroundColor: |           backgroundColor: | ||||||
|               Theme.of(context).floatingActionButtonTheme.backgroundColor, |               Theme.of(context).floatingActionButtonTheme.backgroundColor, | ||||||
|           shape: const CircleBorder(), |  | ||||||
|         ), |         ), | ||||||
|         children: [ |         children: [ | ||||||
|           Row( |           Row( | ||||||
|             children: [ |             children: [ | ||||||
|               Text('writePostTypeStory').tr(), |               Text('writePost').tr(), | ||||||
|               const Gap(20), |               const Gap(20), | ||||||
|               FloatingActionButton( |               FloatingActionButton( | ||||||
|                 heroTag: null, |                 heroTag: null, | ||||||
|                 tooltip: 'writePostTypeStory'.tr(), |                 tooltip: 'writePost'.tr(), | ||||||
|                 onPressed: () { |                 onPressed: () { | ||||||
|                   GoRouter.of(context).pushNamed('postEditor', pathParameters: { |                   GoRouter.of(context).pushNamed('postEditor').then((value) { | ||||||
|                     'mode': 'stories', |  | ||||||
|                   }).then((value) { |  | ||||||
|                     if (value == true) { |                     if (value == true) { | ||||||
|                       refreshPosts(); |                       refreshPosts(); | ||||||
|                     } |                     } | ||||||
|                   }); |                   }); | ||||||
|                   _fabKey.currentState!.toggle(); |                   _fabKey.currentState!.toggle(); | ||||||
|                 }, |                 }, | ||||||
|                 child: const Icon(Symbols.post_rounded), |                 child: const Icon(Symbols.edit), | ||||||
|               ), |  | ||||||
|             ], |  | ||||||
|           ), |  | ||||||
|           Row( |  | ||||||
|             children: [ |  | ||||||
|               Text('writePostTypeArticle').tr(), |  | ||||||
|               const Gap(20), |  | ||||||
|               FloatingActionButton( |  | ||||||
|                 heroTag: null, |  | ||||||
|                 tooltip: 'writePostTypeArticle'.tr(), |  | ||||||
|                 onPressed: () { |  | ||||||
|                   GoRouter.of(context).pushNamed('postEditor', pathParameters: { |  | ||||||
|                     'mode': 'articles', |  | ||||||
|                   }).then((value) { |  | ||||||
|                     if (value == true) { |  | ||||||
|                       refreshPosts(); |  | ||||||
|                     } |  | ||||||
|                   }); |  | ||||||
|                   _fabKey.currentState!.toggle(); |  | ||||||
|                 }, |  | ||||||
|                 child: const Icon(Symbols.news), |  | ||||||
|               ), |  | ||||||
|             ], |  | ||||||
|           ), |  | ||||||
|           Row( |  | ||||||
|             children: [ |  | ||||||
|               Text('writePostTypeQuestion').tr(), |  | ||||||
|               const Gap(20), |  | ||||||
|               FloatingActionButton( |  | ||||||
|                 heroTag: null, |  | ||||||
|                 tooltip: 'writePostTypeQuestion'.tr(), |  | ||||||
|                 onPressed: () { |  | ||||||
|                   GoRouter.of(context).pushNamed('postEditor', pathParameters: { |  | ||||||
|                     'mode': 'questions', |  | ||||||
|                   }).then((value) { |  | ||||||
|                     if (value == true) { |  | ||||||
|                       refreshPosts(); |  | ||||||
|                     } |  | ||||||
|                   }); |  | ||||||
|                   _fabKey.currentState!.toggle(); |  | ||||||
|                 }, |  | ||||||
|                 child: const Icon(Symbols.question_answer), |  | ||||||
|               ), |  | ||||||
|             ], |  | ||||||
|           ), |  | ||||||
|           Row( |  | ||||||
|             children: [ |  | ||||||
|               Text('writePostTypeVideo').tr(), |  | ||||||
|               const Gap(20), |  | ||||||
|               FloatingActionButton( |  | ||||||
|                 heroTag: null, |  | ||||||
|                 tooltip: 'writePostTypeVideo'.tr(), |  | ||||||
|                 onPressed: () { |  | ||||||
|                   GoRouter.of(context).pushNamed('postEditor', pathParameters: { |  | ||||||
|                     'mode': 'videos', |  | ||||||
|                   }).then((value) { |  | ||||||
|                     if (value == true) { |  | ||||||
|                       refreshPosts(); |  | ||||||
|                     } |  | ||||||
|                   }); |  | ||||||
|                   _fabKey.currentState!.toggle(); |  | ||||||
|                 }, |  | ||||||
|                 child: const Icon(Symbols.video_call), |  | ||||||
|               ), |               ), | ||||||
|             ], |             ], | ||||||
|           ), |           ), | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ import 'package:surface/controllers/post_write_controller.dart'; | |||||||
| import 'package:surface/providers/config.dart'; | import 'package:surface/providers/config.dart'; | ||||||
| import 'package:surface/providers/sn_attachment.dart'; | import 'package:surface/providers/sn_attachment.dart'; | ||||||
| import 'package:surface/providers/sn_network.dart'; | import 'package:surface/providers/sn_network.dart'; | ||||||
|  | import 'package:surface/providers/sn_realm.dart'; | ||||||
| import 'package:surface/types/attachment.dart'; | import 'package:surface/types/attachment.dart'; | ||||||
| import 'package:surface/types/post.dart'; | import 'package:surface/types/post.dart'; | ||||||
| import 'package:surface/types/realm.dart'; | import 'package:surface/types/realm.dart'; | ||||||
| @@ -36,7 +37,8 @@ import 'package:provider/provider.dart'; | |||||||
| import 'package:surface/widgets/post/post_poll_editor.dart'; | import 'package:surface/widgets/post/post_poll_editor.dart'; | ||||||
| import 'package:uuid/uuid.dart'; | import 'package:uuid/uuid.dart'; | ||||||
|  |  | ||||||
| import '../../providers/sn_realm.dart'; | const kPostTypes = ['Story', 'Article', 'Question', 'Video']; | ||||||
|  | const kPostTypeAliases = ['stories', 'articles', 'questions', 'videos']; | ||||||
|  |  | ||||||
| class PostEditorExtra { | class PostEditorExtra { | ||||||
|   final String? text; |   final String? text; | ||||||
| @@ -53,7 +55,6 @@ class PostEditorExtra { | |||||||
| } | } | ||||||
|  |  | ||||||
| class PostEditorScreen extends StatefulWidget { | class PostEditorScreen extends StatefulWidget { | ||||||
|   final String mode; |  | ||||||
|   final int? postEditId; |   final int? postEditId; | ||||||
|   final int? postReplyId; |   final int? postReplyId; | ||||||
|   final int? postRepostId; |   final int? postRepostId; | ||||||
| @@ -61,7 +62,6 @@ class PostEditorScreen extends StatefulWidget { | |||||||
|  |  | ||||||
|   const PostEditorScreen({ |   const PostEditorScreen({ | ||||||
|     super.key, |     super.key, | ||||||
|     required this.mode, |  | ||||||
|     required this.postEditId, |     required this.postEditId, | ||||||
|     required this.postReplyId, |     required this.postReplyId, | ||||||
|     required this.postRepostId, |     required this.postRepostId, | ||||||
| @@ -72,7 +72,10 @@ class PostEditorScreen extends StatefulWidget { | |||||||
|   State<PostEditorScreen> createState() => _PostEditorScreenState(); |   State<PostEditorScreen> createState() => _PostEditorScreenState(); | ||||||
| } | } | ||||||
|  |  | ||||||
| class _PostEditorScreenState extends State<PostEditorScreen> { | class _PostEditorScreenState extends State<PostEditorScreen> | ||||||
|  |     with SingleTickerProviderStateMixin { | ||||||
|  |   late final TabController _tabController = | ||||||
|  |       TabController(length: 4, vsync: this); | ||||||
|   late final PostWriteController _writeController = PostWriteController( |   late final PostWriteController _writeController = PostWriteController( | ||||||
|     doLoadFromTemporary: widget.postEditId == null, |     doLoadFromTemporary: widget.postEditId == null, | ||||||
|   ); |   ); | ||||||
| @@ -209,6 +212,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> { | |||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   void dispose() { |   void dispose() { | ||||||
|  |     _tabController.dispose(); | ||||||
|     _writeController.dispose(); |     _writeController.dispose(); | ||||||
|     if (!kIsWeb && !(Platform.isAndroid || Platform.isIOS)) { |     if (!kIsWeb && !(Platform.isAndroid || Platform.isIOS)) { | ||||||
|       hotKeyManager.unregister(_pasteHotKey); |       hotKeyManager.unregister(_pasteHotKey); | ||||||
| @@ -220,14 +224,13 @@ class _PostEditorScreenState extends State<PostEditorScreen> { | |||||||
|   void initState() { |   void initState() { | ||||||
|     super.initState(); |     super.initState(); | ||||||
|     _registerHotKey(); |     _registerHotKey(); | ||||||
|     if (!PostWriteController.kTitleMap.keys.contains(widget.mode)) { |  | ||||||
|       context.showErrorDialog('Unknown post type'); |  | ||||||
|       Navigator.pop(context); |  | ||||||
|     } else { |  | ||||||
|       _writeController.setMode(widget.mode); |  | ||||||
|     } |  | ||||||
|     _fetchRealms(); |     _fetchRealms(); | ||||||
|     _fetchPublishers(); |     _fetchPublishers(); | ||||||
|  |     _tabController.addListener(() { | ||||||
|  |       if (_tabController.indexIsChanging) { | ||||||
|  |         _writeController.setMode(kPostTypeAliases[_tabController.index]); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|     _writeController.fetchRelatedPost( |     _writeController.fetchRelatedPost( | ||||||
|       context, |       context, | ||||||
|       editing: widget.postEditId, |       editing: widget.postEditId, | ||||||
| @@ -255,26 +258,10 @@ class _PostEditorScreenState extends State<PostEditorScreen> { | |||||||
|                 Navigator.pop(context); |                 Navigator.pop(context); | ||||||
|               }, |               }, | ||||||
|             ), |             ), | ||||||
|             title: RichText( |             title: Text( | ||||||
|               textAlign: TextAlign.center, |               _writeController.title.isNotEmpty | ||||||
|               text: TextSpan(children: [ |                   ? _writeController.title | ||||||
|                 TextSpan( |                   : 'untitled'.tr(), | ||||||
|                   text: _writeController.title.isNotEmpty |  | ||||||
|                       ? _writeController.title |  | ||||||
|                       : 'untitled'.tr(), |  | ||||||
|                   style: Theme.of(context).textTheme.titleLarge!.copyWith( |  | ||||||
|                         color: Theme.of(context).appBarTheme.foregroundColor!, |  | ||||||
|                       ), |  | ||||||
|                 ), |  | ||||||
|                 const TextSpan(text: '\n'), |  | ||||||
|                 TextSpan( |  | ||||||
|                   text: PostWriteController.kTitleMap[widget.mode]!.tr(), |  | ||||||
|                   style: Theme.of(context).textTheme.bodySmall!.copyWith( |  | ||||||
|                         color: Theme.of(context).appBarTheme.foregroundColor!, |  | ||||||
|                       ), |  | ||||||
|                 ), |  | ||||||
|               ]), |  | ||||||
|               maxLines: 2, |  | ||||||
|             ), |             ), | ||||||
|             actions: [ |             actions: [ | ||||||
|               IconButton( |               IconButton( | ||||||
| @@ -283,6 +270,24 @@ class _PostEditorScreenState extends State<PostEditorScreen> { | |||||||
|               ), |               ), | ||||||
|               const Gap(8), |               const Gap(8), | ||||||
|             ], |             ], | ||||||
|  |             bottom: _writeController.isNotEmpty | ||||||
|  |                 ? null | ||||||
|  |                 : TabBar( | ||||||
|  |                     controller: _tabController, | ||||||
|  |                     tabs: [ | ||||||
|  |                       for (final type in kPostTypes) | ||||||
|  |                         Tab( | ||||||
|  |                           child: Text( | ||||||
|  |                             'postType$type'.tr(), | ||||||
|  |                             style: TextStyle( | ||||||
|  |                               color: Theme.of(context) | ||||||
|  |                                   .appBarTheme | ||||||
|  |                                   .foregroundColor!, | ||||||
|  |                             ), | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                     ], | ||||||
|  |                   ), | ||||||
|           ), |           ), | ||||||
|           body: Column( |           body: Column( | ||||||
|             children: [ |             children: [ | ||||||
| @@ -374,7 +379,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> { | |||||||
|                   children: [ |                   children: [ | ||||||
|                     SingleChildScrollView( |                     SingleChildScrollView( | ||||||
|                       padding: EdgeInsets.only(bottom: 160), |                       padding: EdgeInsets.only(bottom: 160), | ||||||
|                       child: StyledWidget(switch (_writeController.mode) { |                       child: switch (_writeController.mode) { | ||||||
|                         'stories' => _PostStoryEditor( |                         'stories' => _PostStoryEditor( | ||||||
|                             controller: _writeController, |                             controller: _writeController, | ||||||
|                             onTapPublisher: _showPublisherPopup, |                             onTapPublisher: _showPublisherPopup, | ||||||
| @@ -396,8 +401,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> { | |||||||
|                             onTapRealm: _showRealmPopup, |                             onTapRealm: _showRealmPopup, | ||||||
|                           ), |                           ), | ||||||
|                         _ => const Placeholder(), |                         _ => const Placeholder(), | ||||||
|                       }) |                       }, | ||||||
|                           .padding(top: 8), |  | ||||||
|                     ), |                     ), | ||||||
|                     if (_writeController.attachments.isNotEmpty || |                     if (_writeController.attachments.isNotEmpty || | ||||||
|                         _writeController.thumbnail != null) |                         _writeController.thumbnail != null) | ||||||
| @@ -720,7 +724,7 @@ class _PostStoryEditor extends StatelessWidget { | |||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return Container( |     return Container( | ||||||
|       padding: const EdgeInsets.symmetric(horizontal: 12), |       padding: const EdgeInsets.only(left: 12, right: 12, top: 8), | ||||||
|       constraints: const BoxConstraints(maxWidth: 640), |       constraints: const BoxConstraints(maxWidth: 640), | ||||||
|       child: Row( |       child: Row( | ||||||
|         crossAxisAlignment: CrossAxisAlignment.start, |         crossAxisAlignment: CrossAxisAlignment.start, | ||||||
| @@ -969,7 +973,7 @@ class _PostQuestionEditor extends StatelessWidget { | |||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return Container( |     return Container( | ||||||
|       padding: const EdgeInsets.symmetric(horizontal: 12), |       padding: const EdgeInsets.only(left: 12, right: 12, top: 8), | ||||||
|       constraints: const BoxConstraints(maxWidth: 640), |       constraints: const BoxConstraints(maxWidth: 640), | ||||||
|       child: Row( |       child: Row( | ||||||
|         crossAxisAlignment: CrossAxisAlignment.start, |         crossAxisAlignment: CrossAxisAlignment.start, | ||||||
| @@ -1053,7 +1057,7 @@ class _PostQuestionEditor extends StatelessWidget { | |||||||
|             ), |             ), | ||||||
|           ), |           ), | ||||||
|         ], |         ], | ||||||
|       ).padding(top: 8), |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -1154,7 +1158,7 @@ class _PostVideoEditor extends StatelessWidget { | |||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return Container( |     return Container( | ||||||
|       padding: const EdgeInsets.symmetric(horizontal: 12), |       padding: const EdgeInsets.only(left: 12, right: 12, top: 8), | ||||||
|       constraints: const BoxConstraints(maxWidth: 640), |       constraints: const BoxConstraints(maxWidth: 640), | ||||||
|       child: Row( |       child: Row( | ||||||
|         crossAxisAlignment: CrossAxisAlignment.start, |         crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user