treewide cleanups

This commit is contained in:
Henry Hiles 2026-10-23 20:26:37 -04:00
commit 853116ab2f
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
16 changed files with 88 additions and 138 deletions

View file

@ -243,17 +243,14 @@ class RoomChatController(final ChatParams info)
ref.invalidate(provider);
}
var taggedMessage = text;
for (final tag in tags) {
final escaped = RegExp.escape(tag.id);
final pattern = RegExp(r"@+(" + escaped + r")(#[^#]*#)?");
taggedMessage = taggedMessage.replaceAllMapped(
pattern,
// Replaces each formatted tag, like "@id#name#", with its ID
final taggedMessage = tags.fold(
text,
(message, tag) => message.replaceAllMapped(
RegExp(r"@+(" + RegExp.escape(tag.id) + r")(#[^#]*#)?"),
(match) => match.group(1)!,
),
);
}
final client = ref.read(ClientController.provider.notifier);
final event = await client.sendMessage(

View file

@ -25,55 +25,50 @@ class SpacesController extends AsyncNotifier<IList<Space>> {
MapEntry(spaceId, edges.map((edge) => edge.childId).toList()),
);
Set<String> collectDescendants(String startId) {
final visited = <String>{};
final stack = [startId];
while (stack.isNotEmpty) {
final current = stack.removeLast();
final children = childrenById[current] ?? const [];
for (final child in children) {
if (visited.add(child)) {
stack.add(child);
}
}
}
return visited;
}
/// Everything under [spaceId], however deep, skipping any already
/// [visited] so cycles end.
ISet<String> collectDescendants(
String spaceId, [
ISet<String> visited = const .empty(),
]) => (childrenById[spaceId] ?? const []).fold(
visited,
(visited, childId) => visited.contains(childId)
? visited
: collectDescendants(childId, visited.add(childId)),
);
Space buildSpace(String spaceId) {
final space = rooms[spaceId];
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(
id: spaceId,
room: space,
title: space?.metadata?.name ?? "Unnamed Space",
children: .new(directRooms),
subSpaces: .new(subSpaces),
children: .new(
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,
),
);
}

View file

@ -1,53 +1,48 @@
import "package:collection/collection.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_state.dart";
import "package:nexus/models/content/content.dart";
import "package:nexus/models/content/membership.dart";
import "package:nexus/models/content/power_levels.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/room.dart";
class ViaController(final Room room) extends Notifier<String> {
@override
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 server = userId?.split(":").lastOrNull;
if (server != null) {
servers.add(server);
}
}
final joinedMembers =
(room.state[EventType.membership.type]?.values ?? const [])
.map(
(rowId) => switch (room.events[rowId]) {
Event(
stateKey: final userId?,
content: MembershipContent(status: .join),
) =>
userId,
_ => null,
},
)
.nonNulls
.toIList();
addUserId(ref.watch(ClientStateController.provider)?.userId);
final powerLevelsEventId = room.state[EventType.powerLevels.type]?[""];
final powerLevels = powerLevelsEventId == null
? null
: room.events[powerLevelsEventId];
if (powerLevels?.content case PowerLevelsContent(:final users)) {
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;
}
final servers = [?ref.watch(ClientStateController.provider)?.userId]
.toIList()
.addAll(powerLevelUsers)
.addAll(joinedMembers)
.map((userId) => userId.substring(userId.indexOf(":") + 1))
.whereNoDuplicates()
.take(5)
.toISet();
return servers.isEmpty
? ""

View file

@ -147,7 +147,7 @@ class const App({super.key}) extends StatelessWidget {
.watch(SettingsController.provider)
.maybeWhen(
data: (settings) => settings.theme,
orElse: () => ThemeMode.system,
orElse: () => .system,
),
home: child,
);

View file

@ -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);
}

View file

@ -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;

View file

@ -399,11 +399,11 @@ class const Composer(
if (!shiftPressed) {
send();
return KeyEventResult.handled;
return .handled;
}
}
return KeyEventResult.ignored;
return .ignored;
},
// Overrides the keyboard shortcut for pasting
child: Actions(

View file

@ -38,7 +38,7 @@ class const RelationPreview(
onPressed: toggleShouldMention,
child: Text(
shouldMention ? "@On" : "@Off",
style: TextStyle(
style: .new(
fontWeight: .w900,
color: shouldMention ? null : Theme.of(context).disabledColor,
),

View file

@ -76,7 +76,7 @@ class const Html(
src == null
? SizedBox.shrink()
: InlineCustomWidget(
alignment: PlaceholderAlignment.middle,
alignment: .middle,
child: ExpandableImage(
.new(mxc: src),
downloadFilename: src.pathSegments.last,

View file

@ -49,7 +49,7 @@ class const MessageDisplayname(
final children => Row(
spacing: 4,
mainAxisSize: .min,
children: [for (final child in children) Flexible(child: child)],
children: children.map((child) => Flexible(child: child)).toList(),
),
},
),

View file

@ -27,7 +27,7 @@ final class const MessageImage(
child: Image(
image: MxcImage(ref, request),
width: info?.width?.toDouble(),
fit: BoxFit.fitWidth,
fit: .fitWidth,
loadingBuilder: (_, child, loadingProgress) =>
loadingProgress == null
? child

View file

@ -151,7 +151,7 @@ class const NotificationsList({super.key})
listController: listController.value,
controller: scrollController,
itemCount: value.length,
padding: const EdgeInsets.symmetric(
padding: const .symmetric(
horizontal: 8,
vertical: 12,
),

View file

@ -146,7 +146,7 @@ final class const RoomMenuButton(
),
child: ListTile(
leading: Icon(Icons.logout, color: danger),
title: Text("Leave", style: TextStyle(color: danger)),
title: Text("Leave", style: .new(color: danger)),
),
),
],

View file

@ -42,7 +42,7 @@ final class const DialogListTile<T>({
child: Chip(
label: Text(
value is T ? getName(value as T) : "None",
overflow: TextOverflow.ellipsis,
overflow: .ellipsis,
style: onChanged == null || saving.active
? .new(color: Colors.grey)
: null,

View file

@ -36,7 +36,7 @@ final class const RadioSheet<T>({
onChanged: (value) => chosen.value = (value as T,),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisSize: .min,
children: options
.map(
(option) => RadioListTile<T>(

View file

@ -67,7 +67,7 @@ class const SyncStatusBanner({super.key}) extends ConsumerWidget {
banner.title,
maxLines: 1,
overflow: .ellipsis,
style: TextStyle(
style: .new(
color: banner.foreground,
fontWeight: .bold,
),