diff --git a/README.md b/README.md index 73f00de..702fd89 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [x] Member list - [ ] Colors based off of power level - [ ] Sort by power level -- [ ] Notifications using UnifiedPush ([#35](https://git.federated.nexus/Nexus/nexus/issues/35)) +- [ ] Notifications using UnifiedPush - [ ] Group calls using [MSC4195](https://github.com/matrix-org/matrix-spec-proposals/pull/4195) - [ ] Invites - [ ] Settings diff --git a/lib/controllers/account_data_controller.dart b/lib/controllers/account_data_controller.dart index 0926e08..125d7cf 100644 --- a/lib/controllers/account_data_controller.dart +++ b/lib/controllers/account_data_controller.dart @@ -4,10 +4,10 @@ import "package:nexus/models/account_data.dart"; class AccountDataController extends Notifier> { @override - IMap build() => .new(); + IMap build() => const IMap.empty(); void update(IMap newData) => - state = .new({...state.unlock, ...newData.unlock}); + state = IMap({...state.unlock, ...newData.unlock}); static final provider = NotifierProvider>( diff --git a/lib/controllers/author_controller.dart b/lib/controllers/author_controller.dart index 77202ca..70070e1 100644 --- a/lib/controllers/author_controller.dart +++ b/lib/controllers/author_controller.dart @@ -1,6 +1,7 @@ import "dart:async"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/user_controller.dart"; +import "package:nexus/models/configs/user_config.dart"; import "package:nexus/models/content/membership.dart"; import "package:nexus/models/event.dart"; @@ -12,11 +13,11 @@ class AuthorController extends AsyncNotifier { Future build() async { final member = await ref.watch( UserController.provider( - .new(roomId: event.roomId, userId: event.sender), + UserConfig(roomId: event.roomId, userId: event.sender), ).future, ); - return .new( + return MembershipContent( status: member.status, avatarUrl: event.pmp?.avatarUrl ?? member.avatarUrl, displayName: event.pmp?.displayName ?? member.displayName, diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index fb57735..fc4a31f 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -14,6 +14,7 @@ import "package:nexus/controllers/sync_status_controller.dart"; import "package:nexus/controllers/top_level_spaces_controller.dart"; import "package:nexus/helpers/extensions/gomuks_buffer.dart"; import "package:nexus/main.dart"; +import "package:nexus/models/client_state.dart"; import "package:nexus/models/event.dart"; import "package:nexus/models/paginate.dart"; import "package:nexus/models/requests/get_event_request.dart"; @@ -30,6 +31,7 @@ import "package:nexus/models/requests/send_message_request.dart"; import "package:nexus/models/requests/set_membership_request.dart"; import "package:nexus/models/room.dart"; import "package:nexus/models/sync_data.dart"; +import "package:nexus/models/sync_status.dart"; import "package:nexus/src/third_party/gomuks.g.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:path_provider/path_provider.dart"; @@ -64,12 +66,12 @@ class ClientController extends AsyncNotifier { case "client_state": ref .watch(ClientStateController.provider.notifier) - .set(.fromJson(decodedMuksEvent)); + .set(ClientState.fromJson(decodedMuksEvent)); break; case "sync_status": ref .watch(SyncStatusController.provider.notifier) - .set(.fromJson(decodedMuksEvent)); + .set(SyncStatus.fromJson(decodedMuksEvent)); break; case "init_complete": ref.watch(InitCompleteController.provider.notifier).complete(); @@ -79,10 +81,12 @@ class ClientController extends AsyncNotifier { ref .watch(RoomsController.provider.notifier) .update( - .new({ - event.roomId: .new(events: .new({event.rowId: event})), - }), - .new(), + { + event.roomId: Room( + events: {event.rowId: event}.toIMap(), + ), + }.toIMap(), + const ISet.empty(), ); break; @@ -206,11 +210,9 @@ class ClientController extends AsyncNotifier { (await _sendCommand("get_room_state", request.toJson())) as List?; final response = await getState(request); - return .new( - (response ?? await getState(request.copyWith(refetch: true)) ?? []).map( - (event) => .fromJson(event), - ), - ); + return (response ?? await getState(request.copyWith(refetch: true)) ?? []) + .map((event) => Event.fromJson(event)) + .toIList(); } Future?> getRelatedEvents( @@ -218,20 +220,20 @@ class ClientController extends AsyncNotifier { ) async { final response = (await _sendCommand("get_related_events", request.toJson())) as List?; - return .new(response?.map((event) => .fromJson(event))); + return response?.map((event) => Event.fromJson(event)).toIList(); } Future getEvent(GetEventRequest request) async { final json = await _sendCommand("get_event", request.toJson()); - return json == null ? null : .fromJson(json); + return json == null ? null : Event.fromJson(json); } Future paginate(PaginateRequest request) async => - .fromJson(await _sendCommand("paginate", request.toJson())); + Paginate.fromJson(await _sendCommand("paginate", request.toJson())); Future getProfile(String userId) async { final json = await _sendCommand("get_profile", {"user_id": userId}); - return .fromJsonWithCatch({...json, "id": userId}); + return Profile.fromJsonWithCatch({...json, "id": userId}); } Future reportEvent(ReportRequest request) => diff --git a/lib/controllers/client_state_controller.dart b/lib/controllers/client_state_controller.dart index 1b77ecb..998d4a1 100644 --- a/lib/controllers/client_state_controller.dart +++ b/lib/controllers/client_state_controller.dart @@ -5,7 +5,9 @@ class ClientStateController extends Notifier { @override Null build() => null; - void set(ClientState newState) => state = newState; + void set(ClientState newState) { + state = newState; + } static final provider = NotifierProvider( ClientStateController.new, diff --git a/lib/controllers/cross_cache_controller.dart b/lib/controllers/cross_cache_controller.dart index 4d5611a..1d6d4b6 100644 --- a/lib/controllers/cross_cache_controller.dart +++ b/lib/controllers/cross_cache_controller.dart @@ -2,8 +2,11 @@ import "package:cross_cache/cross_cache.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; class CrossCacheController extends Notifier { + static const String spaceKey = "space"; + static const String roomKey = "room"; + @override - CrossCache build() => .new(); + CrossCache build() => CrossCache(); static final provider = NotifierProvider( CrossCacheController.new, diff --git a/lib/controllers/emoji_controller.dart b/lib/controllers/emoji_controller.dart index caea3de..358f98b 100644 --- a/lib/controllers/emoji_controller.dart +++ b/lib/controllers/emoji_controller.dart @@ -12,7 +12,10 @@ class EmojiController extends AsyncNotifier { @override Future build() async { final response = await get( - .https("github.com", "github/gemoji/raw/refs/heads/master/db/emoji.json"), + Uri.https( + "github.com", + "github/gemoji/raw/refs/heads/master/db/emoji.json", + ), ); if (response.statusCode != 200) { @@ -27,19 +30,19 @@ class EmojiController extends AsyncNotifier { .toIList(); final categoryMap = entries.fold>>( - .new(), + const IMap.empty(), (acc, entry) => acc.update( entry.category, (list) => list.add(entry.emoji), - ifAbsent: () => .new([entry.emoji]), + ifAbsent: () => IList([entry.emoji]), ), ); final keywordMap = entries.fold>>( - .new(), + const IMap.empty(), (acc, entry) => acc.add( entry.emoji, - .new([...entry.tags, ...entry.aliases, entry.description]), + IList([...entry.tags, ...entry.aliases, entry.description]), ), ); @@ -68,9 +71,9 @@ class EmojiController extends AsyncNotifier { ); final customKeywords = IMap( - .fromEntries( + Map.fromEntries( keywordMap.entries.map( - (e) => .new(e.key, e.value.toList(growable: false)), + (e) => MapEntry(e.key, e.value.toList(growable: false)), ), ), ); @@ -78,7 +81,8 @@ class EmojiController extends AsyncNotifier { return (customCategories, customKeywords); } - static final provider = AsyncNotifierProvider( - EmojiController.new, - ); + static final provider = + AsyncNotifierProvider.autoDispose( + EmojiController.new, + ); } diff --git a/lib/controllers/members_controller.dart b/lib/controllers/members_controller.dart index 330785c..64e1ef7 100644 --- a/lib/controllers/members_controller.dart +++ b/lib/controllers/members_controller.dart @@ -16,7 +16,7 @@ class MembersController extends AsyncNotifier> { RoomsController.provider.select((value) => value[roomId]), ); - if (room == null) return .new(); + if (room == null) return const ISet.empty(); if (!room.hasFetchedMembers) { final fetchedState = await ref @@ -38,7 +38,7 @@ class MembersController extends AsyncNotifier> { .map((rowId) => room.events[rowId]) .nonNulls .toISet() ?? - .new(); + const ISet.empty(); } static final provider = AsyncNotifierProvider.autoDispose diff --git a/lib/controllers/multi_provider_controller.dart b/lib/controllers/multi_provider_controller.dart index 52dd8d9..e23ecaa 100644 --- a/lib/controllers/multi_provider_controller.dart +++ b/lib/controllers/multi_provider_controller.dart @@ -7,8 +7,9 @@ class MultiProviderController extends AsyncNotifier { final IList providers; @override - Future build() => - .wait(providers.map((provider) => ref.watch(provider.future))); + FutureOr build() async => await Future.wait( + providers.map((provider) => ref.watch(provider.future)), + ); static final provider = AsyncNotifierProvider.family< diff --git a/lib/controllers/power_level_controller.dart b/lib/controllers/power_level_controller.dart index 917ee31..2539778 100644 --- a/lib/controllers/power_level_controller.dart +++ b/lib/controllers/power_level_controller.dart @@ -4,6 +4,7 @@ import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/power_levels.dart"; +import "package:nexus/models/requests/membership_action.dart"; class PowerLevelController extends Notifier { final PowerLevelConfig config; @@ -13,7 +14,7 @@ class PowerLevelController extends Notifier { bool build() { if (config case EventPowerLevelConfig(:final eventType)) { assert( - eventType != .redaction, + eventType != EventType.redaction, "Checking power level for a redaction should use [PowerLevelConfig.redaction].", ); } @@ -28,7 +29,6 @@ class PowerLevelController extends Notifier { final content = event?.content is PowerLevelsContent ? event!.content : PowerLevelsContent(); - final user = ref.watch( ClientStateController.provider.select((value) => value?.userId), ); @@ -45,15 +45,15 @@ class PowerLevelController extends Notifier { MembershipActionPowerLevelConfig(:final action, :final targetUser) => switch (action) { - .invite => userLevel >= content.invite, + MembershipAction.invite => userLevel >= content.invite, - .kick => + MembershipAction.kick => userLevel >= content.kick && userLevel > powerLevelOf(targetUser), - .ban => + MembershipAction.ban => userLevel >= content.ban && userLevel > powerLevelOf(targetUser), - .unban => userLevel >= content.ban, + MembershipAction.unban => userLevel >= content.ban, }, StatePowerLevelConfig(:final eventType) => diff --git a/lib/controllers/reactions_controller.dart b/lib/controllers/reactions_controller.dart index db9af9b..8c199a9 100644 --- a/lib/controllers/reactions_controller.dart +++ b/lib/controllers/reactions_controller.dart @@ -4,6 +4,7 @@ import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/models/configs/reactions_config.dart"; import "package:nexus/models/content/reaction.dart"; +import "package:nexus/models/requests/get_related_events_request.dart"; class ReactionsController extends AsyncNotifier>> { final ReactionsConfig config; @@ -22,7 +23,7 @@ class ReactionsController extends AsyncNotifier>> { ? await ref .watch(ClientController.provider.notifier) .getRelatedEvents( - .new( + GetRelatedEventsRequest( roomId: config.roomId, eventId: eventInfo!.$1, relationType: "m.annotation", @@ -32,18 +33,18 @@ class ReactionsController extends AsyncNotifier>> { return reactionEvents ?.where((event) => event.redactedBy == null) - .fold>>(.new(), (acc, event) { + .fold>>(IMap(), (acc, event) { if (event.content case ReactionContent(:final key?)) { return acc.update( key, (list) => list.add(event.sender), - ifAbsent: () => .new([event.sender]), + ifAbsent: () => IList([event.sender]), ); } return acc; }) ?? - .new(); + const IMap.empty(); } static final provider = diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index 84ac7b0..d8fea0c 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -9,8 +9,12 @@ import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/reaction.dart"; import "package:nexus/models/event.dart"; +import "package:nexus/models/requests/get_related_events_request.dart"; +import "package:nexus/models/requests/get_room_state_request.dart"; +import "package:nexus/models/requests/paginate_request.dart"; import "package:nexus/models/requests/redact_event_request.dart"; import "package:nexus/models/relation_type.dart"; +import "package:nexus/models/requests/send_event_request.dart"; import "package:nexus/models/requests/send_message_request.dart"; import "package:nexus/models/room.dart"; @@ -24,10 +28,12 @@ class RoomChatController extends AsyncNotifier> { final room = ref.watch( RoomsController.provider.select((rooms) => rooms[roomId]), ); - if (room == null) return .new(); + if (room == null) return const IList.empty(); if (!room.hasFetchedState) { - final state = await client.getRoomState(.new(roomId: roomId)); + final state = await client.getRoomState( + GetRoomStateRequest(roomId: roomId), + ); await ref.read(RoomsController.provider.notifier).addState(roomId, state); } @@ -38,13 +44,16 @@ class RoomChatController extends AsyncNotifier> { loadOlder(); } - return room.timeline - .toEntryIList(compare: (a, b) => (a?.key ?? 0).compareTo(b?.key ?? 0)) - .map((element) => element.value) - .toIList() - .addAll(room.sticky) + return IMap.fromValues( + keyMapper: (id) => 9999999 + (id ?? 0), + values: room.sticky, + ) + .addAll(room.timeline) + .toEntryIList(compare: (a, b) => (b?.key ?? 0).compareTo(a?.key ?? 0)) .map((entry) { - final foundEvent = entry == null ? null : room.events[entry]; + final foundEvent = entry.value == null + ? null + : room.events[entry.value!]; final editedEvent = foundEvent == null || foundEvent.lastEditRowId == 0 @@ -77,7 +86,7 @@ class RoomChatController extends AsyncNotifier> { final response = await ref .watch(ClientController.provider.notifier) .paginate( - .new( + PaginateRequest( roomId: roomId, maxTimelineId: timelineKeys?.isNotEmpty == true ? timelineKeys?.reduce(min) @@ -103,7 +112,7 @@ class RoomChatController extends AsyncNotifier> { ), ), }), - .new(), + const ISet.empty(), ); return response.hasMore; @@ -144,20 +153,20 @@ class RoomChatController extends AsyncNotifier> { text: taggedMessage, relation: relation == null ? null - : .new(eventId: relation.eventId, relationType: relationType), + : Relation(eventId: relation.eventId, relationType: relationType), ), ); ref .watch(RoomsController.provider.notifier) .update( - .new({ - roomId: .new( - events: .new({event.rowId: event}), - sticky: .new({event.rowId}), + { + roomId: Room( + events: {event.rowId: event}.toIMap(), + sticky: {event.rowId}.toISet(), ), - }), - .new(), + }.toIMap(), + const ISet.empty(), ); } @@ -168,7 +177,7 @@ class RoomChatController extends AsyncNotifier> { ) async { final client = ref.watch(ClientController.provider.notifier); final allReactionEvents = await client.getRelatedEvents( - .new( + GetRelatedEventsRequest( roomId: roomId, eventId: event.eventId, relationType: "m.annotation", @@ -190,7 +199,9 @@ class RoomChatController extends AsyncNotifier> { if (reactionEvent != null) { await ref .watch(ClientController.provider.notifier) - .redactEvent(.new(eventId: reactionEvent.eventId, roomId: roomId)); + .redactEvent( + RedactEventRequest(eventId: reactionEvent.eventId, roomId: roomId), + ); } } @@ -198,7 +209,7 @@ class RoomChatController extends AsyncNotifier> { final client = ref.watch(ClientController.provider.notifier); await client.sendEvent( - .new( + SendEventRequest( roomId: roomId, type: EventType.reaction.type, content: { diff --git a/lib/controllers/rooms_controller.dart b/lib/controllers/rooms_controller.dart index d0c6eb9..c4b36c1 100644 --- a/lib/controllers/rooms_controller.dart +++ b/lib/controllers/rooms_controller.dart @@ -1,29 +1,33 @@ import "dart:isolate"; + import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/models/event.dart"; +import "package:nexus/models/read_receipt.dart"; import "package:nexus/models/room.dart"; class RoomsController extends Notifier> { @override - IMap build() => .new(); + IMap build() => const IMap.empty(); Future addState( String roomId, IList state, { bool isMembers = false, }) async => update( - .new({ + { roomId: Room( - events: .fromEntries(state.map((event) => .new(event.rowId, event))), + events: IMap.fromEntries( + state.map((event) => MapEntry(event.rowId, event)), + ), hasFetchedState: true, hasFetchedMembers: isMembers, state: await Isolate.run(() { - final newState = state.fold>>( - .new(), + final newState = state.fold( + const IMap>.empty(), (previousValue, stateEvent) => previousValue.add( stateEvent.type, - (previousValue[stateEvent.type] ?? .new()).add( + (previousValue[stateEvent.type] ?? const IMap.empty()).add( stateEvent.stateKey!, stateEvent.rowId, ), @@ -32,8 +36,8 @@ class RoomsController extends Notifier> { return newState; }), ), - }), - .new(), + }.toIMap(), + const ISet.empty(), ); void update(IMap rooms, ISet leftRooms) { @@ -61,7 +65,9 @@ class RoomsController extends Notifier> { existing.state, (previousValue, event) => previousValue.add( event.key, - (previousValue[event.key] ?? .new()).addAll(event.value), + (previousValue[event.key] ?? const IMap.empty()).addAll( + event.value, + ), ), ), reset: false, @@ -76,7 +82,9 @@ class RoomsController extends Notifier> { existing.receipts, (receiptAcc, event) => receiptAcc.add( event.key, - (receiptAcc[event.key] ?? .new()).addAll(event.value), + (receiptAcc[event.key] ?? IList()).addAll( + event.value, + ), ), ), ) ?? diff --git a/lib/controllers/shared_prefs_controller.dart b/lib/controllers/shared_prefs_controller.dart index 876fc47..f4dcdae 100644 --- a/lib/controllers/shared_prefs_controller.dart +++ b/lib/controllers/shared_prefs_controller.dart @@ -3,7 +3,7 @@ import "package:shared_preferences/shared_preferences.dart"; class SharedPrefsController extends AsyncNotifier { @override - Future build() async => .getInstance(); + Future build() => SharedPreferences.getInstance(); static final provider = AsyncNotifierProvider( diff --git a/lib/controllers/space_edges_controller.dart b/lib/controllers/space_edges_controller.dart index 81347c5..12694d6 100644 --- a/lib/controllers/space_edges_controller.dart +++ b/lib/controllers/space_edges_controller.dart @@ -4,7 +4,7 @@ import "package:nexus/models/space_edge.dart"; class SpaceEdgesController extends Notifier>> { @override - IMap> build() => .new(); + IMap> build() => const IMap.empty(); void set(IMap> newEdges) => state = state.addAll(newEdges); diff --git a/lib/controllers/spaces_controller.dart b/lib/controllers/spaces_controller.dart index 548ff20..7a503ad 100644 --- a/lib/controllers/spaces_controller.dart +++ b/lib/controllers/spaces_controller.dart @@ -7,6 +7,8 @@ import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/controllers/top_level_spaces_controller.dart"; import "package:nexus/controllers/space_edges_controller.dart"; import "package:nexus/models/space.dart"; +import "package:nexus/models/room.dart"; +import "package:nexus/models/space_edge.dart"; class SpacesController extends Notifier> { @override @@ -18,15 +20,15 @@ class SpacesController extends Notifier> { final childRoomsBySpaceId = IMap.fromEntries( topLevelSpaceIds.map((spaceId) { ISet walk(String currentId) { - final children = spaceEdges[currentId] ?? .new(); + final children = spaceEdges[currentId] ?? IList(); - return children.fold>(.new(), (acc, edge) { + return children.fold>(const ISet.empty(), (acc, edge) { final childId = edge.childId; final isSpace = spaceEdges.containsKey(childId); return acc - .addAll(!isSpace ? ISet([childId]) : const .empty()) - .addAll(isSpace ? walk(childId) : const .empty()); + .addAll(!isSpace ? ISet([childId]) : const ISet.empty()) + .addAll(isSpace ? walk(childId) : const ISet.empty()); }); } @@ -86,7 +88,7 @@ class SpacesController extends Notifier> { final room = rooms[id]; if (room == null) return null; - final children = childRoomsBySpaceId[id] ?? .new(); + final children = childRoomsBySpaceId[id] ?? IList(); return Space( id: id, title: room.metadata?.name ?? "Unnamed Room", @@ -98,13 +100,13 @@ class SpacesController extends Notifier> { .toIList(); return [ - .new( + Space( id: "home", title: "Home", icon: Icons.home, children: homeRooms, ), - .new( + Space( id: "dms", title: "Direct Messages", icon: Icons.people, @@ -114,19 +116,18 @@ class SpacesController extends Notifier> { ] .map( (space) => space.copyWith( - children: .new( - space.children - .sortedBy( - (element) => - element - .metadata - ?.sortingTimestamp - .millisecondsSinceEpoch ?? - 0, - ) - .sortedBy((room) => room.metadata?.unreadMessages ?? 0) - .reversed, - ), + children: space.children + .sortedBy( + (element) => + element + .metadata + ?.sortingTimestamp + .millisecondsSinceEpoch ?? + 0, + ) + .sortedBy((room) => room.metadata?.unreadMessages ?? 0) + .reversed + .toIList(), ), ) .toIList(); diff --git a/lib/controllers/sync_status_controller.dart b/lib/controllers/sync_status_controller.dart index 256c8e2..8475d9d 100644 --- a/lib/controllers/sync_status_controller.dart +++ b/lib/controllers/sync_status_controller.dart @@ -7,7 +7,7 @@ class SyncStatusController extends Notifier { Null build() => null; void set(SyncStatus newStatus) { - if (newStatus.type == .permanentlyFailed) { + if (newStatus.type == SyncStatusType.permanentlyFailed) { showError(newStatus.error ?? "Syncing failed"); } state = newStatus; diff --git a/lib/controllers/top_level_spaces_controller.dart b/lib/controllers/top_level_spaces_controller.dart index 321e29d..e1f9c88 100644 --- a/lib/controllers/top_level_spaces_controller.dart +++ b/lib/controllers/top_level_spaces_controller.dart @@ -3,7 +3,7 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; class TopLevelSpacesController extends Notifier> { @override - IList build() => .new(); + IList build() => const IList.empty(); void set(IList newSpaces) => state = newSpaces; diff --git a/lib/controllers/url_preview_controller.dart b/lib/controllers/url_preview_controller.dart index bb5626a..e54ebff 100644 --- a/lib/controllers/url_preview_controller.dart +++ b/lib/controllers/url_preview_controller.dart @@ -19,7 +19,7 @@ class UrlPreviewController extends AsyncNotifier { if (homeserver != null && !link.contains("matrix.to")) { { final response = await get( - .parse(homeserver) + Uri.parse(homeserver) .resolve("/_matrix/client/v1/media/preview_url") .replace(queryParameters: {"url": link}), headers: await ref.watch(HeaderController.provider.future), @@ -34,7 +34,7 @@ class UrlPreviewController extends AsyncNotifier { ? null : Uri.tryParse(mxc)?.mxcToHttps(homeserver); - return .fromJson(decodedValue).copyWith(imageUrl: image); + return OpenGraphData.fromJson(decodedValue).copyWith(imageUrl: image); } } } diff --git a/lib/controllers/user_controller.dart b/lib/controllers/user_controller.dart index 7d20e90..5a47ba6 100644 --- a/lib/controllers/user_controller.dart +++ b/lib/controllers/user_controller.dart @@ -6,6 +6,7 @@ import "package:nexus/controllers/profile_controller.dart"; import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/models/configs/user_config.dart"; import "package:nexus/models/content/membership.dart"; +import "package:nexus/models/membership_status.dart"; class UserController extends AsyncNotifier { final UserConfig config; @@ -30,9 +31,8 @@ class UserController extends AsyncNotifier { final profile = await ref.watch( ProfileController.provider(config.userId).future, ); - - return .new( - status: .leave, + return MembershipContent( + status: MembershipStatus.leave, avatarUrl: profile.avatarUrl, displayName: profile.displayName ?? config.userId.localpart, ); diff --git a/lib/controllers/via_controller.dart b/lib/controllers/via_controller.dart index 0b5890a..69e9f4a 100644 --- a/lib/controllers/via_controller.dart +++ b/lib/controllers/via_controller.dart @@ -5,6 +5,7 @@ import "package:nexus/controllers/client_state_controller.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/membership_status.dart"; import "package:nexus/models/room.dart"; class ViaController extends Notifier { @@ -44,7 +45,7 @@ class ViaController extends Notifier { : room.events[membershipEventId]; if (member?.content case MembershipContent(:final status)) { - if (status == .join) { + if (status == MembershipStatus.join) { addUserId(member?.stateKey); } } diff --git a/lib/helpers/extensions/gomuks_buffer.dart b/lib/helpers/extensions/gomuks_buffer.dart index cc16b46..88cfd5a 100644 --- a/lib/helpers/extensions/gomuks_buffer.dart +++ b/lib/helpers/extensions/gomuks_buffer.dart @@ -7,8 +7,8 @@ import "package:nexus/src/third_party/gomuks.g.dart"; extension GomuksOwnedBufferToX on GomuksOwnedBuffer { Uint8List toBytes() { try { - if (base == nullptr || length <= 0) return .new(0); - return .fromList(base.asTypedList(length)); + if (base == nullptr || length <= 0) return Uint8List(0); + return Uint8List.fromList(base.asTypedList(length)); } finally { calloc.free(base); } diff --git a/lib/helpers/extensions/mxc_to_https.dart b/lib/helpers/extensions/mxc_to_https.dart index b21f056..910f87d 100644 --- a/lib/helpers/extensions/mxc_to_https.dart +++ b/lib/helpers/extensions/mxc_to_https.dart @@ -1,4 +1,5 @@ extension MxcToHttps on Uri { - Uri mxcToHttps(String homeserver) => - .parse(homeserver).resolve("_matrix/client/v1/media/download/$host$path"); + Uri mxcToHttps(String homeserver) => Uri.parse( + homeserver, + ).resolve("_matrix/client/v1/media/download/$host$path"); } diff --git a/lib/helpers/extensions/scheme_to_theme.dart b/lib/helpers/extensions/scheme_to_theme.dart index b7d7972..7e2bea6 100644 --- a/lib/helpers/extensions/scheme_to_theme.dart +++ b/lib/helpers/extensions/scheme_to_theme.dart @@ -1,16 +1,16 @@ import "package:flutter/material.dart"; extension SchemeToTheme on ColorScheme { - ThemeData get theme => .from(colorScheme: this).copyWith( - cardTheme: .new(color: primaryContainer), - popupMenuTheme: .new( - shape: RoundedRectangleBorder(borderRadius: .circular(16)), - color: surfaceContainerHigh, - ), + ThemeData get theme => ThemeData.from(colorScheme: this).copyWith( + cardTheme: CardThemeData(color: primaryContainer), appBarTheme: AppBarTheme( titleSpacing: 0, backgroundColor: surfaceContainerLow, ), + popupMenuTheme: PopupMenuThemeData( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + color: surfaceContainerHigh, + ), textTheme: ThemeData( fontFamilyFallback: ["sans", "emoji"], brightness: brightness, diff --git a/lib/helpers/extensions/show_context_menu.dart b/lib/helpers/extensions/show_context_menu.dart index c860115..d2b1efd 100644 --- a/lib/helpers/extensions/show_context_menu.dart +++ b/lib/helpers/extensions/show_context_menu.dart @@ -9,8 +9,8 @@ extension ShowContextMenu on BuildContext { showMenu( context: this, - constraints: .loose(Size.infinite), - position: .fromLTRB( + constraints: BoxConstraints.loose(Size.infinite), + position: RelativeRect.fromLTRB( globalPosition.dx, globalPosition.dy, overlay.size.width - globalPosition.dx, diff --git a/lib/helpers/extensions/show_user_popover.dart b/lib/helpers/extensions/show_user_popover.dart index e703721..1a5e7cb 100644 --- a/lib/helpers/extensions/show_user_popover.dart +++ b/lib/helpers/extensions/show_user_popover.dart @@ -14,9 +14,9 @@ extension ShowUserPopover on BuildContext { children: [ PopupMenuItem( enabled: false, - padding: .symmetric(horizontal: 16, vertical: 8), + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: IconTheme( - data: .new(), + data: IconThemeData(), child: UserPopover(member, userId, roomId: roomId), ), ), diff --git a/lib/helpers/extensions/size_to_string.dart b/lib/helpers/extensions/size_to_string.dart index a9db345..654df9a 100644 --- a/lib/helpers/extensions/size_to_string.dart +++ b/lib/helpers/extensions/size_to_string.dart @@ -2,7 +2,14 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; extension SizeToString on int { String get sizeAsString { - const suffixes = IListConst(["B", "KB", "MB", "GB", "TB", "PB"]); + const IListConst suffixes = IListConst([ + "B", + "KB", + "MB", + "GB", + "TB", + "PB", + ]); var i = 0; var size = toDouble(); diff --git a/lib/helpers/launch_helper.dart b/lib/helpers/launch_helper.dart index 575395f..f872ef7 100644 --- a/lib/helpers/launch_helper.dart +++ b/lib/helpers/launch_helper.dart @@ -10,7 +10,9 @@ class LaunchHelper { try { return await ul.launchUrl( url, - mode: useWebview ? .inAppBrowserView : .externalApplication, + mode: useWebview + ? ul.LaunchMode.inAppBrowserView + : ul.LaunchMode.externalApplication, ); } on PlatformException catch (_) { return false; diff --git a/lib/models/content/content.dart b/lib/models/content/content.dart index e7b1141..ab17db8 100644 --- a/lib/models/content/content.dart +++ b/lib/models/content/content.dart @@ -34,7 +34,7 @@ class Content { ?.contentFromJson ?? Content.fromJson)(json); } catch (error) { - if (error is Error) return .new(parseError: error); + if (error is Error) return Content(parseError: error); rethrow; } } diff --git a/lib/pages/chat_page.dart b/lib/pages/chat_page.dart index 2c0ef97..3d6a35f 100644 --- a/lib/pages/chat_page.dart +++ b/lib/pages/chat_page.dart @@ -1,6 +1,5 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/emoji_controller.dart"; import "package:nexus/controllers/init_complete_controller.dart"; import "package:nexus/controllers/key_controller.dart"; import "package:nexus/widgets/appbar.dart"; @@ -18,7 +17,6 @@ class ChatPage extends ConsumerWidget { final showMembersByDefault = constraints.maxWidth > 1000; final initComplete = ref.watch(InitCompleteController.provider); final roomId = ref.watch(KeyController.provider(KeyController.roomKey)); - ref.read(EmojiController.provider); return Scaffold( appBar: initComplete ? null : Appbar(), @@ -37,7 +35,7 @@ class ChatPage extends ConsumerWidget { ) : Center( child: Column( - mainAxisSize: .min, + mainAxisSize: MainAxisSize.min, children: [Loading(), Text("Syncing...")], ), ), diff --git a/lib/pages/verify_page.dart b/lib/pages/verify_page.dart index 0469fa4..387c640 100644 --- a/lib/pages/verify_page.dart +++ b/lib/pages/verify_page.dart @@ -17,8 +17,8 @@ class VerifyPage extends HookConsumerWidget { body: AlertDialog( title: Text("Verify"), content: Column( - mainAxisSize: .min, - crossAxisAlignment: .start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Enter your recovery key or passphrase below to unlock encrypted events.\nYour passphrase is usually not the same as your password.", @@ -41,11 +41,11 @@ class VerifyPage extends HookConsumerWidget { : () async { final scaffoldMessenger = ScaffoldMessenger.of(context); final snackbar = scaffoldMessenger.showSnackBar( - .new( + SnackBar( content: Text( "Attempting to verify with recovery key...", ), - duration: .new(days: 999), + duration: Duration(days: 999), ), ); @@ -60,13 +60,13 @@ class VerifyPage extends HookConsumerWidget { isVerifying.value = false; if (context.mounted) { scaffoldMessenger.showSnackBar( - .new( + SnackBar( backgroundColor: Theme.of( context, ).colorScheme.errorContainer, content: Text( "Verification failed. Is your passphrase correct?\nError: $error", - style: .new( + style: TextStyle( color: Theme.of( context, ).colorScheme.onErrorContainer, diff --git a/lib/widgets/appbar.dart b/lib/widgets/appbar.dart index 811788a..08c717b 100644 --- a/lib/widgets/appbar.dart +++ b/lib/widgets/appbar.dart @@ -18,11 +18,11 @@ class Appbar extends StatelessWidget implements PreferredSizeWidget { this.backgroundColor, this.scrolledUnderElevation, this.leading, - this.actions = const .empty(), + this.actions = const IList.empty(), }); @override - Size get preferredSize => const .fromHeight(kToolbarHeight); + Size get preferredSize => const Size.fromHeight(kToolbarHeight); @override Widget build(BuildContext context) { @@ -42,7 +42,7 @@ class Appbar extends StatelessWidget implements PreferredSizeWidget { leading: InkWell(onTap: onTap, child: leading), backgroundColor: backgroundColor, scrolledUnderElevation: scrolledUnderElevation, - actionsPadding: const .symmetric(horizontal: 8), + actionsPadding: const EdgeInsets.symmetric(horizontal: 8), title: InkWell( onTap: onTap, child: IgnorePointer(child: title), diff --git a/lib/widgets/avatar_or_hash.dart b/lib/widgets/avatar_or_hash.dart index caac0c7..5308ef7 100644 --- a/lib/widgets/avatar_or_hash.dart +++ b/lib/widgets/avatar_or_hash.dart @@ -48,7 +48,7 @@ class AvatarOrHash extends ConsumerWidget { smallSize: 12, backgroundColor: Theme.of(context).colorScheme.primary, child: ClipRRect( - borderRadius: .all(.circular((height - 8) / 2.5)), + borderRadius: BorderRadius.all(Radius.circular((height - 8) / 2.5)), child: SizedBox( width: height, height: height, @@ -60,10 +60,10 @@ class AvatarOrHash extends ConsumerWidget { ref.watch(CrossCacheController.provider), headers: ref.headers, ), - fit: .cover, + fit: BoxFit.cover, loadingBuilder: (_, child, loadingProgress) => - loadingProgress == null ? child : fallback ?? box, - errorBuilder: (_, _, _) => fallback ?? box, + loadingProgress == null ? child : box, + errorBuilder: (_, _, _) => box, ), ), ), diff --git a/lib/widgets/composer/composer.dart b/lib/widgets/composer/composer.dart index d090cac..9f37999 100644 --- a/lib/widgets/composer/composer.dart +++ b/lib/widgets/composer/composer.dart @@ -4,6 +4,8 @@ import "package:flutter_hooks/flutter_hooks.dart"; import "package:fluttertagger/fluttertagger.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/power_level_controller.dart"; +import "package:nexus/models/configs/power_level_config.dart"; +import "package:nexus/models/content/content.dart"; import "package:nexus/models/event.dart"; import "package:nexus/models/relation_type.dart"; import "package:nexus/widgets/composer/mention_overlay.dart"; @@ -40,7 +42,7 @@ class Composer extends HookConsumerWidget { final shouldMention = useState(true); final query = useState(""); - if (relationType == .edit && controller.value.text.isEmpty) { + if (relationType == RelationType.edit && controller.value.text.isEmpty) { controller.value.text = relatedEvent?.localContent?.editSource ?? ""; } @@ -49,7 +51,7 @@ class Composer extends HookConsumerWidget { onSend( controller.value.formattedText, shouldMention: shouldMention.value, - tags: .new(controller.value.tags), + tags: controller.value.tags.toIList(), ); onDismiss(); @@ -58,13 +60,13 @@ class Composer extends HookConsumerWidget { final style = TextStyle( color: theme.colorScheme.primary, - fontWeight: .bold, + fontWeight: FontWeight.bold, ); return Padding( - padding: .all(12), + padding: EdgeInsetsGeometry.all(12), child: ClipRRect( - borderRadius: .all(.circular(12)), + borderRadius: BorderRadius.all(Radius.circular(12)), child: Column( children: [ RelationPreview( @@ -77,14 +79,17 @@ class Composer extends HookConsumerWidget { ), Container( color: theme.colorScheme.surfaceContainerHighest, - padding: .symmetric(horizontal: 8), + padding: EdgeInsets.symmetric(horizontal: 8), child: Row( spacing: 8, - mainAxisAlignment: .center, + mainAxisAlignment: MainAxisAlignment.center, children: ref.watch( PowerLevelController.provider( - .new(eventType: .message, roomId: roomId), + PowerLevelConfig( + eventType: EventType.message, + roomId: roomId, + ), ), ) ? [ @@ -119,7 +124,7 @@ class Composer extends HookConsumerWidget { ), Expanded( child: FlutterTagger( - triggerStrategy: .eager, + triggerStrategy: TriggerStrategy.eager, overlay: MentionOverlay( roomId, query: query.value, @@ -139,16 +144,16 @@ class Composer extends HookConsumerWidget { maxLines: 12, minLines: 1, autofocus: true, - decoration: .new( + decoration: InputDecoration( hintText: "Your message here...", - border: .none, + border: InputBorder.none, ), controller: controller.value, key: key, onSubmitted: (_) => send(), // Don't defocus on submit onEditingComplete: () {}, - textInputAction: .done, + textInputAction: TextInputAction.done, focusNode: node, ), ), @@ -162,7 +167,10 @@ class Composer extends HookConsumerWidget { : [ Expanded( child: Padding( - padding: .symmetric(horizontal: 8, vertical: 12), + padding: EdgeInsetsGeometry.symmetric( + horizontal: 8, + vertical: 12, + ), child: Text( "You don't have permission to send messages in this room...", ), diff --git a/lib/widgets/composer/mention_overlay.dart b/lib/widgets/composer/mention_overlay.dart index ea5dc6a..b4af97a 100644 --- a/lib/widgets/composer/mention_overlay.dart +++ b/lib/widgets/composer/mention_overlay.dart @@ -5,7 +5,9 @@ import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/controllers/via_controller.dart"; import "package:nexus/helpers/extensions/better_when.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/membership_status.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/loading.dart"; @@ -27,18 +29,21 @@ class MentionOverlay extends ConsumerWidget { final rooms = ref.watch(RoomsController.provider); return Padding( - padding: .all(8), + padding: EdgeInsets.all(8), child: ClipRRect( - borderRadius: .all(.circular(12)), + borderRadius: BorderRadius.all(Radius.circular(12)), child: Container( color: Theme.of(context).colorScheme.surfaceContainerHigh, - padding: .all(8), + padding: EdgeInsets.all(8), child: switch (triggerCharacter) { "@" => ref .watch( MembersByStatusController.provider( - .new(roomId: roomId, status: .join), + MembersByStatusConfig( + roomId: roomId, + status: MembershipStatus.join, + ), ), ) .betterWhen( diff --git a/lib/widgets/composer/relation_preview.dart b/lib/widgets/composer/relation_preview.dart index c9cc271..f2bcaf6 100644 --- a/lib/widgets/composer/relation_preview.dart +++ b/lib/widgets/composer/relation_preview.dart @@ -27,34 +27,38 @@ class RelationPreview extends ConsumerWidget { return Container( color: theme.colorScheme.surfaceContainerHigh, - padding: .symmetric(horizontal: 12), + padding: EdgeInsets.symmetric(horizontal: 12), child: Row( spacing: 8, children: [ - if (relationType == .edit) - Text("Editing message:", style: .new(fontWeight: .bold)), + if (relationType == RelationType.edit) + Text( + "Editing message:", + style: TextStyle(fontWeight: FontWeight.bold), + ), Expanded( child: Padding( - padding: .symmetric(vertical: 8), + padding: EdgeInsets.symmetric(vertical: 8), child: EventPreview(relatedEvent!), ), ), - if (relationType == .reply) + if (relationType == RelationType.reply) TextButton( onPressed: toggleShouldMention, child: Text( shouldMention ? "@On" : "@Off", style: TextStyle( - fontWeight: .w900, + fontWeight: FontWeight.w900, color: shouldMention ? null : Theme.of(context).disabledColor, ), ), ), IconButton( - tooltip: "Cancel ${relationType == .edit ? "edit" : "reply"}", + tooltip: + "Cancel ${relationType == RelationType.edit ? "edit" : "reply"}", onPressed: onDismiss, icon: const Icon(Icons.close), iconSize: 20, diff --git a/lib/widgets/divider_text.dart b/lib/widgets/divider_text.dart index f53795d..ca78844 100644 --- a/lib/widgets/divider_text.dart +++ b/lib/widgets/divider_text.dart @@ -14,9 +14,9 @@ class DividerText extends StatelessWidget { child: Divider(color: Theme.of(context).colorScheme.onSurface), ), ConstrainedBox( - constraints: .new(maxWidth: constraints.maxWidth - 32), + constraints: BoxConstraints(maxWidth: constraints.maxWidth - 32), child: Padding( - padding: const .all(8), + padding: const EdgeInsets.all(8), child: Text(text, style: Theme.of(context).textTheme.labelLarge), ), ), diff --git a/lib/widgets/emoji_picker_button.dart b/lib/widgets/emoji_picker_button.dart index bbe1cdc..e8805ca 100644 --- a/lib/widgets/emoji_picker_button.dart +++ b/lib/widgets/emoji_picker_button.dart @@ -20,14 +20,14 @@ class EmojiPickerButton extends HookConsumerWidget { Widget build(_, WidgetRef ref) => IconButton( onPressed: () async { onPressed?.call(); - final controller = this.controller ?? .new(); + final controller = this.controller ?? TextEditingController(); final emojis = await ref.watch(EmojiController.provider.future); if (context.mounted) { showModalBottomSheet( context: context, builder: (context) => EmojiKeyboardView( - config: .new( + config: EmojiViewConfig( showRecentTab: false, customCategories: emojis.$1.unlock, customKeywords: emojis.$2.unlock, @@ -37,8 +37,7 @@ class EmojiPickerButton extends HookConsumerWidget { textController: controller ..addListener(() { // Without this, there will sometimes be a debugLocked is not true error sometimes - // It might be preferable to use a microtask instead of a `Future.delayed`. - Future.delayed(.zero, () { + Future.delayed(Duration.zero, () { if (context.mounted) Navigator.of(context).pop(); }); onSelection?.call(controller.text); diff --git a/lib/widgets/event_preview.dart b/lib/widgets/event_preview.dart index 7a40a75..5a16fc7 100644 --- a/lib/widgets/event_preview.dart +++ b/lib/widgets/event_preview.dart @@ -12,16 +12,16 @@ class EventPreview extends StatelessWidget { @override Widget build(BuildContext context) => IgnorePointer( child: Padding( - padding: .symmetric(vertical: 4), + padding: EdgeInsets.symmetric(vertical: 4), child: Row( - mainAxisSize: .min, + mainAxisSize: MainAxisSize.min, spacing: 12, children: [ if (event.content is MessageContent) MessageAvatar(event), Flexible( child: Wrap( - crossAxisAlignment: .center, + crossAxisAlignment: WrapCrossAlignment.center, spacing: 8, runSpacing: 2, children: [ diff --git a/lib/widgets/expandable_image.dart b/lib/widgets/expandable_image.dart index df4cbd0..ac5bbe1 100644 --- a/lib/widgets/expandable_image.dart +++ b/lib/widgets/expandable_image.dart @@ -20,12 +20,14 @@ class ExpandableImage extends ConsumerWidget { builder: (_) => LayoutBuilder( builder: (context, constraints) => Dialog( backgroundColor: Colors.transparent, - insetPadding: .all(constraints.maxWidth / 100), + insetPadding: EdgeInsets.all(constraints.maxWidth / 100), child: ConstrainedBox( - constraints: .new(minWidth: min(constraints.maxWidth, 1000)), + constraints: BoxConstraints( + minWidth: min(constraints.maxWidth, 1000), + ), child: InteractiveViewer( child: Image( - fit: .contain, + fit: BoxFit.contain, errorBuilder: (_, error, stackTrace) => ErrorDialog( "Loading failed for $source\nError: $error", stackTrace, diff --git a/lib/widgets/file_card.dart b/lib/widgets/file_card.dart index afdad89..7e3bb6f 100644 --- a/lib/widgets/file_card.dart +++ b/lib/widgets/file_card.dart @@ -15,7 +15,11 @@ class FileCard extends StatelessWidget { color: Theme.of(context).colorScheme.surfaceContainer, child: ListTile( leading: Icon(Icons.file_copy), - title: Text(filename ?? "file", maxLines: 1, overflow: .ellipsis), + title: Text( + filename ?? "file", + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), subtitle: info?.size == null ? null : Text(info!.size!.sizeAsString), // TODO: Downloading files trailing: IconButton(onPressed: null, icon: Icon(Icons.download)), diff --git a/lib/widgets/flash_wrapper.dart b/lib/widgets/flash_wrapper.dart index 057565e..f52ea25 100644 --- a/lib/widgets/flash_wrapper.dart +++ b/lib/widgets/flash_wrapper.dart @@ -7,13 +7,13 @@ class FlashWrapper extends StatelessWidget { @override Widget build(BuildContext context) => ClipRRect( - borderRadius: .all(.circular(12)), + borderRadius: BorderRadius.all(Radius.circular(12)), child: AnimatedContainer( - padding: isFlashing ? .all(8) : .all(0), + padding: isFlashing ? EdgeInsets.all(8) : EdgeInsets.all(0), color: isFlashing ? Theme.of(context).colorScheme.onSurface.withAlpha(50) : Colors.transparent, - duration: .new(milliseconds: 250), + duration: Duration(milliseconds: 250), child: child, ), ); diff --git a/lib/widgets/form_text_input.dart b/lib/widgets/form_text_input.dart index 8b48883..21b2e5c 100644 --- a/lib/widgets/form_text_input.dart +++ b/lib/widgets/form_text_input.dart @@ -55,12 +55,14 @@ class FormTextInput extends StatelessWidget { maxLines: maxLines, maxLength: maxLength, inputFormatters: formatters, - textCapitalization: capitalize ? .sentences : .none, + textCapitalization: capitalize + ? TextCapitalization.sentences + : TextCapitalization.none, initialValue: initialValue, autocorrect: autocorrect, obscureText: obscure, onTap: onTap, - decoration: .new( + decoration: InputDecoration( labelText: title, border: border ?? (outlined ? null : const UnderlineInputBorder()), suffixIcon: trailing, diff --git a/lib/widgets/html/code_block.dart b/lib/widgets/html/code_block.dart index a5c3dee..80950ce 100644 --- a/lib/widgets/html/code_block.dart +++ b/lib/widgets/html/code_block.dart @@ -11,20 +11,20 @@ class CodeBlock extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); return ClipRRect( - borderRadius: .all(.circular(16)), + borderRadius: BorderRadius.all(Radius.circular(16)), child: ColoredBox( color: theme.colorScheme.surfaceContainerHighest, child: IntrinsicWidth( child: Column( children: [ Row( - mainAxisAlignment: .spaceBetween, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( - padding: .symmetric(horizontal: 8), + padding: EdgeInsets.symmetric(horizontal: 8), child: Text( lang.substring(0, min(lang.length, 15)), - style: .new(fontFamily: "monospace"), + style: TextStyle(fontFamily: "monospace"), ), ), TextButton.icon( @@ -37,13 +37,13 @@ class CodeBlock extends StatelessWidget { ColoredBox( color: theme.colorScheme.surfaceContainerHigh, child: Container( - constraints: .new(minWidth: 250), - padding: .all(8), + constraints: BoxConstraints(minWidth: 250), + padding: EdgeInsets.all(8), child: SelectableText( code, minLines: 1, maxLines: 99, - style: .new(fontFamily: "monospace"), + style: TextStyle(fontFamily: "monospace"), ), ), ), diff --git a/lib/widgets/html/html.dart b/lib/widgets/html/html.dart index 85ec9ae..449f400 100644 --- a/lib/widgets/html/html.dart +++ b/lib/widgets/html/html.dart @@ -86,7 +86,9 @@ class Html extends ConsumerWidget { ), errorBuilder: (_, error, _) => Text( "Image Failed to Load", - style: .new(color: Theme.of(context).colorScheme.error), + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), ), height: height.toDouble(), width: width?.toDouble(), @@ -144,14 +146,15 @@ class Html extends ConsumerWidget { element.attributes .mapTo?>( (key, value) => switch (key) { - "data-mx-color" => .new("color", value), - "data-mx-bg-color" => .new("background-color", value), + "data-mx-color" => MapEntry("color", value), + "data-mx-bg-color" => MapEntry("background-color", value), _ => null, }, ) .nonNulls, ), }, - onTapUrl: (url) => ref.watch(LaunchHelper.provider).launchUrl(.parse(url)), + onTapUrl: (url) => + ref.watch(LaunchHelper.provider).launchUrl(Uri.parse(url)), ); } diff --git a/lib/widgets/html/mention_chip.dart b/lib/widgets/html/mention_chip.dart index 141d5a0..28caa65 100644 --- a/lib/widgets/html/mention_chip.dart +++ b/lib/widgets/html/mention_chip.dart @@ -3,6 +3,7 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/user_controller.dart"; import "package:nexus/helpers/extensions/link_to_mention.dart"; import "package:nexus/helpers/extensions/show_user_popover.dart"; +import "package:nexus/models/configs/user_config.dart"; class MentionChip extends ConsumerWidget { final String? roomId; @@ -15,7 +16,9 @@ class MentionChip extends ConsumerWidget { final membership = mention?.startsWith("@") == true ? ref .watch( - UserController.provider(.new(roomId: roomId, userId: mention!)), + UserController.provider( + UserConfig(roomId: roomId, userId: mention!), + ), ) .whenOrNull(data: (data) => data) : null; @@ -38,8 +41,8 @@ class MentionChip extends ConsumerWidget { label: Text( (membership == null ? null : "@${membership.displayName}") ?? mention, - style: .new( - fontWeight: .bold, + style: TextStyle( + fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.onPrimary, ), ), diff --git a/lib/widgets/html/quoted.dart b/lib/widgets/html/quoted.dart index e582b06..6640118 100644 --- a/lib/widgets/html/quoted.dart +++ b/lib/widgets/html/quoted.dart @@ -8,9 +8,9 @@ class Quoted extends StatelessWidget { Widget build(BuildContext context) => Container( decoration: BoxDecoration( border: Border( - left: .new(width: 4, color: Theme.of(context).dividerColor), + left: BorderSide(width: 4, color: Theme.of(context).dividerColor), ), ), - child: Padding(padding: .only(left: 8), child: child), + child: Padding(padding: EdgeInsets.only(left: 8), child: child), ); } diff --git a/lib/widgets/html/spoiler_text.dart b/lib/widgets/html/spoiler_text.dart index a7a457b..9a42bff 100644 --- a/lib/widgets/html/spoiler_text.dart +++ b/lib/widgets/html/spoiler_text.dart @@ -13,15 +13,15 @@ class SpoilerText extends HookWidget { return InkWell( onTap: () => revealed.value = !revealed.value, child: AnimatedContainer( - duration: const .new(milliseconds: 100), - padding: const .symmetric(horizontal: 4, vertical: 2), + duration: const Duration(milliseconds: 100), + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), decoration: BoxDecoration( color: revealed.value ? Colors.transparent : Colors.blueGrey, - borderRadius: .circular(4), + borderRadius: BorderRadius.circular(4), ), child: Text( text, - style: .new(color: revealed.value ? null : Colors.transparent), + style: TextStyle(color: revealed.value ? null : Colors.transparent), ), ), ); diff --git a/lib/widgets/join_dialog.dart b/lib/widgets/join_dialog.dart index e20ab2a..e718200 100644 --- a/lib/widgets/join_dialog.dart +++ b/lib/widgets/join_dialog.dart @@ -1,4 +1,5 @@ import "package:collection/collection.dart"; +import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; @@ -6,6 +7,7 @@ import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/key_controller.dart"; import "package:nexus/controllers/spaces_controller.dart"; import "package:nexus/helpers/extensions/link_to_mention.dart"; +import "package:nexus/models/requests/join_room_request.dart"; import "package:nexus/widgets/form_text_input.dart"; class JoinDialog extends HookWidget { @@ -18,8 +20,8 @@ class JoinDialog extends HookWidget { return AlertDialog( title: Text("Join a Room"), content: Column( - mainAxisSize: .min, - crossAxisAlignment: .start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Enter the room alias, Matrix URI, or Matrix.to link."), SizedBox(height: 12), @@ -43,7 +45,7 @@ class JoinDialog extends HookWidget { final scaffoldMessenger = ScaffoldMessenger.of(context); final snackbar = scaffoldMessenger.showSnackBar( - .new( + SnackBar( content: Text("Joining room $roomIdOrAlias."), duration: Duration(days: 999), ), @@ -53,9 +55,9 @@ class JoinDialog extends HookWidget { final id = await ref .watch(ClientController.provider.notifier) .joinRoom( - .new( + JoinRoomRequest( roomIdOrAlias: roomIdOrAlias, - via: .new( + via: IList( Uri.tryParse( roomAlias.text.replaceAll("/#", ""), )?.queryParametersAll["via"] ?? @@ -67,9 +69,9 @@ class JoinDialog extends HookWidget { snackbar.close(); scaffoldMessenger.showSnackBar( - .new( + SnackBar( content: Text("Room $roomIdOrAlias successfully joined."), - action: .new( + action: SnackBarAction( label: "Open", onPressed: () async { final spaces = ref.watch(SpacesController.provider); @@ -111,13 +113,13 @@ class JoinDialog extends HookWidget { snackbar.close(); if (context.mounted) { scaffoldMessenger.showSnackBar( - .new( + SnackBar( backgroundColor: Theme.of( context, ).colorScheme.errorContainer, content: Text( error.toString(), - style: .new( + style: TextStyle( color: Theme.of(context).colorScheme.onErrorContainer, ), ), diff --git a/lib/widgets/lazy_loading/message_displayname.dart b/lib/widgets/lazy_loading/message_displayname.dart index ffa5dc0..7ff4b09 100644 --- a/lib/widgets/lazy_loading/message_displayname.dart +++ b/lib/widgets/lazy_loading/message_displayname.dart @@ -18,46 +18,52 @@ class MessageDisplayname extends ConsumerWidget { }); @override - Widget build(BuildContext context, WidgetRef ref) => switch (ref.watch( - AuthorController.provider(event), - )) { - AsyncData(:final value) || AsyncLoading(:final value?) => InkWell( - onTapUp: clickable - ? (details) => context.showUserPopover( - value, - event.sender, - roomId: event.roomId, - globalPosition: details.globalPosition, - ) - : null, - child: Wrap( - spacing: 4, - crossAxisAlignment: .center, - children: [ - Text( - value.displayName ?? event.sender.localpart, - style: - style ?? .new(color: event.sender.colorHash, fontWeight: .bold), - maxLines: 1, - overflow: .ellipsis, - ), - - if (event.pmp != null) - Text( - "(via ${event.sender})", - style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: event.sender.colorHash, - fontWeight: .bold, + Widget build(BuildContext context, WidgetRef ref) => + switch (ref.watch(AuthorController.provider(event))) { + AsyncData(:final value) || AsyncLoading(:final value?) => InkWell( + onTapUp: clickable + ? (details) => context.showUserPopover( + value, + event.sender, + roomId: event.roomId, + globalPosition: details.globalPosition, + ) + : null, + child: Wrap( + spacing: 4, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text( + value.displayName ?? event.sender.localpart, + style: + style ?? + TextStyle( + color: event.sender.colorHash, + fontWeight: FontWeight.bold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - maxLines: 1, - overflow: .ellipsis, - ), - ], - ), - ), - _ => Text( - event.sender.localpart, - style: .new(color: event.sender.colorHash, fontWeight: .bold), - ), - }; + + if (event.pmp != null) + Text( + "(via ${event.sender})", + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: event.sender.colorHash, + fontWeight: FontWeight.bold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + _ => Text( + event.sender.localpart, + style: TextStyle( + color: event.sender.colorHash, + fontWeight: FontWeight.bold, + ), + ), + }; } diff --git a/lib/widgets/linkified_text.dart b/lib/widgets/linkified_text.dart index 653248c..c6f1c11 100644 --- a/lib/widgets/linkified_text.dart +++ b/lib/widgets/linkified_text.dart @@ -14,10 +14,10 @@ class LinkifiedText extends ConsumerWidget { text: text, maxLines: maxLines, style: style, - options: .new(humanize: false), + options: LinkifyOptions(humanize: false), onOpen: (link) => - ref.watch(LaunchHelper.provider).launchUrl(.parse(link.url)), - linkStyle: .new(color: Theme.of(context).colorScheme.primary), - overflow: maxLines == null ? null : .ellipsis, + ref.watch(LaunchHelper.provider).launchUrl(Uri.parse(link.url)), + linkStyle: TextStyle(color: Theme.of(context).colorScheme.primary), + overflow: maxLines == null ? null : TextOverflow.ellipsis, ); } diff --git a/lib/widgets/loading.dart b/lib/widgets/loading.dart index fc84563..9bb2858 100644 --- a/lib/widgets/loading.dart +++ b/lib/widgets/loading.dart @@ -7,7 +7,7 @@ class Loading extends StatelessWidget { @override Widget build(BuildContext context) => Center( child: Padding( - padding: .all(16), + padding: EdgeInsets.all(16), child: SizedBox(height: height, child: CircularProgressIndicator()), ), ); diff --git a/lib/widgets/member_list.dart b/lib/widgets/member_list.dart index 00633a7..3d7ecec 100644 --- a/lib/widgets/member_list.dart +++ b/lib/widgets/member_list.dart @@ -5,6 +5,7 @@ import "package:nexus/controllers/members_by_status_controller.dart"; import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/helpers/extensions/show_user_popover.dart"; import "package:nexus/helpers/extensions/string_to_color.dart"; +import "package:nexus/models/configs/members_by_status_config.dart"; import "package:nexus/models/content/membership.dart"; import "package:nexus/models/membership_status.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; @@ -20,7 +21,7 @@ class MemberList extends HookConsumerWidget { final status = useState(MembershipStatus.join); final membersProvider = ref.watch( MembersByStatusController.provider( - .new(roomId: roomId, status: status.value), + MembersByStatusConfig(roomId: roomId, status: status.value), ), ); @@ -32,7 +33,7 @@ class MemberList extends HookConsumerWidget { scrolledUnderElevation: 0, leading: Icon(Icons.people), title: Text("Members"), - actionsPadding: .only(right: 4), + actionsPadding: EdgeInsets.only(right: 4), actions: [ if (Scaffold.of(context).hasEndDrawer) IconButton( @@ -43,24 +44,24 @@ class MemberList extends HookConsumerWidget { ], ), Wrap( - alignment: .center, + alignment: WrapAlignment.center, spacing: 8, runSpacing: 8, children: [ FilterChip( label: Text("Joined"), - onSelected: (value) => status.value = .join, - selected: status.value == .join, + onSelected: (value) => status.value = MembershipStatus.join, + selected: status.value == MembershipStatus.join, ), FilterChip( label: Text("Invited"), - onSelected: (value) => status.value = .invite, - selected: status.value == .invite, + onSelected: (value) => status.value = MembershipStatus.invite, + selected: status.value == MembershipStatus.invite, ), FilterChip( label: Text("Banned"), - onSelected: (value) => status.value = .ban, - selected: status.value == .ban, + onSelected: (value) => status.value = MembershipStatus.ban, + selected: status.value == MembershipStatus.ban, ), ], ), @@ -92,15 +93,15 @@ class MemberList extends HookConsumerWidget { ), title: Text( displayName ?? member.stateKey!.localpart, - overflow: .ellipsis, - style: .new( + overflow: TextOverflow.ellipsis, + style: TextStyle( color: member.stateKey!.colorHash, - fontWeight: .bold, + fontWeight: FontWeight.bold, ), ), subtitle: Text( member.stateKey!, - overflow: .ellipsis, + overflow: TextOverflow.ellipsis, ), ), ), diff --git a/lib/widgets/message_image.dart b/lib/widgets/message_image.dart index d92783a..e2cc823 100644 --- a/lib/widgets/message_image.dart +++ b/lib/widgets/message_image.dart @@ -17,7 +17,7 @@ class MessageImage extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) => ExpandableImage( url.toString(), child: ClipRRect( - borderRadius: .all(.circular(8)), + borderRadius: BorderRadius.all(Radius.circular(8)), child: Image( image: CachedNetworkImage( url.toString(), @@ -47,7 +47,7 @@ class MessageImage extends ConsumerWidget { errorBuilder: (context, error, stackTrace) => Center( child: Text( "Image Failed to Load", - style: .new(color: Theme.of(context).colorScheme.error), + style: TextStyle(color: Theme.of(context).colorScheme.error), ), ), ), diff --git a/lib/widgets/players/audio.dart b/lib/widgets/players/audio.dart index 0c96579..f75afe5 100644 --- a/lib/widgets/players/audio.dart +++ b/lib/widgets/players/audio.dart @@ -16,7 +16,9 @@ class AudioPlayer extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final player = useMemoized( - () => Player(configuration: .new(bufferSize: 128 * 1024 * 1024)), + () => Player( + configuration: PlayerConfiguration(bufferSize: 128 * 1024 * 1024), + ), ); final playing = useState(false); @@ -64,7 +66,7 @@ class AudioPlayer extends HookConsumerWidget { child: Card( color: Theme.of(context).colorScheme.surfaceContainer, child: Padding( - padding: .only(left: 8, right: 16), + padding: EdgeInsetsGeometry.only(left: 8, right: 16), child: Row( children: [ IconButton( @@ -86,7 +88,7 @@ class AudioPlayer extends HookConsumerWidget { : duration.value.inMilliseconds.toDouble(), value: position.value.inMilliseconds.toDouble(), onChanged: (value) => - player.seek(.new(milliseconds: value.toInt())), + player.seek(Duration(milliseconds: value.toInt())), ), ), Text( diff --git a/lib/widgets/players/video.dart b/lib/widgets/players/video.dart index 8083860..9621e4f 100644 --- a/lib/widgets/players/video.dart +++ b/lib/widgets/players/video.dart @@ -16,7 +16,9 @@ class VideoPlayer extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final player = useMemoized( - () => Player(configuration: .new(bufferSize: 128 * 1024 * 1024)), + () => Player( + configuration: PlayerConfiguration(bufferSize: 128 * 1024 * 1024), + ), ); final controller = useMemoized(() => VideoController(player)); diff --git a/lib/widgets/reaction_row.dart b/lib/widgets/reaction_row.dart index e41c2eb..4935ed7 100644 --- a/lib/widgets/reaction_row.dart +++ b/lib/widgets/reaction_row.dart @@ -8,6 +8,7 @@ import "package:nexus/controllers/reactions_controller.dart"; import "package:nexus/controllers/room_chat_controller.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; +import "package:nexus/models/configs/reactions_config.dart"; import "package:nexus/models/event.dart"; import "package:nexus/widgets/error_dialog.dart"; import "package:nexus/main.dart"; @@ -23,7 +24,7 @@ class ReactionRow extends ConsumerWidget { return switch (ref.watch( ReactionsController.provider( - .new(roomId: event.roomId, eventRowId: event.rowId), + ReactionsConfig(roomId: event.roomId, eventRowId: event.rowId), ), )) { AsyncData(value: final IMap>? reactors) || @@ -46,7 +47,7 @@ class ReactionRow extends ConsumerWidget { showCheckmark: false, selected: selected, label: Row( - mainAxisSize: .min, + mainAxisSize: MainAxisSize.min, spacing: 8, children: [ Flexible( @@ -63,9 +64,15 @@ class ReactionRow extends ConsumerWidget { ref.watch(CrossCacheController.provider), ), ) - : Text(reaction, overflow: .ellipsis), + : Text( + reaction, + overflow: TextOverflow.ellipsis, + ), + ), + Text( + count.toString(), + overflow: TextOverflow.ellipsis, ), - Text(count.toString(), overflow: .ellipsis), ], ), onSelected: enabled.value diff --git a/lib/widgets/renderers/event.dart b/lib/widgets/renderers/event.dart index d1c1c7c..d1442bc 100644 --- a/lib/widgets/renderers/event.dart +++ b/lib/widgets/renderers/event.dart @@ -64,11 +64,12 @@ class EventRenderer extends ConsumerWidget { textOnly: textOnly, ), - MembershipContent content => switch (event.previousContent) { - MembershipContent(:final status) => - status == content.status ? null : MembershipRenderer(event), - _ => MembershipRenderer(event), - }, + MembershipContent content => + event.previousContent is MembershipContent && + (event.previousContent as MembershipContent).status == + content.status + ? null + : MembershipRenderer(event), AvatarContent() => GenericEventRenderer(Icons.interests, [ MessageDisplayname(event), @@ -80,12 +81,12 @@ class EventRenderer extends ConsumerWidget { Text("created the room"), ]), - PowerLevelsContent() => GenericEventRenderer(Icons.power, [ + PowerLevelsContent() => GenericEventRenderer(Icons.add, [ MessageDisplayname(event), Text("changed the room's power levels"), ]), - JoinRulesContent() => GenericEventRenderer(Icons.rule, [ + JoinRulesContent() => GenericEventRenderer(Icons.add, [ MessageDisplayname(event), Text("changed the room's join rules"), ]), @@ -100,10 +101,10 @@ class EventRenderer extends ConsumerWidget { MessageDisplayname(event), 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)", + HistoryVisibility.invited => "since invited", + HistoryVisibility.joined => "since joined", + HistoryVisibility.shared => "all history visible (shared)", + HistoryVisibility.worldReadable => "all history visible (world readable)", }}", ), ]), @@ -157,7 +158,7 @@ class EventRenderer extends ConsumerWidget { ); return Column( - crossAxisAlignment: .start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ if (child != null) ...[ if (textOnly) @@ -167,7 +168,7 @@ class EventRenderer extends ConsumerWidget { onSecondaryTapUp: contextMenuCallback, onLongPressStart: contextMenuCallback, child: Padding( - padding: isGrouped ? .zero : .only(top: 8), + padding: isGrouped ? EdgeInsets.zero : EdgeInsets.only(top: 8), child: child, ), ), @@ -182,7 +183,12 @@ class EventRenderer extends ConsumerWidget { color: theme.colorScheme.error, ), ), - ].map((child) => Padding(padding: .only(left: 4), child: child)), + ].map( + (child) => Padding( + padding: EdgeInsetsGeometry.only(left: 4), + child: child, + ), + ), ], ] else if (textOnly) Text("Unknown event type", style: errorStyle), diff --git a/lib/widgets/renderers/generic_event.dart b/lib/widgets/renderers/generic_event.dart index 4f4380a..5efd724 100644 --- a/lib/widgets/renderers/generic_event.dart +++ b/lib/widgets/renderers/generic_event.dart @@ -7,11 +7,14 @@ class GenericEventRenderer extends StatelessWidget { @override Widget build(BuildContext context) => Padding( - padding: .only(bottom: 8), + padding: EdgeInsets.only(bottom: 8), child: Row( spacing: 8, children: [ - Padding(padding: .symmetric(horizontal: 4), child: Icon(icon)), + Padding( + padding: EdgeInsets.symmetric(horizontal: 4), + child: Icon(icon), + ), Expanded(child: Wrap(spacing: 4, children: children)), ], ), diff --git a/lib/widgets/renderers/membership.dart b/lib/widgets/renderers/membership.dart index c8c91ba..c109aed 100644 --- a/lib/widgets/renderers/membership.dart +++ b/lib/widgets/renderers/membership.dart @@ -4,6 +4,7 @@ import "package:nexus/helpers/extensions/show_user_popover.dart"; import "package:nexus/helpers/extensions/string_to_color.dart"; import "package:nexus/models/content/membership.dart"; import "package:nexus/models/event.dart"; +import "package:nexus/models/membership_status.dart"; import "package:nexus/widgets/lazy_loading/message_displayname.dart"; import "package:nexus/widgets/renderers/generic_event.dart"; @@ -28,21 +29,24 @@ class MembershipRenderer extends StatelessWidget { globalPosition: details.globalPosition, ), child: Text( - overflow: .ellipsis, + overflow: TextOverflow.ellipsis, content.displayName ?? event.stateKey!.localpart, maxLines: 1, - style: .new(color: event.sender.colorHash, fontWeight: .bold), + style: TextStyle( + color: event.sender.colorHash, + fontWeight: FontWeight.bold, + ), ), ), Text( - overflow: .ellipsis, + overflow: TextOverflow.ellipsis, maxLines: 1, "${switch (content.status) { - .invite => "was invited to", - .join => "joined", - .leave => event.sender == event.stateKey ? "left" : (event.unsigned["prev_content"]?["membership"] == "ban" ? "was unbanned from" : "was kicked from"), - .ban => "was banned from", - .knock => "asked to join", + MembershipStatus.invite => "was invited to", + MembershipStatus.join => "joined", + MembershipStatus.leave => event.sender == event.stateKey ? "left" : (event.unsigned["prev_content"]?["membership"] == "ban" ? "was unbanned from" : "was kicked from"), + MembershipStatus.ban => "was banned from", + MembershipStatus.knock => "asked to join", }} the room${event.sender == event.stateKey ? "" : " by "}", ), if (event.sender != event.stateKey) MessageDisplayname(event), diff --git a/lib/widgets/renderers/message.dart b/lib/widgets/renderers/message.dart index 3470246..0dc09e6 100644 --- a/lib/widgets/renderers/message.dart +++ b/lib/widgets/renderers/message.dart @@ -9,6 +9,7 @@ import "package:nexus/models/content/encrypted.dart"; import "package:nexus/models/content/message.dart"; import "package:nexus/models/content/sticker.dart"; import "package:nexus/models/event.dart"; +import "package:nexus/models/requests/get_event_request.dart"; import "package:nexus/widgets/file_card.dart"; import "package:nexus/widgets/html/html.dart"; import "package:nexus/widgets/lazy_loading/message_avatar.dart"; @@ -48,19 +49,19 @@ class MessageRenderer extends ConsumerWidget { child: Text( format(event.timestamp), maxLines: 1, - overflow: .ellipsis, + overflow: TextOverflow.ellipsis, style: theme.textTheme.labelSmall?.copyWith(color: Colors.grey), ), ); final textStyle = TextStyle( fontSize: event.localContent?.bigEmoji == true ? 32 : null, - fontStyle: event.content is EmoteMessageContent ? .italic : null, + fontStyle: event.content is EmoteMessageContent ? FontStyle.italic : null, ); return Row( - crossAxisAlignment: .start, - mainAxisSize: .min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, spacing: 8, children: [ if (!textOnly) @@ -71,7 +72,7 @@ class MessageRenderer extends ConsumerWidget { Flexible( child: Column( spacing: 4, - crossAxisAlignment: .start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ if (!isGrouped && !textOnly) Row( @@ -82,7 +83,7 @@ class MessageRenderer extends ConsumerWidget { ], ), Card( - margin: textOnly ? .zero : .only(bottom: 4), + margin: textOnly ? EdgeInsets.zero : EdgeInsets.only(bottom: 4), color: textOnly ? Colors.transparent : ref.watch( @@ -98,21 +99,24 @@ class MessageRenderer extends ConsumerWidget { elevation: textOnly ? 0 : null, child: Padding( - padding: textOnly ? .zero : .all(12), + padding: textOnly ? EdgeInsets.zero : EdgeInsets.all(12), child: Column( - crossAxisAlignment: .start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ if (!textOnly && event.replyTo != null) Card( - margin: .only(bottom: 8), + margin: EdgeInsets.only(bottom: 8), color: theme.colorScheme.surfaceContainerHigh, child: InkWell( onTap: onTapReply, child: Padding( - padding: .symmetric(vertical: 8, horizontal: 12), + padding: EdgeInsetsGeometry.symmetric( + vertical: 8, + horizontal: 12, + ), child: switch (ref.watch( EventController.provider( - .new( + GetEventRequest( roomId: event.roomId, eventId: event.replyTo!, ), @@ -141,10 +145,12 @@ class MessageRenderer extends ConsumerWidget { ? Text( body, maxLines: maxLines, - overflow: .ellipsis, + overflow: TextOverflow.ellipsis, ) : ConstrainedBox( - constraints: .loose(.square(200)), + constraints: BoxConstraints.loose( + Size.square(200), + ), child: MessageImage( url.mxcToHttps( ref.watch( @@ -193,9 +199,9 @@ class MessageRenderer extends ConsumerWidget { :final formattedBody, :final format, ) => Column( - crossAxisAlignment: .start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - format == .html && !textOnly + format == MessageFormat.html && !textOnly ? Html( roomId: event.roomId, textStyle: textStyle, @@ -237,7 +243,9 @@ class MessageRenderer extends ConsumerWidget { ), )) { final url? => ConstrainedBox( - constraints: .loose(.square(500)), + constraints: BoxConstraints.loose( + Size.square(500), + ), child: switch (event.content) { VideoMessageContent(:final info) => VideoPlayer(url, info), @@ -278,7 +286,7 @@ class MessageRenderer extends ConsumerWidget { ), MessageContent(:final body) => Row( spacing: 8, - mainAxisSize: .min, + mainAxisSize: MainAxisSize.min, children: [ Text("Unknown message type:", style: errorStyle), Text(body), diff --git a/lib/widgets/room_appbar.dart b/lib/widgets/room_appbar.dart index d16c2df..b40f48d 100644 --- a/lib/widgets/room_appbar.dart +++ b/lib/widgets/room_appbar.dart @@ -38,17 +38,17 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { : () => showDialog( context: context, builder: (context) => Dialog( - constraints: .loose(.fromWidth(400)), + constraints: BoxConstraints.loose(Size.fromWidth(400)), child: Padding( - padding: .all(24), + padding: EdgeInsetsGeometry.all(24), child: Column( - mainAxisSize: .min, - crossAxisAlignment: .start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, spacing: 8, children: [ Row( spacing: 12, - mainAxisSize: .min, + mainAxisSize: MainAxisSize.min, children: [ if (room.metadata?.avatar != null) ExpandableImage( @@ -71,7 +71,7 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { Expanded( child: Text( room.metadata?.name ?? "Unnamed Room", - overflow: .ellipsis, + overflow: TextOverflow.ellipsis, maxLines: 3, style: Theme.of(context).textTheme.headlineSmall, ), @@ -105,18 +105,18 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { title: room == null ? null : Column( - crossAxisAlignment: .start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( room.metadata?.name ?? "Unnamed Room", - overflow: .ellipsis, + overflow: TextOverflow.ellipsis, maxLines: 1, ), if (room.metadata?.topic?.isNotEmpty == true) Text( room.metadata!.topic!, maxLines: 1, - overflow: .ellipsis, + overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.labelMedium?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), diff --git a/lib/widgets/room_chat.dart b/lib/widgets/room_chat.dart index 9e3d281..733c5ba 100644 --- a/lib/widgets/room_chat.dart +++ b/lib/widgets/room_chat.dart @@ -12,9 +12,11 @@ import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/controllers/room_chat_controller.dart"; import "package:nexus/controllers/via_controller.dart"; import "package:nexus/models/configs/power_level_config.dart"; +import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/message.dart"; import "package:nexus/models/event.dart"; import "package:nexus/models/relation_type.dart"; +import "package:nexus/models/requests/report_request.dart"; import "package:nexus/widgets/composer/composer.dart"; import "package:nexus/widgets/emoji_picker_button.dart"; import "package:nexus/widgets/renderers/event.dart"; @@ -77,73 +79,10 @@ class RoomChat extends HookConsumerWidget { final listController = useRef(ListController()); final scrollController = useScrollController(); - final controllerData = ref.watch(controllerProvider); - - final topEventBeforeLoad = useState(null); - - Future loadOlder() async { - if (controllerData case AsyncData(:final value)) { - topEventBeforeLoad.value = value.firstOrNull?.eventId; - await notifier.loadOlder(); - } - } - - useEffect(() { - ref - .read(controllerProvider.future) - .then( - (_) => WidgetsBinding.instance.addPostFrameCallback((_) { - if (scrollController.hasClients) { - scrollController.jumpTo( - scrollController.position.maxScrollExtent - .000001, - ); - } - }), - ); - - return null; - }, [scrollController.hasClients]); - - useEffect(() { - if (controllerData case AsyncData( - :final value, - ) when scrollController.hasClients) { - if (topEventBeforeLoad.value != null) { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (scrollController.hasClients) { - final index = value.indexWhere( - (event) => event.eventId == topEventBeforeLoad.value, - ); - if (index != -1) { - listController.value.jumpToItem( - index: index, - scrollController: scrollController, - alignment: 0, - ); - } - } - topEventBeforeLoad.value = null; - }); - } else if (scrollController.position.atEdge && - scrollController.position.pixels != 0) { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (scrollController.hasClients) { - scrollController.jumpTo( - scrollController.position.maxScrollExtent, - ); - } - }); - } - } - - return null; - }, [controllerData]); useEffect(() { Future listener() async { - if (!scrollController.hasClients || !scrollController.position.atEdge) { - return; - } + if (!scrollController.position.atEdge) return; final room = ref.watch( RoomsController.provider.select((value) => value[roomId]), @@ -151,21 +90,20 @@ class RoomChat extends HookConsumerWidget { if (room == null) return; if (scrollController.position.pixels == 0) { - if (room.hasMore) { - await loadOlder(); - } - } else { await client.markRead(room); + } else { + if (room.hasMore) await notifier.loadOlder(); } } scrollController.addListener(listener); return () => scrollController.removeListener(listener); - }, [roomId, controllerData]); + }, [roomId]); final composerNode = useFocusNode( onKeyEvent: (_, event) { - if (event is KeyDownEvent && event.logicalKey == .escape) { + if (event is KeyDownEvent && + event.logicalKey == LogicalKeyboardKey.escape) { relatedEvent.value = null; return KeyEventResult.handled; } @@ -180,7 +118,7 @@ class RoomChat extends HookConsumerWidget { return [ if (ref.watch( PowerLevelController.provider( - .new(eventType: .reaction, roomId: roomId), + PowerLevelConfig(eventType: EventType.reaction, roomId: roomId), ), )) PopupMenuItem( @@ -196,7 +134,7 @@ class RoomChat extends HookConsumerWidget { value["m.recent_emoji"] ?.content["recent_emoji"] ?? [], - ).map((entry) => entry["emoji"]).toIList(), + ).map((entry) => entry["emoji"]), ), ), "👍", @@ -229,13 +167,13 @@ class RoomChat extends HookConsumerWidget { ), if (ref.watch( PowerLevelController.provider( - PowerLevelConfig(eventType: .message, roomId: roomId), + PowerLevelConfig(eventType: EventType.message, roomId: roomId), ), )) PopupMenuItem( onTap: () { relatedEvent.value = event; - relationType.value = .reply; + relationType.value = RelationType.reply; composerNode.requestFocus(); }, child: ListTile(leading: Icon(Icons.reply), title: Text("Reply")), @@ -244,7 +182,7 @@ class RoomChat extends HookConsumerWidget { PopupMenuItem( onTap: () { relatedEvent.value = event; - relationType.value = .edit; + relationType.value = RelationType.edit; composerNode.requestFocus(); }, child: ListTile(leading: Icon(Icons.edit), title: Text("Edit")), @@ -269,7 +207,10 @@ class RoomChat extends HookConsumerWidget { ), if (ref.watch( PowerLevelController.provider( - .redaction(targetUser: event.sender, roomId: roomId), + PowerLevelConfig.redaction( + targetUser: event.sender, + roomId: roomId, + ), ), )) PopupMenuItem( @@ -281,8 +222,8 @@ class RoomChat extends HookConsumerWidget { return AlertDialog( title: Text("Delete Message"), content: Column( - mainAxisSize: .min, - crossAxisAlignment: .start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Are you sure you want to delete this message? This can not be reversed.", @@ -320,7 +261,7 @@ class RoomChat extends HookConsumerWidget { ), child: ListTile( leading: Icon(Icons.delete, color: danger), - title: Text("Delete", style: .new(color: danger)), + title: Text("Delete", style: TextStyle(color: danger)), ), ), PopupMenuItem( @@ -332,8 +273,8 @@ class RoomChat extends HookConsumerWidget { return AlertDialog( title: Text("Report"), content: Column( - mainAxisSize: .min, - crossAxisAlignment: .start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Report this event to your server administrators, who can take action like banning this server or room.", @@ -356,7 +297,7 @@ class RoomChat extends HookConsumerWidget { TextButton( onPressed: () { client.reportEvent( - .new( + ReportRequest( roomId: roomId, eventId: event.eventId, reason: reasonController.text.isEmpty @@ -375,12 +316,14 @@ class RoomChat extends HookConsumerWidget { ), child: ListTile( leading: Icon(Icons.report, color: danger), - title: Text("Report", style: .new(color: danger)), + title: Text("Report", style: TextStyle(color: danger)), ), ), ].toIList(); } + final controllerData = ref.watch(controllerProvider); + return Scaffold( appBar: RoomAppbar( roomId: roomId, @@ -398,23 +341,16 @@ class RoomChat extends HookConsumerWidget { children: [ Positioned.fill( child: Padding( - padding: .symmetric(horizontal: 12), + padding: EdgeInsets.symmetric(horizontal: 12), child: switch (controllerData) { AsyncData(:final value) || AsyncLoading(:final value?) => CustomScrollView( + reverse: true, controller: scrollController, slivers: [ - SliverToBoxAdapter( - child: Padding( - padding: .symmetric(vertical: 36), - child: Center( - child: ElevatedButton( - onPressed: controllerData is AsyncData - ? loadOlder - : null, - child: Text("Load More"), - ), - ), + SliverPadding( + padding: EdgeInsetsGeometry.only( + bottom: composerSize.value, ), ), @@ -423,7 +359,7 @@ class RoomChat extends HookConsumerWidget { itemCount: value.length, itemBuilder: (_, index) { final event = value[index]; - final previousEvent = value.getOrNull(index - 1); + final previousEvent = value.getOrNull(index + 1); return FlashWrapper( EventRenderer( event, @@ -435,15 +371,19 @@ class RoomChat extends HookConsumerWidget { ), scrollController: scrollController, alignment: 0.5, - duration: (_) => .new(milliseconds: 700), + duration: (_) => + Duration(milliseconds: 700), curve: (_) => Curves.easeInOut, ); flashingEvent.value = replyId; - await Future.delayed(.new(seconds: 1), () { - if (flashingEvent.value == replyId) { - flashingEvent.value = null; - } - }); + await Future.delayed( + Duration(seconds: 1), + () { + if (flashingEvent.value == replyId) { + flashingEvent.value = null; + } + }, + ); }, getEventOptions: getEventOptions, isGrouped: @@ -461,8 +401,18 @@ class RoomChat extends HookConsumerWidget { }, ), - SliverPadding( - padding: .only(bottom: composerSize.value), + SliverToBoxAdapter( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 36), + child: Center( + child: controllerData is AsyncLoading + ? Loading() + : ElevatedButton( + onPressed: notifier.loadOlder, + child: Text("Load More"), + ), + ), + ), ), ], ), diff --git a/lib/widgets/room_menu.dart b/lib/widgets/room_menu.dart index f4313fa..86ac5bd 100644 --- a/lib/widgets/room_menu.dart +++ b/lib/widgets/room_menu.dart @@ -34,7 +34,7 @@ class RoomMenu extends ConsumerWidget { final vias = ref.watch(ViaController.provider(room!)); await Clipboard.setData( - .new( + ClipboardData( text: "matrix:roomid/${room!.metadata?.id.substring(1)}$vias)", ), @@ -63,7 +63,7 @@ class RoomMenu extends ConsumerWidget { Navigator.of(context).pop(); final snackbar = ScaffoldMessenger.of(context) .showSnackBar( - .new( + SnackBar( content: Text("Leaving room..."), duration: Duration(days: 1), ), diff --git a/lib/widgets/sidebar.dart b/lib/widgets/sidebar.dart index 3217173..8afe3c5 100644 --- a/lib/widgets/sidebar.dart +++ b/lib/widgets/sidebar.dart @@ -74,14 +74,14 @@ class Sidebar extends HookConsumerWidget { ), ), label: Text(space.title), - padding: .only(top: 4), + padding: EdgeInsets.only(top: 4), ), ) .toList(), selectedIndex: selectedIndex, trailingAtBottom: true, trailing: Padding( - padding: .symmetric(vertical: 16), + padding: EdgeInsets.symmetric(vertical: 16), child: Column( spacing: 8, children: [ @@ -136,7 +136,10 @@ class Sidebar extends HookConsumerWidget { selectedSpace.title, ), - title: Text(selectedSpace.title, overflow: .ellipsis), + title: Text( + selectedSpace.title, + overflow: TextOverflow.ellipsis, + ), backgroundColor: Colors.transparent, actions: [ RoomMenu( diff --git a/lib/widgets/url_preview.dart b/lib/widgets/url_preview.dart index 669c756..83c6604 100644 --- a/lib/widgets/url_preview.dart +++ b/lib/widgets/url_preview.dart @@ -13,24 +13,25 @@ class UrlPreview extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) => ConstrainedBox( - constraints: .loose(.fromWidth(400)), + constraints: BoxConstraints.loose(Size.fromWidth(400)), child: ref .watch(UrlPreviewController.provider(link)) .betterWhen( data: (preview) => preview == null ? SizedBox.shrink() : InkWell( - onTap: () => - ref.watch(LaunchHelper.provider).launchUrl(.parse(link)), + onTap: () => ref + .watch(LaunchHelper.provider) + .launchUrl(Uri.parse(link)), child: Card( - margin: .symmetric(vertical: 4), + margin: EdgeInsets.symmetric(vertical: 4), color: Theme.of( context, ).colorScheme.surfaceContainerHighest, child: Padding( - padding: .all(16), + padding: EdgeInsetsGeometry.all(16), child: Column( - crossAxisAlignment: .start, + crossAxisAlignment: CrossAxisAlignment.start, spacing: 4, children: [ if (preview.title != null) @@ -44,7 +45,9 @@ class UrlPreview extends ConsumerWidget { ], if (preview.imageUrl != null) ClipRRect( - borderRadius: .all(.circular(8)), + borderRadius: BorderRadius.all( + Radius.circular(8), + ), child: Image( errorBuilder: (_, _, _) => SizedBox.shrink(), width: preview.width, @@ -53,7 +56,7 @@ class UrlPreview extends ConsumerWidget { ref.watch(CrossCacheController.provider), headers: ref.headers, ), - fit: .fitWidth, + fit: BoxFit.fitWidth, ), ), ], diff --git a/lib/widgets/user_popover.dart b/lib/widgets/user_popover.dart index 65fff87..165852e 100644 --- a/lib/widgets/user_popover.dart +++ b/lib/widgets/user_popover.dart @@ -9,8 +9,11 @@ import "package:nexus/controllers/profile_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; +import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/content/membership.dart"; +import "package:nexus/models/membership_status.dart"; import "package:nexus/models/requests/membership_action.dart"; +import "package:nexus/models/requests/set_membership_request.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/main.dart"; import "package:nexus/widgets/expandable_image.dart"; @@ -36,8 +39,8 @@ class UserPopover extends ConsumerWidget { return AlertDialog( title: Text("${toBeginningOfSentenceCase(action.name)} $userId"), content: Column( - mainAxisSize: .min, - crossAxisAlignment: .start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Are you sure you want to ${action.name} $userId?"), SizedBox(height: 12), @@ -59,7 +62,7 @@ class UserPopover extends ConsumerWidget { Navigator.of(context).pop(); client .setMembership( - .new( + SetMembershipRequest( userId: userId, roomId: roomId!, action: action, @@ -77,18 +80,20 @@ class UserPopover extends ConsumerWidget { ); final actionButton = ButtonStyle( - padding: WidgetStatePropertyAll(.symmetric(horizontal: 24, vertical: 18)), + padding: WidgetStatePropertyAll( + EdgeInsets.symmetric(horizontal: 24, vertical: 18), + ), shape: WidgetStatePropertyAll( - RoundedRectangleBorder(borderRadius: .circular(8)), + RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), ); return Column( spacing: 16, - crossAxisAlignment: .stretch, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Wrap( - alignment: .center, + alignment: WrapAlignment.center, spacing: 16, runSpacing: 8, children: [ @@ -121,7 +126,7 @@ class UserPopover extends ConsumerWidget { .betterWhen( loading: SizedBox.shrink, data: (profile) => Wrap( - alignment: .center, + alignment: WrapAlignment.center, spacing: 4, runSpacing: 4, children: [ @@ -130,7 +135,7 @@ class UserPopover extends ConsumerWidget { )) Chip( label: Text(pronoun.summary), - labelStyle: .new( + labelStyle: TextStyle( color: theme.colorScheme.onPrimary, ), color: WidgetStatePropertyAll( @@ -140,7 +145,7 @@ class UserPopover extends ConsumerWidget { if (profile.timezone != null) Chip( label: Text(profile.timezone!), - labelStyle: .new( + labelStyle: TextStyle( color: theme.colorScheme.onPrimary, ), color: WidgetStatePropertyAll( @@ -157,7 +162,7 @@ class UserPopover extends ConsumerWidget { if (userId != ref.watch(ClientStateController.provider)?.userId && roomId != null) Wrap( - alignment: .center, + alignment: WrapAlignment.center, spacing: 8, runSpacing: 8, children: [ @@ -170,17 +175,17 @@ class UserPopover extends ConsumerWidget { if (ref.watch( PowerLevelController.provider( - .membershipAction( - action: .kick, + PowerLevelConfig.membershipAction( + action: MembershipAction.kick, roomId: roomId!, targetUser: userId, ), ), ) && - member.status == .join || - member.status == .invite) + member.status == MembershipStatus.join || + member.status == MembershipStatus.invite) FilledButton.icon( - onPressed: () => showMembershipDialog(.kick), + onPressed: () => showMembershipDialog(MembershipAction.kick), label: Text("Kick"), style: actionButton.copyWith( backgroundColor: WidgetStatePropertyAll( @@ -194,19 +199,23 @@ class UserPopover extends ConsumerWidget { ), if (ref.watch( PowerLevelController.provider( - .membershipAction( + PowerLevelConfig.membershipAction( roomId: roomId!, - action: .ban, + action: MembershipAction.ban, targetUser: userId, ), ), )) ElevatedButton.icon( onPressed: () => showMembershipDialog( - member.status == .ban ? .unban : .ban, + member.status == MembershipStatus.ban + ? MembershipAction.unban + : MembershipAction.ban, ), icon: Icon(Icons.gavel), - label: Text(member.status == .ban ? "Unban" : "Ban"), + label: Text( + member.status == MembershipStatus.ban ? "Unban" : "Ban", + ), style: actionButton.copyWith( backgroundColor: WidgetStatePropertyAll( theme.colorScheme.errorContainer, diff --git a/linux/nix/pkg/default.nix b/linux/nix/pkg/default.nix index 091f625..717f1c1 100644 --- a/linux/nix/pkg/default.nix +++ b/linux/nix/pkg/default.nix @@ -38,7 +38,7 @@ flutter.buildFlutterApplication { postInstall = '' install -D assets/icon.svg $out/share/icons/hicolor/scalable/apps/nexus.svg install -Dm755 linux/nexus.federated.Nexus.desktop -t $out/share/applications - wrapProgram $out/bin/nexus \ + wrapProgram $out/bin/nexus \ --suffix LD_LIBRARY_PATH : $out/app/nexus/lib '';