Remove flutter chat #26

Manually merged
Henry-Hiles merged 108 commits from remove-flutter-chat into main 2026-05-22 15:26:28 -04:00
19 changed files with 153 additions and 158 deletions
Showing only changes of commit df5040e06c - Show all commits

remove selected room/space controllers

Henry Hiles 2026-05-20 12:33:14 -04:00
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs

View file

@ -12,14 +12,14 @@ class KeyController extends Notifier<String?> {
String? build() => String? build() =>
ref.watch(SharedPrefsController.provider).requireValue.getString(key); ref.watch(SharedPrefsController.provider).requireValue.getString(key);
Future<void> set(String? id) async { Future<void> set(String? value) async {
final prefs = ref.watch(SharedPrefsController.provider).requireValue; final prefs = ref.watch(SharedPrefsController.provider).requireValue;
state = id; state = value;
if (id == null) { if (value == null) {
prefs.remove(key); prefs.remove(key);
} else { } else {
prefs.setString(key, id); prefs.setString(key, value);
} }
} }

View file

@ -1,21 +1,21 @@
import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/members_controller.dart"; import "package:nexus/controllers/members_controller.dart";
import "package:nexus/models/configs/members_by_status_config.dart";
import "package:nexus/models/content/membership.dart"; import "package:nexus/models/content/membership.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
import "package:nexus/models/membership_status.dart";
class MembersByTypeController extends AsyncNotifier<IList<Event>> { class MembersByStatusController extends AsyncNotifier<IList<Event>> {
final MembershipStatus filterStatus; final MembersByStatusConfig config;
MembersByTypeController(this.filterStatus); MembersByStatusController(this.config);
@override @override
Future<IList<Event>> build() => ref.watch( Future<IList<Event>> build() => ref.watch(
MembersController.provider.selectAsync( MembersController.provider(config.roomId).selectAsync(
(members) => members (members) => members
.where( .where(
(membership) => switch (membership.content) { (membership) => switch (membership.content) {
MembershipContent(:final status) => filterStatus == status, MembershipContent(:final status) => config.status == status,
_ => false, _ => false,
}, },
) )
@ -25,8 +25,8 @@ class MembersByTypeController extends AsyncNotifier<IList<Event>> {
static final provider = static final provider =
AsyncNotifierProvider.family< AsyncNotifierProvider.family<
MembersByTypeController, MembersByStatusController,
IList<Event>, IList<Event>,
MembershipStatus MembersByStatusConfig
>(MembersByTypeController.new); >(MembersByStatusController.new);
} }

View file

@ -3,48 +3,39 @@ import "package:fast_immutable_collections/fast_immutable_collections.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_controller.dart";
import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/controllers/rooms_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart";
import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/content.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
import "package:nexus/models/requests/get_room_state_request.dart"; import "package:nexus/models/requests/get_room_state_request.dart";
class MembersController extends AsyncNotifier<IList<Event>> { class MembersController extends AsyncNotifier<IList<Event>> {
final String roomId;
MembersController(this.roomId);
@override @override
Future<IList<Event>> build() async { Future<IList<Event>> build() async {
final data = ref.watch( final room = ref.watch(
SelectedRoomController.provider.select( RoomsController.provider.select((value) => value[roomId]),
(value) => value?.metadata == null
? null
: (
value!.metadata!.id,
value.metadata!.hasMemberList,
value.hasFetchedMembers,
),
),
); );
if (data == null) return const IList.empty();
if (!data.$3) { if (room == null) return const IList.empty();
if (!room.hasFetchedMembers) {
final fetchedState = await ref final fetchedState = await ref
.watch(ClientController.provider.notifier) .watch(ClientController.provider.notifier)
.getRoomState( .getRoomState(
GetRoomStateRequest( GetRoomStateRequest(
roomId: data.$1, roomId: roomId,
fetchMembers: data.$2 == false, fetchMembers: room.metadata?.hasMemberList ?? true,
includeMembers: true, includeMembers: true,
), ),
); );
ref ref
.read(RoomsController.provider.notifier) .read(RoomsController.provider.notifier)
.addState(data.$1, fetchedState, isMembers: true); .addState(roomId, fetchedState, isMembers: true);
} }
final room = ref.watch( return room.state[EventType.membership.type]?.values
RoomsController.provider.select((value) => value[data.$1]),
);
return room?.state[EventType.membership.type]?.values
.map( .map(
(rowId) => (rowId) =>
room.events.firstWhereOrNull((event) => event.rowId == rowId), room.events.firstWhereOrNull((event) => event.rowId == rowId),
@ -54,8 +45,6 @@ class MembersController extends AsyncNotifier<IList<Event>> {
const IList.empty(); const IList.empty();
} }
static final provider = static final provider = AsyncNotifierProvider.autoDispose
AsyncNotifierProvider.autoDispose<MembersController, IList<Event>>( .family<MembersController, IList<Event>, String>(MembersController.new);
MembersController.new,
);
} }

View file

@ -1,7 +1,7 @@
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/client_state_controller.dart"; import "package:nexus/controllers/client_state_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/controllers/rooms_controller.dart";
import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/configs/power_level_config.dart";
import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/content.dart";
import "package:nexus/models/content/power_levels.dart"; import "package:nexus/models/content/power_levels.dart";
@ -20,7 +20,10 @@ class PowerLevelController extends Notifier<bool> {
); );
} }
final room = ref.watch(SelectedRoomController.provider); final room = ref.watch(
RoomsController.provider.select((value) => value[config.roomId]),
);
final event = room?.events.firstWhereOrNull( final event = room?.events.firstWhereOrNull(
(event) => event.rowId == room.state[EventType.powerLevels.type]?[""], (event) => event.rowId == room.state[EventType.powerLevels.type]?[""],
); );

View file

@ -12,6 +12,8 @@ class ProfileController extends AsyncNotifier<Profile> {
return client.getProfile(userId); return client.getProfile(userId);
} }
static final provider = AsyncNotifierProvider.autoDispose static final provider =
.family<ProfileController, Profile, String>(ProfileController.new); AsyncNotifierProvider.family<ProfileController, Profile, String>(
ProfileController.new,
);
} }

View file

@ -1,24 +0,0 @@
import "package:collection/collection.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/key_controller.dart";
import "package:nexus/controllers/selected_space_controller.dart";
import "package:nexus/models/room.dart";
class SelectedRoomController extends Notifier<Room?> {
@override
Room? build() {
final space = ref.watch(SelectedSpaceController.provider);
final selectedRoomId = ref.watch(
KeyController.provider(KeyController.roomKey),
);
return space.children.firstWhereOrNull(
(room) => room.metadata?.id == selectedRoomId,
) ??
space.children.firstOrNull;
}
static final provider = NotifierProvider<SelectedRoomController, Room?>(
SelectedRoomController.new,
);
}

View file

@ -1,22 +0,0 @@
import "package:collection/collection.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/key_controller.dart";
import "package:nexus/controllers/spaces_controller.dart";
import "package:nexus/models/space.dart";
class SelectedSpaceController extends Notifier<Space> {
@override
Space build() {
final spaces = ref.watch(SpacesController.provider);
final selectedSpaceId = ref.watch(
KeyController.provider(KeyController.spaceKey),
);
return spaces.firstWhereOrNull((space) => space.id == selectedSpaceId) ??
spaces.first;
}
static final provider = NotifierProvider<SelectedSpaceController, Space>(
SelectedSpaceController.new,
);
}

View file

@ -13,18 +13,6 @@ class UserController extends AsyncNotifier<MembershipContent> {
@override @override
Future<MembershipContent> build() async { Future<MembershipContent> build() async {
final member = await ref.watch(
MembersController.provider.selectAsync(
(value) => value.firstWhereOrNull(
(membership) => membership.stateKey == userId,
),
),
);
if (member?.content case final MembershipContent content) {
return content;
}
final profile = await ref.watch(ProfileController.provider(userId).future); final profile = await ref.watch(ProfileController.provider(userId).future);
return MembershipContent( return MembershipContent(
status: MembershipStatus.leave, status: MembershipStatus.leave,

View file

@ -0,0 +1,15 @@
import "package:freezed_annotation/freezed_annotation.dart";
import "package:nexus/models/membership_status.dart";
part "members_by_status_config.freezed.dart";
part "members_by_status_config.g.dart";
@freezed
abstract class MembersByStatusConfig with _$MembersByStatusConfig {
const factory MembersByStatusConfig({
required String roomId,
required MembershipStatus status,
}) = _MembersByStatusConfig;
factory MembersByStatusConfig.fromJson(Map<String, Object?> json) =>
_$MembersByStatusConfigFromJson(json);
}

View file

@ -5,17 +5,24 @@ part "power_level_config.freezed.dart";
@freezed @freezed
sealed class PowerLevelConfig with _$PowerLevelConfig { sealed class PowerLevelConfig with _$PowerLevelConfig {
const factory PowerLevelConfig({required EventType eventType}) = const factory PowerLevelConfig({
EventPowerLevelConfig; required EventType eventType,
required String roomId,
}) = EventPowerLevelConfig;
const factory PowerLevelConfig.membershipAction({ const factory PowerLevelConfig.membershipAction({
required MembershipAction action, required MembershipAction action,
required String targetUser, required String targetUser,
required String roomId,
}) = MembershipActionPowerLevelConfig; }) = MembershipActionPowerLevelConfig;
const factory PowerLevelConfig.state({required EventType eventType}) = const factory PowerLevelConfig.state({
StatePowerLevelConfig; required EventType eventType,
required String roomId,
}) = StatePowerLevelConfig;
const factory PowerLevelConfig.redaction({required String targetUser}) = const factory PowerLevelConfig.redaction({
RedactionPowerLevelConfig; required String targetUser,
required String roomId,
}) = RedactionPowerLevelConfig;
} }

View file

@ -1,6 +1,7 @@
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/init_complete_controller.dart"; import "package:nexus/controllers/init_complete_controller.dart";
import "package:nexus/controllers/key_controller.dart";
import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/appbar.dart";
import "package:nexus/widgets/sidebar.dart"; import "package:nexus/widgets/sidebar.dart";
import "package:nexus/widgets/room_chat.dart"; import "package:nexus/widgets/room_chat.dart";
@ -15,22 +16,22 @@ class ChatPage extends ConsumerWidget {
final isDesktop = constraints.maxWidth > 650; final isDesktop = constraints.maxWidth > 650;
final showMembersByDefault = constraints.maxWidth > 1000; final showMembersByDefault = constraints.maxWidth > 1000;
final initComplete = ref.watch(InitCompleteController.provider); final initComplete = ref.watch(InitCompleteController.provider);
final roomId = ref.watch(KeyController.provider(KeyController.roomKey));
return Scaffold( return Scaffold(
appBar: initComplete ? null : Appbar(), appBar: initComplete ? null : Appbar(),
body: initComplete body: initComplete
? Builder( ? Row(
builder: (context) => Row(
children: [ children: [
if (isDesktop) Sidebar(isDesktop: isDesktop), if (isDesktop) Sidebar(isDesktop: isDesktop),
Expanded( Expanded(
child: RoomChat( child: RoomChat(
roomId: roomId,
isDesktop: isDesktop, isDesktop: isDesktop,
showMembersByDefault: showMembersByDefault, showMembersByDefault: showMembersByDefault,
), ),
), ),
], ],
),
) )
: Center( : Center(
child: Column( child: Column(

View file

@ -13,6 +13,7 @@ import "package:nexus/widgets/composer/relation_preview.dart";
import "package:nexus/widgets/emoji_picker_button.dart"; import "package:nexus/widgets/emoji_picker_button.dart";
class ChatBox extends HookConsumerWidget { class ChatBox extends HookConsumerWidget {
final String roomId;
final Event? relatedEvent; final Event? relatedEvent;
final RelationType relationType; final RelationType relationType;
final VoidCallback onDismiss; final VoidCallback onDismiss;
@ -23,7 +24,8 @@ class ChatBox extends HookConsumerWidget {
required IList<Tag> tags, required IList<Tag> tags,
}) })
onSend; onSend;
const ChatBox({ const ChatBox(
this.roomId, {
required this.relatedEvent, required this.relatedEvent,
required this.relationType, required this.relationType,
required this.onDismiss, required this.onDismiss,
@ -88,7 +90,10 @@ class ChatBox extends HookConsumerWidget {
children: children:
ref.watch( ref.watch(
PowerLevelController.provider( PowerLevelController.provider(
PowerLevelConfig(eventType: EventType.message), PowerLevelConfig(
eventType: EventType.message,
roomId: roomId,
),
), ),
) )
? [ ? [
@ -125,6 +130,7 @@ class ChatBox extends HookConsumerWidget {
child: FlutterTagger( child: FlutterTagger(
triggerStrategy: TriggerStrategy.eager, triggerStrategy: TriggerStrategy.eager,
overlay: MentionOverlay( overlay: MentionOverlay(
roomId,
query: query.value, query: query.value,
triggerCharacter: triggerCharacter.value, triggerCharacter: triggerCharacter.value,
addTag: ({required id, required name}) { addTag: ({required id, required name}) {

View file

@ -1,10 +1,11 @@
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_by_type_controller.dart"; import "package:nexus/controllers/members_by_status_controller.dart";
import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/controllers/rooms_controller.dart";
import "package:nexus/controllers/via_controller.dart"; import "package:nexus/controllers/via_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/helpers/extensions/get_localpart.dart";
import "package:nexus/models/configs/members_by_status_config.dart";
import "package:nexus/models/content/membership.dart"; import "package:nexus/models/content/membership.dart";
import "package:nexus/models/membership_status.dart"; import "package:nexus/models/membership_status.dart";
import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/avatar_or_hash.dart";
@ -13,8 +14,10 @@ import "package:nexus/widgets/loading.dart";
class MentionOverlay extends ConsumerWidget { class MentionOverlay extends ConsumerWidget {
final String? triggerCharacter; final String? triggerCharacter;
final String query; final String query;
final String roomId;
final void Function({required String id, required String name}) addTag; final void Function({required String id, required String name}) addTag;
const MentionOverlay({ const MentionOverlay(
this.roomId, {
required this.query, required this.query,
required this.addTag, required this.addTag,
required this.triggerCharacter, required this.triggerCharacter,
@ -36,7 +39,12 @@ class MentionOverlay extends ConsumerWidget {
"@" => "@" =>
ref ref
.watch( .watch(
MembersByTypeController.provider(MembershipStatus.join), MembersByStatusController.provider(
MembersByStatusConfig(
roomId: roomId,
status: MembershipStatus.join,
),
),
) )
.betterWhen( .betterWhen(
data: (members) => ListView( data: (members) => ListView(

View file

@ -1,22 +1,26 @@
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/members_by_type_controller.dart"; import "package:nexus/controllers/members_by_status_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/helpers/extensions/get_localpart.dart";
import "package:nexus/helpers/extensions/show_user_popover.dart"; import "package:nexus/helpers/extensions/show_user_popover.dart";
import "package:nexus/models/configs/members_by_status_config.dart";
import "package:nexus/models/content/membership.dart"; import "package:nexus/models/content/membership.dart";
import "package:nexus/models/membership_status.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 HookConsumerWidget { class MemberList extends HookConsumerWidget {
const MemberList({super.key}); final String roomId;
const MemberList(this.roomId, {super.key});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final status = useState(MembershipStatus.join); final status = useState(MembershipStatus.join);
final membersProvider = ref.watch( final membersProvider = ref.watch(
MembersByTypeController.provider(status.value), MembersByStatusController.provider(
MembersByStatusConfig(roomId: roomId, status: status.value),
),
); );
return Drawer( return Drawer(

View file

@ -2,7 +2,7 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart";
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/client_state_controller.dart"; import "package:nexus/controllers/client_state_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/controllers/rooms_controller.dart";
import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart";
import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/appbar.dart";
import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/avatar_or_hash.dart";
@ -13,7 +13,9 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget {
final bool isDesktop; final bool isDesktop;
final void Function(BuildContext context)? onOpenMemberList; final void Function(BuildContext context)? onOpenMemberList;
final void Function(BuildContext context) onOpenDrawer; final void Function(BuildContext context) onOpenDrawer;
final String? roomId;
const RoomAppbar({ const RoomAppbar({
required this.roomId,
required this.isDesktop, required this.isDesktop,
required this.onOpenDrawer, required this.onOpenDrawer,
this.onOpenMemberList, this.onOpenMemberList,
@ -25,7 +27,9 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final room = ref.watch(SelectedRoomController.provider); final room = roomId == null
? null
: ref.watch(RoomsController.provider.select((value) => value[roomId!]));
return Appbar( return Appbar(
leading: isDesktop leading: isDesktop
? room == null ? room == null

View file

@ -8,7 +8,6 @@ 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/power_level_controller.dart"; import "package:nexus/controllers/power_level_controller.dart";
import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/controllers/rooms_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart";
import "package:nexus/controllers/room_chat_controller.dart"; import "package:nexus/controllers/room_chat_controller.dart";
import "package:nexus/controllers/via_controller.dart"; import "package:nexus/controllers/via_controller.dart";
import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/configs/power_level_config.dart";
@ -32,7 +31,9 @@ import "package:super_sliver_list/super_sliver_list.dart";
class RoomChat extends HookConsumerWidget { class RoomChat extends HookConsumerWidget {
final bool isDesktop; final bool isDesktop;
final bool showMembersByDefault; final bool showMembersByDefault;
final String? roomId;
const RoomChat({ const RoomChat({
required this.roomId,
required this.isDesktop, required this.isDesktop,
required this.showMembersByDefault, required this.showMembersByDefault,
super.key, super.key,
@ -47,15 +48,12 @@ class RoomChat extends HookConsumerWidget {
final memberListOpened = useState<bool>(showMembersByDefault); final memberListOpened = useState<bool>(showMembersByDefault);
final userId = ref.watch(ClientStateController.provider)?.userId; final userId = ref.watch(ClientStateController.provider)?.userId;
final roomId = ref.watch(
SelectedRoomController.provider.select((value) => value?.metadata?.id),
);
final theme = Theme.of(context); final theme = Theme.of(context);
if (roomId == null || userId == null) { if (userId == null || this.roomId == null) {
return Scaffold( return Scaffold(
appBar: RoomAppbar( appBar: RoomAppbar(
roomId: this.roomId,
isDesktop: isDesktop, isDesktop: isDesktop,
onOpenDrawer: (_) => Scaffold.of(context).openDrawer(), onOpenDrawer: (_) => Scaffold.of(context).openDrawer(),
onOpenMemberList: null, onOpenMemberList: null,
@ -69,6 +67,8 @@ class RoomChat extends HookConsumerWidget {
); );
} }
final roomId = this.roomId!;
final controllerProvider = RoomChatController.provider(roomId); final controllerProvider = RoomChatController.provider(roomId);
final notifier = ref.watch(controllerProvider.notifier); final notifier = ref.watch(controllerProvider.notifier);
@ -76,10 +76,13 @@ class RoomChat extends HookConsumerWidget {
final listController = useRef(ListController()); final listController = useRef(ListController());
final scrollController = useScrollController(); final scrollController = useScrollController();
scrollController.addListener(() async {
useEffect(() {
Future<void> listener() async {
if (!scrollController.position.atEdge) return; if (!scrollController.position.atEdge) return;
if (scrollController.position.pixels == 0) { if (scrollController.position.pixels == 0) {
context.mounted;
final room = ref.watch( final room = ref.watch(
RoomsController.provider.select((value) => value[roomId]), RoomsController.provider.select((value) => value[roomId]),
); );
@ -87,7 +90,11 @@ class RoomChat extends HookConsumerWidget {
} else { } else {
await notifier.loadOlder(); await notifier.loadOlder();
} }
}); }
scrollController.addListener(listener);
return () => scrollController.removeListener(listener);
}, [roomId]);
final composerNode = useFocusNode( final composerNode = useFocusNode(
onKeyEvent: (_, event) { onKeyEvent: (_, event) {
@ -107,7 +114,7 @@ class RoomChat extends HookConsumerWidget {
return [ return [
if (ref.watch( if (ref.watch(
PowerLevelController.provider( PowerLevelController.provider(
PowerLevelConfig(eventType: EventType.reaction), PowerLevelConfig(eventType: EventType.reaction, roomId: roomId),
), ),
)) ))
PopupMenuItem( PopupMenuItem(
@ -156,7 +163,7 @@ class RoomChat extends HookConsumerWidget {
), ),
if (ref.watch( if (ref.watch(
PowerLevelController.provider( PowerLevelController.provider(
PowerLevelConfig(eventType: EventType.message), PowerLevelConfig(eventType: EventType.message, roomId: roomId),
), ),
)) ))
PopupMenuItem( PopupMenuItem(
@ -178,7 +185,9 @@ class RoomChat extends HookConsumerWidget {
), ),
PopupMenuItem( PopupMenuItem(
onTap: () async { onTap: () async {
final room = ref.watch(SelectedRoomController.provider); final room = ref.watch(
RoomsController.provider.select((value) => value[roomId]),
);
if (room == null) return; if (room == null) return;
final vias = ref.watch(ViaController.provider(room)); final vias = ref.watch(ViaController.provider(room));
@ -194,7 +203,10 @@ class RoomChat extends HookConsumerWidget {
), ),
if (ref.watch( if (ref.watch(
PowerLevelController.provider( PowerLevelController.provider(
PowerLevelConfig.redaction(targetUser: event.sender), PowerLevelConfig.redaction(
targetUser: event.sender,
roomId: roomId,
),
), ),
)) ))
PopupMenuItem( PopupMenuItem(
@ -308,6 +320,7 @@ class RoomChat extends HookConsumerWidget {
return Scaffold( return Scaffold(
appBar: RoomAppbar( appBar: RoomAppbar(
roomId: roomId,
isDesktop: isDesktop, isDesktop: isDesktop,
onOpenDrawer: (_) => Scaffold.of(context).openDrawer(), onOpenDrawer: (_) => Scaffold.of(context).openDrawer(),
onOpenMemberList: (thisContext) { onOpenMemberList: (thisContext) {
@ -387,6 +400,7 @@ class RoomChat extends HookConsumerWidget {
), ),
), ),
ChatBox( ChatBox(
roomId,
node: composerNode, node: composerNode,
onSend: (text, {required shouldMention, required tags}) => onSend: (text, {required shouldMention, required tags}) =>
notifier notifier
@ -407,11 +421,11 @@ class RoomChat extends HookConsumerWidget {
), ),
if (memberListOpened.value == true && showMembersByDefault) if (memberListOpened.value == true && showMembersByDefault)
MemberList(), MemberList(roomId),
], ],
), ),
endDrawer: showMembersByDefault ? null : MemberList(), endDrawer: showMembersByDefault ? null : MemberList(roomId),
); );
} }
} }

View file

@ -1,7 +1,7 @@
import "package:collection/collection.dart";
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/key_controller.dart"; import "package:nexus/controllers/key_controller.dart";
import "package:nexus/controllers/selected_space_controller.dart";
import "package:nexus/controllers/spaces_controller.dart"; import "package:nexus/controllers/spaces_controller.dart";
import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/avatar_or_hash.dart";
import "package:nexus/widgets/join_dialog.dart"; import "package:nexus/widgets/join_dialog.dart";
@ -31,7 +31,9 @@ class Sidebar extends HookConsumerWidget {
); );
final selectedIndex = indexOfSelected == -1 ? 0 : indexOfSelected; final selectedIndex = indexOfSelected == -1 ? 0 : indexOfSelected;
final selectedSpace = ref.watch(SelectedSpaceController.provider); final selectedSpace =
spaces.firstWhereOrNull((space) => space.id == selectedSpaceId) ??
spaces.first;
final indexOfSelectedRoom = selectedSpace.children.indexWhere( final indexOfSelectedRoom = selectedSpace.children.indexWhere(
(room) => room.metadata?.id == selectedRoomId, (room) => room.metadata?.id == selectedRoomId,

View file

@ -6,7 +6,6 @@ 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/power_level_controller.dart"; import "package:nexus/controllers/power_level_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/helpers/extensions/get_localpart.dart"; import "package:nexus/helpers/extensions/get_localpart.dart";
import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart";
@ -23,16 +22,14 @@ import "package:nexus/widgets/form_text_input.dart";
class UserPopover extends ConsumerWidget { class UserPopover extends ConsumerWidget {
final MembershipContent member; final MembershipContent member;
final String userId; final String userId;
const UserPopover(this.member, this.userId, {super.key}); final String? roomId;
const UserPopover(this.member, this.userId, {this.roomId, super.key});
@override @override
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 client = ref.watch(ClientController.provider.notifier);
final roomId = ref.watch(
SelectedRoomController.provider.select((room) => room?.metadata?.id),
);
void showMembershipDialog(MembershipAction action) => showDialog( void showMembershipDialog(MembershipAction action) => showDialog(
context: context, context: context,
@ -164,6 +161,7 @@ class UserPopover extends ConsumerWidget {
PowerLevelController.provider( PowerLevelController.provider(
PowerLevelConfig.membershipAction( PowerLevelConfig.membershipAction(
action: MembershipAction.kick, action: MembershipAction.kick,
roomId: roomId!,
targetUser: userId, targetUser: userId,
), ),
), ),
@ -185,6 +183,7 @@ class UserPopover extends ConsumerWidget {
if (ref.watch( if (ref.watch(
PowerLevelController.provider( PowerLevelController.provider(
PowerLevelConfig.membershipAction( PowerLevelConfig.membershipAction(
roomId: roomId!,
action: MembershipAction.ban, action: MembershipAction.ban,
targetUser: userId, targetUser: userId,
), ),

View file

@ -5,7 +5,6 @@ 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/cross_cache_controller.dart"; import "package:nexus/controllers/cross_cache_controller.dart";
import "package:nexus/controllers/room_chat_controller.dart"; import "package:nexus/controllers/room_chat_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart";
import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/extensions/get_headers.dart";
import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart";
import "package:nexus/main.dart"; import "package:nexus/main.dart";