treewide cleanups
This commit is contained in:
parent
53d9c2b1e6
commit
853116ab2f
16 changed files with 88 additions and 138 deletions
|
|
@ -243,17 +243,14 @@ class RoomChatController(final ChatParams info)
|
||||||
ref.invalidate(provider);
|
ref.invalidate(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
var taggedMessage = text;
|
// Replaces each formatted tag, like "@id#name#", with its ID
|
||||||
|
final taggedMessage = tags.fold(
|
||||||
for (final tag in tags) {
|
text,
|
||||||
final escaped = RegExp.escape(tag.id);
|
(message, tag) => message.replaceAllMapped(
|
||||||
final pattern = RegExp(r"@+(" + escaped + r")(#[^#]*#)?");
|
RegExp(r"@+(" + RegExp.escape(tag.id) + r")(#[^#]*#)?"),
|
||||||
|
|
||||||
taggedMessage = taggedMessage.replaceAllMapped(
|
|
||||||
pattern,
|
|
||||||
(match) => match.group(1)!,
|
(match) => match.group(1)!,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
final client = ref.read(ClientController.provider.notifier);
|
final client = ref.read(ClientController.provider.notifier);
|
||||||
final event = await client.sendMessage(
|
final event = await client.sendMessage(
|
||||||
|
|
|
||||||
|
|
@ -25,55 +25,50 @@ class SpacesController extends AsyncNotifier<IList<Space>> {
|
||||||
MapEntry(spaceId, edges.map((edge) => edge.childId).toList()),
|
MapEntry(spaceId, edges.map((edge) => edge.childId).toList()),
|
||||||
);
|
);
|
||||||
|
|
||||||
Set<String> collectDescendants(String startId) {
|
/// Everything under [spaceId], however deep, skipping any already
|
||||||
final visited = <String>{};
|
/// [visited] so cycles end.
|
||||||
final stack = [startId];
|
ISet<String> collectDescendants(
|
||||||
|
String spaceId, [
|
||||||
while (stack.isNotEmpty) {
|
ISet<String> visited = const .empty(),
|
||||||
final current = stack.removeLast();
|
]) => (childrenById[spaceId] ?? const []).fold(
|
||||||
final children = childrenById[current] ?? const [];
|
visited,
|
||||||
|
(visited, childId) => visited.contains(childId)
|
||||||
for (final child in children) {
|
? visited
|
||||||
if (visited.add(child)) {
|
: collectDescendants(childId, visited.add(childId)),
|
||||||
stack.add(child);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return visited;
|
|
||||||
}
|
|
||||||
|
|
||||||
Space buildSpace(String spaceId) {
|
Space buildSpace(String spaceId) {
|
||||||
final space = rooms[spaceId];
|
final space = rooms[spaceId];
|
||||||
final directChildrenIds = childrenById[spaceId] ?? const [];
|
final directChildrenIds = childrenById[spaceId] ?? const [];
|
||||||
|
|
||||||
final directRooms = <Room>[];
|
|
||||||
final subSpaces = <Subspace>[];
|
|
||||||
|
|
||||||
for (final childId in directChildrenIds) {
|
|
||||||
final room = rooms[childId];
|
|
||||||
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(
|
return .new(
|
||||||
id: spaceId,
|
id: spaceId,
|
||||||
room: space,
|
room: space,
|
||||||
title: space?.metadata?.name ?? "Unnamed Space",
|
title: space?.metadata?.name ?? "Unnamed Space",
|
||||||
children: .new(directRooms),
|
children: .new(
|
||||||
subSpaces: .new(subSpaces),
|
directChildrenIds
|
||||||
|
.where((childId) => !childrenById.containsKey(childId))
|
||||||
|
.map((childId) => rooms[childId])
|
||||||
|
.nonNulls,
|
||||||
|
),
|
||||||
|
subSpaces: .new(
|
||||||
|
directChildrenIds
|
||||||
|
.where(childrenById.containsKey)
|
||||||
|
.map(
|
||||||
|
(childId) => switch (rooms[childId]) {
|
||||||
|
final room? => Subspace(
|
||||||
|
room: room,
|
||||||
|
children: .new(
|
||||||
|
collectDescendants(childId)
|
||||||
|
.map((id) => rooms[id])
|
||||||
|
.nonNulls,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
null => null,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.nonNulls,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,48 @@
|
||||||
import "package:collection/collection.dart";
|
|
||||||
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/client_state.dart";
|
import "package:nexus/controllers/client_state.dart";
|
||||||
import "package:nexus/models/content/content.dart";
|
import "package:nexus/models/content/content.dart";
|
||||||
import "package:nexus/models/content/membership.dart";
|
import "package:nexus/models/content/membership.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/room.dart";
|
import "package:nexus/models/room.dart";
|
||||||
|
|
||||||
class ViaController(final Room room) extends Notifier<String> {
|
class ViaController(final Room room) extends Notifier<String> {
|
||||||
@override
|
@override
|
||||||
String build() {
|
String build() {
|
||||||
final servers = <String>{};
|
final powerLevelUsers =
|
||||||
|
switch (room.state[EventType.powerLevels.type]?[""]) {
|
||||||
|
final rowId? => switch (room.events[rowId]?.content) {
|
||||||
|
PowerLevelsContent(:final users) => users.keys.toIList(),
|
||||||
|
_ => null,
|
||||||
|
},
|
||||||
|
null => null,
|
||||||
|
} ??
|
||||||
|
.new([]);
|
||||||
|
|
||||||
void addUserId(String? userId) {
|
final joinedMembers =
|
||||||
final server = userId?.split(":").lastOrNull;
|
(room.state[EventType.membership.type]?.values ?? const [])
|
||||||
if (server != null) {
|
.map(
|
||||||
servers.add(server);
|
(rowId) => switch (room.events[rowId]) {
|
||||||
}
|
Event(
|
||||||
}
|
stateKey: final userId?,
|
||||||
|
content: MembershipContent(status: .join),
|
||||||
|
) =>
|
||||||
|
userId,
|
||||||
|
_ => null,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.nonNulls
|
||||||
|
.toIList();
|
||||||
|
|
||||||
addUserId(ref.watch(ClientStateController.provider)?.userId);
|
final servers = [?ref.watch(ClientStateController.provider)?.userId]
|
||||||
|
.toIList()
|
||||||
final powerLevelsEventId = room.state[EventType.powerLevels.type]?[""];
|
.addAll(powerLevelUsers)
|
||||||
final powerLevels = powerLevelsEventId == null
|
.addAll(joinedMembers)
|
||||||
? null
|
.map((userId) => userId.substring(userId.indexOf(":") + 1))
|
||||||
: room.events[powerLevelsEventId];
|
.whereNoDuplicates()
|
||||||
|
.take(5)
|
||||||
if (powerLevels?.content case PowerLevelsContent(:final users)) {
|
.toISet();
|
||||||
for (final userId in users.keys) {
|
|
||||||
addUserId(userId);
|
|
||||||
if (servers.length >= 5) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final members = room.state[EventType.membership.type]?.values.toIList();
|
|
||||||
for (var i = 0; servers.length < 5; i++) {
|
|
||||||
final membershipEventId = members?.getOrNull(i);
|
|
||||||
final member = membershipEventId == null
|
|
||||||
? null
|
|
||||||
: room.events[membershipEventId];
|
|
||||||
|
|
||||||
if (member?.content case MembershipContent(:final status)) {
|
|
||||||
if (status == .join) {
|
|
||||||
addUserId(member?.stateKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (members?.getOrNull(i) == null) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return servers.isEmpty
|
return servers.isEmpty
|
||||||
? ""
|
? ""
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ class const App({super.key}) extends StatelessWidget {
|
||||||
.watch(SettingsController.provider)
|
.watch(SettingsController.provider)
|
||||||
.maybeWhen(
|
.maybeWhen(
|
||||||
data: (settings) => settings.theme,
|
data: (settings) => settings.theme,
|
||||||
orElse: () => ThemeMode.system,
|
orElse: () => .system,
|
||||||
),
|
),
|
||||||
home: child,
|
home: child,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
|
||||||
import "package:freezed_annotation/freezed_annotation.dart";
|
|
||||||
import "package:flutter/widgets.dart";
|
|
||||||
part "emoji.freezed.dart";
|
|
||||||
part "emoji.g.dart";
|
|
||||||
|
|
||||||
@Freezed(toJson: false, fromJson: false)
|
|
||||||
@JsonSerializable(createToJson: false)
|
|
||||||
class Emoji({
|
|
||||||
@JsonKey(
|
|
||||||
fromJson: Emoji.widgetFromJson,
|
|
||||||
readValue: Emoji.readWidgetValueFromJson,
|
|
||||||
)
|
|
||||||
required final Widget widget,
|
|
||||||
@JsonKey(name: "emoji") required final String value,
|
|
||||||
required final IList<String> aliases,
|
|
||||||
required final String description,
|
|
||||||
required final IList<String> tags,
|
|
||||||
}) with _$Emoji {
|
|
||||||
static Widget widgetFromJson(String emoji) => Text(emoji);
|
|
||||||
|
|
||||||
static String readWidgetValueFromJson(Map<dynamic, dynamic> json, _) =>
|
|
||||||
json["emoji"];
|
|
||||||
factory Emoji.fromJson(Map<String, Object?> json) => _$EmojiFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
|
||||||
import "package:freezed_annotation/freezed_annotation.dart";
|
|
||||||
import "package:nexus/models/emoji.dart";
|
|
||||||
import "package:flutter/widgets.dart";
|
|
||||||
part "emoji_category.freezed.dart";
|
|
||||||
|
|
||||||
@Freezed(toJson: false, fromJson: false)
|
|
||||||
class EmojiCategory({
|
|
||||||
required final Widget icon,
|
|
||||||
required final String name,
|
|
||||||
required final IList<Emoji> emojis,
|
|
||||||
}) with _$EmojiCategory;
|
|
||||||
|
|
@ -399,11 +399,11 @@ class const Composer(
|
||||||
|
|
||||||
if (!shiftPressed) {
|
if (!shiftPressed) {
|
||||||
send();
|
send();
|
||||||
return KeyEventResult.handled;
|
return .handled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return KeyEventResult.ignored;
|
return .ignored;
|
||||||
},
|
},
|
||||||
// Overrides the keyboard shortcut for pasting
|
// Overrides the keyboard shortcut for pasting
|
||||||
child: Actions(
|
child: Actions(
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class const RelationPreview(
|
||||||
onPressed: toggleShouldMention,
|
onPressed: toggleShouldMention,
|
||||||
child: Text(
|
child: Text(
|
||||||
shouldMention ? "@On" : "@Off",
|
shouldMention ? "@On" : "@Off",
|
||||||
style: TextStyle(
|
style: .new(
|
||||||
fontWeight: .w900,
|
fontWeight: .w900,
|
||||||
color: shouldMention ? null : Theme.of(context).disabledColor,
|
color: shouldMention ? null : Theme.of(context).disabledColor,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ class const Html(
|
||||||
src == null
|
src == null
|
||||||
? SizedBox.shrink()
|
? SizedBox.shrink()
|
||||||
: InlineCustomWidget(
|
: InlineCustomWidget(
|
||||||
alignment: PlaceholderAlignment.middle,
|
alignment: .middle,
|
||||||
child: ExpandableImage(
|
child: ExpandableImage(
|
||||||
.new(mxc: src),
|
.new(mxc: src),
|
||||||
downloadFilename: src.pathSegments.last,
|
downloadFilename: src.pathSegments.last,
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ class const MessageDisplayname(
|
||||||
final children => Row(
|
final children => Row(
|
||||||
spacing: 4,
|
spacing: 4,
|
||||||
mainAxisSize: .min,
|
mainAxisSize: .min,
|
||||||
children: [for (final child in children) Flexible(child: child)],
|
children: children.map((child) => Flexible(child: child)).toList(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ final class const MessageImage(
|
||||||
child: Image(
|
child: Image(
|
||||||
image: MxcImage(ref, request),
|
image: MxcImage(ref, request),
|
||||||
width: info?.width?.toDouble(),
|
width: info?.width?.toDouble(),
|
||||||
fit: BoxFit.fitWidth,
|
fit: .fitWidth,
|
||||||
loadingBuilder: (_, child, loadingProgress) =>
|
loadingBuilder: (_, child, loadingProgress) =>
|
||||||
loadingProgress == null
|
loadingProgress == null
|
||||||
? child
|
? child
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ class const NotificationsList({super.key})
|
||||||
listController: listController.value,
|
listController: listController.value,
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
itemCount: value.length,
|
itemCount: value.length,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const .symmetric(
|
||||||
horizontal: 8,
|
horizontal: 8,
|
||||||
vertical: 12,
|
vertical: 12,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ final class const RoomMenuButton(
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Icon(Icons.logout, color: danger),
|
leading: Icon(Icons.logout, color: danger),
|
||||||
title: Text("Leave", style: TextStyle(color: danger)),
|
title: Text("Leave", style: .new(color: danger)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ final class const DialogListTile<T>({
|
||||||
child: Chip(
|
child: Chip(
|
||||||
label: Text(
|
label: Text(
|
||||||
value is T ? getName(value as T) : "None",
|
value is T ? getName(value as T) : "None",
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: .ellipsis,
|
||||||
style: onChanged == null || saving.active
|
style: onChanged == null || saving.active
|
||||||
? .new(color: Colors.grey)
|
? .new(color: Colors.grey)
|
||||||
: null,
|
: null,
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ final class const RadioSheet<T>({
|
||||||
onChanged: (value) => chosen.value = (value as T,),
|
onChanged: (value) => chosen.value = (value as T,),
|
||||||
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: .min,
|
||||||
children: options
|
children: options
|
||||||
.map(
|
.map(
|
||||||
(option) => RadioListTile<T>(
|
(option) => RadioListTile<T>(
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class const SyncStatusBanner({super.key}) extends ConsumerWidget {
|
||||||
banner.title,
|
banner.title,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: .ellipsis,
|
overflow: .ellipsis,
|
||||||
style: TextStyle(
|
style: .new(
|
||||||
color: banner.foreground,
|
color: banner.foreground,
|
||||||
fontWeight: .bold,
|
fontWeight: .bold,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue