diff --git a/.gitmodules b/.gitmodules index dc5e02e..145276a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "gomuks"] path = gomuks - url = https://github.com/henry-hiles/gomuks + url = https://github.com/gomuks/gomuks branch = main diff --git a/README.md b/README.md index d191d6e..d706c9c 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [x] Edits - [x] Attachments - [x] Unencrypted - - [x] Encrypted + - [ ] Encrypted - [x] Blurhashing - [ ] Downloading attachments - [x] Opening attachments in their own view diff --git a/gomuks b/gomuks index 1f11743..c06eb1c 160000 --- a/gomuks +++ b/gomuks @@ -1 +1 @@ -Subproject commit 1f11743884a64765b1444b250377a09ebcaba93c +Subproject commit c06eb1cbda2d49ff199a351e2d55df6ff7ab602b diff --git a/lib/controllers/client.dart b/lib/controllers/client.dart index 2cb1d66..10e153d 100644 --- a/lib/controllers/client.dart +++ b/lib/controllers/client.dart @@ -18,7 +18,6 @@ import "package:nexus/models/content/message.dart"; import "package:nexus/models/event.dart"; import "package:nexus/models/oauth_auth_code_response.dart"; import "package:nexus/models/paginate.dart"; -import "package:nexus/models/requests/download_media.dart"; import "package:nexus/models/requests/get_event.dart"; import "package:nexus/models/requests/get_related_events.dart"; import "package:nexus/models/requests/get_room_state.dart"; @@ -272,9 +271,6 @@ class ClientController extends AsyncNotifier { Future uploadMedia(UploadMediaRequest request) async => .fromJson(await _sendCommand("upload_media", request.toJson())); - Future downloadMedia(DownloadMediaRequest request) async => - .new((await _sendCommand("download_media", request.toJson()))["path"]); - Future logout() => _sendCommand("logout"); Future markRead(Room room) async { diff --git a/lib/controllers/cross_cache.dart b/lib/controllers/cross_cache.dart new file mode 100644 index 0000000..4d5611a --- /dev/null +++ b/lib/controllers/cross_cache.dart @@ -0,0 +1,11 @@ +import "package:cross_cache/cross_cache.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; + +class CrossCacheController extends Notifier { + @override + CrossCache build() => .new(); + + static final provider = NotifierProvider( + CrossCacheController.new, + ); +} diff --git a/lib/controllers/room_chat.dart b/lib/controllers/room_chat.dart index 558817c..ac979e0 100644 --- a/lib/controllers/room_chat.dart +++ b/lib/controllers/room_chat.dart @@ -121,16 +121,9 @@ class RoomChatController extends AsyncNotifier?> { required RelationType relationType, Event? relation, }) async { - Content? baseContent; - if (relationType == .edit) { - baseContent = relation?.content; - } else { - final attachment = ref.watch(AttachmentController.provider(roomId)); - if (attachment != null && attachment.$2 == null) return; + final attachment = ref.watch(AttachmentController.provider(roomId)); - baseContent = attachment?.$2; - ref.invalidate(AttachmentController.provider(roomId)); - } + if (attachment != null && attachment.$2 == null) return; var taggedMessage = text; @@ -148,7 +141,7 @@ class RoomChatController extends AsyncNotifier?> { final event = await client.sendMessage( SendMessageRequest( roomId: roomId, - baseContent: baseContent, + baseContent: attachment?.$2, mentions: Mentions( userIds: [ if (shouldMention == true && @@ -165,6 +158,8 @@ class RoomChatController extends AsyncNotifier?> { ), ); + ref.invalidate(AttachmentController.provider(roomId)); + ref .watch(RoomsController.provider.notifier) .update( diff --git a/lib/controllers/url_preview.dart b/lib/controllers/url_preview.dart index 2a81a3c..0f807bb 100644 --- a/lib/controllers/url_preview.dart +++ b/lib/controllers/url_preview.dart @@ -3,6 +3,7 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:http/http.dart"; import "package:nexus/controllers/client_state.dart"; import "package:nexus/controllers/header.dart"; +import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/models/open_graph_data.dart"; class UrlPreviewController extends AsyncNotifier { @@ -29,9 +30,12 @@ class UrlPreviewController extends AsyncNotifier { final decodedValue = json.decode(response.body); if (decodedValue is! Map) return null; - return .fromJson( - decodedValue, - ).copyWith(imageUrl: decodedValue["og:image"]); + final mxc = decodedValue["og:image"]; + final image = mxc == null + ? null + : Uri.tryParse(mxc)?.mxcToHttps(homeserver); + + return .fromJson(decodedValue).copyWith(imageUrl: image); } } } diff --git a/lib/helpers/extensions/mxc_to_https.dart b/lib/helpers/extensions/mxc_to_https.dart new file mode 100644 index 0000000..b21f056 --- /dev/null +++ b/lib/helpers/extensions/mxc_to_https.dart @@ -0,0 +1,4 @@ +extension MxcToHttps on Uri { + Uri mxcToHttps(String homeserver) => + .parse(homeserver).resolve("_matrix/client/v1/media/download/$host$path"); +} diff --git a/lib/helpers/mxc_image.dart b/lib/helpers/mxc_image.dart deleted file mode 100644 index 3adb10e..0000000 --- a/lib/helpers/mxc_image.dart +++ /dev/null @@ -1,35 +0,0 @@ -import "dart:ui"; -import "package:flutter/widgets.dart"; -import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/models/requests/download_media.dart"; - -class MxcImage extends ImageProvider { - final WidgetRef ref; - final DownloadMediaRequest request; - const MxcImage(this.ref, this.request); - - @override - Future obtainKey(ImageConfiguration configuration) => - Future.value(this); - - @override - ImageStreamCompleter loadImage(MxcImage key, ImageDecoderCallback decode) => - MultiFrameImageStreamCompleter(codec: _loadAsync(decode), scale: 1.0); - - Future _loadAsync(ImageDecoderCallback decode) async { - final file = await ref - .read(ClientController.provider.notifier) - .downloadMedia(request); - final buffer = await ImmutableBuffer.fromFilePath(file.path); - - return decode(buffer); - } - - @override - bool operator ==(Object other) => - other is MxcImage && other.request == request; - - @override - int get hashCode => request.hashCode; -} diff --git a/lib/models/content/message.dart b/lib/models/content/message.dart index e7e8923..acbe70e 100644 --- a/lib/models/content/message.dart +++ b/lib/models/content/message.dart @@ -12,9 +12,6 @@ typedef EncryptedFile = Map; @Freezed(unionKey: "msgtype", fallbackUnion: "default") abstract class MessageContent extends Content with _$MessageContent { MessageContent._(); - static String? mediaUrlFromJson(Map json, String key) => - json[key] ?? json["file"]?[key]; - factory MessageContent({String? body}) = UnknownMessageContent; @FreezedUnionValue("m.text") @@ -46,7 +43,7 @@ abstract class MessageContent extends Content with _$MessageContent { EncryptedFile? file, String? filename, ImageInfo? info, - @JsonKey(readValue: MessageContent.mediaUrlFromJson) required Uri url, + Uri? url, }) = ImageMessageContent; @FreezedUnionValue("m.file") @@ -57,7 +54,7 @@ abstract class MessageContent extends Content with _$MessageContent { EncryptedFile? file, String? filename, FileInfo? info, - @JsonKey(readValue: MessageContent.mediaUrlFromJson) required Uri url, + Uri? url, }) = FileMessageContent; @FreezedUnionValue("m.audio") @@ -68,7 +65,7 @@ abstract class MessageContent extends Content with _$MessageContent { EncryptedFile? file, String? filename, AudioInfo? info, - @JsonKey(readValue: MessageContent.mediaUrlFromJson) required Uri url, + Uri? url, }) = AudioMessageContent; @FreezedUnionValue("m.video") @@ -79,7 +76,7 @@ abstract class MessageContent extends Content with _$MessageContent { EncryptedFile? file, String? filename, VideoInfo? info, - @JsonKey(readValue: MessageContent.mediaUrlFromJson) required Uri url, + Uri? url, }) = VideoMessageContent; @FreezedUnionValue("m.location") diff --git a/lib/models/requests/download_media.dart b/lib/models/requests/download_media.dart deleted file mode 100644 index b5d5771..0000000 --- a/lib/models/requests/download_media.dart +++ /dev/null @@ -1,16 +0,0 @@ -import "package:freezed_annotation/freezed_annotation.dart"; -part "download_media.freezed.dart"; -part "download_media.g.dart"; - -@freezed -abstract class DownloadMediaRequest with _$DownloadMediaRequest { - const factory DownloadMediaRequest({ - required Uri mxc, - @Default(false) bool encrypted, - @Default(false) bool isAvatar, - @Default(false) bool thumbnailAvatar, - }) = _DownloadMediaRequest; - - factory DownloadMediaRequest.fromJson(Map json) => - _$DownloadMediaRequestFromJson(json); -} diff --git a/lib/models/requests/oauth/register_client.dart b/lib/models/requests/oauth/register_client.dart index cf4f9e0..c4ef68f 100644 --- a/lib/models/requests/oauth/register_client.dart +++ b/lib/models/requests/oauth/register_client.dart @@ -16,9 +16,6 @@ abstract class OAuthRegisterClientRequest with _$OAuthRegisterClientRequest { IList? grantTypes, IList? redirectUris, IList? responseTypes, - - @Default(AuthMethod.none) - @JsonKey(name: "token_endpoint_auth_method") AuthMethod? authMethod, }) = _OAuthRegisterClientRequest; diff --git a/lib/widgets/avatar_or_hash.dart b/lib/widgets/avatar_or_hash.dart index 75c08b9..1cca8c6 100644 --- a/lib/widgets/avatar_or_hash.dart +++ b/lib/widgets/avatar_or_hash.dart @@ -1,7 +1,11 @@ import "package:color_hash/color_hash.dart"; +import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/helpers/mxc_image.dart"; +import "package:nexus/controllers/client_state.dart"; +import "package:nexus/controllers/cross_cache.dart"; +import "package:nexus/helpers/extensions/get_headers.dart"; +import "package:nexus/helpers/extensions/mxc_to_https.dart"; class AvatarOrHash extends ConsumerWidget { final Uri? avatar; @@ -23,6 +27,15 @@ class AvatarOrHash extends ConsumerWidget { child: Center(child: Icon(Icons.person, size: height / 2)), ); + final parsedAvatar = avatar?.mxcToHttps( + ref.watch( + ClientStateController.provider.select( + (value) => value?.homeserverUrl, + ), + ) ?? + "", + ); + return SizedBox( width: height, height: height, @@ -32,10 +45,14 @@ class AvatarOrHash extends ConsumerWidget { child: SizedBox( width: height, height: height, - child: avatar == null + child: parsedAvatar == null ? fallback ?? box : Image( - image: MxcImage(ref, .new(mxc: avatar!, isAvatar: true)), + image: CachedNetworkImage( + parsedAvatar.toString(), + ref.watch(CrossCacheController.provider), + headers: ref.headers, + ), fit: .cover, loadingBuilder: (_, child, loadingProgress) => loadingProgress == null ? child : fallback ?? box, diff --git a/lib/widgets/expandable_image.dart b/lib/widgets/expandable_image.dart index ee4f900..4990458 100644 --- a/lib/widgets/expandable_image.dart +++ b/lib/widgets/expandable_image.dart @@ -1,18 +1,19 @@ +import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:m3e_buttons/m3e_buttons.dart"; -import "package:nexus/helpers/mxc_image.dart"; -import "package:nexus/models/requests/download_media.dart"; +import "package:nexus/controllers/cross_cache.dart"; +import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/widgets/error_dialog.dart"; class ExpandableImage extends ConsumerWidget { final Widget child; - final DownloadMediaRequest? request; - const ExpandableImage(this.request, {required this.child, super.key}); + final String? source; + const ExpandableImage(this.source, {required this.child, super.key}); @override Widget build(BuildContext context, WidgetRef ref) => InkWell( - onTap: request == null + onTap: source == null ? null : () => showDialog( context: context, @@ -26,10 +27,14 @@ class ExpandableImage extends ConsumerWidget { maxScale: 10, child: Image( errorBuilder: (_, error, stackTrace) => ErrorDialog( - "Loading failed for ${request?.mxc}\nError: $error", + "Loading failed for $source\nError: $error", stackTrace, ), - image: MxcImage(ref, request!), + image: CachedNetworkImage( + source!, + ref.watch(CrossCacheController.provider), + headers: ref.headers, + ), ), ), ), diff --git a/lib/widgets/html/html.dart b/lib/widgets/html/html.dart index 5437285..4113eac 100644 --- a/lib/widgets/html/html.dart +++ b/lib/widgets/html/html.dart @@ -1,10 +1,14 @@ +import "package:cross_cache/cross_cache.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart"; +import "package:nexus/controllers/client_state.dart"; +import "package:nexus/controllers/cross_cache.dart"; +import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/extensions/link_to_mention.dart"; +import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/helpers/launch_helper.dart"; -import "package:nexus/helpers/mxc_image.dart"; import "package:nexus/widgets/expandable_image.dart"; import "package:nexus/widgets/html/mention_chip.dart"; import "package:nexus/widgets/html/spoiler_text.dart"; @@ -36,7 +40,16 @@ class Html extends ConsumerWidget { (element.attributes.keys.contains("data-mx-emoticon") ? 32 : null) ?? 300; final width = int.tryParse(element.attributes["width"] ?? ""); - final src = Uri.tryParse(element.attributes["src"] ?? ""); + final src = Uri.tryParse(element.attributes["src"] ?? "") + ?.mxcToHttps( + ref.watch( + ClientStateController.provider.select( + (value) => value?.homeserverUrl, + ), + ) ?? + "", + ) + .toString(); return switch (element.localName) { "code" => @@ -64,9 +77,13 @@ class Html extends ConsumerWidget { : InlineCustomWidget( alignment: PlaceholderAlignment.middle, child: ExpandableImage( - .new(mxc: src), + src, child: Image( - image: MxcImage(ref, .new(mxc: src)), + image: CachedNetworkImage( + src, + ref.watch(CrossCacheController.provider), + headers: ref.headers, + ), errorBuilder: (_, error, _) => Text( "Image Failed to Load", style: .new(color: Theme.of(context).colorScheme.error), diff --git a/lib/widgets/message_image.dart b/lib/widgets/message_image.dart index 914a252..9a4caaf 100644 --- a/lib/widgets/message_image.dart +++ b/lib/widgets/message_image.dart @@ -1,57 +1,57 @@ +import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:flutter_blurhash/flutter_blurhash.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/helpers/mxc_image.dart"; +import "package:nexus/controllers/cross_cache.dart"; +import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/models/info/image.dart" as i; -import "package:nexus/models/requests/download_media.dart"; import "package:nexus/widgets/expandable_image.dart"; import "package:nexus/widgets/loading.dart"; class MessageImage extends ConsumerWidget { final Uri url; final i.ImageInfo? info; - final bool encrypted; - const MessageImage(this.url, {this.info, required this.encrypted, super.key}); + const MessageImage(this.url, {this.info, super.key}); @override - Widget build(BuildContext context, WidgetRef ref) { - final request = DownloadMediaRequest(mxc: url, encrypted: encrypted); - return ExpandableImage( - request, - child: ClipRRect( - borderRadius: .all(.circular(8)), - child: AspectRatio( - aspectRatio: (info?.width ?? 1) / (info?.height ?? 1), - child: Image( - image: MxcImage(ref, request), - width: info?.width, - fit: BoxFit.fitWidth, - loadingBuilder: (_, child, loadingProgress) => - loadingProgress == null - ? child - : switch (info?.blurHash) { - final blurHash? => - info?.width == null || info?.height == null - ? SizedBox( - width: 200, - height: 200, - child: BlurHash(hash: blurHash), - ) - : SizedBox( - width: info!.width, - child: BlurHash(hash: blurHash), - ), - _ => Loading(), - }, - errorBuilder: (context, error, stackTrace) => Center( - child: Text( - "Image Failed to Load", - style: .new(color: Theme.of(context).colorScheme.error), - ), + Widget build(BuildContext context, WidgetRef ref) => ExpandableImage( + url.toString(), + child: ClipRRect( + borderRadius: .all(.circular(8)), + child: AspectRatio( + aspectRatio: (info?.width ?? 1) / (info?.height ?? 1), + child: Image( + image: CachedNetworkImage( + url.toString(), + ref.watch(CrossCacheController.provider), + headers: ref.headers, + ), + width: info?.width, + fit: BoxFit.fitWidth, + loadingBuilder: (_, child, loadingProgress) => loadingProgress == null + ? child + : switch (info?.blurHash) { + final blurHash? => + info?.width == null || info?.height == null + ? SizedBox( + width: 200, + height: 200, + child: BlurHash(hash: blurHash), + ) + : SizedBox( + width: info!.width, + child: BlurHash(hash: blurHash), + ), + _ => Loading(), + }, + errorBuilder: (context, error, stackTrace) => Center( + child: Text( + "Image Failed to Load", + style: .new(color: Theme.of(context).colorScheme.error), ), ), ), ), - ); - } + ), + ); } diff --git a/lib/widgets/reaction_row.dart b/lib/widgets/reaction_row.dart index b2fa5ca..2035b59 100644 --- a/lib/widgets/reaction_row.dart +++ b/lib/widgets/reaction_row.dart @@ -1,10 +1,13 @@ +import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_state.dart"; +import "package:nexus/controllers/cross_cache.dart"; import "package:nexus/controllers/reactions.dart"; import "package:nexus/controllers/room_chat.dart"; -import "package:nexus/helpers/mxc_image.dart"; +import "package:nexus/helpers/extensions/get_headers.dart"; +import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/models/event.dart"; import "package:nexus/widgets/error_dialog.dart"; import "package:nexus/main.dart"; @@ -52,9 +55,16 @@ class ReactionRow extends ConsumerWidget { child: reaction.startsWith("mxc://") ? Image( height: 20, - image: MxcImage( - ref, - .new(mxc: Uri.parse(reaction)), + image: CachedNetworkImage( + headers: ref.headers, + Uri.parse(reaction) + .mxcToHttps( + clientState!.homeserverUrl!, + ) + .toString(), + ref.watch( + CrossCacheController.provider, + ), ), ) : Text(reaction, overflow: .ellipsis), diff --git a/lib/widgets/renderers/message.dart b/lib/widgets/renderers/message.dart index 6d660af..b731cd0 100644 --- a/lib/widgets/renderers/message.dart +++ b/lib/widgets/renderers/message.dart @@ -4,6 +4,7 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:linkify/linkify.dart"; import "package:nexus/controllers/client_state.dart"; import "package:nexus/controllers/event.dart"; +import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/models/content/encrypted.dart"; import "package:nexus/models/content/message.dart"; import "package:nexus/models/content/sticker.dart"; @@ -145,9 +146,14 @@ class MessageRenderer extends ConsumerWidget { : ConstrainedBox( constraints: .loose(.square(200)), child: MessageImage( - url, + url.mxcToHttps( + ref.watch( + ClientStateController.provider.select( + (value) => value!.homeserverUrl!, + ), + ), + ), info: info, - encrypted: false, ), ), // TODO: Handle locations @@ -223,30 +229,35 @@ class MessageRenderer extends ConsumerWidget { FileMessageContent(:final url) || VideoMessageContent(:final url) || AudioMessageContent(:final url)) - ConstrainedBox( - constraints: .loose(.square(500)), - child: switch (event.content) { - VideoMessageContent(:final info) => - VideoPlayer(url, info), - AudioMessageContent(:final info) => - AudioPlayer(url, info), - FileMessageContent( - :final info, - :final filename, - ) => - FileCard(url, info, filename: filename), - ImageMessageContent( - :final info, - :final file, - ) => - MessageImage( - url, - info: info, - encrypted: file != null, - ), - _ => SizedBox.shrink(), - }, - ), + switch (url?.mxcToHttps( + ref.watch( + ClientStateController.provider.select( + (value) => value!.homeserverUrl!, + ), + ), + )) { + final url? => ConstrainedBox( + constraints: .loose(.square(500)), + child: switch (event.content) { + VideoMessageContent(:final info) => + VideoPlayer(url, info), + AudioMessageContent(:final info) => + AudioPlayer(url, info), + FileMessageContent( + :final info, + :final filename, + ) => + FileCard(url, info, filename: filename), + ImageMessageContent(:final info) => + MessageImage(url, info: info), + _ => SizedBox.shrink(), + }, + ), + _ => Text( + "Nexus currently cannot handle encrypted media", + style: errorStyle, + ), + }, if (event.lastEditRowId != 0) Text( diff --git a/lib/widgets/room_appbar.dart b/lib/widgets/room_appbar.dart index 93eca85..4598881 100644 --- a/lib/widgets/room_appbar.dart +++ b/lib/widgets/room_appbar.dart @@ -1,9 +1,11 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/rooms.dart"; +import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/expandable_image.dart"; +import "package:nexus/controllers/client_state.dart"; import "package:nexus/widgets/linkified_text.dart"; import "package:nexus/widgets/room_menu.dart"; @@ -51,9 +53,15 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { children: [ if (room.metadata?.avatar != null) ExpandableImage( - room.metadata?.avatar == null - ? null - : .new(mxc: room.metadata!.avatar!), + room.metadata!.avatar! + .mxcToHttps( + ref.watch( + ClientStateController.provider.select( + (value) => value!.homeserverUrl!, + ), + ), + ) + .toString(), child: AvatarOrHash( room.metadata?.avatar, room.metadata?.name ?? "Unnamed Room", diff --git a/lib/widgets/url_preview.dart b/lib/widgets/url_preview.dart index 5831300..19fe06b 100644 --- a/lib/widgets/url_preview.dart +++ b/lib/widgets/url_preview.dart @@ -1,9 +1,11 @@ +import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/cross_cache.dart"; import "package:nexus/controllers/url_preview.dart"; import "package:nexus/helpers/extensions/better_when.dart"; +import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/launch_helper.dart"; -import "package:nexus/helpers/mxc_image.dart"; class UrlPreview extends ConsumerWidget { final String link; @@ -46,9 +48,10 @@ class UrlPreview extends ConsumerWidget { child: Image( errorBuilder: (_, _, _) => SizedBox.shrink(), width: preview.width, - image: MxcImage( - ref, - .new(mxc: preview.imageUrl!), + image: CachedNetworkImage( + preview.imageUrl.toString(), + ref.watch(CrossCacheController.provider), + headers: ref.headers, ), fit: .fitWidth, ), diff --git a/lib/widgets/user_bottom_sheet.dart b/lib/widgets/user_bottom_sheet.dart index 6f9f154..55e2502 100644 --- a/lib/widgets/user_bottom_sheet.dart +++ b/lib/widgets/user_bottom_sheet.dart @@ -10,6 +10,7 @@ import "package:nexus/controllers/power_level.dart"; import "package:nexus/controllers/profile.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/content/membership.dart"; import "package:nexus/models/membership_action.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; @@ -96,7 +97,15 @@ class UserBottomSheet extends ConsumerWidget { SizedBox(height: 18), ExpandableImage( - member.avatarUrl == null ? null : .new(mxc: member.avatarUrl!), + member.avatarUrl + ?.mxcToHttps( + ref.watch( + ClientStateController.provider.select( + (value) => value!.homeserverUrl!, + ), + ), + ) + .toString(), child: AvatarOrHash( member.avatarUrl, member.displayName ?? userId.localpart, diff --git a/linux/nix/pkg/gomuks.nix b/linux/nix/pkg/gomuks.nix index 40fe29c..ad0412e 100644 --- a/linux/nix/pkg/gomuks.nix +++ b/linux/nix/pkg/gomuks.nix @@ -11,7 +11,7 @@ buildGoModule (finalAttrs: { src = "${src}/gomuks"; - vendorHash = "sha256-4QU51WGiNKYm7z/ajnas2qQaMK5BgyHwPfmNpQvW0qg="; + vendorHash = "sha256-mNZEHOGByVqK1kSLC/Cf1VEvkDxRen+TmoV5CXvGrZ4="; buildPhase = '' runHook preBuild diff --git a/pubspec.lock b/pubspec.lock index c989ed4..3a0aeb5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -249,6 +249,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.1" + cross_cache: + dependency: "direct main" + description: + name: cross_cache + sha256: "4983a16603cc99b0a14de6a772fa8ee4533411f46f3c423f1386fea7566049c5" + url: "https://pub.dev" + source: hosted + version: "1.1.0" cross_file: dependency: transitive description: @@ -289,6 +297,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.14" + dio: + dependency: transitive + description: + name: dio + sha256: ea2bad3c89a27635ce2d85cce4d6b199da49a5a48ec77b03e45b65a3b90922b0 + url: "https://pub.dev" + source: hosted + version: "5.10.0" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: dd58dc3861eb36edb13b217efc006a1c21e5bbc341de8c229b85634fa5e362e4 + url: "https://pub.dev" + source: hosted + version: "2.2.0" dynamic_color: dependency: "direct main" description: @@ -662,6 +686,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.1" + idb_shim: + dependency: transitive + description: + name: idb_shim + sha256: "3448298f244bc76a14aca71461eeab6add2b8a877930521c42c2e8b71b644590" + url: "https://pub.dev" + source: hosted + version: "2.9.6+2" image: dependency: transitive description: @@ -1216,6 +1248,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.2" + sembast: + dependency: transitive + description: + name: sembast + sha256: a58b26925e23071cf0f4754d8449aabe829e9a9930a872fb18e6ae4c2c88e025 + url: "https://pub.dev" + source: hosted + version: "3.8.9+1" shared_preferences: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 72748b1..2669fea 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,6 +50,7 @@ dependencies: fluttertagger: 2.3.2 dynamic_polls: 0.0.7 flutter_hooks: 0.21.3+1 + cross_cache: 1.1.0 ffi: 2.2.0 hooks: 2.0.2 code_assets: 1.2.1