forked from Nexus/nexus
Compare commits
11 commits
c10c74a1f3
...
e1d7a30a06
| Author | SHA1 | Date | |
|---|---|---|---|
|
e1d7a30a06 |
|||
|
0c61623a94 |
|||
|
0a9b71230b |
|||
|
15c057707f |
|||
|
9cc18e16b8 |
|||
|
240984a832 |
|||
|
633e83b915 |
|||
|
c5122fc34f |
|||
|
457de3c77c |
|||
|
c9aa0173d8 |
|||
|
22aa26fd05 |
10 changed files with 246 additions and 216 deletions
|
|
@ -8,12 +8,13 @@ import "package:nexus/models/content/create.dart";
|
||||||
import "package:nexus/models/content/power_levels.dart";
|
import "package:nexus/models/content/power_levels.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
|
|
||||||
class MembersGroupedController extends AsyncNotifier<IMap<int?, ISet<Event>>> {
|
class MembersGroupedController
|
||||||
|
extends AsyncNotifier<IList<MapEntry<int?, ISet<Event>>>> {
|
||||||
final MembersByStatusConfig config;
|
final MembersByStatusConfig config;
|
||||||
MembersGroupedController(this.config);
|
MembersGroupedController(this.config);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<IMap<int?, ISet<Event>>> build() async {
|
Future<IList<MapEntry<int?, ISet<Event>>>> build() async {
|
||||||
final room = ref.watch(
|
final room = ref.watch(
|
||||||
RoomsController.provider.select((value) => value[config.roomId]),
|
RoomsController.provider.select((value) => value[config.roomId]),
|
||||||
);
|
);
|
||||||
|
|
@ -42,7 +43,8 @@ class MembersGroupedController extends AsyncNotifier<IMap<int?, ISet<Event>>> {
|
||||||
MembersByStatusController.provider(config).future,
|
MembersByStatusController.provider(config).future,
|
||||||
);
|
);
|
||||||
|
|
||||||
return members.fold<IMap<int?, ISet<Event>>>(.new(), (result, event) {
|
return members
|
||||||
|
.fold<IMap<int?, ISet<Event>>>(.new(), (result, event) {
|
||||||
final groupKey = creators?.contains(event.stateKey!) == true
|
final groupKey = creators?.contains(event.stateKey!) == true
|
||||||
? null
|
? null
|
||||||
: content.users[event.stateKey!] ?? content.usersDefault;
|
: content.users[event.stateKey!] ?? content.usersDefault;
|
||||||
|
|
@ -52,13 +54,17 @@ class MembersGroupedController extends AsyncNotifier<IMap<int?, ISet<Event>>> {
|
||||||
(value) => value.add(event),
|
(value) => value.add(event),
|
||||||
ifAbsent: () => .new({event}),
|
ifAbsent: () => .new({event}),
|
||||||
);
|
);
|
||||||
});
|
})
|
||||||
|
.toEntryIList(
|
||||||
|
compare: (a, b) =>
|
||||||
|
(b?.key ?? double.infinity).compareTo(a?.key ?? double.infinity),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static final provider =
|
static final provider =
|
||||||
AsyncNotifierProvider.family<
|
AsyncNotifierProvider.family<
|
||||||
MembersGroupedController,
|
MembersGroupedController,
|
||||||
IMap<int?, ISet<Event>>,
|
IList<MapEntry<int?, ISet<Event>>>,
|
||||||
MembersByStatusConfig
|
MembersByStatusConfig
|
||||||
>(MembersGroupedController.new);
|
>(MembersGroupedController.new);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ import "package:nexus/controllers/account_data_controller.dart";
|
||||||
import "package:nexus/controllers/rooms_controller.dart";
|
import "package:nexus/controllers/rooms_controller.dart";
|
||||||
import "package:nexus/controllers/top_level_spaces_controller.dart";
|
import "package:nexus/controllers/top_level_spaces_controller.dart";
|
||||||
import "package:nexus/controllers/space_edges_controller.dart";
|
import "package:nexus/controllers/space_edges_controller.dart";
|
||||||
|
import "package:nexus/models/room.dart";
|
||||||
import "package:nexus/models/space.dart";
|
import "package:nexus/models/space.dart";
|
||||||
|
import "package:nexus/models/subspace.dart";
|
||||||
|
|
||||||
class SpacesController extends Notifier<IList<Space>> {
|
class SpacesController extends Notifier<IList<Space>> {
|
||||||
@override
|
@override
|
||||||
|
|
@ -14,91 +16,97 @@ class SpacesController extends Notifier<IList<Space>> {
|
||||||
final rooms = ref.watch(RoomsController.provider);
|
final rooms = ref.watch(RoomsController.provider);
|
||||||
final topLevelSpaceIds = ref.watch(TopLevelSpacesController.provider);
|
final topLevelSpaceIds = ref.watch(TopLevelSpacesController.provider);
|
||||||
final spaceEdges = ref.watch(SpaceEdgesController.provider);
|
final spaceEdges = ref.watch(SpaceEdgesController.provider);
|
||||||
|
final accountData = ref.watch(AccountDataController.provider);
|
||||||
|
|
||||||
final childRoomsBySpaceId = IMap.fromEntries(
|
final childrenById = {
|
||||||
topLevelSpaceIds.map((spaceId) {
|
for (final entry in spaceEdges.entries)
|
||||||
ISet<String> walk(String currentId) {
|
entry.key: entry.value.map((e) => e.childId).toList(),
|
||||||
final children = spaceEdges[currentId] ?? .new();
|
};
|
||||||
|
|
||||||
return children.fold<ISet<String>>(.new(), (acc, edge) {
|
Set<String> collectDescendants(String startId) {
|
||||||
final childId = edge.childId;
|
final visited = <String>{};
|
||||||
final isSpace = spaceEdges.containsKey(childId);
|
final stack = [startId];
|
||||||
|
|
||||||
return acc
|
while (stack.isNotEmpty) {
|
||||||
.addAll(!isSpace ? ISet([childId]) : const .empty())
|
final current = stack.removeLast();
|
||||||
.addAll(isSpace ? walk(childId) : const .empty());
|
final children = childrenById[current] ?? const [];
|
||||||
});
|
|
||||||
|
for (final child in children) {
|
||||||
|
if (visited.add(child)) {
|
||||||
|
stack.add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MapEntry(
|
return visited;
|
||||||
spaceId,
|
}
|
||||||
walk(spaceId).map((id) => rooms[id]).nonNulls.toIList(),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
final allNestedRoomIds = childRoomsBySpaceId.values
|
Space buildSpace(String spaceId) {
|
||||||
.expand((l) => l)
|
final space = rooms[spaceId];
|
||||||
.map(
|
final directChildrenIds = childrenById[spaceId] ?? const [];
|
||||||
(room) => rooms.entries
|
|
||||||
.firstWhere(
|
final directRooms = <Room>[];
|
||||||
(entry) => entry.value.metadata?.id == room.metadata?.id,
|
final subSpaces = <Subspace>[];
|
||||||
)
|
|
||||||
.key,
|
for (final childId in directChildrenIds) {
|
||||||
)
|
final room = rooms[childId];
|
||||||
.toISet();
|
if (room == null) continue;
|
||||||
|
|
||||||
|
if (childrenById.containsKey(childId)) {
|
||||||
|
final descendants = collectDescendants(childId);
|
||||||
|
|
||||||
|
subSpaces.add(
|
||||||
|
.new(
|
||||||
|
room: room,
|
||||||
|
children: .new(descendants.map((id) => rooms[id]).nonNulls),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
directRooms.add(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .new(
|
||||||
|
id: spaceId,
|
||||||
|
room: space,
|
||||||
|
title: space?.metadata?.name ?? "Unnamed Space",
|
||||||
|
children: .new(directRooms),
|
||||||
|
subSpaces: .new(subSpaces),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final spaces = topLevelSpaceIds.map(buildSpace).toIList();
|
||||||
|
|
||||||
|
final usedRoomIds = {
|
||||||
|
for (final space in spaces) ...[
|
||||||
|
...space.children.map((r) => r.metadata?.id),
|
||||||
|
...space.subSpaces.expand((s) => s.children.map((r) => r.metadata?.id)),
|
||||||
|
],
|
||||||
|
}.nonNulls.toISet();
|
||||||
|
|
||||||
|
final directMessages = IMap(
|
||||||
|
accountData["m.direct"]?.content ?? {},
|
||||||
|
).values.expand((e) => e).toISet();
|
||||||
|
|
||||||
final otherRooms = rooms.entries
|
final otherRooms = rooms.entries
|
||||||
.where(
|
.where(
|
||||||
(e) =>
|
(e) =>
|
||||||
!allNestedRoomIds.contains(e.key) &&
|
!usedRoomIds.contains(e.key) &&
|
||||||
!topLevelSpaceIds.contains(e.key) &&
|
!topLevelSpaceIds.contains(e.key) &&
|
||||||
!spaceEdges.containsKey(e.key),
|
!childrenById.containsKey(e.key),
|
||||||
)
|
)
|
||||||
.map((e) => e.value);
|
.map((e) => e.value)
|
||||||
|
.toIList();
|
||||||
final accountData = ref.watch(AccountDataController.provider);
|
|
||||||
|
|
||||||
final directMessages = IMap(
|
|
||||||
accountData["m.direct"]?.content ?? {},
|
|
||||||
).values.expand((element) => element);
|
|
||||||
|
|
||||||
final homeRooms = otherRooms
|
final homeRooms = otherRooms
|
||||||
.where(
|
.where((r) => !directMessages.contains(r.metadata?.id))
|
||||||
(room) =>
|
|
||||||
directMessages.any(
|
|
||||||
(directMessage) => directMessage == room.metadata?.id,
|
|
||||||
) ==
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.toIList();
|
.toIList();
|
||||||
|
|
||||||
final dmRooms = otherRooms
|
final dmRooms = otherRooms
|
||||||
.where(
|
.where((r) => directMessages.contains(r.metadata?.id))
|
||||||
(room) => directMessages.any(
|
|
||||||
(directMessage) => directMessage == room.metadata?.id,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toIList();
|
.toIList();
|
||||||
|
|
||||||
final topLevelSpacesList = topLevelSpaceIds
|
final allSpaces = <Space>[
|
||||||
.map((id) {
|
|
||||||
final room = rooms[id];
|
|
||||||
if (room == null) return null;
|
|
||||||
|
|
||||||
final children = childRoomsBySpaceId[id] ?? .new();
|
|
||||||
return Space(
|
|
||||||
subSpaces: const IList.empty(), // TODO
|
|
||||||
id: id,
|
|
||||||
title: room.metadata?.name ?? "Unnamed Room",
|
|
||||||
room: room,
|
|
||||||
children: children,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.nonNulls
|
|
||||||
.toIList();
|
|
||||||
|
|
||||||
return <Space>[
|
|
||||||
.new(
|
.new(
|
||||||
id: "home",
|
id: "home",
|
||||||
title: "Home",
|
title: "Home",
|
||||||
|
|
@ -113,8 +121,10 @@ class SpacesController extends Notifier<IList<Space>> {
|
||||||
children: dmRooms,
|
children: dmRooms,
|
||||||
subSpaces: .new(),
|
subSpaces: .new(),
|
||||||
),
|
),
|
||||||
...topLevelSpacesList,
|
...spaces,
|
||||||
]
|
];
|
||||||
|
|
||||||
|
return allSpaces
|
||||||
.map(
|
.map(
|
||||||
(space) => space.copyWith(
|
(space) => space.copyWith(
|
||||||
children: .new(
|
children: .new(
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,9 @@ class LoginPage extends HookConsumerWidget {
|
||||||
validator: requiredValidator,
|
validator: requiredValidator,
|
||||||
controller: password,
|
controller: password,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
|
onFieldSubmitted: (_) => tryLogin(),
|
||||||
|
// Don't defocus on submit
|
||||||
|
onEditingComplete: () {},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,22 @@ class VerifyPage extends HookConsumerWidget {
|
||||||
final inputError = useState<String?>(null);
|
final inputError = useState<String?>(null);
|
||||||
final formKey = useRef(GlobalKey<FormState>());
|
final formKey = useRef(GlobalKey<FormState>());
|
||||||
|
|
||||||
|
Future<void> verify() async {
|
||||||
|
isLoading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (formKey.value.currentState?.validate() != true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputError.value = await ref
|
||||||
|
.watch(ClientController.provider.notifier)
|
||||||
|
.verify(passphraseController.text);
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: Appbar(),
|
appBar: Appbar(),
|
||||||
body: AlertDialog(
|
body: AlertDialog(
|
||||||
|
|
@ -40,29 +56,16 @@ class VerifyPage extends HookConsumerWidget {
|
||||||
label: Text("Recovery Key or Passphrase"),
|
label: Text("Recovery Key or Passphrase"),
|
||||||
errorText: inputError.value,
|
errorText: inputError.value,
|
||||||
),
|
),
|
||||||
|
onFieldSubmitted: (_) => verify(),
|
||||||
|
// Don't defocus on submit
|
||||||
|
onEditingComplete: () {},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: isLoading.value
|
onPressed: isLoading.value ? null : verify,
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
isLoading.value = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (formKey.value.currentState?.validate() != true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
inputError.value = await ref
|
|
||||||
.watch(ClientController.provider.notifier)
|
|
||||||
.verify(passphraseController.text);
|
|
||||||
} finally {
|
|
||||||
isLoading.value = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text("Verify"),
|
child: Text("Verify"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@ class MentionChip extends ConsumerWidget {
|
||||||
child: IgnorePointer(
|
child: IgnorePointer(
|
||||||
child: Chip(
|
child: Chip(
|
||||||
label: Text(
|
label: Text(
|
||||||
(membership == null ? null : "@${membership.displayName}") ??
|
(membership?.displayName == null
|
||||||
|
? null
|
||||||
|
: "@${membership!.displayName}") ??
|
||||||
mention,
|
mention,
|
||||||
style: .new(
|
style: .new(
|
||||||
fontWeight: .bold,
|
fontWeight: .bold,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ 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:m3e_buttons/m3e_buttons.dart";
|
import "package:m3e_buttons/m3e_buttons.dart";
|
||||||
import "package:material_segmented_list/material_segmented_list.dart";
|
import "package:m3e_card_list/m3e_card_list.dart";
|
||||||
|
import "package:nexus/controllers/members_by_status_controller.dart";
|
||||||
import "package:nexus/controllers/members_grouped_controller.dart";
|
import "package:nexus/controllers/members_grouped_controller.dart";
|
||||||
import "package:nexus/helpers/extensions/get_localpart.dart";
|
import "package:nexus/helpers/extensions/get_localpart.dart";
|
||||||
import "package:nexus/helpers/extensions/string_to_color.dart";
|
import "package:nexus/helpers/extensions/string_to_color.dart";
|
||||||
|
|
@ -20,31 +21,26 @@ class MemberList extends HookConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final status = useState(MembershipStatus.join);
|
final statusIndex = useState(0);
|
||||||
|
|
||||||
final membersData = ref.watch(
|
|
||||||
MembersGroupedController.provider(
|
|
||||||
.new(roomId: roomId, status: status.value),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final options = <String, MembershipStatus>{
|
final options = <String, MembershipStatus>{
|
||||||
"Joined": .join,
|
"Joined": .join,
|
||||||
"Invited": .invite,
|
"Invited": .invite,
|
||||||
"Banned": .ban,
|
"Banned": .ban,
|
||||||
};
|
};
|
||||||
|
final status = options.values.toIList()[statusIndex.value];
|
||||||
|
|
||||||
return Drawer(
|
return Drawer(
|
||||||
shape: Border(),
|
shape: Border(),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
if (Scaffold.of(context).hasEndDrawer)
|
||||||
AppBar(
|
AppBar(
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
leading: Icon(Icons.people),
|
leading: Icon(Icons.people),
|
||||||
title: Text("Members"),
|
title: Text("Members"),
|
||||||
actionsPadding: .only(right: 4),
|
actionsPadding: .only(right: 4),
|
||||||
actions: [
|
actions: [
|
||||||
if (Scaffold.of(context).hasEndDrawer)
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: Scaffold.of(context).closeEndDrawer,
|
onPressed: Scaffold.of(context).closeEndDrawer,
|
||||||
icon: Icon(Icons.close),
|
icon: Icon(Icons.close),
|
||||||
|
|
@ -52,18 +48,18 @@ class MemberList extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
M3EToggleButtonGroup(
|
Padding(
|
||||||
type: .connected,
|
padding: .symmetric(vertical: 8),
|
||||||
selectedIndex: options.values.toIList().indexOf(status.value),
|
child: M3EToggleButtonGroup(
|
||||||
|
selectedIndex: statusIndex.value,
|
||||||
onSelectedIndexChanged: (index) =>
|
onSelectedIndexChanged: (index) =>
|
||||||
status.value = options.values.elementAt(index ?? 0),
|
statusIndex.value = index ?? statusIndex.value,
|
||||||
// overflow: M3EButtonGroupOverflow.menu,
|
|
||||||
actions: options
|
actions: options
|
||||||
.mapTo(
|
.mapTo(
|
||||||
(name, value) => M3EToggleButtonGroupAction(
|
(name, value) => M3EToggleButtonGroupAction(
|
||||||
checkedLabel: Text(
|
checkedLabel: Text(
|
||||||
"$name${switch (membersData) {
|
"$name${switch (ref.watch(MembersByStatusController.provider(.new(roomId: roomId, status: value)))) {
|
||||||
AsyncData(:final value) || AsyncLoading(:final value?) => " (${value.values.expand((element) => element).length})",
|
AsyncData(:final value) || AsyncLoading(:final value?) => " (${value.length})",
|
||||||
_ => "",
|
_ => "",
|
||||||
}}",
|
}}",
|
||||||
),
|
),
|
||||||
|
|
@ -72,8 +68,13 @@ class MemberList extends HookConsumerWidget {
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
switch (membersData) {
|
switch (ref.watch(
|
||||||
|
MembersGroupedController.provider(
|
||||||
|
.new(roomId: roomId, status: status),
|
||||||
|
),
|
||||||
|
)) {
|
||||||
AsyncError(:final error, :final stackTrace) => ErrorDialog(
|
AsyncError(:final error, :final stackTrace) => ErrorDialog(
|
||||||
error,
|
error,
|
||||||
stackTrace,
|
stackTrace,
|
||||||
|
|
@ -84,71 +85,76 @@ class MemberList extends HookConsumerWidget {
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: .symmetric(vertical: 18),
|
padding: .symmetric(vertical: 18),
|
||||||
child: Text(
|
child: Text(
|
||||||
"No ${options.keys.toIList()[options.values.toIList().indexOf(status.value)]} Members",
|
"No ${options.keys.toIList()[statusIndex.value]} Members",
|
||||||
style: Theme.of(context).textTheme.headlineSmall,
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Expanded(
|
: Expanded(
|
||||||
child: ListView(
|
child: CustomScrollView(
|
||||||
padding: .all(12),
|
slivers: [
|
||||||
children: [
|
|
||||||
for (final MapEntry(key: powerLevel, value: members)
|
for (final MapEntry(key: powerLevel, value: members)
|
||||||
in value.toEntryIList(
|
in value) ...[
|
||||||
compare: (a, b) => (b?.key ?? double.infinity)
|
SliverToBoxAdapter(
|
||||||
.compareTo(a?.key ?? double.infinity),
|
child: Padding(
|
||||||
)) ...[
|
padding: .symmetric(horizontal: 16),
|
||||||
Padding(
|
|
||||||
padding: .symmetric(horizontal: 4),
|
|
||||||
child: DividerText(
|
child: DividerText(
|
||||||
powerLevel == null
|
powerLevel == null
|
||||||
? "Creators"
|
? "Creators"
|
||||||
: "Power Level $powerLevel",
|
: "Power Level $powerLevel",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SegmentedListSection(
|
),
|
||||||
children: members
|
SliverM3ECardList(
|
||||||
.map(
|
padding: .all(4),
|
||||||
(member) => switch (member.content) {
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.surfaceContainerHigh,
|
||||||
|
margin: .symmetric(horizontal: 12, vertical: 4),
|
||||||
|
itemCount: members.length,
|
||||||
|
itemBuilder: (context, index) =>
|
||||||
|
switch (members[index].content) {
|
||||||
MembershipContent(
|
MembershipContent(
|
||||||
:final avatarUrl,
|
:final avatarUrl,
|
||||||
:final displayName,
|
:final displayName,
|
||||||
) =>
|
) =>
|
||||||
SegmentedListTile(
|
ListTile(
|
||||||
onTap: () {},
|
|
||||||
// context.showUserPopover(
|
|
||||||
// member.content as MembershipContent,
|
|
||||||
// member.stateKey!,
|
|
||||||
// roomId: roomId,
|
|
||||||
// globalPosition: details.globalPosition,
|
|
||||||
// ),
|
|
||||||
title: Text(
|
title: Text(
|
||||||
displayName ??
|
displayName ??
|
||||||
member.stateKey!.localpart,
|
members[index]
|
||||||
|
.stateKey!
|
||||||
|
.localpart,
|
||||||
overflow: .ellipsis,
|
overflow: .ellipsis,
|
||||||
style: .new(
|
style: .new(
|
||||||
color: member.stateKey!.colorHash,
|
color: members[index]
|
||||||
|
.stateKey!
|
||||||
|
.colorHash,
|
||||||
fontWeight: .bold,
|
fontWeight: .bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
member.stateKey!,
|
members[index].stateKey!,
|
||||||
overflow: .ellipsis,
|
overflow: .ellipsis,
|
||||||
),
|
),
|
||||||
leading: AvatarOrHash(
|
leading: AvatarOrHash(
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
displayName ??
|
displayName ??
|
||||||
member.sender.localpart,
|
members[index].sender.localpart,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_ => throw Exception(
|
_ => throw Exception(
|
||||||
"Member content was not MembershipContent",
|
"Member content was not MembershipContent",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
onTap: (index) {
|
||||||
.toList(),
|
// context.showUserPopover(
|
||||||
|
// member.content as MembershipContent,
|
||||||
|
// member.stateKey!,
|
||||||
|
// roomId: roomId,
|
||||||
|
// globalPosition: details.globalPosition,
|
||||||
|
// ),
|
||||||
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 4),
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class Sidebar extends HookConsumerWidget {
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return Drawer(
|
return Drawer(
|
||||||
width: 340,
|
width: 330,
|
||||||
shape: Border(),
|
shape: Border(),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -206,19 +206,20 @@ class Sidebar extends HookConsumerWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: NavigationRailM3E(
|
child: NavigationRailM3E(
|
||||||
expandedWidth: 360,
|
expandedWidth: double.infinity,
|
||||||
scrollable: true,
|
scrollable: true,
|
||||||
background: Colors.transparent,
|
background: Colors.transparent,
|
||||||
type: .alwaysExpand,
|
type: .alwaysExpand,
|
||||||
selectedIndex: selectedRoomIndex ?? 0,
|
selectedIndex: selectedRoomIndex ?? 0,
|
||||||
sections: [
|
sections: [
|
||||||
.new(
|
.new(
|
||||||
|
header: selectedSpace.room == null ? null : Text("Rooms"),
|
||||||
destinations: roomsToDestinations(selectedSpace.children),
|
destinations: roomsToDestinations(selectedSpace.children),
|
||||||
),
|
),
|
||||||
for (final subSpace in selectedSpace.subSpaces)
|
for (final subSpace in selectedSpace.subSpaces)
|
||||||
.new(
|
.new(
|
||||||
header: Text(
|
header: Text(
|
||||||
subSpace.room.metadata?.name ?? "Unnamed Room",
|
subSpace.room.metadata?.name ?? "Unnamed Space",
|
||||||
),
|
),
|
||||||
destinations: roomsToDestinations(subSpace.children),
|
destinations: roomsToDestinations(subSpace.children),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ flutter.buildFlutterApplication {
|
||||||
window_size = "sha256-XelNtp7tpZ91QCEcvewVphNUtgQX7xrp5QP0oFo6DgM=";
|
window_size = "sha256-XelNtp7tpZ91QCEcvewVphNUtgQX7xrp5QP0oFo6DgM=";
|
||||||
emoji_text_field = "sha256-3TOys09EP2GRo6pUBGPXaqBlE39O2Cmwt42Hs1cTDKo=";
|
emoji_text_field = "sha256-3TOys09EP2GRo6pUBGPXaqBlE39O2Cmwt42Hs1cTDKo=";
|
||||||
linkify = "sha256-mxV/XHLxF9cn7sUPr2SUNjVmDr5lbxkuGCbNdyiZi2c=";
|
linkify = "sha256-mxV/XHLxF9cn7sUPr2SUNjVmDr5lbxkuGCbNdyiZi2c=";
|
||||||
navigation_rail_m3e = "sha256-Zkqo8RkNSWiELB+Bk6iLsgft9THuJiMtysEwXNS/ZgY=";
|
navigation_rail_m3e = "sha256-+2awDTQnK58gGRY1nuHckG/jjxarsYSRu9ovR4i4TEc=";
|
||||||
};
|
};
|
||||||
|
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
|
|
|
||||||
18
pubspec.lock
18
pubspec.lock
|
|
@ -783,6 +783,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.3"
|
version: "0.0.3"
|
||||||
|
m3e_card_list:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: m3e_card_list
|
||||||
|
sha256: d4aba0123cccda40ac80789befa8d355e1dc16aa7dcee910157690b0546d78d6
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.0"
|
||||||
m3e_design:
|
m3e_design:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -807,14 +815,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.0"
|
version: "0.13.0"
|
||||||
material_segmented_list:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: material_segmented_list
|
|
||||||
sha256: "384bfd41a78e745397ceff1dd39700961e6a5419ad911d1797bcc13ea3824241"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.5"
|
|
||||||
measure_size:
|
measure_size:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -993,7 +993,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.5"
|
version: "2.1.5"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba"
|
sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba"
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ dependencies:
|
||||||
intl: 0.20.2
|
intl: 0.20.2
|
||||||
fast_immutable_collections: 11.2.0
|
fast_immutable_collections: 11.2.0
|
||||||
path_provider: 2.1.5
|
path_provider: 2.1.5
|
||||||
path_provider_android: 2.2.23
|
|
||||||
url_launcher: 6.3.2
|
url_launcher: 6.3.2
|
||||||
freezed_annotation: 3.1.0
|
freezed_annotation: 3.1.0
|
||||||
image_picker: 1.2.2
|
image_picker: 1.2.2
|
||||||
|
|
@ -62,12 +61,12 @@ dependencies:
|
||||||
media_kit_video: 2.0.1
|
media_kit_video: 2.0.1
|
||||||
media_kit_libs_video: 1.0.7
|
media_kit_libs_video: 1.0.7
|
||||||
measure_size: ^5.0.2
|
measure_size: ^5.0.2
|
||||||
material_segmented_list: ^1.0.5
|
|
||||||
m3e_buttons: ^0.0.3
|
m3e_buttons: ^0.0.3
|
||||||
navigation_rail_m3e:
|
navigation_rail_m3e:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Henry-Hiles/material_3_expressive
|
url: https://github.com/Henry-Hiles/material_3_expressive
|
||||||
path: packages/navigation_rail_m3e
|
path: packages/navigation_rail_m3e
|
||||||
|
m3e_card_list: ^0.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: 2.15.0
|
build_runner: 2.15.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue