From 756ae3346c7ace7a0dbf53aee0e7569d33182f94 Mon Sep 17 00:00:00 2001 From: 4sterisk <4sterisk@ymail.ne.jp> Date: Tue, 26 Nov 2024 22:12:09 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=81=E3=83=A3=E3=83=B3=E3=83=8D?= =?UTF-8?q?=E3=83=AB=E5=86=85=E6=A4=9C=E7=B4=A2=E3=81=AE=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../channels_page/channel_detail_page.dart | 10 +++- .../channels_page/channel_note_search.dart | 32 +++++++++++++ .../channel_detail_page_test.dart | 48 +++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 lib/view/channels_page/channel_note_search.dart diff --git a/lib/view/channels_page/channel_detail_page.dart b/lib/view/channels_page/channel_detail_page.dart index 7085958c7..5a57bbd17 100644 --- a/lib/view/channels_page/channel_detail_page.dart +++ b/lib/view/channels_page/channel_detail_page.dart @@ -6,6 +6,7 @@ import "package:miria/providers.dart"; import "package:miria/router/app_router.dart"; import "package:miria/view/channels_page/channel_detail_info.dart"; import "package:miria/view/channels_page/channel_note_highlight.dart"; +import "package:miria/view/channels_page/channel_note_search.dart"; import "package:miria/view/channels_page/channel_timeline.dart"; import "package:miria/view/common/account_scope.dart"; @@ -27,7 +28,7 @@ class ChannelDetailPage extends ConsumerWidget implements AutoRouteWrapper { @override Widget build(BuildContext context, WidgetRef ref) { return DefaultTabController( - length: 3, + length: 4, child: Scaffold( appBar: AppBar( title: Text(S.of(context).channel), @@ -36,7 +37,10 @@ class ChannelDetailPage extends ConsumerWidget implements AutoRouteWrapper { Tab(child: Text(S.of(context).channelInformation)), Tab(child: Text(S.of(context).timeline)), Tab(child: Text(S.of(context).highlight)), + Tab(child: Text(S.of(context).search)), ], + isScrollable: true, + tabAlignment: TabAlignment.center, ), ), body: TabBarView( @@ -55,6 +59,10 @@ class ChannelDetailPage extends ConsumerWidget implements AutoRouteWrapper { padding: const EdgeInsets.only(left: 10, right: 10), child: ChannelNoteHighlight(channelId: channelId), ), + Padding( + padding: const EdgeInsets.only(left: 10, right: 10), + child: ChannelNoteSearch(channelId: channelId), + ), ], ), floatingActionButton: ref.read(accountContextProvider).isSame diff --git a/lib/view/channels_page/channel_note_search.dart b/lib/view/channels_page/channel_note_search.dart new file mode 100644 index 000000000..c68376fe3 --- /dev/null +++ b/lib/view/channels_page/channel_note_search.dart @@ -0,0 +1,32 @@ +import "package:flutter/material.dart"; +import "package:flutter_hooks/flutter_hooks.dart"; +import "package:hooks_riverpod/hooks_riverpod.dart"; +import "package:miria/view/search_page/note_search.dart"; + +class ChannelNoteSearch extends HookConsumerWidget { + final String channelId; + const ChannelNoteSearch({required this.channelId, super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final searchQuery = useState(""); + + return Column( + children: [ + const Padding(padding: EdgeInsets.only(top: 5)), + TextField( + decoration: const InputDecoration(prefixIcon: Icon(Icons.search)), + textInputAction: TextInputAction.done, + onSubmitted: (value) => searchQuery.value = value, + ), + Expanded( + child: NoteSearchList( + query: searchQuery.value, + channelId: channelId, + localOnly: false, + ), + ), + ], + ); + } +} diff --git a/test/view/channel_detail_page/channel_detail_page_test.dart b/test/view/channel_detail_page/channel_detail_page_test.dart index c1f3e2b57..07bb5daee 100644 --- a/test/view/channel_detail_page/channel_detail_page_test.dart +++ b/test/view/channel_detail_page/channel_detail_page_test.dart @@ -1,3 +1,4 @@ +import "package:flutter/material.dart"; import "package:flutter_test/flutter_test.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:miria/providers.dart"; @@ -313,5 +314,52 @@ void main() { ), ); }); + + testWidgets("チャンネル内の検索でノートが表示されること", (tester) async { + final notes = MockMisskeyNotes(); + final channel = MockMisskeyChannels(); + final misskey = MockMisskey(); + when(misskey.channels).thenReturn(channel); + when(channel.show(any)).thenAnswer( + (_) async => + TestData.channel1.copyWith(bannerUrl: null, isFollowing: false), + ); + when(misskey.notes).thenReturn(notes); + when(notes.search(any)).thenAnswer((_) async => [TestData.note1]); + + await tester.pumpWidget( + ProviderScope( + overrides: [misskeyProvider.overrideWith((_) => misskey)], + child: DefaultRootWidget( + initialRoute: ChannelDetailRoute( + accountContext: TestData.accountContext, + channelId: TestData.channel1.id, + ), + ), + ), + ); + await tester.pumpAndSettle(); + + await tester.tap(find.text("検索")); + await tester.pumpAndSettle(); + + await tester.enterText(find.byType(TextField), "Misskey"); + await tester.testTextInput.receiveAction(TextInputAction.done); + await tester.pumpAndSettle(); + + verify( + notes.search( + argThat( + equals( + NotesSearchRequest( + query: "Misskey", + channelId: TestData.channel1.id, + ), + ), + ), + ), + ).called(1); + expect(find.text(TestData.note1.text!), findsOneWidget); + }); }); }