parent
dbf30c3706
commit
d37ec2878b
21 changed files with 561 additions and 147 deletions
|
|
@ -191,10 +191,8 @@ class ClientController extends AsyncNotifier<int> {
|
|||
Future<RoomSummary> getRoomSummary(JoinRoomRequest request) async =>
|
||||
.fromJson(await _sendCommand("get_room_summary", request.toJson()));
|
||||
|
||||
Future<void> leaveRoom(Room room) async {
|
||||
if (room.metadata == null) return;
|
||||
await _sendCommand("leave_room", {"room_id": room.metadata!.id});
|
||||
}
|
||||
Future<void> leaveRoom(String roomId) =>
|
||||
_sendCommand("leave_room", {"room_id": roomId});
|
||||
|
||||
Future<IList<Event>> getRoomState(GetRoomStateRequest request) async {
|
||||
Future<List?> getState(GetRoomStateRequest request) async =>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import "dart:async";
|
||||
import "dart:ffi";
|
||||
|
||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:nexus/controllers/account_data.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/controllers/client_state.dart";
|
||||
import "package:nexus/controllers/init_complete.dart";
|
||||
import "package:nexus/controllers/invites.dart";
|
||||
import "package:nexus/controllers/rooms.dart";
|
||||
import "package:nexus/controllers/space_edges.dart";
|
||||
import "package:nexus/controllers/sync_status.dart";
|
||||
|
|
@ -61,10 +63,12 @@ class GomuksListenerController extends AsyncNotifier<void> {
|
|||
final syncData = SyncData.fromJson(decodedMuksEvent);
|
||||
final roomProvider = RoomsController.provider;
|
||||
final accountDataProvider = AccountDataController.provider;
|
||||
final invitesProvider = InvitesController.provider;
|
||||
|
||||
if (syncData.clearState) {
|
||||
ref.invalidate(roomProvider);
|
||||
ref.invalidate(accountDataProvider);
|
||||
ref.invalidate(invitesProvider);
|
||||
}
|
||||
|
||||
ref
|
||||
|
|
@ -73,6 +77,13 @@ class GomuksListenerController extends AsyncNotifier<void> {
|
|||
ref
|
||||
.read(accountDataProvider.notifier)
|
||||
.update(syncData.accountData);
|
||||
ref
|
||||
.read(invitesProvider.notifier)
|
||||
.update(
|
||||
syncData.invitedRooms,
|
||||
joinedRooms: syncData.rooms.keys.toISet(),
|
||||
leftRooms: syncData.leftRooms,
|
||||
);
|
||||
|
||||
if (syncData.topLevelSpaces case final topLevelSpaces?) {
|
||||
ref
|
||||
|
|
|
|||
117
lib/controllers/invites.dart
Normal file
117
lib/controllers/invites.dart
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import "package:collection/collection.dart";
|
||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/controllers/client_state.dart";
|
||||
import "package:nexus/helpers/extensions/get_localpart.dart";
|
||||
import "package:nexus/models/content/avatar.dart";
|
||||
import "package:nexus/models/content/canonical_alias.dart";
|
||||
import "package:nexus/models/content/content.dart";
|
||||
import "package:nexus/models/content/create.dart";
|
||||
import "package:nexus/models/content/encryption.dart";
|
||||
import "package:nexus/models/content/history_visibility.dart";
|
||||
import "package:nexus/models/content/join_rules.dart";
|
||||
import "package:nexus/models/content/membership.dart";
|
||||
import "package:nexus/models/content/name.dart";
|
||||
import "package:nexus/models/content/topic.dart";
|
||||
import "package:nexus/models/invited_room.dart";
|
||||
import "package:nexus/models/room_summary.dart";
|
||||
|
||||
/// Pending invites, newest first.
|
||||
class InvitesController extends Notifier<IList<RoomSummary>> {
|
||||
@override
|
||||
IList<RoomSummary> build() => .new();
|
||||
|
||||
/// Adds new invites, and removes invites for rooms that have since been
|
||||
/// joined or left.
|
||||
void update(
|
||||
IList<InvitedRoom> invitedRooms, {
|
||||
ISet<String> joinedRooms = const .empty(),
|
||||
ISet<String> leftRooms = const .empty(),
|
||||
}) {
|
||||
final userId = ref.read(ClientStateController.provider)?.userId;
|
||||
|
||||
RoomSummary toSummary(InvitedRoom invite) {
|
||||
final summary = invite.inviteState.fold(
|
||||
RoomSummary(roomId: invite.roomId, membership: .invite),
|
||||
(summary, event) => switch (event.content) {
|
||||
NameContent(:final name) when name.isNotEmpty => summary.copyWith(
|
||||
name: name,
|
||||
),
|
||||
AvatarContent(:final url) => summary.copyWith(avatarUrl: url),
|
||||
TopicContent(:final topic) when topic.isNotEmpty => summary.copyWith(
|
||||
topic: topic,
|
||||
),
|
||||
CanonicalAliasContent(:final alias) => summary.copyWith(
|
||||
canonicalAlias: alias,
|
||||
),
|
||||
JoinRulesContent(:final joinRule) => summary.copyWith(
|
||||
joinRule: joinRule,
|
||||
),
|
||||
CreateContent(:final type, :final roomVersion) => summary.copyWith(
|
||||
roomType: type,
|
||||
roomVersion: roomVersion,
|
||||
),
|
||||
EncryptionContent(:final algorithm) => summary.copyWith(
|
||||
encryption: algorithm,
|
||||
),
|
||||
HistoryVisibilityContent(:final historyVisibility) =>
|
||||
summary.copyWith(
|
||||
worldReadable: historyVisibility == .worldReadable,
|
||||
),
|
||||
MembershipContent(:final isDirect) when event.stateKey == userId =>
|
||||
summary.copyWith(invitedBy: event.sender, isDirect: isDirect),
|
||||
_ => summary,
|
||||
},
|
||||
);
|
||||
|
||||
// The inviter is only known once the user's own invite has been found
|
||||
final inviterProfile = switch (invite.inviteState
|
||||
.firstWhereOrNull(
|
||||
(event) =>
|
||||
event.type == EventType.membership.type &&
|
||||
event.stateKey == summary.invitedBy,
|
||||
)
|
||||
?.content) {
|
||||
MembershipContent content => content,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
return summary.copyWith(
|
||||
inviterProfile: inviterProfile,
|
||||
// Direct messages usually have no name, so use the inviter's
|
||||
name:
|
||||
summary.name ??
|
||||
(summary.isDirect
|
||||
? inviterProfile?.displayName ?? summary.invitedBy?.localpart
|
||||
: null),
|
||||
);
|
||||
}
|
||||
|
||||
final newInvites = invitedRooms.map(toSummary).toIList();
|
||||
final newRoomIds = newInvites.map((invite) => invite.roomId).toISet();
|
||||
final removed = joinedRooms.addAll(leftRooms);
|
||||
|
||||
state = newInvites
|
||||
.addAll(state.where((invite) => !newRoomIds.contains(invite.roomId)))
|
||||
.removeWhere((invite) => removed.contains(invite.roomId));
|
||||
}
|
||||
|
||||
void remove(String roomId) =>
|
||||
state = state.removeWhere((invite) => invite.roomId == roomId);
|
||||
|
||||
Future<void> decline(String roomId) async {
|
||||
await ref.read(ClientController.provider.notifier).leaveRoom(roomId);
|
||||
remove(roomId);
|
||||
}
|
||||
|
||||
/// Declines every invite, removing each as it succeeds. If any fail, the
|
||||
/// first error is thrown once all have finished.
|
||||
Future<void> declineAll() =>
|
||||
Future.wait(state.map((invite) => decline(invite.roomId)));
|
||||
|
||||
static final provider =
|
||||
NotifierProvider<InvitesController, IList<RoomSummary>>(
|
||||
InvitesController.new,
|
||||
);
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import "dart:io";
|
|||
import "package:flutter/services.dart";
|
||||
import "package:material_ui/material_ui.dart";
|
||||
import "package:nexus/controllers/portal.dart";
|
||||
import "package:nexus/controllers/sidebar_page.dart";
|
||||
import "package:nexus/main.dart";
|
||||
import "package:flutter_local_notifications/flutter_local_notifications.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
|
|
@ -35,6 +36,7 @@ class NotificationController
|
|||
|
||||
if (int.tryParse(details.payload!) case final rowId?) {
|
||||
navigatorKey.currentState?.popUntil((route) => route.isFirst);
|
||||
ref.read(SidebarPageController.provider.notifier).set(.notifications);
|
||||
ref
|
||||
.read(NotificationRequestController.provider.notifier)
|
||||
.request(rowId);
|
||||
|
|
@ -53,6 +55,7 @@ class NotificationController
|
|||
|
||||
if (int.tryParse(call.arguments) case int rowId) {
|
||||
navigatorKey.currentState?.popUntil((route) => route.isFirst);
|
||||
ref.read(SidebarPageController.provider.notifier).set(.notifications);
|
||||
ref
|
||||
.read(NotificationRequestController.provider.notifier)
|
||||
.request(rowId);
|
||||
|
|
|
|||
13
lib/controllers/sidebar_page.dart
Normal file
13
lib/controllers/sidebar_page.dart
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/models/sidebar_page.dart";
|
||||
|
||||
class SidebarPageController extends Notifier<SidebarPage> {
|
||||
@override
|
||||
SidebarPage build() => .rooms;
|
||||
|
||||
void set(SidebarPage page) => state = page;
|
||||
|
||||
static final provider = NotifierProvider<SidebarPageController, SidebarPage>(
|
||||
SidebarPageController.new,
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue