Watch
1
0
Fork
You've already forked nexus
0
forked from Nexus/nexus

add support for creating rooms

Fixes #81
This commit is contained in:
Henry Hiles 2026-10-15 19:49:04 -04:00
commit ed84410039
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
13 changed files with 540 additions and 66 deletions

View file

@ -0,0 +1,111 @@
import "dart:async";
import "package:collection/collection.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:material_ui/material_ui.dart";
import "package:nexus/controllers/client.dart";
import "package:nexus/controllers/rooms.dart";
import "package:nexus/controllers/sidebar_page.dart";
import "package:nexus/controllers/spaces.dart";
import "package:nexus/helpers/extensions/focus_room.dart";
import "package:nexus/helpers/extensions/get_all_child_rooms.dart";
import "package:nexus/helpers/extensions/with_progress_snackbar.dart";
import "package:nexus/models/requests/create_room.dart";
extension CreateRoom on BuildContext {
/// Creates a room, showing progress and errors in snackbars, and opens it
/// once it has synced. [request] may be a future, so any lookups needed to
/// build it happen while the progress snackbar is shown.
///
/// Returns the created room's ID, or null if creating it failed.
Future<String?> createRoom(
CreateRoomRequest request, {
required String name,
}) async {
final container = ProviderScope.containerOf(this);
final messenger = ScaffoldMessenger.of(this);
final result = await withProgressSnackbar("Creating $name...", () async {
final roomId = await container
.read(ClientController.provider.notifier)
.createRoom(request);
// Wait for the room to show up in the room list (under DMs, if it's
// one) before opening it.
final synced = Completer<bool>();
final subscription = container.listen(
SpacesController.provider.select(
(async) =>
async.value?.any(
(space) =>
space.id == roomId ||
((!request.isDirect || space.id == "dms") &&
space.allChildRooms.any(
(room) => room.metadata?.id == roomId,
)),
) ??
false,
),
(_, found) {
if (found && !synced.isCompleted) synced.complete(true);
},
fireImmediately: true,
);
return (
roomId,
await synced.future
.timeout(const .new(seconds: 15), onTimeout: () => false)
.whenComplete(subscription.close),
);
});
if (result == null) return null;
final (roomId, synced) = result;
if (synced && await container.focusRoom(roomId)) {
container.read(SidebarPageController.provider.notifier).set(.rooms);
} else {
messenger.showSnackBar(
.new(
content: Text("Created $name, but it hasn't finished syncing yet."),
),
);
}
return roomId;
}
/// Opens the existing DM with [userId], or creates one if there isn't one.
Future<void> startDirectMessage(
String userId, {
required String name,
bool encrypted = true,
String? inviteReason,
}) async {
final container = ProviderScope.containerOf(this);
final existing = container
.read(RoomsController.provider)
.values
.firstWhereOrNull((room) => room.metadata?.dmUserId == userId)
?.metadata
?.id;
if (existing != null) {
if (await container.focusRoom(existing)) {
container.read(SidebarPageController.provider.notifier).set(.rooms);
}
return;
}
await createRoom(
.new(
isDirect: true,
preset: .trustedPrivateChat,
invite: .new([userId]),
inviteReason: inviteReason,
initialState: encrypted ? .new([.encryption]) : const .empty(),
),
name: "a DM with $name",
);
}
}