From a5d08fe16e6af1de9546b00a98046383ef417588 Mon Sep 17 00:00:00 2001 From: sorairo Date: Tue, 29 Aug 2023 05:37:42 +0900 Subject: [PATCH 01/12] =?UTF-8?q?#269=20=E5=AF=BE=E5=BF=9C=20(WIP)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/repository/account_repository.dart | 7 +- lib/repository/tab_settings_repository.dart | 11 +++ .../federation_announcements.dart | 77 ++++++++++++++-- lib/view/time_line_page/time_line_page.dart | 88 +++++++++++++++++-- pubspec.lock | 8 +- pubspec.yaml | 2 +- 6 files changed, 172 insertions(+), 21 deletions(-) diff --git a/lib/repository/account_repository.dart b/lib/repository/account_repository.dart index 4f7522763..0df88f47f 100644 --- a/lib/repository/account_repository.dart +++ b/lib/repository/account_repository.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -59,8 +60,10 @@ class AccountRepository extends ChangeNotifier { final index = _account.indexOf(account); if (index == -1) return; if (accountDataValidated.isNotEmpty && accountDataValidated[index]) return; - _account[index] = _account[index] - .copyWith(i: await reader(misskeyProvider(_account[index])).i.i()); + final i = await reader(misskeyProvider(_account[index])).i.i(); + _account[index] = _account[index].copyWith(i: i); + tabSettingsRepository.updateAccount(account, i); + accountDataValidated[index] = true; notifyListeners(); } diff --git a/lib/repository/tab_settings_repository.dart b/lib/repository/tab_settings_repository.dart index e7b1fe0b4..5d36405c7 100644 --- a/lib/repository/tab_settings_repository.dart +++ b/lib/repository/tab_settings_repository.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:miria/model/account.dart'; import 'package:miria/model/tab_setting.dart'; +import 'package:misskey_dart/misskey_dart.dart'; import 'package:shared_preferences/shared_preferences.dart'; class TabSettingsRepository extends ChangeNotifier { @@ -28,6 +29,16 @@ class TabSettingsRepository extends ChangeNotifier { } } + void updateAccount(Account account, IResponse response) { + for (var i = 0; i < _tabSettings.length; i++) { + if (_tabSettings[i].account == account) { + _tabSettings[i] = _tabSettings[i] + .copyWith(account: _tabSettings[i].account.copyWith(i: response)); + } + } + notifyListeners(); + } + Future save(List tabSettings) async { _tabSettings = tabSettings.toList(); final prefs = await SharedPreferences.getInstance(); diff --git a/lib/view/federation_page/federation_announcements.dart b/lib/view/federation_page/federation_announcements.dart index 863ea0efc..a238283da 100644 --- a/lib/view/federation_page/federation_announcements.dart +++ b/lib/view/federation_page/federation_announcements.dart @@ -45,7 +45,7 @@ class FederationAnnouncements extends ConsumerWidget { } } -class Announcement extends StatelessWidget { +class Announcement extends ConsumerStatefulWidget { final AnnouncementsResponse data; final String host; @@ -55,8 +55,22 @@ class Announcement extends StatelessWidget { required this.host, }); + @override + ConsumerState createState() => AnnouncementState(); +} + +class AnnouncementState extends ConsumerState { + late AnnouncementsResponse data; + + @override + void initState() { + super.initState(); + data = widget.data; + } + @override Widget build(BuildContext context) { + final icon = data.icon; return Padding( padding: const EdgeInsets.all(10), child: Card( @@ -67,9 +81,16 @@ class Announcement extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ - Text( - data.title, - style: Theme.of(context).textTheme.titleMedium, + Row( + children: [ + if (icon != null) AnnouncementIcon(iconType: icon), + Expanded( + child: Text( + data.title, + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ], ), Align( alignment: Alignment.centerRight, @@ -78,11 +99,55 @@ class Announcement extends StatelessWidget { const Padding(padding: EdgeInsets.only(top: 10)), MfmText( mfmText: data.text, - host: AccountScope.of(context).host == host ? null : host, - ) + host: AccountScope.of(context).host == widget.host + ? null + : widget.host, + ), + if (AccountScope.of(context).host == widget.host && + data.isRead == false) + ElevatedButton( + onPressed: () async { + await ref + .read(misskeyProvider(AccountScope.of(context))) + .i + .readAnnouncement(IReadAnnouncementRequest( + announcementId: data.id)); + setState(() { + data = data.copyWith(isRead: true); + }); + }, + child: const Text("ほい")) ], ), ), )); } } + +class AnnouncementIcon extends StatelessWidget { + final AnnouncementIconType iconType; + + const AnnouncementIcon({ + super.key, + required this.iconType, + }); + + @override + Widget build(BuildContext context) { + switch (iconType) { + case AnnouncementIconType.info: + return const Icon(Icons.info); + case AnnouncementIconType.warning: + return const Icon( + Icons.warning, + color: Colors.yellow, + ); + case AnnouncementIconType.error: + return const Icon(Icons.error, color: Colors.red); + case AnnouncementIconType.success: + return const Icon(Icons.check, color: Colors.blue); + default: + return const SizedBox.shrink(); + } + } +} diff --git a/lib/view/time_line_page/time_line_page.dart b/lib/view/time_line_page/time_line_page.dart index 0dcff714e..edce72835 100644 --- a/lib/view/time_line_page/time_line_page.dart +++ b/lib/view/time_line_page/time_line_page.dart @@ -44,13 +44,6 @@ class TimeLinePageState extends ConsumerState { @override void initState() { - tabSettings = ref.read( - tabSettingsRepositoryProvider.select((repo) => repo.tabSettings.toList()), - ); - currentIndex = tabSettings.indexOf(widget.initialTabSetting); - pageController = PageController(initialPage: currentIndex); - scrollControllers = - List.generate(tabSettings.length, (_) => TimelineScrollController()); super.initState(); } @@ -93,6 +86,13 @@ class TimeLinePageState extends ConsumerState { @override void didChangeDependencies() { super.didChangeDependencies(); + tabSettings = ref.watch( + tabSettingsRepositoryProvider.select((repo) => repo.tabSettings.toList()), + ); + currentIndex = tabSettings.indexOf(widget.initialTabSetting); + pageController = PageController(initialPage: currentIndex); + scrollControllers = + List.generate(tabSettings.length, (_) => TimelineScrollController()); } void reload() { @@ -286,6 +286,7 @@ class TimeLinePageState extends ConsumerState { ], ), ), + BannerArea(index: currentIndex), if (socketTimeline?.isLoading == true) const Padding( padding: EdgeInsets.only(top: 10), @@ -362,3 +363,76 @@ class TimeLinePageState extends ConsumerState { ); } } + +class BannerArea extends ConsumerWidget { + final int index; + + const BannerArea({super.key, required this.index}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final bannerAnnouncement = ref.watch(accountRepository.select((value) => + value.account + .where((element) => + element == + ref + .read(tabSettingsRepositoryProvider) + .tabSettings + .toList()[index] + .account) + .firstOrNull + ?.i + .unreadAnnouncements)); + + // ダイアログの実装が大変なので(状態管理とか)いったんバナーと一緒に扱う + final bannerData = bannerAnnouncement + ?.where((element) => + element.display == AnnouncementDisplayType.banner || + element.display == AnnouncementDisplayType.dialog) + .firstOrNull; + + if (bannerData == null) return const SizedBox.shrink(); + + return Container( + width: double.infinity, + padding: const EdgeInsets.only(left: 10, top: 3, bottom: 3), + decoration: BoxDecoration(color: Theme.of(context).primaryColor), + child: Row( + children: [ + if (bannerData.icon != null) + AnnouncementIcon(iconType: bannerData.icon!), + const Padding(padding: EdgeInsets.only(left: 10)), + Text( + "${bannerData.title} ${bannerData.text}", + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Colors.white), + ), + ], + ), + ); + } +} + +class AnnouncementIcon extends StatelessWidget { + final AnnouncementIconType iconType; + + const AnnouncementIcon({super.key, required this.iconType}); + + @override + Widget build(BuildContext context) { + switch (iconType) { + case AnnouncementIconType.info: + return const Icon(Icons.info, color: Colors.white); + case AnnouncementIconType.warning: + return const Icon(Icons.warning, color: Colors.white); + case AnnouncementIconType.error: + return const Icon(Icons.error, color: Colors.white); + case AnnouncementIconType.success: + return const Icon(Icons.check, color: Colors.white); + default: + return const SizedBox.shrink(); + } + } +} diff --git a/pubspec.lock b/pubspec.lock index 7975d8d44..afcb5bb1c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -788,11 +788,9 @@ packages: misskey_dart: dependency: "direct main" description: - path: "." - ref: HEAD - resolved-ref: a933c8f5e0de1359cca34d261e247906f1a5448d - url: "https://github.com/shiosyakeyakini-info/misskey_dart.git" - source: git + path: "../misskey_dart" + relative: true + source: path version: "1.0.0" mockito: dependency: "direct dev" diff --git a/pubspec.yaml b/pubspec.yaml index 322c4c058..8105f296f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: flutter_html: 3.0.0-alpha.6 flutter_riverpod: ^2.3.2 misskey_dart: - git: https://github.com/shiosyakeyakini-info/misskey_dart.git + path: ../misskey_dart/ mfm_renderer: git: https://github.com/shiosyakeyakini-info/mfm_renderer.git tuple: ^2.0.1 From 947bfd227c9acb4d1ed2c2e3ec720d7c1ce51373 Mon Sep 17 00:00:00 2001 From: sorairo Date: Tue, 29 Aug 2023 05:53:34 +0900 Subject: [PATCH 02/12] =?UTF-8?q?pubspec.yaml=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pubspec.lock | 8 +++++--- pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index afcb5bb1c..934610a0c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -788,9 +788,11 @@ packages: misskey_dart: dependency: "direct main" description: - path: "../misskey_dart" - relative: true - source: path + path: "." + ref: HEAD + resolved-ref: "2913789026aad06322441d1fad0f5652738529ef" + url: "https://github.com/shiosyakeyakini-info/misskey_dart.git" + source: git version: "1.0.0" mockito: dependency: "direct dev" diff --git a/pubspec.yaml b/pubspec.yaml index 00b42718f..258b9daf1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: flutter_html: 3.0.0-alpha.6 flutter_riverpod: ^2.3.2 misskey_dart: - path: ../misskey_dart/ + git: https://github.com/shiosyakeyakini-info/misskey_dart.git mfm_renderer: git: https://github.com/shiosyakeyakini-info/mfm_renderer.git tuple: ^2.0.1 From 8621f63f9b3d5b44b2a6e50c2e5b0a9bb3bd4a93 Mon Sep 17 00:00:00 2001 From: NPL <66727014+Npepperlinux@users.noreply.github.com> Date: Thu, 31 Aug 2023 09:57:48 +0900 Subject: [PATCH 03/12] =?UTF-8?q?Linux=E3=81=AE=E3=83=95=E3=82=A9=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=82=B3=E3=83=BC=E3=83=AB=E3=83=90=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/view/themes/app_theme_scope.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/view/themes/app_theme_scope.dart b/lib/view/themes/app_theme_scope.dart index 1e3eafe68..dd634f301 100644 --- a/lib/view/themes/app_theme_scope.dart +++ b/lib/view/themes/app_theme_scope.dart @@ -65,7 +65,8 @@ class AppThemeScopeState extends ConsumerState { } List resolveFontFamilyCallback() { - if (defaultTargetPlatform == TargetPlatform.windows) { + if (defaultTargetPlatform == TargetPlatform.windows || + defaultTargetPlatform == TargetPlatform.linux) { return ["Noto Sans CJK JP", "KosugiMaru", "BIZ UDPGothic"]; } if (defaultTargetPlatform == TargetPlatform.iOS || @@ -101,7 +102,8 @@ class AppThemeScopeState extends ConsumerState { fontFamily: "Segoe UI Emoji", fontFamilyFallback: ["Segoe UI Emoji", "Noto Color Emoji", "Meiryo"]); } - if (defaultTargetPlatform == TargetPlatform.android) { + if (defaultTargetPlatform == TargetPlatform.android || + defaultTargetPlatform == TargetPlatform.linux) { return const TextStyle( fontFamily: "Noto Color Emoji", fontFamilyFallback: ["Noto Color Emoji", "Noto Sans JP"]); From 7d2236a213f5318a2f9d1b6970f7e143c261c421 Mon Sep 17 00:00:00 2001 From: poppingmoon <63451158+poppingmoon@users.noreply.github.com> Date: Fri, 1 Sep 2023 05:52:44 +0900 Subject: [PATCH 04/12] =?UTF-8?q?detailed=E3=81=A7=E3=81=AF=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=83=8E=E3=83=BC=E3=83=88=E3=81=AEregister=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/repository/note_repository.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/repository/note_repository.dart b/lib/repository/note_repository.dart index 8362cca8e..6d5f41c31 100644 --- a/lib/repository/note_repository.dart +++ b/lib/repository/note_repository.dart @@ -33,9 +33,12 @@ class NoteRepository extends ChangeNotifier { } void _registerNote(Note note) { + final registeredNote = _notes[note.id]; _notes[note.id] = note.copyWith( renote: note.renote ?? _notes[note.renoteId], reply: note.reply ?? _notes[note.replyId], + poll: note.poll ?? registeredNote?.poll, + myReaction: note.myReaction ?? registeredNote?.myReaction, ); _noteStatuses[note.id] ??= const NoteStatus( isCwOpened: false, From 9823977aebfc9d84efd16956e083cf3ba4d8752e Mon Sep 17 00:00:00 2001 From: poppingmoon <63451158+poppingmoon@users.noreply.github.com> Date: Fri, 1 Sep 2023 06:36:00 +0900 Subject: [PATCH 05/12] =?UTF-8?q?MisskeyNote=E3=81=AEbuild=E6=99=82?= =?UTF-8?q?=E3=81=ABisCwOpened=E3=82=92=E6=9B=B4=E6=96=B0=E3=81=97?= =?UTF-8?q?=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/view/common/misskey_notes/misskey_note.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/view/common/misskey_notes/misskey_note.dart b/lib/view/common/misskey_notes/misskey_note.dart index 1d7d3c7d5..fef65a53f 100644 --- a/lib/view/common/misskey_notes/misskey_note.dart +++ b/lib/view/common/misskey_notes/misskey_note.dart @@ -11,7 +11,6 @@ import 'package:miria/extensions/user_extension.dart'; import 'package:miria/model/account.dart'; import 'package:miria/model/misskey_emoji_data.dart'; import 'package:miria/providers.dart'; -import 'package:miria/repository/note_repository.dart'; import 'package:miria/router/app_router.dart'; import 'package:miria/view/common/account_scope.dart'; import 'package:miria/view/common/avatar_icon.dart'; @@ -236,13 +235,14 @@ class MisskeyNoteState extends ConsumerState { shouldCollaposed(displayTextNodes!)); ref.read(notesProvider(AccountScope.of(context))).updateNoteStatus( - widget.note.id, - (status) => NoteStatus( - isCwOpened: false, + widget.note.id, + (status) => status.copyWith( isLongVisible: isLongVisible, isReactionedRenote: isReactionedRenote, - isLongVisibleInitialized: true), - isNotify: false); + isLongVisibleInitialized: true, + ), + isNotify: false, + ); } final userId = From 2a80c8dc699b9da56c9767b39d0566a24001e686 Mon Sep 17 00:00:00 2001 From: poppingmoon <63451158+poppingmoon@users.noreply.github.com> Date: Sat, 2 Sep 2023 15:38:08 +0900 Subject: [PATCH 06/12] add trailing commas --- .../photo_edit_state_notifier.dart | 248 ++++++++++++------ 1 file changed, 163 insertions(+), 85 deletions(-) diff --git a/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart b/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart index f9aec226d..7a92f0501 100644 --- a/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart +++ b/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart @@ -79,7 +79,8 @@ class PhotoEditStateNotifier extends StateNotifier { throw UnsupportedError("$file is unsupported."); } final imageData = await ImageDescriptor.encoded( - await ImmutableBuffer.fromUint8List(initialImage)); + await ImmutableBuffer.fromUint8List(initialImage), + ); final defaultSize = Size(imageData.width.toDouble(), imageData.height.toDouble()); @@ -93,7 +94,9 @@ class PhotoEditStateNotifier extends StateNotifier { } Future _drawImage( - PhotoEdit attemptState, bool isReactionMarge) async { + PhotoEdit attemptState, + bool isReactionMarge, + ) async { final initialImage = state.initialImage; if (initialImage == null) return null; final editorOption = ImageEditorOption(); @@ -104,18 +107,27 @@ class PhotoEditStateNotifier extends StateNotifier { if (!attemptState.clipMode) { final offset = attemptState.cropOffset; final size = attemptState.cropSize; - editorOption.addOption(ClipOption( - x: offset.dx, y: offset.dy, width: size.width, height: size.height)); + editorOption.addOption( + ClipOption( + x: offset.dx, + y: offset.dy, + width: size.width, + height: size.height, + ), + ); } final presets = ColorFilterPresets().presets; for (final colorPreset in attemptState.adaptivePresets) { editorOption.addOptions( - presets.firstWhere((element) => element.name == colorPreset).option); + presets.firstWhere((element) => element.name == colorPreset).option, + ); } final result = await ImageEditor.editImage( - image: initialImage, imageEditorOption: editorOption); + image: initialImage, + imageEditorOption: editorOption, + ); return result; } @@ -139,22 +151,26 @@ class PhotoEditStateNotifier extends StateNotifier { final padding = 20 * state.defaultSize.width / state.actualSize.width; final removedPaddingImage = await ImageEditor.editImage( - image: resultImage, - imageEditorOption: ImageEditorOption() - ..addOptions([ - ClipOption( - x: padding + - (state.defaultSize.width - state.cropSize.width) / 2, - y: padding + - (state.defaultSize.height - state.cropSize.height) / 2, - width: state.cropSize.width, - height: state.cropSize.height) - ])); + image: resultImage, + imageEditorOption: ImageEditorOption() + ..addOptions([ + ClipOption( + x: padding + (state.defaultSize.width - state.cropSize.width) / 2, + y: padding + (state.defaultSize.height - state.cropSize.height) / 2, + width: state.cropSize.width, + height: state.cropSize.height, + ), + ]), + ); return removedPaddingImage; } Offset resolveRotatedOffset( - Offset cropOffset, Size cropSize, Size defaultSize, int angle) { + Offset cropOffset, + Size cropSize, + Size defaultSize, + int angle, + ) { final rotatedX = defaultSize.width - cropSize.width - cropOffset.dx; return Offset(cropOffset.dy, rotatedX); } @@ -162,30 +178,41 @@ class PhotoEditStateNotifier extends StateNotifier { /// 画像を回転する void rotate() { final angle = ((state.angle - 90) % 360).abs(); - draw(state.copyWith( - angle: angle, - defaultSize: Size(state.defaultSize.height, state.defaultSize.width), - cropSize: Size(state.cropSize.height, state.cropSize.width), - cropOffset: resolveRotatedOffset( - state.cropOffset, state.cropSize, state.defaultSize, angle), - emojis: [ - for (final emoji in state.emojis) - emoji.copyWith( + draw( + state.copyWith( + angle: angle, + defaultSize: Size(state.defaultSize.height, state.defaultSize.width), + cropSize: Size(state.cropSize.height, state.cropSize.width), + cropOffset: resolveRotatedOffset( + state.cropOffset, + state.cropSize, + state.defaultSize, + angle, + ), + emojis: [ + for (final emoji in state.emojis) + emoji.copyWith( angle: emoji.angle - pi / 2, - position: Offset(emoji.position.dy, - state.defaultSize.width - emoji.scale - emoji.position.dx)) - ], - selectedEmojiIndex: null, - )); + position: Offset( + emoji.position.dy, + state.defaultSize.width - emoji.scale - emoji.position.dx, + ), + ), + ], + selectedEmojiIndex: null, + ), + ); } /// 画像を切り取る void crop() { - draw(state.copyWith( - clipMode: !state.clipMode, - colorFilterMode: false, - selectedEmojiIndex: null, - )); + draw( + state.copyWith( + clipMode: !state.clipMode, + colorFilterMode: false, + selectedEmojiIndex: null, + ), + ); } void decideDrawArea(Size size) { @@ -198,10 +225,13 @@ class PhotoEditStateNotifier extends StateNotifier { final defaultSize = state.defaultSize; final offset = state.cropOffset; final validatedCropSize = Size( - max(0, min(size.width, defaultSize.width - offset.dx + 1)), - max(0, min(size.height, defaultSize.height - offset.dy + 1))); - final validateOffset = Offset(max(0, min(offset.dx, defaultSize.width)), - max(0, min(offset.dy, defaultSize.height))); + max(0, min(size.width, defaultSize.width - offset.dx + 1)), + max(0, min(size.height, defaultSize.height - offset.dy + 1)), + ); + final validateOffset = Offset( + max(0, min(offset.dx, defaultSize.width)), + max(0, min(offset.dy, defaultSize.height)), + ); return state.copyWith( cropSize: validatedCropSize, @@ -211,36 +241,60 @@ class PhotoEditStateNotifier extends StateNotifier { /// 画像の切り取り範囲の左上を移動する void cropMoveLeftTop(PointerMoveEvent detail) { - state = validateCrop(state.copyWith( + state = validateCrop( + state.copyWith( cropOffset: state.cropOffset + detail.localDelta, - cropSize: Size(state.cropSize.width - detail.localDelta.dx, - state.cropSize.height - detail.localDelta.dy))); + cropSize: Size( + state.cropSize.width - detail.localDelta.dx, + state.cropSize.height - detail.localDelta.dy, + ), + ), + ); } /// 画像の切り取り範囲の右上を移動する void cropMoveRightTop(PointerMoveEvent detail) { - state = validateCrop(state.copyWith( + state = validateCrop( + state.copyWith( cropOffset: Offset( - state.cropOffset.dx, state.cropOffset.dy + detail.localDelta.dy), - cropSize: Size(state.cropSize.width + detail.localDelta.dx, - state.cropSize.height - detail.localDelta.dy))); + state.cropOffset.dx, + state.cropOffset.dy + detail.localDelta.dy, + ), + cropSize: Size( + state.cropSize.width + detail.localDelta.dx, + state.cropSize.height - detail.localDelta.dy, + ), + ), + ); } /// 画像の切り取り範囲の左下を移動する void cropMoveLeftBottom(PointerMoveEvent detail) { - state = validateCrop(state.copyWith( + state = validateCrop( + state.copyWith( cropOffset: Offset( - state.cropOffset.dx + detail.localDelta.dx, state.cropOffset.dy), - cropSize: Size(state.cropSize.width - detail.localDelta.dx, - state.cropSize.height + detail.localDelta.dy))); + state.cropOffset.dx + detail.localDelta.dx, + state.cropOffset.dy, + ), + cropSize: Size( + state.cropSize.width - detail.localDelta.dx, + state.cropSize.height + detail.localDelta.dy, + ), + ), + ); } /// 画像の切り取り範囲の右下を移動する void cropMoveRightBottom(PointerMoveEvent detail) { - state = validateCrop(state.copyWith( + state = validateCrop( + state.copyWith( cropOffset: Offset(state.cropOffset.dx, state.cropOffset.dy), - cropSize: Size(state.cropSize.width + detail.localDelta.dx, - state.cropSize.height + detail.localDelta.dy))); + cropSize: Size( + state.cropSize.width + detail.localDelta.dx, + state.cropSize.height + detail.localDelta.dy, + ), + ), + ); } /// 画像の色調補正のプレビューを切り替える @@ -258,18 +312,20 @@ class PhotoEditStateNotifier extends StateNotifier { final editedImage = state.editedImage; if (editedImage == null) return; final previewImage = await ImageEditor.editImage( - image: editedImage, - imageEditorOption: ImageEditorOption() - ..addOption(const ScaleOption(300, 300, keepRatio: true))); + image: editedImage, + imageEditorOption: ImageEditorOption() + ..addOption(const ScaleOption(300, 300, keepRatio: true)), + ); if (previewImage == null) return; final result = [ for (final preset in ColorFilterPresets().presets) ColorFilterPreview( - name: preset.name, - image: await ImageEditor.editImage( - image: previewImage, - imageEditorOption: ImageEditorOption() - ..addOptions(preset.option))) + name: preset.name, + image: await ImageEditor.editImage( + image: previewImage, + imageEditorOption: ImageEditorOption()..addOptions(preset.option), + ), + ), ].whereNotNull(); state = state.copyWith(colorFilterPreviewImages: result.toList()); @@ -283,7 +339,8 @@ class PhotoEditStateNotifier extends StateNotifier { await draw(state.copyWith(adaptivePresets: list)); } else { await draw( - state.copyWith(adaptivePresets: [...state.adaptivePresets, name])); + state.copyWith(adaptivePresets: [...state.adaptivePresets, name]), + ); } await createPreviewImage(); } @@ -291,9 +348,10 @@ class PhotoEditStateNotifier extends StateNotifier { /// リアクションを追加する Future addReaction(Account account, BuildContext context) async { final reaction = await showDialog( - context: context, - builder: (context) => - ReactionPickerDialog(account: account, isAcceptSensitive: true)); + context: context, + builder: (context) => + ReactionPickerDialog(account: account, isAcceptSensitive: true), + ); if (reaction == null) return; switch (reaction) { @@ -302,9 +360,12 @@ class PhotoEditStateNotifier extends StateNotifier { if (_acceptReactions.none((e) => e == reaction.baseName)) { if (!mounted) return; final dialogResult = await showDialog( - context: context, - builder: (context) => LicenseConfirmDialog( - emoji: reaction.baseName, account: account)); + context: context, + builder: (context) => LicenseConfirmDialog( + emoji: reaction.baseName, + account: account, + ), + ); if (dialogResult != true) return; _acceptReactions.add(reaction.baseName); } @@ -316,16 +377,22 @@ class PhotoEditStateNotifier extends StateNotifier { return; } - state = state.copyWith(emojis: [ - ...state.emojis, - EditedEmojiData( + state = state.copyWith( + emojis: [ + ...state.emojis, + EditedEmojiData( emoji: reaction, scale: 100, position: Offset( - state.cropOffset.dx + (state.cropSize.width) / 2 - 50, - state.cropOffset.dy + (state.cropSize.height) / 2 - 50), - angle: 0), - ], selectedEmojiIndex: state.emojis.length, clipMode: false); + state.cropOffset.dx + (state.cropSize.width) / 2 - 50, + state.cropOffset.dy + (state.cropSize.height) / 2 - 50, + ), + angle: 0, + ), + ], + selectedEmojiIndex: state.emojis.length, + clipMode: false, + ); } double _startScale = 0; @@ -345,8 +412,9 @@ class PhotoEditStateNotifier extends StateNotifier { if (selectedIndex == null) return; final emojis = state.emojis.toList(); emojis[selectedIndex] = emojis[selectedIndex].copyWith( - scale: _startScale * detail.scale, - angle: _startAngle + detail.rotation); + scale: _startScale * detail.scale, + angle: _startAngle + detail.rotation, + ); state = state.copyWith(emojis: emojis, clipMode: false); } @@ -357,9 +425,11 @@ class PhotoEditStateNotifier extends StateNotifier { if (selectedIndex == null) return; final emojis = state.emojis.toList(); emojis[selectedIndex] = emojis[selectedIndex].copyWith( - position: Offset( - emojis[selectedIndex].position.dx + event.localDelta.dx, - emojis[selectedIndex].position.dy + event.localDelta.dy)); + position: Offset( + emojis[selectedIndex].position.dx + event.localDelta.dx, + emojis[selectedIndex].position.dy + event.localDelta.dy, + ), + ); state = state.copyWith(emojis: emojis); } @@ -367,11 +437,19 @@ class PhotoEditStateNotifier extends StateNotifier { /// リアクションを選択する void selectReaction(int index) { state = state.copyWith( - clipMode: false, colorFilterMode: false, selectedEmojiIndex: index); + clipMode: false, + colorFilterMode: false, + selectedEmojiIndex: index, + ); } void clearSelectMode() { - draw(state.copyWith( - selectedEmojiIndex: null, colorFilterMode: false, clipMode: false)); + draw( + state.copyWith( + selectedEmojiIndex: null, + colorFilterMode: false, + clipMode: false, + ), + ); } } From a19869349fd878beee0536931a30501454851e5e Mon Sep 17 00:00:00 2001 From: poppingmoon <63451158+poppingmoon@users.noreply.github.com> Date: Sat, 2 Sep 2023 16:48:40 +0900 Subject: [PATCH 07/12] =?UTF-8?q?=E4=BD=BF=E3=82=8F=E3=82=8C=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=81=AA=E3=81=84=E5=BC=95=E6=95=B0=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/providers.dart | 5 +---- .../photo_edit_state_notifier.dart | 17 ++++------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/lib/providers.dart b/lib/providers.dart index 1a526b112..d12721829 100644 --- a/lib/providers.dart +++ b/lib/providers.dart @@ -161,10 +161,7 @@ final errorEventProvider = final photoEditProvider = StateNotifierProvider.autoDispose( - (ref) => PhotoEditStateNotifier( - const PhotoEdit(), - ref.read(dioProvider), - ), + (ref) => PhotoEditStateNotifier(const PhotoEdit()), ); final importExportRepository = diff --git a/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart b/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart index 7a92f0501..ec5931bfb 100644 --- a/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart +++ b/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart @@ -3,8 +3,6 @@ import 'dart:typed_data'; import 'dart:ui'; import 'package:collection/collection.dart'; -import 'package:dio/dio.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -56,13 +54,9 @@ class EditedEmojiData with _$EditedEmojiData { } class PhotoEditStateNotifier extends StateNotifier { - static List _acceptReactions = []; + static final List _acceptReactions = []; - final Dio dio; - PhotoEditStateNotifier( - super.state, - this.dio, - ); + PhotoEditStateNotifier(super.state); /// 状態を初期化する Future initialize(MisskeyPostFile file) async { @@ -93,10 +87,7 @@ class PhotoEditStateNotifier extends StateNotifier { ); } - Future _drawImage( - PhotoEdit attemptState, - bool isReactionMarge, - ) async { + Future _drawImage(PhotoEdit attemptState) async { final initialImage = state.initialImage; if (initialImage == null) return null; final editorOption = ImageEditorOption(); @@ -133,7 +124,7 @@ class PhotoEditStateNotifier extends StateNotifier { /// 画像の描画を反映する Future draw(PhotoEdit attemptState) async { - final editedImage = await _drawImage(attemptState, false); + final editedImage = await _drawImage(attemptState); if (editedImage == null) return; state = attemptState.copyWith(editedImage: editedImage); } From b18f555f0cfe4d8f5015a02c68420921ecae7121 Mon Sep 17 00:00:00 2001 From: poppingmoon <63451158+poppingmoon@users.noreply.github.com> Date: Sat, 2 Sep 2023 16:51:27 +0900 Subject: [PATCH 08/12] =?UTF-8?q?=E3=82=AF=E3=83=AD=E3=83=83=E3=83=97?= =?UTF-8?q?=E6=99=82=E3=81=AB=E5=85=83=E3=81=AE=E7=94=BB=E5=83=8F=E3=81=AE?= =?UTF-8?q?=E3=82=B5=E3=82=A4=E3=82=BA=E3=82=92=E8=B6=85=E3=81=88=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../photo_edit_page/photo_edit_state_notifier.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart b/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart index ec5931bfb..989144cb5 100644 --- a/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart +++ b/lib/state_notifier/photo_edit_page/photo_edit_state_notifier.dart @@ -215,18 +215,18 @@ class PhotoEditStateNotifier extends StateNotifier { final size = state.cropSize; final defaultSize = state.defaultSize; final offset = state.cropOffset; - final validatedCropSize = Size( - max(0, min(size.width, defaultSize.width - offset.dx + 1)), - max(0, min(size.height, defaultSize.height - offset.dy + 1)), - ); - final validateOffset = Offset( + final validatedOffset = Offset( max(0, min(offset.dx, defaultSize.width)), max(0, min(offset.dy, defaultSize.height)), ); + final validatedCropSize = Size( + max(1, min(size.width, defaultSize.width - validatedOffset.dx)), + max(1, min(size.height, defaultSize.height - validatedOffset.dy)), + ); return state.copyWith( cropSize: validatedCropSize, - cropOffset: validateOffset, + cropOffset: validatedOffset, ); } From 87ab88dcaa137bfb40c639d23909d1d8cd5b027c Mon Sep 17 00:00:00 2001 From: poppingmoon <63451158+poppingmoon@users.noreply.github.com> Date: Sun, 3 Sep 2023 20:18:18 +0900 Subject: [PATCH 09/12] =?UTF-8?q?=E6=A4=9C=E7=B4=A2=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E3=81=A7=E3=82=BF=E3=83=96=E5=A4=89=E6=9B=B4=E6=99=82=E3=81=AB?= =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=BC=E3=82=AB=E3=82=B9=E3=82=92=E5=88=87?= =?UTF-8?q?=E3=82=8A=E6=9B=BF=E3=81=88=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/view/search_page/note_search.dart | 8 ++- lib/view/search_page/search_page.dart | 82 ++++++++++++++++++++------- lib/view/user_select_dialog.dart | 9 ++- 3 files changed, 77 insertions(+), 22 deletions(-) diff --git a/lib/view/search_page/note_search.dart b/lib/view/search_page/note_search.dart index 9bf7a5c93..9e1108605 100644 --- a/lib/view/search_page/note_search.dart +++ b/lib/view/search_page/note_search.dart @@ -13,8 +13,13 @@ import 'package:misskey_dart/misskey_dart.dart'; class NoteSearch extends ConsumerStatefulWidget { final String? initialSearchText; + final FocusNode? focusNode; - const NoteSearch({super.key, required this.initialSearchText}); + const NoteSearch({ + super.key, + required this.initialSearchText, + this.focusNode, + }); @override ConsumerState createState() => NoteSearchState(); @@ -52,6 +57,7 @@ class NoteSearchState extends ConsumerState { decoration: const InputDecoration( prefixIcon: Icon(Icons.search), ), + focusNode: widget.focusNode, autofocus: true, textInputAction: TextInputAction.done, onSubmitted: (value) { diff --git a/lib/view/search_page/search_page.dart b/lib/view/search_page/search_page.dart index 72ff57289..652deaf3e 100644 --- a/lib/view/search_page/search_page.dart +++ b/lib/view/search_page/search_page.dart @@ -31,34 +31,76 @@ class SearchPage extends ConsumerStatefulWidget { } class NoteSearchPageState extends ConsumerState { + late final List focusNodes; + int tabIndex = 0; + + @override + void initState() { + super.initState(); + focusNodes = [FocusNode(), FocusNode()]; + } + + @override + void dispose() { + for (final focusNode in focusNodes) { + focusNode.dispose(); + } + super.dispose(); + } + @override Widget build(BuildContext context) { return DefaultTabController( length: 2, child: AccountScope( - account: widget.account, - child: Scaffold( - appBar: AppBar( - title: const Text("検索"), - bottom: const TabBar( - tabs: [ - Tab(text: "ノート"), - Tab(text: "ユーザー"), - ], - ), - ), - body: TabBarView( + account: widget.account, + child: Scaffold( + appBar: AppBar( + title: const Text("検索"), + bottom: const TabBar( + tabs: [ + Tab(text: "ノート"), + Tab(text: "ユーザー"), + ], + ), + ), + body: Builder( + builder: (context) { + final tabController = DefaultTabController.of(context); + tabController.addListener(() { + if (tabController.index != tabIndex) { + focusNodes[tabController.index].requestFocus(); + setState(() { + tabIndex = tabController.index; + }); + } + }); + return TabBarView( + controller: tabController, children: [ - NoteSearch(initialSearchText: widget.initialSearchText), + NoteSearch( + initialSearchText: widget.initialSearchText, + focusNode: focusNodes[0], + ), Padding( - padding: - const EdgeInsets.only(left: 10, right: 10, top: 10), - child: UserSelectContent( - onSelected: (item) => context.pushRoute(UserRoute( - userId: item.id, account: widget.account)), - )) + padding: + const EdgeInsets.only(left: 10, right: 10, top: 10), + child: UserSelectContent( + focusNode: focusNodes[1], + onSelected: (item) => context.pushRoute( + UserRoute( + userId: item.id, + account: widget.account, + ), + ), + ), + ), ], - ))), + ); + }, + ), + ), + ), ); } } diff --git a/lib/view/user_select_dialog.dart b/lib/view/user_select_dialog.dart index 8cbeeb570..fff251c83 100644 --- a/lib/view/user_select_dialog.dart +++ b/lib/view/user_select_dialog.dart @@ -31,7 +31,13 @@ class UserSelectDialog extends StatelessWidget { class UserSelectContent extends ConsumerStatefulWidget { final void Function(User) onSelected; - const UserSelectContent({super.key, required this.onSelected}); + final FocusNode? focusNode; + + const UserSelectContent({ + super.key, + required this.onSelected, + this.focusNode, + }); @override ConsumerState createState() => @@ -55,6 +61,7 @@ class UserSelectContentState extends ConsumerState { children: [ TextField( controller: queryController, + focusNode: widget.focusNode, autofocus: true, decoration: const InputDecoration(prefixIcon: Icon(Icons.search)), onSubmitted: (value) { From 7726c7f5e78f0fcad6b596c542ae2de1818f1e95 Mon Sep 17 00:00:00 2001 From: poppingmoon <63451158+poppingmoon@users.noreply.github.com> Date: Sun, 3 Sep 2023 20:48:28 +0900 Subject: [PATCH 10/12] =?UTF-8?q?TextEditingController=E3=82=92dispose?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/state_notifier/photo_edit_page/image_meta_dialog.dart | 7 +++++++ lib/view/dialogs/note_detail_dialog.dart | 6 ++++++ lib/view/login_page/api_key_login.dart | 7 +++++++ lib/view/login_page/mi_auth_login.dart | 6 ++++++ lib/view/login_page/password_login.dart | 8 ++++++++ lib/view/note_create_page/cw_text_area.dart | 6 ++++++ lib/view/note_create_page/file_settings_dialog.dart | 6 ++++++ lib/view/note_create_page/vote_area.dart | 6 ++++++ lib/view/search_page/note_search.dart | 6 ++++++ .../tab_settings_page/tab_settings_page.dart | 6 ++++++ .../hard_mute_page/hard_mute_page.dart | 6 ++++++ lib/view/time_line_page/timeline_note.dart | 8 +++++--- lib/view/user_page/update_memo_dialog.dart | 6 ++++++ lib/view/user_select_dialog.dart | 6 ++++++ 14 files changed, 87 insertions(+), 3 deletions(-) diff --git a/lib/state_notifier/photo_edit_page/image_meta_dialog.dart b/lib/state_notifier/photo_edit_page/image_meta_dialog.dart index e57ac5837..b93adb29f 100644 --- a/lib/state_notifier/photo_edit_page/image_meta_dialog.dart +++ b/lib/state_notifier/photo_edit_page/image_meta_dialog.dart @@ -29,6 +29,13 @@ class ImageMetaDialogState extends ConsumerState { late final TextEditingController captionController = TextEditingController() ..text = widget.initialMeta.caption; + @override + void dispose() { + fileNameController.dispose(); + captionController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return AlertDialog( diff --git a/lib/view/dialogs/note_detail_dialog.dart b/lib/view/dialogs/note_detail_dialog.dart index 9dbcce9b6..57351fee7 100644 --- a/lib/view/dialogs/note_detail_dialog.dart +++ b/lib/view/dialogs/note_detail_dialog.dart @@ -52,6 +52,12 @@ class NoteDetailDialogState extends ConsumerState { }); } + @override + void dispose() { + reactionTextField.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return AlertDialog( diff --git a/lib/view/login_page/api_key_login.dart b/lib/view/login_page/api_key_login.dart index 40c43e016..9dc6f31ba 100644 --- a/lib/view/login_page/api_key_login.dart +++ b/lib/view/login_page/api_key_login.dart @@ -19,6 +19,13 @@ class APiKeyLoginState extends ConsumerState { final serverController = TextEditingController(); final apiKeyController = TextEditingController(); + @override + void dispose() { + serverController.dispose(); + apiKeyController.dispose(); + super.dispose(); + } + Future login() async { try { IndicatorView.showIndicator(context); diff --git a/lib/view/login_page/mi_auth_login.dart b/lib/view/login_page/mi_auth_login.dart index 07346aa0c..6415b3d50 100644 --- a/lib/view/login_page/mi_auth_login.dart +++ b/lib/view/login_page/mi_auth_login.dart @@ -19,6 +19,12 @@ class MiAuthLoginState extends ConsumerState { final serverController = TextEditingController(); bool isAuthed = false; + @override + void dispose() { + serverController.dispose(); + super.dispose(); + } + Future login() async { try { IndicatorView.showIndicator(context); diff --git a/lib/view/login_page/password_login.dart b/lib/view/login_page/password_login.dart index 48cab8937..d3ca3c74b 100644 --- a/lib/view/login_page/password_login.dart +++ b/lib/view/login_page/password_login.dart @@ -17,6 +17,14 @@ class PasswordLoginState extends ConsumerState { final userController = TextEditingController(); final passwordController = TextEditingController(); + @override + void dispose() { + serverController.dispose(); + userController.dispose(); + passwordController.dispose(); + super.dispose(); + } + Future login() async { await ref.read(accountRepository).loginAsPassword( serverController.text, userController.text, passwordController.text); diff --git a/lib/view/note_create_page/cw_text_area.dart b/lib/view/note_create_page/cw_text_area.dart index c4e021e78..02e9c7527 100644 --- a/lib/view/note_create_page/cw_text_area.dart +++ b/lib/view/note_create_page/cw_text_area.dart @@ -25,6 +25,12 @@ class CwTextAreaState extends ConsumerState { }); } + @override + void dispose() { + cwController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { ref.listen( diff --git a/lib/view/note_create_page/file_settings_dialog.dart b/lib/view/note_create_page/file_settings_dialog.dart index 530f2ccb3..d43a10d1d 100644 --- a/lib/view/note_create_page/file_settings_dialog.dart +++ b/lib/view/note_create_page/file_settings_dialog.dart @@ -35,6 +35,12 @@ class FileSettingsDialogState extends ConsumerState { isNsfw = widget.file.isNsfw; } + @override + void dispose() { + captionController.dispose(); + super.dispose(); + } + String generateRandomText() { var str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" .split(""); diff --git a/lib/view/note_create_page/vote_area.dart b/lib/view/note_create_page/vote_area.dart index 94d363f65..4830729cb 100644 --- a/lib/view/note_create_page/vote_area.dart +++ b/lib/view/note_create_page/vote_area.dart @@ -112,6 +112,12 @@ class VoteContentListItemState extends ConsumerState { }); } + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Padding( diff --git a/lib/view/search_page/note_search.dart b/lib/view/search_page/note_search.dart index 9bf7a5c93..1bca1837a 100644 --- a/lib/view/search_page/note_search.dart +++ b/lib/view/search_page/note_search.dart @@ -36,6 +36,12 @@ class NoteSearchState extends ConsumerState { } } + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final selectedUser = ref.watch(noteSearchUserProvider); diff --git a/lib/view/settings_page/tab_settings_page/tab_settings_page.dart b/lib/view/settings_page/tab_settings_page/tab_settings_page.dart index f0c56c0ae..4bf5fcf73 100644 --- a/lib/view/settings_page/tab_settings_page/tab_settings_page.dart +++ b/lib/view/settings_page/tab_settings_page/tab_settings_page.dart @@ -85,6 +85,12 @@ class TabSettingsAddDialogState extends ConsumerState { } } + @override + void dispose() { + nameController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/view/several_account_settings_page/hard_mute_page/hard_mute_page.dart b/lib/view/several_account_settings_page/hard_mute_page/hard_mute_page.dart index 2d5376d2d..f46cd0be9 100644 --- a/lib/view/several_account_settings_page/hard_mute_page/hard_mute_page.dart +++ b/lib/view/several_account_settings_page/hard_mute_page/hard_mute_page.dart @@ -20,6 +20,12 @@ class HardMutePage extends ConsumerStatefulWidget { class HardMutePageState extends ConsumerState { final controller = TextEditingController(); + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + String muteValueString(List? wordMutes) { if (wordMutes == null) return ""; diff --git a/lib/view/time_line_page/timeline_note.dart b/lib/view/time_line_page/timeline_note.dart index 205ae9185..6af0cc4e9 100644 --- a/lib/view/time_line_page/timeline_note.dart +++ b/lib/view/time_line_page/timeline_note.dart @@ -2,7 +2,9 @@ import 'package:flutter/material.dart'; import 'package:miria/view/themes/app_theme.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -final timelineNoteProvider = Provider((ref) => TextEditingController()); +final timelineNoteProvider = + ChangeNotifierProvider.autoDispose((ref) => TextEditingController()); + final timelineFocusNode = ChangeNotifierProvider.autoDispose((ref) => FocusNode()); @@ -26,10 +28,10 @@ class TimelineNoteFieldState extends ConsumerState { return Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0), child: TextField( - focusNode: ref.read(timelineFocusNode), + focusNode: ref.watch(timelineFocusNode), keyboardType: TextInputType.multiline, maxLines: null, - controller: ref.read(timelineNoteProvider), + controller: ref.watch(timelineNoteProvider), decoration: noteStyle, ), ); diff --git a/lib/view/user_page/update_memo_dialog.dart b/lib/view/user_page/update_memo_dialog.dart index cdb8b0c13..30d4b8254 100644 --- a/lib/view/user_page/update_memo_dialog.dart +++ b/lib/view/user_page/update_memo_dialog.dart @@ -38,6 +38,12 @@ class UpdateMemoDialogState extends ConsumerState { controller.text = widget.initialMemo; } + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return AlertDialog( diff --git a/lib/view/user_select_dialog.dart b/lib/view/user_select_dialog.dart index 8cbeeb570..307230dab 100644 --- a/lib/view/user_select_dialog.dart +++ b/lib/view/user_select_dialog.dart @@ -45,6 +45,12 @@ final usersSelectDialogOriginProvider = class UserSelectContentState extends ConsumerState { final queryController = TextEditingController(); + @override + void dispose() { + queryController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final origin = ref.watch(usersSelectDialogOriginProvider); From 1acc2e9b89755120d50fcd7d364beadb957824ee Mon Sep 17 00:00:00 2001 From: NPL <66727014+Npepperlinux@users.noreply.github.com> Date: Mon, 4 Sep 2023 21:23:28 +0900 Subject: [PATCH 11/12] Update app_theme_scope.dart MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LinuxでNoto Sans CJK JPを使用するように修正 --- lib/view/themes/app_theme_scope.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/view/themes/app_theme_scope.dart b/lib/view/themes/app_theme_scope.dart index dd634f301..f7af6c359 100644 --- a/lib/view/themes/app_theme_scope.dart +++ b/lib/view/themes/app_theme_scope.dart @@ -60,6 +60,9 @@ class AppThemeScopeState extends ConsumerState { defaultTargetPlatform == TargetPlatform.macOS) { return "SF Pro Text"; } + if (defaultTargetPlatform == TargetPlatform.linux) { + return "Noto Sans CJK JP"; + } return "KosugiMaru"; } From af6bcf156f801b2788fbee8f507afb25ae682ddc Mon Sep 17 00:00:00 2001 From: sorairo Date: Sat, 9 Sep 2023 11:45:24 +0900 Subject: [PATCH 12/12] =?UTF-8?q?#269=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/providers.dart | 8 +- lib/repository/account_repository.dart | 24 +++- lib/repository/main_stream_repository.dart | 15 +- lib/router/app_router.dart | 2 + lib/router/app_router.gr.dart | 48 +++++++ .../announcements_page.dart | 26 ++++ .../federation_announcements.dart | 132 ++++++++++++++---- lib/view/time_line_page/time_line_page.dart | 89 ++++++++++-- pubspec.lock | 2 +- test/test_util/mock.mocks.dart | 102 +++++++++----- 10 files changed, 368 insertions(+), 80 deletions(-) create mode 100644 lib/view/announcements_page/announcements_page.dart diff --git a/lib/providers.dart b/lib/providers.dart index 1a526b112..eb8482f4a 100644 --- a/lib/providers.dart +++ b/lib/providers.dart @@ -121,10 +121,10 @@ final antennaTimelineProvider = final mainStreamRepositoryProvider = ChangeNotifierProvider.family( (ref, account) => MainStreamRepository( - ref.read(misskeyProvider(account)), - ref.read(emojiRepositoryProvider(account)), - account, - )); + ref.read(misskeyProvider(account)), + ref.read(emojiRepositoryProvider(account)), + account, + ref.read(accountRepository))); final favoriteProvider = ChangeNotifierProvider.autoDispose .family((ref, account) => FavoriteRepository( diff --git a/lib/repository/account_repository.dart b/lib/repository/account_repository.dart index 0df88f47f..6f045ef69 100644 --- a/lib/repository/account_repository.dart +++ b/lib/repository/account_repository.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -13,7 +12,6 @@ import 'package:miria/providers.dart'; import 'package:miria/repository/account_settings_repository.dart'; import 'package:miria/repository/tab_settings_repository.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:miria/view/common/constants.dart'; import 'package:miria/view/common/error_dialog_handler.dart'; import 'package:misskey_dart/misskey_dart.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -68,6 +66,28 @@ class AccountRepository extends ChangeNotifier { notifyListeners(); } + Future createUnreadAnnouncement( + Account account, AnnouncementsResponse announcement) async { + final i = _account[_account.indexOf(account)].i.copyWith( + unreadAnnouncements: [ + ..._account[_account.indexOf(account)].i.unreadAnnouncements, + announcement + ]); + _account[_account.indexOf(account)] = + _account[_account.indexOf(account)].copyWith(i: i); + tabSettingsRepository.updateAccount(account, i); + notifyListeners(); + } + + Future removeUnreadAnnouncement(Account account) async { + final i = + _account[_account.indexOf(account)].i.copyWith(unreadAnnouncements: []); + _account[_account.indexOf(account)] = + _account[_account.indexOf(account)].copyWith(i: i); + tabSettingsRepository.updateAccount(account, i); + notifyListeners(); + } + Future _addIfTabSettingNothing() async { if (_account.length == 1) { final account = _account.first; diff --git a/lib/repository/main_stream_repository.dart b/lib/repository/main_stream_repository.dart index 030dbfc0c..244e05a8d 100644 --- a/lib/repository/main_stream_repository.dart +++ b/lib/repository/main_stream_repository.dart @@ -1,6 +1,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; import 'package:miria/model/account.dart'; +import 'package:miria/repository/account_repository.dart'; import 'package:miria/repository/emoji_repository.dart'; import 'package:misskey_dart/misskey_dart.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -11,9 +12,15 @@ class MainStreamRepository extends ChangeNotifier { final Misskey misskey; final EmojiRepository emojiRepository; final Account account; + final AccountRepository accountRepository; SocketController? socketController; - MainStreamRepository(this.misskey, this.emojiRepository, this.account); + MainStreamRepository( + this.misskey, + this.emojiRepository, + this.account, + this.accountRepository, + ); Future latestMarkAs(String id) async { final prefs = await SharedPreferences.getInstance(); @@ -60,12 +67,18 @@ class MainStreamRepository extends ChangeNotifier { hasUnreadNotification = true; notifyListeners(); }, + onReadAllAnnouncements: () { + accountRepository.removeUnreadAnnouncement(account); + }, onEmojiAdded: (_) { emojiRepository.loadFromSource(); }, onEmojiUpdated: (_) { emojiRepository.loadFromSource(); }, + onAnnouncementCreated: (announcement) { + accountRepository.createUnreadAnnouncement(account, announcement); + }, ); misskey.startStreaming(); Future(() async { diff --git a/lib/router/app_router.dart b/lib/router/app_router.dart index c91722092..6eaab8b04 100644 --- a/lib/router/app_router.dart +++ b/lib/router/app_router.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:miria/model/account.dart'; import 'package:miria/model/image_file.dart'; import 'package:miria/model/tab_setting.dart'; +import 'package:miria/view/announcements_page/announcements_page.dart'; import 'package:miria/view/antenna_page/antenna_page.dart'; import 'package:miria/view/channels_page/channels_page.dart'; import 'package:miria/view/clip_list_page/clip_detail_page.dart'; @@ -76,6 +77,7 @@ class AppRouter extends _$AppRouter { AutoRoute(page: ExploreRoleUsersRoute.page), AutoRoute(page: SearchRoute.page), AutoRoute(page: FederationRoute.page), + AutoRoute(page: AnnouncementRoute.page), AutoRoute(page: LoginRoute.page), AutoRoute(page: SettingsRoute.page), AutoRoute(page: GeneralSettingsRoute.page), diff --git a/lib/router/app_router.gr.dart b/lib/router/app_router.gr.dart index 872b5a2a8..330c2f00e 100644 --- a/lib/router/app_router.gr.dart +++ b/lib/router/app_router.gr.dart @@ -387,6 +387,16 @@ abstract class _$AppRouter extends RootStackRouter { ), ); }, + AnnouncementRoute.name: (routeData) { + final args = routeData.argsAs(); + return AutoRoutePage( + routeData: routeData, + child: AnnouncementPage( + key: args.key, + account: args.account, + ), + ); + }, }; } @@ -1752,3 +1762,41 @@ class FavoritedNoteRouteArgs { return 'FavoritedNoteRouteArgs{key: $key, account: $account}'; } } + +/// generated route for +/// [AnnouncementPage] +class AnnouncementRoute extends PageRouteInfo { + AnnouncementRoute({ + Key? key, + required Account account, + List? children, + }) : super( + AnnouncementRoute.name, + args: AnnouncementRouteArgs( + key: key, + account: account, + ), + initialChildren: children, + ); + + static const String name = 'AnnouncementRoute'; + + static const PageInfo page = + PageInfo(name); +} + +class AnnouncementRouteArgs { + const AnnouncementRouteArgs({ + this.key, + required this.account, + }); + + final Key? key; + + final Account account; + + @override + String toString() { + return 'AnnouncementRouteArgs{key: $key, account: $account}'; + } +} diff --git a/lib/view/announcements_page/announcements_page.dart b/lib/view/announcements_page/announcements_page.dart new file mode 100644 index 000000000..266ce69d8 --- /dev/null +++ b/lib/view/announcements_page/announcements_page.dart @@ -0,0 +1,26 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:miria/model/account.dart'; +import 'package:miria/view/common/account_scope.dart'; +import 'package:miria/view/federation_page/federation_announcements.dart'; + +@RoutePage() +class AnnouncementPage extends StatelessWidget { + final Account account; + + const AnnouncementPage({super.key, required this.account}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("お知らせ")), + body: Padding( + padding: const EdgeInsets.only(left: 10, right: 10), + child: AccountScope( + account: account, + child: FederationAnnouncements(host: account.host), + ), + ), + ); + } +} diff --git a/lib/view/federation_page/federation_announcements.dart b/lib/view/federation_page/federation_announcements.dart index a238283da..54764369c 100644 --- a/lib/view/federation_page/federation_announcements.dart +++ b/lib/view/federation_page/federation_announcements.dart @@ -5,9 +5,10 @@ import 'package:miria/providers.dart'; import 'package:miria/view/common/account_scope.dart'; import 'package:miria/view/common/misskey_notes/mfm_text.dart'; import 'package:miria/view/common/pushable_listview.dart'; +import 'package:miria/view/dialogs/simple_confirm_dialog.dart'; import 'package:misskey_dart/misskey_dart.dart'; -class FederationAnnouncements extends ConsumerWidget { +class FederationAnnouncements extends ConsumerStatefulWidget { final String host; const FederationAnnouncements({ super.key, @@ -15,33 +16,92 @@ class FederationAnnouncements extends ConsumerWidget { }); @override - Widget build(BuildContext context, WidgetRef ref) { + ConsumerState createState() => + FederationAnnouncementsState(); +} + +class FederationAnnouncementsState + extends ConsumerState { + var isActive = true; + + @override + Widget build(BuildContext context) { final account = AccountScope.of(context); - final isCurrentServer = host == AccountScope.of(context).host; - return PushableListView( - initializeFuture: () async { - final Iterable response; - const request = AnnouncementsRequest(); - if (isCurrentServer) { - response = - await ref.read(misskeyProvider(account)).announcements(request); - } else { - response = await MisskeyServer().announcements(host, request); - } - return response.toList(); - }, - nextFuture: (lastItem, _) async { - final Iterable response; - final request = AnnouncementsRequest(untilId: lastItem.id); - if (isCurrentServer) { - response = - await ref.read(misskeyProvider(account)).announcements(request); - } else { - response = await MisskeyServer().announcements(host, request); - } - return response.toList(); - }, - itemBuilder: (context, data) => Announcement(data: data, host: host)); + final isCurrentServer = widget.host == AccountScope.of(context).host; + return Column( + children: [ + Row( + children: [ + Expanded( + child: Center( + child: ToggleButtons( + isSelected: [ + isActive, + !isActive, + ], + onPressed: (value) { + setState(() { + switch (value) { + case 0: + isActive = true; + case 1: + isActive = false; + } + }); + }, + children: const [ + Padding( + padding: EdgeInsets.only(left: 5, right: 5), + child: Text("いまの")), + Padding( + padding: EdgeInsets.only(left: 5, right: 5), + child: Text("前の")), + ], + ), + ), + ), + ], + ), + Expanded( + child: PushableListView( + listKey: isActive, + initializeFuture: () async { + final Iterable response; + final request = + AnnouncementsRequest(isActive: isActive, limit: 10); + if (isCurrentServer) { + response = await ref + .read(misskeyProvider(account)) + .announcements(request); + } else { + response = + await MisskeyServer().announcements(widget.host, request); + } + return response.toList(); + }, + nextFuture: (lastItem, offset) async { + final Iterable response; + // 互換性のためにuntilIdとoffsetを両方いれる + final request = AnnouncementsRequest( + untilId: lastItem.id, + isActive: isActive, + limit: 30, + offset: offset); + if (isCurrentServer) { + response = await ref + .read(misskeyProvider(account)) + .announcements(request); + } else { + response = + await MisskeyServer().announcements(widget.host, request); + } + return response.toList(); + }, + itemBuilder: (context, data) => + Announcement(data: data, host: widget.host)), + ), + ], + ); } } @@ -81,6 +141,12 @@ class AnnouncementState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ + if (data.forYou == true) + Text("あなた宛", + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Theme.of(context).primaryColor)), Row( children: [ if (icon != null) AnnouncementIcon(iconType: icon), @@ -107,8 +173,18 @@ class AnnouncementState extends ConsumerState { data.isRead == false) ElevatedButton( onPressed: () async { + final account = AccountScope.of(context); + if (data.needConfirmationToRead == true) { + final isConfirmed = await SimpleConfirmDialog.show( + context: context, + message: "「${data.title}」の内容ちゃんと読んだか?", + primary: "読んだ", + secondary: "もうちょい待って"); + if (isConfirmed != true) return; + } + await ref - .read(misskeyProvider(AccountScope.of(context))) + .read(misskeyProvider(account)) .i .readAnnouncement(IReadAnnouncementRequest( announcementId: data.id)); diff --git a/lib/view/time_line_page/time_line_page.dart b/lib/view/time_line_page/time_line_page.dart index edce72835..050086df9 100644 --- a/lib/view/time_line_page/time_line_page.dart +++ b/lib/view/time_line_page/time_line_page.dart @@ -35,7 +35,7 @@ class TimeLinePage extends ConsumerStatefulWidget { } class TimeLinePageState extends ConsumerState { - late final List tabSettings; + late List tabSettings; late int currentIndex; late final PageController pageController; late final List scrollControllers; @@ -44,6 +44,13 @@ class TimeLinePageState extends ConsumerState { @override void initState() { + tabSettings = ref.read( + tabSettingsRepositoryProvider.select((repo) => repo.tabSettings.toList()), + ); + currentIndex = tabSettings.indexOf(widget.initialTabSetting); + pageController = PageController(initialPage: currentIndex); + scrollControllers = + List.generate(tabSettings.length, (_) => TimelineScrollController()); super.initState(); } @@ -86,13 +93,6 @@ class TimeLinePageState extends ConsumerState { @override void didChangeDependencies() { super.didChangeDependencies(); - tabSettings = ref.watch( - tabSettingsRepositoryProvider.select((repo) => repo.tabSettings.toList()), - ); - currentIndex = tabSettings.indexOf(widget.initialTabSetting); - pageController = PageController(initialPage: currentIndex); - scrollControllers = - List.generate(tabSettings.length, (_) => TimelineScrollController()); } void reload() { @@ -198,6 +198,9 @@ class TimeLinePageState extends ConsumerState { final socketTimeline = socketTimelineBase is SocketTimelineRepository ? socketTimelineBase : null; + tabSettings = ref.watch( + tabSettingsRepositoryProvider.select((repo) => repo.tabSettings.toList()), + ); return Scaffold( key: scaffoldKey, @@ -259,7 +262,8 @@ class TimeLinePageState extends ConsumerState { TabType.localTimeline, TabType.globalTimeline, TabType.homeTimeline, - ].contains(currentTabSetting.tabType)) + ].contains(currentTabSetting.tabType)) ...[ + AnnoucementInfo(index: currentIndex), IconButton( onPressed: () { showDialog( @@ -271,6 +275,7 @@ class TimeLinePageState extends ConsumerState { }, icon: const Icon(Icons.smart_toy_outlined), ), + ], const Padding( padding: EdgeInsets.only(right: 5), ), @@ -286,7 +291,6 @@ class TimeLinePageState extends ConsumerState { ], ), ), - BannerArea(index: currentIndex), if (socketTimeline?.isLoading == true) const Padding( padding: EdgeInsets.only(top: 10), @@ -308,6 +312,7 @@ class TimeLinePageState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ + BannerArea(index: currentIndex), Expanded( child: MisskeyTimeline( controller: scrollControllers[index], @@ -389,7 +394,7 @@ class BannerArea extends ConsumerWidget { ?.where((element) => element.display == AnnouncementDisplayType.banner || element.display == AnnouncementDisplayType.dialog) - .firstOrNull; + .lastOrNull; if (bannerData == null) return const SizedBox.shrink(); @@ -415,6 +420,68 @@ class BannerArea extends ConsumerWidget { } } +class AnnoucementInfo extends ConsumerWidget { + final int index; + + const AnnoucementInfo({super.key, required this.index}); + + void announcementsRoute(BuildContext context, WidgetRef ref) { + final account = ref.read(accountRepository.select((value) => value.account + .where((element) => + element == + ref + .read(tabSettingsRepositoryProvider) + .tabSettings + .toList()[index] + .account) + .firstOrNull)); + if (account == null) return; + context.pushRoute(AnnouncementRoute(account: account)); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final hasUnread = ref.watch(accountRepository.select((value) => value + .account + .where((element) => + element == + ref + .read(tabSettingsRepositoryProvider) + .tabSettings + .toList()[index] + .account) + .firstOrNull + ?.i + .unreadAnnouncements + .isNotEmpty)); + + if (hasUnread == true) { + return IconButton( + onPressed: () => announcementsRoute(context, ref), + icon: Stack(children: [ + const Icon(Icons.campaign), + Transform.translate( + offset: const Offset(12, 12), + child: SizedBox( + width: 14, + height: 14, + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.white, width: 1.5), + borderRadius: BorderRadius.circular(20), + color: Theme.of(context).primaryColor, + ), + ), + )), + ])); + } else { + return IconButton( + onPressed: () => announcementsRoute(context, ref), + icon: const Icon(Icons.campaign)); + } + } +} + class AnnouncementIcon extends StatelessWidget { final AnnouncementIconType iconType; diff --git a/pubspec.lock b/pubspec.lock index 934610a0c..d5990b1e8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -790,7 +790,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "2913789026aad06322441d1fad0f5652738529ef" + resolved-ref: "619405e43957acfd873dc2febf3167580d8a70a9" url: "https://github.com/shiosyakeyakini-info/misskey_dart.git" source: git version: "1.0.0" diff --git a/test/test_util/mock.mocks.dart b/test/test_util/mock.mocks.dart index 5b9d81327..eabbdaa9b 100644 --- a/test/test_util/mock.mocks.dart +++ b/test/test_util/mock.mocks.dart @@ -4,18 +4,18 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i16; -import 'dart:io' as _i23; -import 'dart:typed_data' as _i24; +import 'dart:io' as _i24; +import 'dart:typed_data' as _i25; import 'dart:ui' as _i18; -import 'package:dio/dio.dart' as _i25; +import 'package:dio/dio.dart' as _i26; import 'package:dio/src/adapter.dart' as _i10; -import 'package:dio/src/cancel_token.dart' as _i26; +import 'package:dio/src/cancel_token.dart' as _i27; import 'package:dio/src/dio_mixin.dart' as _i12; import 'package:dio/src/options.dart' as _i9; import 'package:dio/src/response.dart' as _i13; import 'package:dio/src/transformer.dart' as _i11; -import 'package:file_picker/file_picker.dart' as _i28; +import 'package:file_picker/file_picker.dart' as _i29; import 'package:miria/model/account.dart' as _i17; import 'package:miria/model/account_settings.dart' as _i2; import 'package:miria/model/general_settings.dart' as _i3; @@ -26,13 +26,14 @@ import 'package:miria/repository/emoji_repository.dart' as _i20; import 'package:miria/repository/general_settings_repository.dart' as _i22; import 'package:miria/repository/tab_settings_repository.dart' as _i14; import 'package:misskey_dart/misskey_dart.dart' as _i6; +import 'package:misskey_dart/src/data/base/flash.dart' as _i23; import 'package:misskey_dart/src/data/ping_response.dart' as _i8; import 'package:misskey_dart/src/data/stats_response.dart' as _i7; import 'package:misskey_dart/src/services/api_service.dart' as _i4; import 'package:misskey_dart/src/services/streaming_service.dart' as _i5; import 'package:mockito/mockito.dart' as _i1; -import 'mock.dart' as _i27; +import 'mock.dart' as _i28; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -537,6 +538,21 @@ class MockTabSettingsRepository extends _i1.Mock returnValueForMissingStub: _i16.Future.value(), ) as _i16.Future); @override + void updateAccount( + _i17.Account? account, + _i6.IResponse? response, + ) => + super.noSuchMethod( + Invocation.method( + #updateAccount, + [ + account, + response, + ], + ), + returnValueForMissingStub: null, + ); + @override _i16.Future save(List<_i15.TabSetting>? tabSettings) => (super.noSuchMethod( Invocation.method( @@ -1895,6 +1911,8 @@ class MockMisskey extends _i1.Mock implements _i6.Misskey { _i16.FutureOr Function(_i6.Emoji)? onEmojiAdded, _i16.FutureOr Function(Iterable<_i6.Emoji>)? onEmojiUpdated, _i16.FutureOr Function(Iterable<_i6.Emoji>)? onEmojiDeleted, + _i16.FutureOr Function(_i6.AnnouncementsResponse)? + onAnnouncementCreated, _i16.FutureOr Function(_i6.INotificationsResponse)? onNotification, _i16.FutureOr Function(_i6.Note)? onMention, _i16.FutureOr Function(_i6.Note)? onReply, @@ -1911,6 +1929,7 @@ class MockMisskey extends _i1.Mock implements _i6.Misskey { _i16.FutureOr Function(String)? onUnreadSpecifiedNote, _i16.FutureOr Function()? onReadAllUnreadSpecifiedNotes, _i16.FutureOr Function(_i6.User)? onReceiveFollowRequest, + _i16.FutureOr Function()? onReadAllAnnouncements, }) => (super.noSuchMethod( Invocation.method( @@ -1920,6 +1939,7 @@ class MockMisskey extends _i1.Mock implements _i6.Misskey { #onEmojiAdded: onEmojiAdded, #onEmojiUpdated: onEmojiUpdated, #onEmojiDeleted: onEmojiDeleted, + #onAnnouncementCreated: onAnnouncementCreated, #onNotification: onNotification, #onMention: onMention, #onReply: onReply, @@ -1935,6 +1955,7 @@ class MockMisskey extends _i1.Mock implements _i6.Misskey { #onUnreadSpecifiedNote: onUnreadSpecifiedNote, #onReadAllUnreadSpecifiedNotes: onReadAllUnreadSpecifiedNotes, #onReceiveFollowRequest: onReceiveFollowRequest, + #onReadAllAnnouncements: onReadAllAnnouncements, }, ), returnValue: _FakeSocketController_26( @@ -1946,6 +1967,7 @@ class MockMisskey extends _i1.Mock implements _i6.Misskey { #onEmojiAdded: onEmojiAdded, #onEmojiUpdated: onEmojiUpdated, #onEmojiDeleted: onEmojiDeleted, + #onAnnouncementCreated: onAnnouncementCreated, #onNotification: onNotification, #onMention: onMention, #onReply: onReply, @@ -1961,6 +1983,7 @@ class MockMisskey extends _i1.Mock implements _i6.Misskey { #onUnreadSpecifiedNote: onUnreadSpecifiedNote, #onReadAllUnreadSpecifiedNotes: onReadAllUnreadSpecifiedNotes, #onReceiveFollowRequest: onReceiveFollowRequest, + #onReadAllAnnouncements: onReadAllAnnouncements, }, ), ), @@ -1973,6 +1996,7 @@ class MockMisskey extends _i1.Mock implements _i6.Misskey { #onEmojiAdded: onEmojiAdded, #onEmojiUpdated: onEmojiUpdated, #onEmojiDeleted: onEmojiDeleted, + #onAnnouncementCreated: onAnnouncementCreated, #onNotification: onNotification, #onMention: onMention, #onReply: onReply, @@ -1988,6 +2012,7 @@ class MockMisskey extends _i1.Mock implements _i6.Misskey { #onUnreadSpecifiedNote: onUnreadSpecifiedNote, #onReadAllUnreadSpecifiedNotes: onReadAllUnreadSpecifiedNotes, #onReceiveFollowRequest: onReceiveFollowRequest, + #onReadAllAnnouncements: onReadAllAnnouncements, }, ), ), @@ -2527,6 +2552,17 @@ class MockMisskeyUsers extends _i1.Mock implements _i6.MisskeyUsers { returnValue: _i16.Future.value(), returnValueForMissingStub: _i16.Future.value(), ) as _i16.Future); + @override + _i16.Future> flashs(_i6.UsersFlashsRequest? request) => + (super.noSuchMethod( + Invocation.method( + #flashs, + [request], + ), + returnValue: _i16.Future>.value(<_i23.Flash>[]), + returnValueForMissingStub: + _i16.Future>.value(<_i23.Flash>[]), + ) as _i16.Future>); } /// A class which mocks [MisskeyChannels]. @@ -2771,7 +2807,7 @@ class MockMisskeyDriveFiles extends _i1.Mock implements _i6.MisskeyDriveFiles { @override _i16.Future<_i6.DriveFile> create( _i6.DriveFilesCreateRequest? request, - _i23.File? fileContent, + _i24.File? fileContent, ) => (super.noSuchMethod( Invocation.method( @@ -2806,7 +2842,7 @@ class MockMisskeyDriveFiles extends _i1.Mock implements _i6.MisskeyDriveFiles { @override _i16.Future<_i6.DriveFile> createAsBinary( _i6.DriveFilesCreateRequest? request, - _i24.Uint8List? fileContent, + _i25.Uint8List? fileContent, ) => (super.noSuchMethod( Invocation.method( @@ -2901,7 +2937,7 @@ class MockMisskeyDriveFiles extends _i1.Mock implements _i6.MisskeyDriveFiles { /// A class which mocks [Dio]. /// /// See the documentation for Mockito's code generation for more information. -class MockDio extends _i1.Mock implements _i25.Dio { +class MockDio extends _i1.Mock implements _i26.Dio { @override _i9.BaseOptions get options => (super.noSuchMethod( Invocation.getter(#options), @@ -2990,7 +3026,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Object? data, Map? queryParameters, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, _i9.ProgressCallback? onReceiveProgress, }) => (super.noSuchMethod( @@ -3040,7 +3076,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Uri? uri, { Object? data, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, _i9.ProgressCallback? onReceiveProgress, }) => (super.noSuchMethod( @@ -3088,7 +3124,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Object? data, Map? queryParameters, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, _i9.ProgressCallback? onSendProgress, _i9.ProgressCallback? onReceiveProgress, }) => @@ -3142,7 +3178,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Uri? uri, { Object? data, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, _i9.ProgressCallback? onSendProgress, _i9.ProgressCallback? onReceiveProgress, }) => @@ -3194,7 +3230,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Object? data, Map? queryParameters, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, _i9.ProgressCallback? onSendProgress, _i9.ProgressCallback? onReceiveProgress, }) => @@ -3248,7 +3284,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Uri? uri, { Object? data, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, _i9.ProgressCallback? onSendProgress, _i9.ProgressCallback? onReceiveProgress, }) => @@ -3300,7 +3336,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Object? data, Map? queryParameters, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, }) => (super.noSuchMethod( Invocation.method( @@ -3346,7 +3382,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Uri? uri, { Object? data, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, }) => (super.noSuchMethod( Invocation.method( @@ -3390,7 +3426,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Object? data, Map? queryParameters, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, }) => (super.noSuchMethod( Invocation.method( @@ -3436,7 +3472,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Uri? uri, { Object? data, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, }) => (super.noSuchMethod( Invocation.method( @@ -3480,7 +3516,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Object? data, Map? queryParameters, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, _i9.ProgressCallback? onSendProgress, _i9.ProgressCallback? onReceiveProgress, }) => @@ -3534,7 +3570,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Uri? uri, { Object? data, _i9.Options? options, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, _i9.ProgressCallback? onSendProgress, _i9.ProgressCallback? onReceiveProgress, }) => @@ -3586,7 +3622,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { dynamic savePath, { _i9.ProgressCallback? onReceiveProgress, Map? queryParameters, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, bool? deleteOnError = true, String? lengthHeader = r'content-length', Object? data, @@ -3655,7 +3691,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { Uri? uri, dynamic savePath, { _i9.ProgressCallback? onReceiveProgress, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, bool? deleteOnError = true, String? lengthHeader = r'content-length', Object? data, @@ -3721,7 +3757,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { String? path, { Object? data, Map? queryParameters, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, _i9.Options? options, _i9.ProgressCallback? onSendProgress, _i9.ProgressCallback? onReceiveProgress, @@ -3775,7 +3811,7 @@ class MockDio extends _i1.Mock implements _i25.Dio { _i16.Future<_i13.Response> requestUri( Uri? uri, { Object? data, - _i26.CancelToken? cancelToken, + _i27.CancelToken? cancelToken, _i9.Options? options, _i9.ProgressCallback? onSendProgress, _i9.ProgressCallback? onReceiveProgress, @@ -3851,14 +3887,14 @@ class MockDio extends _i1.Mock implements _i25.Dio { /// /// See the documentation for Mockito's code generation for more information. class MockFilePickerPlatform extends _i1.Mock - implements _i27.FakeFilePickerPlatform { + implements _i28.FakeFilePickerPlatform { @override - _i16.Future<_i28.FilePickerResult?> pickFiles({ + _i16.Future<_i29.FilePickerResult?> pickFiles({ String? dialogTitle, String? initialDirectory, - _i28.FileType? type = _i28.FileType.any, + _i29.FileType? type = _i29.FileType.any, List? allowedExtensions, - dynamic Function(_i28.FilePickerStatus)? onFileLoading, + dynamic Function(_i29.FilePickerStatus)? onFileLoading, bool? allowCompression = true, bool? allowMultiple = false, bool? withData = false, @@ -3882,9 +3918,9 @@ class MockFilePickerPlatform extends _i1.Mock #lockParentWindow: lockParentWindow, }, ), - returnValue: _i16.Future<_i28.FilePickerResult?>.value(), - returnValueForMissingStub: _i16.Future<_i28.FilePickerResult?>.value(), - ) as _i16.Future<_i28.FilePickerResult?>); + returnValue: _i16.Future<_i29.FilePickerResult?>.value(), + returnValueForMissingStub: _i16.Future<_i29.FilePickerResult?>.value(), + ) as _i16.Future<_i29.FilePickerResult?>); @override _i16.Future clearTemporaryFiles() => (super.noSuchMethod( Invocation.method( @@ -3918,7 +3954,7 @@ class MockFilePickerPlatform extends _i1.Mock String? dialogTitle, String? fileName, String? initialDirectory, - _i28.FileType? type = _i28.FileType.any, + _i29.FileType? type = _i29.FileType.any, List? allowedExtensions, bool? lockParentWindow = false, }) =>