Add (WIP) ban/klck ability
This commit is contained in:
parent
c130d28b93
commit
3a1bcb5b8f
13 changed files with 113 additions and 30 deletions
|
|
@ -6,6 +6,7 @@ import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:nexus/controllers/client_state_controller.dart";
|
import "package:nexus/controllers/client_state_controller.dart";
|
||||||
import "package:nexus/controllers/members_controller.dart";
|
import "package:nexus/controllers/members_controller.dart";
|
||||||
import "package:nexus/models/membership.dart";
|
import "package:nexus/models/membership.dart";
|
||||||
|
import "package:nexus/models/membership_status.dart";
|
||||||
|
|
||||||
class AuthorController extends AsyncNotifier<Membership> {
|
class AuthorController extends AsyncNotifier<Membership> {
|
||||||
final Message message;
|
final Message message;
|
||||||
|
|
@ -35,6 +36,7 @@ class AuthorController extends AsyncNotifier<Membership> {
|
||||||
);
|
);
|
||||||
|
|
||||||
return Membership(
|
return Membership(
|
||||||
|
status: member?.status ?? MembershipStatus.leave,
|
||||||
avatarUrl: pmp?.avatarUrl ?? member?.avatarUrl,
|
avatarUrl: pmp?.avatarUrl ?? member?.avatarUrl,
|
||||||
displayName:
|
displayName:
|
||||||
pmp?.displayName ??
|
pmp?.displayName ??
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import "package:nexus/models/requests/paginate_request.dart";
|
||||||
import "package:nexus/models/requests/redact_event_request.dart";
|
import "package:nexus/models/requests/redact_event_request.dart";
|
||||||
import "package:nexus/models/requests/report_request.dart";
|
import "package:nexus/models/requests/report_request.dart";
|
||||||
import "package:nexus/models/requests/send_message_request.dart";
|
import "package:nexus/models/requests/send_message_request.dart";
|
||||||
|
import "package:nexus/models/requests/set_membership_request.dart";
|
||||||
import "package:nexus/models/room.dart";
|
import "package:nexus/models/room.dart";
|
||||||
import "package:nexus/models/sync_data.dart";
|
import "package:nexus/models/sync_data.dart";
|
||||||
import "package:nexus/models/sync_status.dart";
|
import "package:nexus/models/sync_status.dart";
|
||||||
|
|
@ -224,8 +225,11 @@ class ClientController extends AsyncNotifier<int> {
|
||||||
Future<Profile> getProfile(String userId) async =>
|
Future<Profile> getProfile(String userId) async =>
|
||||||
Profile.fromJson(await _sendCommand("get_profile", {"user_id": userId}));
|
Profile.fromJson(await _sendCommand("get_profile", {"user_id": userId}));
|
||||||
|
|
||||||
Future<void> reportEvent(ReportRequest report) =>
|
Future<void> reportEvent(ReportRequest request) =>
|
||||||
_sendCommand("report_event", report.toJson());
|
_sendCommand("report_event", request.toJson());
|
||||||
|
|
||||||
|
Future<void> setMembership(SetMembershipRequest request) =>
|
||||||
|
_sendCommand("set_membership", request.toJson());
|
||||||
|
|
||||||
Future<void> markRead(Room room) async {
|
Future<void> markRead(Room room) async {
|
||||||
final event = room.events.firstWhereOrNull(
|
final event = room.events.firstWhereOrNull(
|
||||||
|
|
|
||||||
25
lib/controllers/members_by_type_controller.dart
Normal file
25
lib/controllers/members_by_type_controller.dart
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
|
import "package:nexus/controllers/members_controller.dart";
|
||||||
|
import "package:nexus/models/membership.dart";
|
||||||
|
import "package:nexus/models/membership_status.dart";
|
||||||
|
|
||||||
|
class MembersByTypeController extends AsyncNotifier<IList<Membership>> {
|
||||||
|
final MembershipStatus status;
|
||||||
|
MembersByTypeController(this.status);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<IList<Membership>> build() => ref.watch(
|
||||||
|
MembersController.provider.selectAsync(
|
||||||
|
(members) =>
|
||||||
|
members.where((membership) => membership.status == status).toIList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static final provider =
|
||||||
|
AsyncNotifierProvider.family<
|
||||||
|
MembersByTypeController,
|
||||||
|
IList<Membership>,
|
||||||
|
MembershipStatus
|
||||||
|
>(MembersByTypeController.new);
|
||||||
|
}
|
||||||
|
|
@ -29,7 +29,7 @@ class MembersController extends AsyncNotifier<IList<Membership>> {
|
||||||
);
|
);
|
||||||
|
|
||||||
return state.nonNulls
|
return state.nonNulls
|
||||||
.where((member) => member.content["membership"] == "join")
|
.where((state) => state.type == "m.room.member")
|
||||||
.map(
|
.map(
|
||||||
(membership) => Membership.fromContent(
|
(membership) => Membership.fromContent(
|
||||||
membership.content,
|
membership.content,
|
||||||
|
|
|
||||||
|
|
@ -145,11 +145,11 @@ class MessageController extends AsyncNotifier<Message?> {
|
||||||
"${content["displayname"] ?? event.stateKey} ${switch (content["membership"]) {
|
"${content["displayname"] ?? event.stateKey} ${switch (content["membership"]) {
|
||||||
"invite" => "was invited to",
|
"invite" => "was invited to",
|
||||||
"join" => "joined",
|
"join" => "joined",
|
||||||
"leave" => event.authorId == event.stateKey ? "left" : "was kicked",
|
"leave" => event.authorId == event.stateKey ? "left" : (event.unsigned["prev_content"]?["membership"] == "ban" ? "was unbanned from" : "was kicked from"),
|
||||||
"ban" => "was banned from",
|
"ban" => "was banned from",
|
||||||
"knock" => "asked to join",
|
"knock" => "asked to join",
|
||||||
_ => "did something relating to",
|
_ => "did something relating to",
|
||||||
}} the room.",
|
}} the room. ${content["reason"] ?? ""}",
|
||||||
),
|
),
|
||||||
|
|
||||||
"m.room.server_acl" => toSystemMessage(
|
"m.room.server_acl" => toSystemMessage(
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import "dart:async";
|
||||||
import "package:collection/collection.dart";
|
import "package:collection/collection.dart";
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||||
import "package:flutter_chat_core/flutter_chat_core.dart" as chat;
|
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:fluttertagger/fluttertagger.dart";
|
import "package:fluttertagger/fluttertagger.dart";
|
||||||
import "package:nexus/controllers/client_controller.dart";
|
import "package:nexus/controllers/client_controller.dart";
|
||||||
|
|
@ -258,21 +257,6 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
|
||||||
if (message != null) insertMessage(message);
|
if (message != null) insertMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<chat.User> resolveUser(String id) async {
|
|
||||||
final user = await ref
|
|
||||||
.watch(ClientController.provider.notifier)
|
|
||||||
.getProfile(id);
|
|
||||||
return chat.User(
|
|
||||||
id: id,
|
|
||||||
name: user.displayName,
|
|
||||||
// imageSource: user.avatarUrl == null
|
|
||||||
// ? null
|
|
||||||
// : (await ref.watch(
|
|
||||||
// AvatarController.provider(user.avatarUrl!.toString()).future,
|
|
||||||
// )).toString(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> scrollToMessage(Message message) async {
|
Future<void> scrollToMessage(Message message) async {
|
||||||
final controller = await future;
|
final controller = await future;
|
||||||
Future<void> setFlashing(bool flashing) => controller.updateMessage(
|
Future<void> setFlashing(bool flashing) => controller.updateMessage(
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:freezed_annotation/freezed_annotation.dart";
|
import "package:freezed_annotation/freezed_annotation.dart";
|
||||||
import "package:nexus/helpers/extensions/mxc_to_https.dart";
|
import "package:nexus/helpers/extensions/mxc_to_https.dart";
|
||||||
|
import "package:nexus/models/membership_status.dart";
|
||||||
part "membership.freezed.dart";
|
part "membership.freezed.dart";
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
abstract class Membership with _$Membership {
|
abstract class Membership with _$Membership {
|
||||||
const Membership._();
|
const Membership._();
|
||||||
const factory Membership({
|
const factory Membership({
|
||||||
|
required MembershipStatus status,
|
||||||
required Uri? avatarUrl,
|
required Uri? avatarUrl,
|
||||||
required String displayName,
|
required String displayName,
|
||||||
required String userId,
|
required String userId,
|
||||||
|
|
@ -17,6 +19,10 @@ abstract class Membership with _$Membership {
|
||||||
String userId,
|
String userId,
|
||||||
String homeserver,
|
String homeserver,
|
||||||
) => Membership(
|
) => Membership(
|
||||||
|
status: MembershipStatus.values.firstWhere(
|
||||||
|
(status) => status.name == content["membership"],
|
||||||
|
orElse: () => MembershipStatus.leave,
|
||||||
|
),
|
||||||
avatarUrl: Uri.tryParse(
|
avatarUrl: Uri.tryParse(
|
||||||
content["avatar_url"] ?? "",
|
content["avatar_url"] ?? "",
|
||||||
)?.mxcToHttps(homeserver),
|
)?.mxcToHttps(homeserver),
|
||||||
|
|
|
||||||
4
lib/models/membership_status.dart
Normal file
4
lib/models/membership_status.dart
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import "package:freezed_annotation/freezed_annotation.dart";
|
||||||
|
|
||||||
|
@JsonEnum()
|
||||||
|
enum MembershipStatus { leave, invite, ban, join }
|
||||||
20
lib/models/requests/set_membership_request.dart
Normal file
20
lib/models/requests/set_membership_request.dart
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import "package:freezed_annotation/freezed_annotation.dart";
|
||||||
|
part "set_membership_request.freezed.dart";
|
||||||
|
part "set_membership_request.g.dart";
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
abstract class SetMembershipRequest with _$SetMembershipRequest {
|
||||||
|
const factory SetMembershipRequest({
|
||||||
|
required String userId,
|
||||||
|
required String roomId,
|
||||||
|
|
||||||
|
@JsonKey(name: "action") required MembershipAction action,
|
||||||
|
@Default(false) @JsonKey(name: "msc4293_redact_events") bool redact,
|
||||||
|
}) = _SetMembershipRequest;
|
||||||
|
|
||||||
|
factory SetMembershipRequest.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$SetMembershipRequestFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonEnum()
|
||||||
|
enum MembershipAction { ban, kick, unban, invite }
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:nexus/controllers/members_controller.dart";
|
import "package:nexus/controllers/members_by_type_controller.dart";
|
||||||
import "package:nexus/controllers/rooms_controller.dart";
|
import "package:nexus/controllers/rooms_controller.dart";
|
||||||
import "package:nexus/helpers/extensions/better_when.dart";
|
import "package:nexus/helpers/extensions/better_when.dart";
|
||||||
|
import "package:nexus/models/membership_status.dart";
|
||||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||||
import "package:nexus/widgets/loading.dart";
|
import "package:nexus/widgets/loading.dart";
|
||||||
|
|
||||||
|
|
@ -31,7 +32,9 @@ class MentionOverlay extends ConsumerWidget {
|
||||||
child: switch (triggerCharacter) {
|
child: switch (triggerCharacter) {
|
||||||
"@" =>
|
"@" =>
|
||||||
ref
|
ref
|
||||||
.watch(MembersController.provider)
|
.watch(
|
||||||
|
MembersByTypeController.provider(MembershipStatus.join),
|
||||||
|
)
|
||||||
.betterWhen(
|
.betterWhen(
|
||||||
data: (members) => ListView(
|
data: (members) => ListView(
|
||||||
children:
|
children:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:nexus/controllers/members_controller.dart";
|
import "package:nexus/controllers/members_by_type_controller.dart";
|
||||||
import "package:nexus/helpers/extensions/better_when.dart";
|
import "package:nexus/helpers/extensions/better_when.dart";
|
||||||
import "package:nexus/helpers/extensions/show_user_popover.dart";
|
import "package:nexus/helpers/extensions/show_user_popover.dart";
|
||||||
|
import "package:nexus/models/membership_status.dart";
|
||||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||||
|
|
||||||
class MemberList extends ConsumerWidget {
|
class MemberList extends ConsumerWidget {
|
||||||
|
|
@ -10,7 +11,9 @@ class MemberList extends ConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final membersProvider = ref.watch(MembersController.provider);
|
final membersProvider = ref.watch(
|
||||||
|
MembersByTypeController.provider(MembershipStatus.join),
|
||||||
|
);
|
||||||
return Drawer(
|
return Drawer(
|
||||||
shape: Border(),
|
shape: Border(),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
|
||||||
|
|
@ -369,7 +369,7 @@ class RoomChat extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
resolveUser: notifier.resolveUser,
|
resolveUser: (_) async => null,
|
||||||
chatController: controller,
|
chatController: controller,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
|
import "package:nexus/controllers/client_controller.dart";
|
||||||
import "package:nexus/controllers/client_state_controller.dart";
|
import "package:nexus/controllers/client_state_controller.dart";
|
||||||
import "package:nexus/controllers/profile_controller.dart";
|
import "package:nexus/controllers/profile_controller.dart";
|
||||||
|
import "package:nexus/controllers/selected_room_controller.dart";
|
||||||
import "package:nexus/helpers/extensions/better_when.dart";
|
import "package:nexus/helpers/extensions/better_when.dart";
|
||||||
import "package:nexus/models/membership.dart";
|
import "package:nexus/models/membership.dart";
|
||||||
|
import "package:nexus/models/membership_status.dart";
|
||||||
|
import "package:nexus/models/requests/set_membership_request.dart";
|
||||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||||
|
import "package:nexus/main.dart";
|
||||||
|
|
||||||
class UserPopover extends ConsumerWidget {
|
class UserPopover extends ConsumerWidget {
|
||||||
final Membership member;
|
final Membership member;
|
||||||
|
|
@ -14,6 +19,11 @@ class UserPopover extends ConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final textTheme = theme.textTheme;
|
final textTheme = theme.textTheme;
|
||||||
|
final client = ref.watch(ClientController.provider.notifier);
|
||||||
|
final roomId = ref.watch(
|
||||||
|
SelectedRoomController.provider.select((room) => room?.metadata?.id),
|
||||||
|
);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
spacing: 16,
|
spacing: 16,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
|
@ -53,14 +63,24 @@ class UserPopover extends ConsumerWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (member.userId != ref.watch(ClientStateController.provider)?.userId)
|
if (member.userId !=
|
||||||
|
ref.watch(ClientStateController.provider)?.userId &&
|
||||||
|
roomId != null)
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
children: [
|
children: [
|
||||||
FilledButton.icon(onPressed: null, label: Text("Message")),
|
FilledButton.icon(onPressed: null, label: Text("Message")),
|
||||||
FilledButton.icon(
|
FilledButton.icon(
|
||||||
onPressed: null,
|
onPressed: () => client
|
||||||
|
.setMembership(
|
||||||
|
SetMembershipRequest(
|
||||||
|
userId: member.userId,
|
||||||
|
roomId: roomId,
|
||||||
|
action: MembershipAction.kick,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.onError(showError),
|
||||||
label: Text("Kick"),
|
label: Text("Kick"),
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: WidgetStatePropertyAll(
|
backgroundColor: WidgetStatePropertyAll(
|
||||||
|
|
@ -72,8 +92,20 @@ class UserPopover extends ConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: null,
|
onPressed: () => client
|
||||||
label: Text("Ban"),
|
.setMembership(
|
||||||
|
SetMembershipRequest(
|
||||||
|
userId: member.userId,
|
||||||
|
roomId: roomId,
|
||||||
|
action: member.status == MembershipStatus.ban
|
||||||
|
? MembershipAction.unban
|
||||||
|
: MembershipAction.ban,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.onError(showError),
|
||||||
|
label: Text(
|
||||||
|
member.status == MembershipStatus.ban ? "Unban" : "Ban",
|
||||||
|
),
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: WidgetStatePropertyAll(
|
backgroundColor: WidgetStatePropertyAll(
|
||||||
theme.colorScheme.errorContainer,
|
theme.colorScheme.errorContainer,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue