♻️ Optimize realm discovery

This commit is contained in:
2026-02-02 13:30:33 +08:00
parent 5cdeb7bd22
commit cfd2a47064
4 changed files with 80 additions and 10 deletions

View File

@@ -233,7 +233,18 @@ final routerProvider = Provider<GoRouter>((ref) {
GoRoute( GoRoute(
name: 'universalSearch', name: 'universalSearch',
path: '/search', path: '/search',
builder: (context, state) => const UniversalSearchScreen(), builder: (context, state) {
final initialTab = state.uri.queryParameters['tab'];
SearchTab tab;
if (initialTab == 'realms') {
tab = SearchTab.realms;
} else if (initialTab == 'fediverse') {
tab = SearchTab.fediverse;
} else {
tab = SearchTab.posts;
}
return UniversalSearchScreen(initialTab: tab);
},
), ),
// Main tabs with TabsScreen shell // Main tabs with TabsScreen shell

View File

@@ -51,7 +51,7 @@ class RealmListScreen extends HookConsumerWidget {
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Symbols.travel_explore), icon: const Icon(Symbols.travel_explore),
onPressed: () => context.pushNamed('discoveryRealms'), onPressed: () => context.pushNamed('universalSearch', queryParameters: {'tab': 'realms'}),
), ),
IconButton( IconButton(
icon: Badge( icon: Badge(

View File

@@ -16,12 +16,13 @@ import 'package:island/widgets/paging/pagination_list.dart';
import 'package:island/widgets/post/post_item.dart'; import 'package:island/widgets/post/post_item.dart';
import 'package:island/widgets/post/post_item_skeleton.dart'; import 'package:island/widgets/post/post_item_skeleton.dart';
import 'package:island/widgets/post/filters/post_filter.dart'; import 'package:island/widgets/post/filters/post_filter.dart';
import 'package:island/widgets/realm/realm_list.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
const kSearchPostListId = 'search'; const kSearchPostListId = 'search';
enum SearchTab { posts, fediverse } enum SearchTab { posts, fediverse, realms }
class UniversalSearchScreen extends HookConsumerWidget { class UniversalSearchScreen extends HookConsumerWidget {
final SearchTab initialTab; final SearchTab initialTab;
@@ -31,7 +32,7 @@ class UniversalSearchScreen extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final tabController = useTabController( final tabController = useTabController(
initialLength: 2, initialLength: 3,
initialIndex: initialTab.index, initialIndex: initialTab.index,
); );
@@ -45,12 +46,13 @@ class UniversalSearchScreen extends HookConsumerWidget {
tabs: [ tabs: [
Tab(text: 'posts'.tr()), Tab(text: 'posts'.tr()),
Tab(text: 'fediverseUsers'.tr()), Tab(text: 'fediverseUsers'.tr()),
Tab(text: 'realms'.tr()),
], ],
), ),
Expanded( Expanded(
child: TabBarView( child: TabBarView(
controller: tabController, controller: tabController,
children: [_PostsSearchTab(), _FediverseSearchTab()], children: [_PostsSearchTab(), _FediverseSearchTab(), _RealmsSearchTab()],
), ),
), ),
], ],
@@ -59,6 +61,64 @@ class UniversalSearchScreen extends HookConsumerWidget {
} }
} }
class _RealmsSearchTab extends HookConsumerWidget {
const _RealmsSearchTab();
@override
Widget build(BuildContext context, WidgetRef ref) {
Timer? debounceTimer;
final searchController = useTextEditingController();
final currentQuery = useState<String?>(null);
return Stack(
children: [
CustomScrollView(
slivers: [
const SliverGap(88),
SliverRealmList(
query: currentQuery.value,
key: ValueKey(currentQuery.value),
),
SliverGap(MediaQuery.of(context).padding.bottom + 16),
],
),
Positioned(
top: 0,
left: 0,
right: 0,
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 560),
child: Padding(
padding: const EdgeInsets.all(16),
child: SearchBar(
elevation: WidgetStateProperty.all(4),
controller: searchController,
hintText: 'search'.tr(),
leading: const Icon(Icons.search),
padding: WidgetStateProperty.all(
const EdgeInsets.symmetric(horizontal: 24),
),
onChanged: (value) {
if (debounceTimer?.isActive ?? false) {
debounceTimer?.cancel();
}
debounceTimer = Timer(const Duration(milliseconds: 300), () {
if (currentQuery.value != value) {
currentQuery.value = value;
}
});
},
),
),
),
),
),
],
);
}
}
class _PostsSearchTab extends HookConsumerWidget { class _PostsSearchTab extends HookConsumerWidget {
const _PostsSearchTab(); const _PostsSearchTab();

View File

@@ -9,10 +9,9 @@ import 'package:island/widgets/paging/pagination_list.dart';
import 'package:island/widgets/realm/realm_list_tile.dart'; import 'package:island/widgets/realm/realm_list_tile.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
final realmListNotifierProvider = AsyncNotifierProvider.autoDispose final realmListNotifierProvider = AsyncNotifierProvider.autoDispose.family(
.family<RealmListNotifier, PaginationState<SnRealm>, String?>( RealmListNotifier.new,
RealmListNotifier.new, );
);
class RealmListNotifier extends AsyncNotifier<PaginationState<SnRealm>> class RealmListNotifier extends AsyncNotifier<PaginationState<SnRealm>>
with AsyncPaginationController<SnRealm> { with AsyncPaginationController<SnRealm> {
@@ -45,7 +44,7 @@ class RealmListNotifier extends AsyncNotifier<PaginationState<SnRealm>>
}; };
final response = await client.get( final response = await client.get(
'/sphere/discovery/realms', '/pass/realms/public',
queryParameters: queryParams, queryParameters: queryParams,
); );
totalCount = int.parse(response.headers.value('X-Total') ?? '0'); totalCount = int.parse(response.headers.value('X-Total') ?? '0');