Skip to content

Commit

Permalink
Merge pull request #407 from poppingmoon/server-list-filter
Browse files Browse the repository at this point in the history
サーバー一覧を名前やurlで絞り込めるように
  • Loading branch information
shiosyakeyakini-info authored Oct 21, 2023
2 parents d32fbf1 + 6636eb1 commit d27f945
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 113 deletions.
6 changes: 6 additions & 0 deletions lib/providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import 'package:miria/repository/tab_settings_repository.dart';
import 'package:miria/repository/time_line_repository.dart';
import 'package:miria/repository/user_list_time_line_repository.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:miria/state_notifier/common/misskey_server_list_notifier.dart';
import 'package:miria/state_notifier/note_create_page/note_create_state_notifier.dart';
import 'package:miria/state_notifier/photo_edit_page/photo_edit_state_notifier.dart';
import 'package:misskey_dart/misskey_dart.dart';
Expand Down Expand Up @@ -257,3 +258,8 @@ final noteCreateProvider = StateNotifierProvider.family
ref.read(errorEventProvider.notifier),
ref.read(notesProvider(account))),
);

final misskeyServerListNotifierProvider = AsyncNotifierProvider.autoDispose<
MisskeyServerListNotifier, List<JoinMisskeyInstanceInfo>>(
MisskeyServerListNotifier.new,
);
51 changes: 51 additions & 0 deletions lib/state_notifier/common/misskey_server_list_notifier.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:misskey_dart/misskey_dart.dart';

final _queryProvider = StateProvider.autoDispose((ref) {
return "";
});

final _instanceInfosProvider = AsyncNotifierProvider.autoDispose<_InstanceInfos,
List<JoinMisskeyInstanceInfo>>(
_InstanceInfos.new,
);

class _InstanceInfos
extends AutoDisposeAsyncNotifier<List<JoinMisskeyInstanceInfo>> {
@override
Future<List<JoinMisskeyInstanceInfo>> build() async {
final response =
await JoinMisskey(host: "instanceapp.misskey.page").instances();
return response.instancesInfos
.sortedByCompare(
(info) => info.nodeInfo?.usage?.users?.total ?? 0,
(a, b) => a.compareTo(b),
)
.reversed
.toList();
}
}

class MisskeyServerListNotifier
extends AutoDisposeAsyncNotifier<List<JoinMisskeyInstanceInfo>> {
@override
Future<List<JoinMisskeyInstanceInfo>> build() async {
final query = ref.watch(_queryProvider);
final instances = await ref.watch(_instanceInfosProvider.future);
if (query.isEmpty) {
return instances;
}
final filtered = instances.where(
(e) => e.name.toLowerCase().contains(query) || e.url.contains(query),
);
final grouped = filtered.groupListsBy(
(e) => e.name.toLowerCase().startsWith(query) || e.url.startsWith(query),
);
return [...grouped[true] ?? [], ...grouped[false] ?? []];
}

void setQuery(String query) {
ref.read(_queryProvider.notifier).state = query.trim().toLowerCase();
}
}
131 changes: 131 additions & 0 deletions lib/view/common/misskey_server_list.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:miria/providers.dart';
import 'package:miria/view/common/constants.dart';
import 'package:miria/view/common/error_detail.dart';
import 'package:misskey_dart/misskey_dart.dart';

class MisskeyServerList extends ConsumerWidget {
final bool isDisableUnloginable;

final void Function(JoinMisskeyInstanceInfo) onTap;

const MisskeyServerList({
super.key,
this.isDisableUnloginable = false,
required this.onTap,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
final servers = ref.watch(misskeyServerListNotifierProvider);
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 800),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: TextField(
decoration: const InputDecoration(
prefixIcon: Icon(Icons.search),
),
onChanged:
ref.read(misskeyServerListNotifierProvider.notifier).setQuery,
),
),
Expanded(
child: servers.when(
skipLoadingOnReload: true,
data: (servers) => ListView.builder(
itemCount: servers.length,
itemBuilder: (context, index) {
final server = servers[index];
final description =
server.description?.replaceAll(htmlTagRemove, "") ?? "";
final available = !isDisableUnloginable ||
server.nodeInfo?.software?.name == "misskey" &&
availableServerVersion
.allMatches(
server.nodeInfo?.software?.version ?? "",
)
.isNotEmpty;
return Padding(
key: ValueKey(server.url),
padding: const EdgeInsets.only(bottom: 10),
child: InkWell(
onTap: available ? () => onTap.call(server) : null,
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: available ? null : Colors.grey.withAlpha(160),
border:
Border.all(color: Theme.of(context).dividerColor),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
if (server.icon)
Padding(
padding: const EdgeInsets.only(right: 10),
child: SizedBox(
width: (Theme.of(context)
.textTheme
.bodyMedium
?.fontSize ??
22) *
MediaQuery.of(context)
.textScaleFactor *
2,
child: Image.network(
"https://instanceapp.misskey.page/instance-icons/${server.url}.webp",
),
),
),
Expanded(child: Text(server.name)),
],
),
Text(
description,
style: Theme.of(context).textTheme.bodySmall,
),
const Padding(padding: EdgeInsets.only(top: 10)),
Text(
"${server.nodeInfo?.usage?.users?.total.format()}人が参加中",
style: Theme.of(context).textTheme.bodySmall,
),
const Padding(padding: EdgeInsets.only(top: 10)),
Align(
alignment: Alignment.centerRight,
child: Text(
"${server.nodeInfo?.software?.name ?? ""} ${server.nodeInfo?.software?.version}",
style: Theme.of(context).textTheme.bodySmall,
textAlign: TextAlign.right,
),
),
if (!available)
Align(
alignment: Alignment.centerRight,
child: Text(
"非対応のサーバーです",
style: Theme.of(context).textTheme.bodySmall,
textAlign: TextAlign.right,
),
),
],
),
),
),
);
},
),
error: (e, st) => ErrorDetail(error: e, stackTrace: st),
loading: () => const Center(child: CircularProgressIndicator()),
),
),
],
),
);
}
}
2 changes: 1 addition & 1 deletion lib/view/explore_page/explore_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:miria/router/app_router.dart';
import 'package:miria/view/common/account_scope.dart';
import 'package:miria/view/login_page/misskey_server_list_dialog.dart';
import 'package:miria/view/common/misskey_server_list.dart';

class ExploreServer extends ConsumerStatefulWidget {
const ExploreServer({super.key});
Expand Down
113 changes: 1 addition & 112 deletions lib/view/login_page/misskey_server_list_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:miria/view/common/constants.dart';
import 'package:miria/view/common/futable_list_builder.dart';
import 'package:misskey_dart/misskey_dart.dart';
import 'package:miria/view/common/misskey_server_list.dart';

class MisskeyServerListDialog extends ConsumerStatefulWidget {
const MisskeyServerListDialog({super.key});
Expand All @@ -27,112 +25,3 @@ class MisskeyServerListDialogState
);
}
}

class MisskeyServerList extends ConsumerWidget {
final bool isDisableUnloginable;

final void Function(JoinMisskeyInstanceInfo) onTap;

const MisskeyServerList({
super.key,
this.isDisableUnloginable = false,
required this.onTap,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
return Align(
alignment: Alignment.center,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 800),
child: FutureListView<JoinMisskeyInstanceInfo>(
future: () async {
final instances =
await JoinMisskey(host: "instanceapp.misskey.page").instances();
return instances.instancesInfos.toList()
..sort((a, b) => (b.nodeInfo?.usage?.users?.total ?? 0)
.compareTo((a.nodeInfo?.usage?.users?.total ?? 0)));
}(),
builder: (context, item) {
final description =
item.description?.replaceAll(htmlTagRemove, "") ?? "";
final available = !isDisableUnloginable ||
item.nodeInfo?.software?.name == "misskey" &&
availableServerVersion
.allMatches(item.nodeInfo?.software?.version ?? "")
.isNotEmpty;

return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Ink(
child: GestureDetector(
onTap: available ? () => onTap.call(item) : null,
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: available ? null : Colors.grey.withAlpha(160),
border:
Border.all(color: Theme.of(context).dividerColor)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Row(
children: [
if (item.icon)
Padding(
padding: const EdgeInsets.only(right: 10),
child: SizedBox(
width: (Theme.of(context)
.textTheme
.bodyMedium
?.fontSize ??
22) *
MediaQuery.of(context).textScaleFactor *
2,
child: Image.network(
"https://instanceapp.misskey.page/instance-icons/${item.url}.webp"),
),
),
Expanded(child: Text(item.name)),
],
),
Text(
description,
style: Theme.of(context).textTheme.bodySmall,
),
const Padding(padding: EdgeInsets.only(top: 10)),
Text(
"${item.nodeInfo?.usage?.users?.total?.format()}人が参加中",
style: Theme.of(context).textTheme.bodySmall,
),
const Padding(padding: EdgeInsets.only(top: 10)),
Align(
alignment: Alignment.centerRight,
child: Text(
"${item.nodeInfo?.software?.name ?? ""} ${item.nodeInfo?.software?.version}",
style: Theme.of(context).textTheme.bodySmall,
textAlign: TextAlign.right,
),
),
if (!available)
Align(
alignment: Alignment.centerRight,
child: Text(
"非対応のサーバーです",
style: Theme.of(context).textTheme.bodySmall,
textAlign: TextAlign.right,
))
],
),
),
),
),
);
},
),
),
);
}
}

0 comments on commit d27f945

Please sign in to comment.