treewide sweepfixes
This commit is contained in:
parent
8fda43ccf8
commit
012ecd9249
41 changed files with 270 additions and 386 deletions
|
|
@ -23,7 +23,7 @@ class AttachmentController(final String roomId)
|
||||||
);
|
);
|
||||||
|
|
||||||
final content = await ref
|
final content = await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.uploadMedia(.new(path: file.path, encrypt: isEncrypted));
|
.uploadMedia(.new(path: file.path, encrypt: isEncrypted));
|
||||||
|
|
||||||
state = (filename, content);
|
state = (filename, content);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ class AuthUrlController(final Uri homeserver)
|
||||||
extends AsyncNotifier<OAuthAuthCodeResponse> {
|
extends AsyncNotifier<OAuthAuthCodeResponse> {
|
||||||
@override
|
@override
|
||||||
Future<OAuthAuthCodeResponse> build() async => ref
|
Future<OAuthAuthCodeResponse> build() async => ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.getAuthUrl(
|
.getAuthUrl(
|
||||||
.new(
|
.new(
|
||||||
homeserverUrl: homeserver,
|
homeserverUrl: homeserver,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import "package:nexus/controllers/client.dart";
|
||||||
class ClientIdController(final Uri homeserver) extends AsyncNotifier<String> {
|
class ClientIdController(final Uri homeserver) extends AsyncNotifier<String> {
|
||||||
@override
|
@override
|
||||||
Future<String> build() => ref
|
Future<String> build() => ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.registerClient(
|
.registerClient(
|
||||||
.new(
|
.new(
|
||||||
clientName: "Nexus",
|
clientName: "Nexus",
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,13 @@ class EventController(final GetEventRequest request)
|
||||||
if (event != null) return event;
|
if (event != null) return event;
|
||||||
|
|
||||||
final fetchedEvent = await ref
|
final fetchedEvent = await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.getEvent(request)
|
.getEvent(request)
|
||||||
.onError((_, _) => null);
|
.onError((_, _) => null);
|
||||||
|
|
||||||
if (fetchedEvent != null) {
|
if (fetchedEvent != null) {
|
||||||
ref
|
ref
|
||||||
.watch(RoomsController.provider.notifier)
|
.read(RoomsController.provider.notifier)
|
||||||
.update(
|
.update(
|
||||||
.new({
|
.new({
|
||||||
request.roomId: .new(
|
request.roomId: .new(
|
||||||
|
|
|
||||||
|
|
@ -40,28 +40,23 @@ class GomuksListenerController extends AsyncNotifier<void> {
|
||||||
switch (muksEventType) {
|
switch (muksEventType) {
|
||||||
case "client_state":
|
case "client_state":
|
||||||
ref
|
ref
|
||||||
.watch(ClientStateController.provider.notifier)
|
.read(ClientStateController.provider.notifier)
|
||||||
.set(.fromJson(decodedMuksEvent));
|
.set(.fromJson(decodedMuksEvent));
|
||||||
break;
|
|
||||||
case "sync_status":
|
case "sync_status":
|
||||||
ref
|
ref
|
||||||
.watch(SyncStatusController.provider.notifier)
|
.read(SyncStatusController.provider.notifier)
|
||||||
.set(.fromJson(decodedMuksEvent));
|
.set(.fromJson(decodedMuksEvent));
|
||||||
break;
|
|
||||||
case "init_complete":
|
case "init_complete":
|
||||||
ref.watch(InitCompleteController.provider.notifier).complete();
|
ref.read(InitCompleteController.provider.notifier).complete();
|
||||||
break;
|
|
||||||
case "send_complete":
|
case "send_complete":
|
||||||
final event = Event.fromJson(decodedMuksEvent["event"]);
|
final event = Event.fromJson(decodedMuksEvent["event"]);
|
||||||
ref
|
ref
|
||||||
.watch(RoomsController.provider.notifier)
|
.read(RoomsController.provider.notifier)
|
||||||
.update(
|
.update(
|
||||||
.new({
|
.new({
|
||||||
event.roomId: .new(events: .new({event.rowId: event})),
|
event.roomId: .new(events: .new({event.rowId: event})),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
|
||||||
case "sync_complete":
|
case "sync_complete":
|
||||||
final syncData = SyncData.fromJson(decodedMuksEvent);
|
final syncData = SyncData.fromJson(decodedMuksEvent);
|
||||||
final roomProvider = RoomsController.provider;
|
final roomProvider = RoomsController.provider;
|
||||||
|
|
@ -73,28 +68,27 @@ class GomuksListenerController extends AsyncNotifier<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ref
|
ref
|
||||||
.watch(roomProvider.notifier)
|
.read(roomProvider.notifier)
|
||||||
.update(syncData.rooms, syncData.leftRooms);
|
.update(syncData.rooms, syncData.leftRooms);
|
||||||
ref
|
ref
|
||||||
.watch(accountDataProvider.notifier)
|
.read(accountDataProvider.notifier)
|
||||||
.update(syncData.accountData);
|
.update(syncData.accountData);
|
||||||
|
|
||||||
if (syncData.topLevelSpaces != null) {
|
if (syncData.topLevelSpaces case final topLevelSpaces?) {
|
||||||
ref
|
ref
|
||||||
.watch(TopLevelSpacesController.provider.notifier)
|
.read(TopLevelSpacesController.provider.notifier)
|
||||||
.set(syncData.topLevelSpaces!);
|
.set(topLevelSpaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (syncData.spaceEdges != null) {
|
if (syncData.spaceEdges case final spaceEdges?) {
|
||||||
ref
|
ref
|
||||||
.watch(SpaceEdgesController.provider.notifier)
|
.read(SpaceEdgesController.provider.notifier)
|
||||||
.set(syncData.spaceEdges!);
|
.set(spaceEdges);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ref
|
// ref
|
||||||
// .watch(SyncStatusController.provider.notifier)
|
// .read(SyncStatusController.provider.notifier)
|
||||||
// .set(SyncStatus.fromJson(decodedMuksEvent));
|
// .set(SyncStatus.fromJson(decodedMuksEvent));
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
debugPrint("Unhandled event: $muksEventType");
|
debugPrint("Unhandled event: $muksEventType");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ class KeyController(final String key) extends AsyncNotifier<String?> {
|
||||||
ref.watch(SharedPrefsController.provider).getString(key);
|
ref.watch(SharedPrefsController.provider).getString(key);
|
||||||
|
|
||||||
Future<void> set(String? value) async {
|
Future<void> set(String? value) async {
|
||||||
final prefs = ref.watch(SharedPrefsController.provider);
|
final prefs = ref.read(SharedPrefsController.provider);
|
||||||
state = .data(value);
|
state = .data(value);
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ class MemberListOpenedController extends AsyncNotifier<bool> {
|
||||||
|
|
||||||
Future<void> set(bool value) async {
|
Future<void> set(bool value) async {
|
||||||
state = .data(value);
|
state = .data(value);
|
||||||
await ref.watch(SharedPrefsController.provider).setBool(key, value);
|
await ref.read(SharedPrefsController.provider).setBool(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static final provider =
|
static final provider =
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class MembersController(final String roomId)
|
||||||
|
|
||||||
if (!room.hasFetchedMembers) {
|
if (!room.hasFetchedMembers) {
|
||||||
final fetchedState = await ref
|
final fetchedState = await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.getRoomState(
|
.getRoomState(
|
||||||
GetRoomStateRequest(
|
GetRoomStateRequest(
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ class NotificationsController(final NotificationsRequest request)
|
||||||
final lastNotification = currentNotifications.lastOrNull;
|
final lastNotification = currentNotifications.lastOrNull;
|
||||||
final lastTs = lastNotification == null
|
final lastTs = lastNotification == null
|
||||||
? null
|
? null
|
||||||
: ref.watch(
|
: ref.read(
|
||||||
RoomsController.provider.select(
|
RoomsController.provider.select(
|
||||||
(rooms) => rooms[lastNotification.$2]
|
(rooms) => rooms[lastNotification.$2]
|
||||||
?.events[lastNotification.$1]
|
?.events[lastNotification.$1]
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ class PushKeyController(final String instance) extends AsyncNotifier<String?> {
|
||||||
|
|
||||||
Future<void> set(String? value) async {
|
Future<void> set(String? value) async {
|
||||||
final provider = KeyController.provider(KeyController.pushKeyKey);
|
final provider = KeyController.provider(KeyController.pushKeyKey);
|
||||||
final notifier = ref.watch(provider.notifier);
|
final notifier = ref.read(provider.notifier);
|
||||||
final prefs = IMap(json.decode(await ref.watch(provider.future) ?? "{}"));
|
final prefs = IMap(json.decode(await ref.read(provider.future) ?? "{}"));
|
||||||
|
|
||||||
state = .data(value);
|
state = .data(value);
|
||||||
notifier.set(
|
notifier.set(
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class ReactionsController(final ReactionsConfig config)
|
||||||
|
|
||||||
final reactionEvents = eventInfo?.$2.isNotEmpty == true
|
final reactionEvents = eventInfo?.$2.isNotEmpty == true
|
||||||
? await ref
|
? await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.getRelatedEvents(
|
.getRelatedEvents(
|
||||||
.new(
|
.new(
|
||||||
roomId: config.roomId,
|
roomId: config.roomId,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class RecentEmojiController extends Notifier<IList<RecentEmoji>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> add(String emoji) => ref
|
Future<void> add(String emoji) => ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.setAccountData(
|
.setAccountData(
|
||||||
.new(
|
.new(
|
||||||
type: AccountData.recentEmojiKey,
|
type: AccountData.recentEmojiKey,
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ class RoomChatController(final ChatParams info)
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteMessage(Event event, {String? reason}) => ref
|
Future<void> deleteMessage(Event event, {String? reason}) => ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.redactEvent(
|
.redactEvent(
|
||||||
RedactEventRequest(
|
RedactEventRequest(
|
||||||
eventId: event.eventId,
|
eventId: event.eventId,
|
||||||
|
|
@ -146,7 +146,7 @@ class RoomChatController(final ChatParams info)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.events.isEmpty) {
|
if (response.events.isEmpty) {
|
||||||
state = .data(state.value);
|
state = .data(chat);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref
|
ref
|
||||||
|
|
@ -234,7 +234,7 @@ class RoomChatController(final ChatParams info)
|
||||||
Content? baseContent;
|
Content? baseContent;
|
||||||
final relatedEvent = relatedEventRowId == null
|
final relatedEvent = relatedEventRowId == null
|
||||||
? null
|
? null
|
||||||
: ref.watch(
|
: ref.read(
|
||||||
RoomsController.provider.select(
|
RoomsController.provider.select(
|
||||||
(rooms) => rooms[info.roomId]?.events[relatedEventRowId],
|
(rooms) => rooms[info.roomId]?.events[relatedEventRowId],
|
||||||
),
|
),
|
||||||
|
|
@ -267,9 +267,7 @@ class RoomChatController(final ChatParams info)
|
||||||
baseContent: baseContent,
|
baseContent: baseContent,
|
||||||
mentions: Mentions(
|
mentions: Mentions(
|
||||||
userIds: [
|
userIds: [
|
||||||
if (shouldMention == true &&
|
if (shouldMention && relatedEvent != null && relationType == .reply)
|
||||||
relatedEvent != null &&
|
|
||||||
relationType == RelationType.reply)
|
|
||||||
relatedEvent.sender,
|
relatedEvent.sender,
|
||||||
].toIList(),
|
].toIList(),
|
||||||
room: taggedMessage.contains("@room"),
|
room: taggedMessage.contains("@room"),
|
||||||
|
|
@ -307,24 +305,20 @@ class RoomChatController(final ChatParams info)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final reactionEvents = allReactionEvents
|
final reactionEvent = allReactionEvents?.firstWhereOrNull(
|
||||||
?.where((event) => event.redactedBy == null)
|
(event) =>
|
||||||
.toIList();
|
event.redactedBy == null &&
|
||||||
|
switch (event.content) {
|
||||||
final reactionEvent = reactionEvents?.firstWhereOrNull(
|
ReactionContent(:final key) =>
|
||||||
(event) => switch (event.content) {
|
key == reaction && event.sender == userId,
|
||||||
ReactionContent(:final key) =>
|
_ => false,
|
||||||
key == reaction && event.sender == userId,
|
},
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (reactionEvent != null) {
|
if (reactionEvent != null) {
|
||||||
await ref
|
await client.redactEvent(
|
||||||
.watch(ClientController.provider.notifier)
|
.new(eventId: reactionEvent.eventId, roomId: info.roomId),
|
||||||
.redactEvent(
|
);
|
||||||
.new(eventId: reactionEvent.eventId, roomId: info.roomId),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class RoomsController extends Notifier<IMap<String, Room>> {
|
||||||
hasMore: incoming.hasMore,
|
hasMore: incoming.hasMore,
|
||||||
clientSticky: incoming.resetClientSticky
|
clientSticky: incoming.resetClientSticky
|
||||||
? incoming.clientSticky
|
? incoming.clientSticky
|
||||||
: (incoming.clientSticky.isEmpty == true
|
: (incoming.clientSticky.isEmpty
|
||||||
? existing.clientSticky
|
? existing.clientSticky
|
||||||
: existing.clientSticky.addAll(
|
: existing.clientSticky.addAll(
|
||||||
incoming.clientSticky,
|
incoming.clientSticky,
|
||||||
|
|
@ -73,9 +73,9 @@ class RoomsController extends Notifier<IMap<String, Room>> {
|
||||||
incoming.hasFetchedMembers || existing.hasFetchedMembers,
|
incoming.hasFetchedMembers || existing.hasFetchedMembers,
|
||||||
hasFetchedState:
|
hasFetchedState:
|
||||||
incoming.hasFetchedState || existing.hasFetchedState,
|
incoming.hasFetchedState || existing.hasFetchedState,
|
||||||
timeline: (incoming.reset
|
timeline: incoming.reset
|
||||||
? incoming.timeline
|
? incoming.timeline
|
||||||
: existing.timeline.addAll(incoming.timeline)),
|
: existing.timeline.addAll(incoming.timeline),
|
||||||
receipts: incoming.receipts.entries.fold(
|
receipts: incoming.receipts.entries.fold(
|
||||||
existing.receipts,
|
existing.receipts,
|
||||||
(receiptAcc, event) => receiptAcc.add(
|
(receiptAcc, event) => receiptAcc.add(
|
||||||
|
|
@ -88,12 +88,7 @@ class RoomsController extends Notifier<IMap<String, Room>> {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
final prunedList = leftRooms.fold(
|
state = leftRooms.fold(merged, (acc, roomId) => acc.remove(roomId));
|
||||||
merged,
|
|
||||||
(acc, roomId) => acc.remove(roomId),
|
|
||||||
);
|
|
||||||
|
|
||||||
state = prunedList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static final provider = NotifierProvider<RoomsController, IMap<String, Room>>(
|
static final provider = NotifierProvider<RoomsController, IMap<String, Room>>(
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import "dart:convert";
|
import "dart:convert";
|
||||||
|
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:nexus/controllers/settings_file.dart";
|
import "package:nexus/controllers/settings_file.dart";
|
||||||
import "package:nexus/models/settings.dart";
|
import "package:nexus/models/settings.dart";
|
||||||
|
|
@ -17,7 +18,7 @@ class SettingsController extends AsyncNotifier<Settings> {
|
||||||
|
|
||||||
Future<void> set(Settings settings) async {
|
Future<void> set(Settings settings) async {
|
||||||
state = .data(settings);
|
state = .data(settings);
|
||||||
final file = await ref.watch(SettingsFileController.provider.future);
|
final file = await ref.read(SettingsFileController.provider.future);
|
||||||
await file.writeAsString(json.encode(settings.toJson()));
|
await file.writeAsString(json.encode(settings.toJson()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import "dart:io";
|
import "dart:io";
|
||||||
|
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:path/path.dart";
|
import "package:path/path.dart";
|
||||||
import "package:path_provider/path_provider.dart";
|
import "package:path_provider/path_provider.dart";
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ class SettingsSectionsController
|
||||||
options: ThemeMode.values,
|
options: ThemeMode.values,
|
||||||
getName: (option) => toBeginningOfSentenceCase(option.name),
|
getName: (option) => toBeginningOfSentenceCase(option.name),
|
||||||
onChanged: (value) => ref
|
onChanged: (value) => ref
|
||||||
.watch(SettingsController.provider.notifier)
|
.read(SettingsController.provider.notifier)
|
||||||
.set(settings.copyWith(theme: value))
|
.set(settings.copyWith(theme: value))
|
||||||
.onError(showError),
|
.onError(showError),
|
||||||
),
|
),
|
||||||
|
|
@ -67,7 +67,7 @@ class SettingsSectionsController
|
||||||
Platform.isMacOS ||
|
Platform.isMacOS ||
|
||||||
Platform.isWindows)
|
Platform.isWindows)
|
||||||
? (value) => ref
|
? (value) => ref
|
||||||
.watch(SettingsController.provider.notifier)
|
.read(SettingsController.provider.notifier)
|
||||||
.set(settings.copyWith(useDynamicTheming: value))
|
.set(settings.copyWith(useDynamicTheming: value))
|
||||||
.onError(showError)
|
.onError(showError)
|
||||||
: null,
|
: null,
|
||||||
|
|
@ -89,7 +89,7 @@ class SettingsSectionsController
|
||||||
secondary: Icon(icon),
|
secondary: Icon(icon),
|
||||||
value: settings.showAllInHome,
|
value: settings.showAllInHome,
|
||||||
onChanged: (value) => ref
|
onChanged: (value) => ref
|
||||||
.watch(SettingsController.provider.notifier)
|
.read(SettingsController.provider.notifier)
|
||||||
.set(settings.copyWith(showAllInHome: value))
|
.set(settings.copyWith(showAllInHome: value))
|
||||||
.onError(showError),
|
.onError(showError),
|
||||||
),
|
),
|
||||||
|
|
@ -106,7 +106,7 @@ class SettingsSectionsController
|
||||||
secondary: Icon(icon),
|
secondary: Icon(icon),
|
||||||
value: settings.showMinimizeButton,
|
value: settings.showMinimizeButton,
|
||||||
onChanged: (value) => ref
|
onChanged: (value) => ref
|
||||||
.watch(SettingsController.provider.notifier)
|
.read(SettingsController.provider.notifier)
|
||||||
.set(settings.copyWith(showMinimizeButton: value))
|
.set(settings.copyWith(showMinimizeButton: value))
|
||||||
.onError(showError),
|
.onError(showError),
|
||||||
),
|
),
|
||||||
|
|
@ -122,7 +122,7 @@ class SettingsSectionsController
|
||||||
value: settings.linuxMobileMode,
|
value: settings.linuxMobileMode,
|
||||||
onChanged: Platform.isLinux
|
onChanged: Platform.isLinux
|
||||||
? (value) => ref
|
? (value) => ref
|
||||||
.watch(SettingsController.provider.notifier)
|
.read(SettingsController.provider.notifier)
|
||||||
.set(settings.copyWith(linuxMobileMode: value))
|
.set(settings.copyWith(linuxMobileMode: value))
|
||||||
.onError(showError)
|
.onError(showError)
|
||||||
: null,
|
: null,
|
||||||
|
|
@ -190,14 +190,14 @@ class SettingsSectionsController
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (await ref
|
if (await ref
|
||||||
.watch(
|
.read(
|
||||||
NotificationController
|
NotificationController
|
||||||
.provider
|
.provider
|
||||||
.notifier,
|
.notifier,
|
||||||
)
|
)
|
||||||
.requestPermissions()) {
|
.requestPermissions()) {
|
||||||
await ref
|
await ref
|
||||||
.watch(
|
.read(
|
||||||
UnifiedPushController
|
UnifiedPushController
|
||||||
.provider
|
.provider
|
||||||
.notifier,
|
.notifier,
|
||||||
|
|
@ -208,7 +208,7 @@ class SettingsSectionsController
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await ref
|
await ref
|
||||||
.watch(
|
.read(
|
||||||
UnifiedPushController
|
UnifiedPushController
|
||||||
.provider
|
.provider
|
||||||
.notifier,
|
.notifier,
|
||||||
|
|
@ -268,7 +268,7 @@ class SettingsSectionsController
|
||||||
onChanged: specVersionsResponse.maybeWhen(
|
onChanged: specVersionsResponse.maybeWhen(
|
||||||
data: (_) =>
|
data: (_) =>
|
||||||
(value) => ref
|
(value) => ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.setAccountData(
|
.setAccountData(
|
||||||
.new(
|
.new(
|
||||||
type: AccountData.invitePermissionConfigKey,
|
type: AccountData.invitePermissionConfigKey,
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@ class SpacesController extends AsyncNotifier<IList<Space>> {
|
||||||
final spaceEdges = ref.watch(SpaceEdgesController.provider);
|
final spaceEdges = ref.watch(SpaceEdgesController.provider);
|
||||||
final accountData = ref.watch(AccountDataController.provider);
|
final accountData = ref.watch(AccountDataController.provider);
|
||||||
|
|
||||||
final childrenById = {
|
final childrenById = spaceEdges.map(
|
||||||
for (final entry in spaceEdges.entries)
|
(spaceId, edges) =>
|
||||||
entry.key: entry.value.map((e) => e.childId).toList(),
|
MapEntry(spaceId, edges.map((edge) => edge.childId).toList()),
|
||||||
};
|
);
|
||||||
|
|
||||||
Set<String> collectDescendants(String startId) {
|
Set<String> collectDescendants(String startId) {
|
||||||
final visited = <String>{};
|
final visited = <String>{};
|
||||||
|
|
@ -78,21 +78,22 @@ class SpacesController extends AsyncNotifier<IList<Space>> {
|
||||||
|
|
||||||
final spaces = topLevelSpaceIds.map(buildSpace).toIList();
|
final spaces = topLevelSpaceIds.map(buildSpace).toIList();
|
||||||
|
|
||||||
final usedRooms = {
|
final usedRooms = spaces
|
||||||
for (final space in spaces) ...[
|
.expand(
|
||||||
...space.children,
|
(space) => [
|
||||||
...space.subSpaces.expand((s) => s.children),
|
...space.children,
|
||||||
],
|
...space.subSpaces.expand((s) => s.children),
|
||||||
}.nonNulls.toISet();
|
],
|
||||||
|
)
|
||||||
|
.toISet();
|
||||||
|
final usedRoomIds = usedRooms.map((room) => room.metadata?.id).toSet();
|
||||||
|
|
||||||
final directMessages = accountData.directMessages.values.flattened;
|
final directMessages = accountData.directMessages.values.flattened.toSet();
|
||||||
|
|
||||||
final otherRooms = rooms.entries
|
final otherRooms = rooms.entries
|
||||||
.where(
|
.where(
|
||||||
(e) =>
|
(e) =>
|
||||||
!usedRooms
|
!usedRoomIds.contains(e.key) &&
|
||||||
.map((element) => element.metadata?.id)
|
|
||||||
.contains(e.key) &&
|
|
||||||
!topLevelSpaceIds.contains(e.key) &&
|
!topLevelSpaceIds.contains(e.key) &&
|
||||||
!childrenById.containsKey(e.key),
|
!childrenById.containsKey(e.key),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class UnifiedPushAllowedController extends AsyncNotifier<String?> {
|
||||||
}
|
}
|
||||||
|
|
||||||
final capabilities = await ref
|
final capabilities = await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.getCapabilities();
|
.getCapabilities();
|
||||||
|
|
||||||
if (capabilities.webpush?.vapid == null) {
|
if (capabilities.webpush?.vapid == null) {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ class UrlPreviewController(final Uri url)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await ref
|
return await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.getUrlPreview(url);
|
.getUrlPreview(url);
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
debugPrintStack(label: error.toString(), stackTrace: stackTrace);
|
debugPrintStack(label: error.toString(), stackTrace: stackTrace);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import "dart:async";
|
import "dart:async";
|
||||||
|
|
||||||
import "package:collection/collection.dart";
|
import "package:collection/collection.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:nexus/controllers/members.dart";
|
import "package:nexus/controllers/members.dart";
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ extension BuildEventOptions on Event {
|
||||||
)).notifier;
|
)).notifier;
|
||||||
|
|
||||||
final isPinned = ref
|
final isPinned = ref
|
||||||
.watch(PinnedIdsController.provider(roomId))
|
.read(PinnedIdsController.provider(roomId))
|
||||||
.contains(eventId);
|
.contains(eventId);
|
||||||
|
|
||||||
void showReasonDialog({
|
void showReasonDialog({
|
||||||
|
|
@ -86,7 +86,7 @@ extension BuildEventOptions on Event {
|
||||||
}
|
}
|
||||||
|
|
||||||
return .new([
|
return .new([
|
||||||
if (ref.watch(
|
if (ref.read(
|
||||||
PowerLevelController.provider(
|
PowerLevelController.provider(
|
||||||
.new(eventType: .reaction, roomId: roomId),
|
.new(eventType: .reaction, roomId: roomId),
|
||||||
),
|
),
|
||||||
|
|
@ -98,30 +98,33 @@ extension BuildEventOptions on Event {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: .center,
|
mainAxisAlignment: .center,
|
||||||
children: [
|
children: [
|
||||||
for (final emoji in {
|
...{
|
||||||
...ref
|
...ref
|
||||||
.watch(RecentEmojiController.provider)
|
.read(RecentEmojiController.provider)
|
||||||
.map((entry) => entry.emoji),
|
.map((entry) => entry.emoji),
|
||||||
"👍",
|
"👍",
|
||||||
"🤣",
|
"🤣",
|
||||||
"😭",
|
"😭",
|
||||||
"🤔",
|
"🤔",
|
||||||
}.take(3))
|
}
|
||||||
IconButton(
|
.take(3)
|
||||||
icon: Text(emoji),
|
.map(
|
||||||
onPressed: () async {
|
(emoji) => IconButton(
|
||||||
Navigator.of(context).pop();
|
icon: Text(emoji),
|
||||||
await ref
|
onPressed: () async {
|
||||||
.watch(notifier)
|
Navigator.of(context).pop();
|
||||||
.sendReaction(emoji, this)
|
await ref
|
||||||
.onError(showError);
|
.read(notifier)
|
||||||
|
.sendReaction(emoji, this)
|
||||||
|
.onError(showError);
|
||||||
|
|
||||||
await ref
|
await ref
|
||||||
.read(RecentEmojiController.provider.notifier)
|
.read(RecentEmojiController.provider.notifier)
|
||||||
.add(emoji)
|
.add(emoji)
|
||||||
.onError(showError);
|
.onError(showError);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.emoji_emotions),
|
icon: const Icon(Icons.emoji_emotions),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|
@ -135,7 +138,7 @@ extension BuildEventOptions on Event {
|
||||||
onSelection: (emoji) {
|
onSelection: (emoji) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
ref
|
ref
|
||||||
.watch(notifier)
|
.read(notifier)
|
||||||
.sendReaction(emoji, this)
|
.sendReaction(emoji, this)
|
||||||
.onError(showError);
|
.onError(showError);
|
||||||
},
|
},
|
||||||
|
|
@ -148,7 +151,7 @@ extension BuildEventOptions on Event {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
if (ref.watch(
|
if (ref.read(
|
||||||
PowerLevelController.provider(
|
PowerLevelController.provider(
|
||||||
.new(eventType: .message, roomId: roomId),
|
.new(eventType: .message, roomId: roomId),
|
||||||
),
|
),
|
||||||
|
|
@ -167,7 +170,7 @@ extension BuildEventOptions on Event {
|
||||||
child: const ListTile(leading: Icon(Icons.edit), title: Text("Edit")),
|
child: const ListTile(leading: Icon(Icons.edit), title: Text("Edit")),
|
||||||
),
|
),
|
||||||
|
|
||||||
if (ref.watch(
|
if (ref.read(
|
||||||
PowerLevelController.provider(
|
PowerLevelController.provider(
|
||||||
.state(eventType: .pinnedEvents, roomId: roomId),
|
.state(eventType: .pinnedEvents, roomId: roomId),
|
||||||
),
|
),
|
||||||
|
|
@ -225,7 +228,7 @@ extension BuildEventOptions on Event {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
if (ref.watch(
|
if (ref.read(
|
||||||
PowerLevelController.provider(
|
PowerLevelController.provider(
|
||||||
.redaction(targetUser: sender, roomId: roomId),
|
.redaction(targetUser: sender, roomId: roomId),
|
||||||
),
|
),
|
||||||
|
|
@ -239,7 +242,7 @@ extension BuildEventOptions on Event {
|
||||||
"This cannot be reversed.",
|
"This cannot be reversed.",
|
||||||
action: "Delete",
|
action: "Delete",
|
||||||
onConfirm: (reason) =>
|
onConfirm: (reason) =>
|
||||||
ref.watch(notifier).deleteMessage(this, reason: reason),
|
ref.read(notifier).deleteMessage(this, reason: reason),
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Icon(Icons.delete, color: danger),
|
leading: Icon(Icons.delete, color: danger),
|
||||||
|
|
@ -249,7 +252,7 @@ extension BuildEventOptions on Event {
|
||||||
|
|
||||||
if (eventId.isFake)
|
if (eventId.isFake)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () => ref.watch(notifier).hideClientSticky(rowId),
|
onTap: () => ref.read(notifier).hideClientSticky(rowId),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Icon(Icons.delete, color: danger),
|
leading: Icon(Icons.delete, color: danger),
|
||||||
title: Text("Hide", style: .new(color: danger)),
|
title: Text("Hide", style: .new(color: danger)),
|
||||||
|
|
@ -260,7 +263,7 @@ extension BuildEventOptions on Event {
|
||||||
onTap: () => showReasonDialog(
|
onTap: () => showReasonDialog(
|
||||||
title: "Report",
|
title: "Report",
|
||||||
description:
|
description:
|
||||||
"Report this this to your server administrators, "
|
"Report this event to your server administrators, "
|
||||||
"who can take action like banning this server or room.",
|
"who can take action like banning this server or room.",
|
||||||
action: "Report",
|
action: "Report",
|
||||||
onConfirm: (reason) => ref
|
onConfirm: (reason) => ref
|
||||||
|
|
|
||||||
23
lib/helpers/extensions/room_destinations.dart
Normal file
23
lib/helpers/extensions/room_destinations.dart
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:navigation_rail_m3e/navigation_rail_m3e.dart";
|
||||||
|
import "package:nexus/models/room.dart";
|
||||||
|
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||||
|
|
||||||
|
extension RoomDestinations on IList<Room> {
|
||||||
|
List<NavigationRailM3EDestination> toDestinations({required bool isDm}) =>
|
||||||
|
map(
|
||||||
|
(room) => NavigationRailM3EDestination(
|
||||||
|
label: room.metadata?.name ?? "Unnamed Room",
|
||||||
|
badgeCount: switch (room.metadata?.unreadNotifications) {
|
||||||
|
0 || null => room.metadata?.unreadMessages == 0 ? null : 0,
|
||||||
|
int unread => unread,
|
||||||
|
},
|
||||||
|
icon: AvatarOrHash(
|
||||||
|
room.metadata?.avatar,
|
||||||
|
room.metadata?.name ?? "Unnamed Room",
|
||||||
|
fallback: isDm ? null : const Icon(Icons.numbers),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).toList();
|
||||||
|
}
|
||||||
|
|
@ -51,7 +51,7 @@ extension ShowAboutDialog on BuildContext {
|
||||||
),
|
),
|
||||||
M3ECardColumn(
|
M3ECardColumn(
|
||||||
onTap: (index) =>
|
onTap: (index) =>
|
||||||
ref.watch(LaunchHelper.provider).launchUrl(switch (index) {
|
ref.read(LaunchHelper.provider).launchUrl(switch (index) {
|
||||||
0 => .https("git.federated.nexus", "nexus/nexus"),
|
0 => .https("git.federated.nexus", "nexus/nexus"),
|
||||||
_ => .https("liberapay.com", "QuadRadical"),
|
_ => .https("liberapay.com", "QuadRadical"),
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import "package:fluttertagger/fluttertagger.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:nexus/controllers/attachment.dart";
|
import "package:nexus/controllers/attachment.dart";
|
||||||
import "package:nexus/controllers/image_picker.dart";
|
import "package:nexus/controllers/image_picker.dart";
|
||||||
|
import "package:image_picker/image_picker.dart";
|
||||||
import "package:nexus/controllers/power_level.dart";
|
import "package:nexus/controllers/power_level.dart";
|
||||||
import "package:nexus/models/content/message.dart";
|
import "package:nexus/models/content/message.dart";
|
||||||
import "package:nexus/models/relation_type.dart";
|
import "package:nexus/models/relation_type.dart";
|
||||||
|
|
@ -89,6 +90,14 @@ class const Composer(
|
||||||
|
|
||||||
final attachment = ref.watch(AttachmentController.provider(roomId));
|
final attachment = ref.watch(AttachmentController.provider(roomId));
|
||||||
|
|
||||||
|
Future<void> attach(Future<XFile?> pick) async {
|
||||||
|
if (await pick case final file?) {
|
||||||
|
await ref
|
||||||
|
.read(AttachmentController.provider(roomId).notifier)
|
||||||
|
.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void send() {
|
void send() {
|
||||||
if (controller.value.text.isEmpty && attachment == null ||
|
if (controller.value.text.isEmpty && attachment == null ||
|
||||||
attachment != null && attachment.$2 == null) {
|
attachment != null && attachment.$2 == null) {
|
||||||
|
|
@ -173,53 +182,33 @@ class const Composer(
|
||||||
tooltip: "Add media",
|
tooltip: "Add media",
|
||||||
enabled: attachment == null,
|
enabled: attachment == null,
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
if (Platform.isAndroid || Platform.isIOS)
|
for (final (source, title, icon) in [
|
||||||
|
if (Platform.isAndroid || Platform.isIOS)
|
||||||
|
(
|
||||||
|
ImageSource.camera,
|
||||||
|
"Camera",
|
||||||
|
Icons.add_a_photo,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ImageSource.gallery,
|
||||||
|
"Gallery",
|
||||||
|
Icons.add_photo_alternate,
|
||||||
|
),
|
||||||
|
])
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text("Camera"),
|
title: Text(title),
|
||||||
leading: Icon(Icons.add_a_photo),
|
leading: Icon(icon),
|
||||||
),
|
),
|
||||||
onTap: () async => ref
|
onTap: () => attach(
|
||||||
.read(
|
ref
|
||||||
AttachmentController.provider(roomId)
|
.read(ImagePickerController.provider)
|
||||||
.notifier,
|
.pickImage(source: source),
|
||||||
)
|
).onError(showError),
|
||||||
.add(
|
|
||||||
(await ref
|
|
||||||
.watch(
|
|
||||||
ImagePickerController.provider,
|
|
||||||
)
|
|
||||||
.pickImage(source: .camera))!,
|
|
||||||
)
|
|
||||||
.onError(showError),
|
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: ListTile(
|
onTap: () =>
|
||||||
title: Text("Gallery"),
|
attach(openFile()).onError(showError),
|
||||||
leading: Icon(Icons.add_photo_alternate),
|
|
||||||
),
|
|
||||||
onTap: () async => ref
|
|
||||||
.read(
|
|
||||||
AttachmentController.provider(roomId)
|
|
||||||
.notifier,
|
|
||||||
)
|
|
||||||
.add(
|
|
||||||
(await ref
|
|
||||||
.watch(
|
|
||||||
ImagePickerController.provider,
|
|
||||||
)
|
|
||||||
.pickImage(source: .gallery))!,
|
|
||||||
)
|
|
||||||
.onError(showError),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
onTap: () async => ref
|
|
||||||
.watch(
|
|
||||||
AttachmentController.provider(roomId)
|
|
||||||
.notifier,
|
|
||||||
)
|
|
||||||
.add((await openFile())!)
|
|
||||||
.onError(showError),
|
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text("Files"),
|
title: Text("Files"),
|
||||||
leading: Icon(Icons.attachment),
|
leading: Icon(Icons.attachment),
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class const EmojiPicker({
|
||||||
|
|
||||||
if (!isFreeText) {
|
if (!isFreeText) {
|
||||||
await ref
|
await ref
|
||||||
.watch(RecentEmojiController.provider.notifier)
|
.read(RecentEmojiController.provider.notifier)
|
||||||
.add(emoji)
|
.add(emoji)
|
||||||
.onError(showError);
|
.onError(showError);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,6 @@ class const Html(
|
||||||
.nonNulls,
|
.nonNulls,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
onTapUrl: (url) => ref.watch(LaunchHelper.provider).launchUrl(.parse(url)),
|
onTapUrl: (url) => ref.read(LaunchHelper.provider).launchUrl(.parse(url)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ final class const LinkifiedText(
|
||||||
style: style,
|
style: style,
|
||||||
options: .new(humanize: false),
|
options: .new(humanize: false),
|
||||||
onOpen: (link) =>
|
onOpen: (link) =>
|
||||||
ref.watch(LaunchHelper.provider).launchUrl(.parse(link.url)),
|
ref.read(LaunchHelper.provider).launchUrl(.parse(link.url)),
|
||||||
linkStyle: .new(color: Theme.of(context).colorScheme.primary),
|
linkStyle: .new(color: Theme.of(context).colorScheme.primary),
|
||||||
overflow: maxLines == null ? null : .ellipsis,
|
overflow: maxLines == null ? null : .ellipsis,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,11 @@ class const MemberList(final String roomId, {super.key})
|
||||||
)
|
)
|
||||||
: Expanded(
|
: Expanded(
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: [
|
slivers: value.expand((group) {
|
||||||
for (final MapEntry(key: powerLevel, value: members)
|
final MapEntry(key: powerLevel, value: members) =
|
||||||
in value) ...[
|
group;
|
||||||
|
|
||||||
|
return [
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: .symmetric(horizontal: 16),
|
padding: .symmetric(horizontal: 16),
|
||||||
|
|
@ -151,8 +153,8 @@ class const MemberList(final String roomId, {super.key})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
];
|
||||||
],
|
}).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
AsyncLoading _ => Loading(),
|
AsyncLoading _ => Loading(),
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class const SelectServerPage({super.key}) extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
final launch = ref.watch(LaunchHelper.provider).launchUrl;
|
final launch = ref.read(LaunchHelper.provider).launchUrl;
|
||||||
|
|
||||||
final isLoading = useState(false);
|
final isLoading = useState(false);
|
||||||
final homeserverUrl = useTextEditingController();
|
final homeserverUrl = useTextEditingController();
|
||||||
|
|
@ -52,7 +52,7 @@ class const SelectServerPage({super.key}) extends HookConsumerWidget {
|
||||||
AuthUrlController.provider(url).future,
|
AuthUrlController.provider(url).future,
|
||||||
);
|
);
|
||||||
|
|
||||||
await ref.watch(LaunchHelper.provider).launchUrl(codeResponse.url);
|
await ref.read(LaunchHelper.provider).launchUrl(codeResponse.url);
|
||||||
|
|
||||||
AppLinks().uriLinkStream.listen((encodedUri) async {
|
AppLinks().uriLinkStream.listen((encodedUri) async {
|
||||||
final state = encodedUri.queryParameters["state"];
|
final state = encodedUri.queryParameters["state"];
|
||||||
|
|
@ -64,7 +64,7 @@ class const SelectServerPage({super.key}) extends HookConsumerWidget {
|
||||||
|
|
||||||
if (code != null) {
|
if (code != null) {
|
||||||
await ref
|
await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.exchangeToken(
|
.exchangeToken(
|
||||||
.new(
|
.new(
|
||||||
homeserverUrl: url,
|
homeserverUrl: url,
|
||||||
|
|
@ -74,7 +74,7 @@ class const SelectServerPage({super.key}) extends HookConsumerWidget {
|
||||||
path: "/",
|
path: "/",
|
||||||
),
|
),
|
||||||
code: code,
|
code: code,
|
||||||
clientId: await ref.watch(
|
clientId: await ref.read(
|
||||||
ClientIdController.provider(url).future,
|
ClientIdController.provider(url).future,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class const SettingsPage({super.key}) extends ConsumerWidget {
|
||||||
barHintText: "Search...",
|
barHintText: "Search...",
|
||||||
isFullScreen: categoriesArePages,
|
isFullScreen: categoriesArePages,
|
||||||
suggestionsBuilder: (suggestionsContext, controller) async {
|
suggestionsBuilder: (suggestionsContext, controller) async {
|
||||||
final categories = await ref.watch(
|
final categories = await ref.read(
|
||||||
SettingsSectionsController.provider.future,
|
SettingsSectionsController.provider.future,
|
||||||
);
|
);
|
||||||
final query = controller.text.toLowerCase();
|
final query = controller.text.toLowerCase();
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class const VerifyPage({super.key}) extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
inputError.value = await ref
|
inputError.value = await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.verify(passphraseController.text);
|
.verify(passphraseController.text);
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class const AudioPlayer(
|
||||||
player.platform?.state = player.platform!.state.copyWith(buffering: true);
|
player.platform?.state = player.platform!.state.copyWith(buffering: true);
|
||||||
scheduleMicrotask(() async {
|
scheduleMicrotask(() async {
|
||||||
final video = await ref
|
final video = await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.downloadMedia(.new(mxc: uri, encrypted: encrypted));
|
.downloadMedia(.new(mxc: uri, encrypted: encrypted));
|
||||||
await player.open(Media(video.path), play: false);
|
await player.open(Media(video.path), play: false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class const VideoPlayer(
|
||||||
player.platform?.state = player.platform!.state.copyWith(buffering: true);
|
player.platform?.state = player.platform!.state.copyWith(buffering: true);
|
||||||
scheduleMicrotask(() async {
|
scheduleMicrotask(() async {
|
||||||
final video = await ref
|
final video = await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.downloadMedia(.new(mxc: uri, encrypted: encrypted));
|
.downloadMedia(.new(mxc: uri, encrypted: encrypted));
|
||||||
await player.open(Media(video.path), play: false);
|
await player.open(Media(video.path), play: false);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,15 @@ class const EventRenderer(
|
||||||
event.sender == previousEvent?.sender &&
|
event.sender == previousEvent?.sender &&
|
||||||
event.pmp?.id == previousEvent?.pmp?.id;
|
event.pmp?.id == previousEvent?.pmp?.id;
|
||||||
|
|
||||||
|
GenericEventRenderer generic(IconData icon, String text) =>
|
||||||
|
.new(icon, maxLines: maxLines, [
|
||||||
|
WidgetSpan(
|
||||||
|
alignment: .middle,
|
||||||
|
child: MessageDisplayname(event, shouldWrap: maxLines == null),
|
||||||
|
),
|
||||||
|
TextSpan(text: text),
|
||||||
|
]);
|
||||||
|
|
||||||
final child = event.redactedBy != null || event.relationType == "m.replace"
|
final child = event.redactedBy != null || event.relationType == "m.replace"
|
||||||
? null
|
? null
|
||||||
: switch (event.content) {
|
: switch (event.content) {
|
||||||
|
|
@ -122,169 +131,67 @@ class const EventRenderer(
|
||||||
_ => MembershipRenderer(event, maxLines: maxLines),
|
_ => MembershipRenderer(event, maxLines: maxLines),
|
||||||
},
|
},
|
||||||
|
|
||||||
AvatarContent() => GenericEventRenderer(
|
AvatarContent() => generic(
|
||||||
Icons.interests,
|
Icons.interests,
|
||||||
maxLines: maxLines,
|
"changed the room avatar",
|
||||||
[
|
|
||||||
WidgetSpan(
|
|
||||||
alignment: .middle,
|
|
||||||
child: MessageDisplayname(
|
|
||||||
event,
|
|
||||||
shouldWrap: maxLines == null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: "changed the room avatar"),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
CreateContent() => generic(Icons.add, "created the room"),
|
||||||
CreateContent() => GenericEventRenderer(
|
PowerLevelsContent() => generic(
|
||||||
Icons.add,
|
|
||||||
maxLines: maxLines,
|
|
||||||
[
|
|
||||||
WidgetSpan(
|
|
||||||
alignment: .middle,
|
|
||||||
child: MessageDisplayname(
|
|
||||||
event,
|
|
||||||
shouldWrap: maxLines == null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: "created the room"),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
PowerLevelsContent() => GenericEventRenderer(
|
|
||||||
Icons.power,
|
Icons.power,
|
||||||
maxLines: maxLines,
|
"changed the room's power levels",
|
||||||
[
|
|
||||||
WidgetSpan(
|
|
||||||
alignment: .middle,
|
|
||||||
child: MessageDisplayname(
|
|
||||||
event,
|
|
||||||
shouldWrap: maxLines == null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: "changed the room's power levels"),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
JoinRulesContent() => generic(
|
||||||
JoinRulesContent() => GenericEventRenderer(
|
|
||||||
Icons.rule,
|
Icons.rule,
|
||||||
maxLines: maxLines,
|
"changed the room's join rules",
|
||||||
[
|
|
||||||
WidgetSpan(
|
|
||||||
alignment: .middle,
|
|
||||||
child: MessageDisplayname(
|
|
||||||
event,
|
|
||||||
shouldWrap: maxLines == null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: "changed the room's join rules"),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
TopicContent() => generic(
|
||||||
TopicContent() => GenericEventRenderer(
|
|
||||||
Icons.description,
|
Icons.description,
|
||||||
maxLines: maxLines,
|
"updated the room topic",
|
||||||
[
|
|
||||||
WidgetSpan(
|
|
||||||
alignment: .middle,
|
|
||||||
child: MessageDisplayname(
|
|
||||||
event,
|
|
||||||
shouldWrap: maxLines == null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: "updated the room topic"),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
HistoryVisibilityContent(:final historyVisibility) => generic(
|
||||||
HistoryVisibilityContent(:final historyVisibility) =>
|
Icons.history,
|
||||||
GenericEventRenderer(Icons.history, maxLines: maxLines, [
|
"changed the room's history visibility to ${switch (historyVisibility) {
|
||||||
WidgetSpan(
|
.invited => "since invited",
|
||||||
alignment: .middle,
|
.joined => "since joined",
|
||||||
child: MessageDisplayname(
|
.shared => "all history visible (shared)",
|
||||||
event,
|
.worldReadable => "all history visible (world readable)",
|
||||||
shouldWrap: maxLines == null,
|
}}",
|
||||||
),
|
),
|
||||||
),
|
PinnedEventsContent() => generic(
|
||||||
TextSpan(
|
|
||||||
text:
|
|
||||||
"changed the room's history visibility to ${switch (historyVisibility) {
|
|
||||||
.invited => "since invited",
|
|
||||||
.joined => "since joined",
|
|
||||||
.shared => "all history visible (shared)",
|
|
||||||
.worldReadable => "all history visible (world readable)",
|
|
||||||
}}",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
|
|
||||||
PinnedEventsContent() => GenericEventRenderer(
|
|
||||||
Icons.push_pin,
|
Icons.push_pin,
|
||||||
maxLines: maxLines,
|
"pinned/unpinned some events",
|
||||||
[
|
|
||||||
WidgetSpan(
|
|
||||||
alignment: .middle,
|
|
||||||
child: MessageDisplayname(
|
|
||||||
event,
|
|
||||||
shouldWrap: maxLines == null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: "pinned/unpinned some events"),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
ServerACLContent() => generic(
|
||||||
ServerACLContent() => GenericEventRenderer(
|
|
||||||
Icons.list,
|
Icons.list,
|
||||||
maxLines: maxLines,
|
"updated the server ban list",
|
||||||
[
|
|
||||||
WidgetSpan(
|
|
||||||
alignment: .middle,
|
|
||||||
child: MessageDisplayname(
|
|
||||||
event,
|
|
||||||
shouldWrap: maxLines == null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: "updated the server ban list"),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
CanonicalAliasContent(:final alias, :final altAliases) => generic(
|
||||||
|
Icons.numbers,
|
||||||
|
switch ([
|
||||||
|
if (event.previousContent case CanonicalAliasContent(
|
||||||
|
alias: final prevAlias,
|
||||||
|
altAliases: final prevAltAliases,
|
||||||
|
)) ...[
|
||||||
|
if (prevAlias != alias)
|
||||||
|
if (alias == null)
|
||||||
|
"removed the room's canonical alias"
|
||||||
|
else
|
||||||
|
"changed the room's canonical alias to $alias",
|
||||||
|
|
||||||
CanonicalAliasContent(:final alias, :final altAliases) =>
|
if (prevAltAliases
|
||||||
GenericEventRenderer(Icons.numbers, maxLines: maxLines, [
|
.remove(alias ?? "")
|
||||||
WidgetSpan(
|
.remove(prevAlias ?? "") !=
|
||||||
alignment: .middle,
|
altAliases.remove(alias ?? "").remove(prevAlias ?? ""))
|
||||||
child: MessageDisplayname(
|
"changed the room's aliases",
|
||||||
event,
|
] else ...[
|
||||||
shouldWrap: maxLines == null,
|
if (alias != null) "set the room's canonical alias",
|
||||||
),
|
if (altAliases.isNotEmpty) "set the room's aliases",
|
||||||
),
|
],
|
||||||
TextSpan(
|
]) {
|
||||||
text: switch ([
|
[] => "did something related to room aliases",
|
||||||
if (event.previousContent case CanonicalAliasContent(
|
List prev => prev.join(" and "),
|
||||||
alias: final prevAlias,
|
},
|
||||||
altAliases: final prevAltAliases,
|
),
|
||||||
)) ...[
|
|
||||||
if (prevAlias != alias)
|
|
||||||
if (alias == null)
|
|
||||||
"removed the room's canonical alias"
|
|
||||||
else
|
|
||||||
"changed the room's canonical alias to $alias",
|
|
||||||
|
|
||||||
if (prevAltAliases
|
|
||||||
.remove(alias ?? "")
|
|
||||||
.remove(prevAlias ?? "") !=
|
|
||||||
altAliases
|
|
||||||
.remove(alias ?? "")
|
|
||||||
.remove(prevAlias ?? ""))
|
|
||||||
"changed the room's aliases",
|
|
||||||
] else ...[
|
|
||||||
if (alias != null) "set the room's canonical alias",
|
|
||||||
if (altAliases.isNotEmpty) "set the room's aliases",
|
|
||||||
],
|
|
||||||
]) {
|
|
||||||
[] => "did something related to room aliases",
|
|
||||||
List prev => prev.join(" and "),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import "package:collection/collection.dart";
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
|
||||||
class const GenericEventRenderer(
|
class const GenericEventRenderer(
|
||||||
|
|
@ -17,12 +18,14 @@ class const GenericEventRenderer(
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text.rich(
|
child: Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: children
|
||||||
for (final (index, child) in children.indexed) ...[
|
.expandIndexed(
|
||||||
if (index > 0) TextSpan(text: " "),
|
(index, child) => [
|
||||||
child,
|
if (index > 0) TextSpan(text: " "),
|
||||||
],
|
child,
|
||||||
],
|
],
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
),
|
),
|
||||||
maxLines: maxLines ?? 999,
|
maxLines: maxLines ?? 999,
|
||||||
overflow: .ellipsis,
|
overflow: .ellipsis,
|
||||||
|
|
|
||||||
|
|
@ -271,8 +271,7 @@ final class const RoomChat({
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
if (memberListOpened == true && showMembersByDefault)
|
if (memberListOpened && showMembersByDefault) MemberList(roomId),
|
||||||
MemberList(roomId),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
import "package:navigation_rail_m3e/navigation_rail_m3e.dart";
|
import "package:navigation_rail_m3e/navigation_rail_m3e.dart";
|
||||||
import "package:nexus/helpers/extensions/get_all_child_rooms.dart";
|
import "package:nexus/helpers/extensions/get_all_child_rooms.dart";
|
||||||
|
import "package:nexus/helpers/extensions/room_destinations.dart";
|
||||||
import "package:nexus/models/nav_page.dart";
|
import "package:nexus/models/nav_page.dart";
|
||||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||||
import "package:nexus/widgets/divider_widget.dart";
|
import "package:nexus/widgets/divider_widget.dart";
|
||||||
|
|
@ -13,32 +14,11 @@ import "package:nexus/widgets/join_dialog.dart";
|
||||||
import "package:nexus/widgets/room_menu_button.dart";
|
import "package:nexus/widgets/room_menu_button.dart";
|
||||||
import "package:nexus/controllers/key.dart";
|
import "package:nexus/controllers/key.dart";
|
||||||
import "package:nexus/controllers/spaces.dart";
|
import "package:nexus/controllers/spaces.dart";
|
||||||
import "package:nexus/models/room.dart";
|
|
||||||
import "package:collection/collection.dart";
|
import "package:collection/collection.dart";
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
|
||||||
|
|
||||||
// Needed for navigation_rail_m3e (#65).
|
// Needed for navigation_rail_m3e (#65).
|
||||||
import "package:flutter/material.dart" as old_mat;
|
import "package:flutter/material.dart" as old_mat;
|
||||||
|
|
||||||
List<NavigationRailM3EDestination> roomDestinations(
|
|
||||||
IList<Room> rooms, {
|
|
||||||
required bool isDm,
|
|
||||||
}) => [
|
|
||||||
for (final room in rooms)
|
|
||||||
.new(
|
|
||||||
label: room.metadata?.name ?? "Unnamed Room",
|
|
||||||
badgeCount: switch (room.metadata?.unreadNotifications) {
|
|
||||||
0 || null => room.metadata?.unreadMessages == 0 ? null : 0,
|
|
||||||
int unread => unread,
|
|
||||||
},
|
|
||||||
icon: AvatarOrHash(
|
|
||||||
room.metadata?.avatar,
|
|
||||||
room.metadata?.name ?? "Unnamed Room",
|
|
||||||
fallback: isDm ? null : const Icon(Icons.numbers),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
class const RoomList({super.key}) extends ConsumerWidget implements NavPage {
|
class const RoomList({super.key}) extends ConsumerWidget implements NavPage {
|
||||||
@override
|
@override
|
||||||
String get title => "Rooms";
|
String get title => "Rooms";
|
||||||
|
|
@ -265,13 +245,12 @@ class const RoomsPane({super.key}) extends ConsumerWidget {
|
||||||
header: selectedSpace.room == null
|
header: selectedSpace.room == null
|
||||||
? null
|
? null
|
||||||
: const DividerWidget(Text("Rooms")),
|
: const DividerWidget(Text("Rooms")),
|
||||||
destinations: roomDestinations(
|
destinations: selectedSpace.children.toDestinations(
|
||||||
selectedSpace.children,
|
|
||||||
isDm: isDm,
|
isDm: isDm,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
for (final subSpace in selectedSpace.subSpaces)
|
...selectedSpace.subSpaces.map(
|
||||||
.new(
|
(subSpace) => NavigationRailM3ESection(
|
||||||
header: DividerWidget(
|
header: DividerWidget(
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: .min,
|
mainAxisSize: .min,
|
||||||
|
|
@ -293,11 +272,9 @@ class const RoomsPane({super.key}) extends ConsumerWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
destinations: roomDestinations(
|
destinations: subSpace.children.toDestinations(isDm: isDm),
|
||||||
subSpace.children,
|
|
||||||
isDm: isDm,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
onDestinationSelected: (value) {
|
onDestinationSelected: (value) {
|
||||||
ref
|
ref
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ final class const RoomMenuButton(
|
||||||
if (room != null) ...[
|
if (room != null) ...[
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final vias = ref.watch(ViaController.provider(room!));
|
final vias = ref.read(ViaController.provider(room!));
|
||||||
|
|
||||||
await Clipboard.setData(
|
await Clipboard.setData(
|
||||||
.new(
|
.new(
|
||||||
|
|
@ -126,8 +126,10 @@ final class const RoomMenuButton(
|
||||||
duration: Duration(days: 1),
|
duration: Duration(days: 1),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await client.leaveRoom(room!);
|
await client
|
||||||
snackbar.close();
|
.leaveRoom(room!)
|
||||||
|
.onError(showError)
|
||||||
|
.whenComplete(snackbar.close);
|
||||||
},
|
},
|
||||||
child: Text("Leave"),
|
child: Text("Leave"),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ class const UrlPreview(final Uri link, {super.key}) extends ConsumerWidget {
|
||||||
data: (preview) => preview == null
|
data: (preview) => preview == null
|
||||||
? SizedBox.shrink()
|
? SizedBox.shrink()
|
||||||
: InkWell(
|
: InkWell(
|
||||||
onTap: () => ref.watch(LaunchHelper.provider).launchUrl(link),
|
onTap: () => ref.read(LaunchHelper.provider).launchUrl(link),
|
||||||
child: Card(
|
child: Card(
|
||||||
margin: .symmetric(vertical: 4),
|
margin: .symmetric(vertical: 4),
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,9 @@ final class const UserBottomSheet(
|
||||||
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 subtitleStyle = textTheme.titleSmall?.copyWith(
|
||||||
|
color: theme.colorScheme.onSurfaceVariant,
|
||||||
|
);
|
||||||
final client = ref.read(ClientController.provider.notifier);
|
final client = ref.read(ClientController.provider.notifier);
|
||||||
|
|
||||||
void showMembershipDialog(MembershipAction action) => showDialog(
|
void showMembershipDialog(MembershipAction action) => showDialog(
|
||||||
|
|
@ -112,13 +115,7 @@ final class const UserBottomSheet(
|
||||||
style: textTheme.headlineLarge,
|
style: textTheme.headlineLarge,
|
||||||
textAlign: .center,
|
textAlign: .center,
|
||||||
),
|
),
|
||||||
SelectableText(
|
SelectableText(userId, textAlign: .center, style: subtitleStyle),
|
||||||
userId,
|
|
||||||
textAlign: .center,
|
|
||||||
style: textTheme.titleSmall?.copyWith(
|
|
||||||
color: theme.colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
ref
|
ref
|
||||||
.watch(ProfileController.provider(userId))
|
.watch(ProfileController.provider(userId))
|
||||||
|
|
@ -151,9 +148,7 @@ final class const UserBottomSheet(
|
||||||
Text(
|
Text(
|
||||||
pronoun.summary,
|
pronoun.summary,
|
||||||
textAlign: .center,
|
textAlign: .center,
|
||||||
style: textTheme.titleSmall?.copyWith(
|
style: subtitleStyle,
|
||||||
color: theme.colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
@ -172,9 +167,7 @@ final class const UserBottomSheet(
|
||||||
Text(
|
Text(
|
||||||
profileResponse.profile.timezone!,
|
profileResponse.profile.timezone!,
|
||||||
textAlign: .center,
|
textAlign: .center,
|
||||||
style: textTheme.titleSmall?.copyWith(
|
style: subtitleStyle,
|
||||||
color: theme.colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
@ -212,7 +205,6 @@ final class const UserBottomSheet(
|
||||||
Padding(
|
Padding(
|
||||||
padding: .only(top: 4),
|
padding: .only(top: 4),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
if (member.status == .join || member.status == .invite)
|
if (member.status == .join || member.status == .invite)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue