Remove flutter chat #26

Manually merged
Henry-Hiles merged 108 commits from remove-flutter-chat into main 2026-05-22 15:26:28 -04:00
4 changed files with 129 additions and 83 deletions
Showing only changes of commit 2aae141c27 - Show all commits

im drunk on the power of pattern matching

this pr will be squash merged so commit messages dont really matter
Henry Hiles 2026-05-18 14:46:22 -04:00
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs

View file

@ -12,7 +12,9 @@ class UrlPreviewController extends AsyncNotifier<OpenGraphData?> {
@override @override
Future<OpenGraphData?> build() async { Future<OpenGraphData?> build() async {
final homeserver = ref.watch(ClientStateController.provider)?.homeserverUrl; final homeserver = ref.watch(
ClientStateController.provider.select((value) => value?.homeserverUrl),
);
if (homeserver != null && !link.contains("matrix.to")) { if (homeserver != null && !link.contains("matrix.to")) {
{ {

View file

@ -33,7 +33,7 @@ abstract class MessageContent extends Content with _$MessageContent {
// EncryptedFile? file // EncryptedFile? file
String? filename, String? filename,
ImageInfo? info, ImageInfo? info,
String? url, Uri? url,
}) = ImageMessageContent; }) = ImageMessageContent;
@FreezedUnionValue("m.file") @FreezedUnionValue("m.file")
@ -44,7 +44,7 @@ abstract class MessageContent extends Content with _$MessageContent {
// EncryptedFile? file // EncryptedFile? file
String? filename, String? filename,
FileInfo? info, FileInfo? info,
String? url, Uri? url,
}) = FileMessageContent; }) = FileMessageContent;
@FreezedUnionValue("m.audio") @FreezedUnionValue("m.audio")
@ -55,7 +55,7 @@ abstract class MessageContent extends Content with _$MessageContent {
// EncryptedFile? file // EncryptedFile? file
String? filename, String? filename,
AudioInfo? info, AudioInfo? info,
String? url, Uri? url,
}) = AudioMessageContent; }) = AudioMessageContent;
@FreezedUnionValue("m.video") @FreezedUnionValue("m.video")
@ -66,7 +66,7 @@ abstract class MessageContent extends Content with _$MessageContent {
// EncryptedFile? file // EncryptedFile? file
String? filename, String? filename,
AudioInfo? info, AudioInfo? info,
String? url, Uri? url,
}) = VideoMessageContent; }) = VideoMessageContent;
@FreezedUnionValue("m.location") @FreezedUnionValue("m.location")

View file

@ -6,9 +6,10 @@ part "image.g.dart";
abstract class ImageInfo with _$ImageInfo { abstract class ImageInfo with _$ImageInfo {
/// Information for images, [size] is in bytes. /// Information for images, [size] is in bytes.
const factory ImageInfo({ const factory ImageInfo({
@JsonKey(name: "h") int? height, @JsonKey(name: "h") double? height,
@JsonKey(name: "w") int? width, @JsonKey(name: "w") double? width,
@JsonKey(name: "mimetype") String? mimeType, @JsonKey(name: "mimetype") String? mimeType,
@JsonKey(name: "xyz.amorgan.blurhash") String? blurHash,
int? size, int? size,
}) = _ImageInfo; }) = _ImageInfo;

View file

@ -1,17 +1,24 @@
import "package:cross_cache/cross_cache.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_blurhash/flutter_blurhash.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/client_state_controller.dart";
import "package:nexus/controllers/cross_cache_controller.dart";
import "package:nexus/helpers/extensions/get_headers.dart";
import "package:nexus/helpers/extensions/mxc_to_https.dart";
import "package:nexus/helpers/extensions/show_context_menu.dart"; import "package:nexus/helpers/extensions/show_context_menu.dart";
import "package:nexus/helpers/launch_helper.dart"; import "package:nexus/helpers/launch_helper.dart";
import "package:nexus/models/content/avatar.dart"; import "package:nexus/models/content/avatar.dart";
import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/content.dart";
import "package:nexus/models/content/message.dart"; import "package:nexus/models/content/message.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
import "package:nexus/widgets/chat_page/expandable_image.dart";
import "package:nexus/widgets/chat_page/html/html.dart"; import "package:nexus/widgets/chat_page/html/html.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
import "package:nexus/widgets/link_preview.dart"; import "package:nexus/widgets/link_preview.dart";
import "package:nexus/widgets/loading.dart";
import "package:timeago/timeago.dart"; import "package:timeago/timeago.dart";
import "package:flutter_linkify/flutter_linkify.dart"; import "package:flutter_linkify/flutter_linkify.dart";
@ -110,62 +117,122 @@ class EventText extends ConsumerWidget {
:final body, :final body,
:final formattedBody, :final formattedBody,
:final format, :final format,
) => ) ||
Column( ImageMessageContent(
children: [ :final body,
format == "org.matrix.custom.html" && :final formattedBody,
!textOnly :final format,
? Html( ) => Column(
textStyle: children: [
event.localContent?.bigEmoji == format == "org.matrix.custom.html" && !textOnly
true ? Html(
? TextStyle(fontSize: 32) textStyle:
: null, event.localContent?.bigEmoji == true
formattedBody!.replaceAllMapped( ? TextStyle(fontSize: 32)
RegExp( : null,
"(<a\\b[^>]*>.*?<\\/a>)|(\\bhttps?:\\/\\/[^\\s<]+)", formattedBody!.replaceAllMapped(
caseSensitive: false, RegExp(
dotAll: true, "(<a\\b[^>]*>.*?<\\/a>)|(\\bhttps?:\\/\\/[^\\s<]+)",
), caseSensitive: false,
(m) { dotAll: true,
// If it's already an <a> tag, leave it unchanged ),
if (m.group(1) != null) { (m) {
return m.group(1)!; // If it's already an <a> tag, leave it unchanged
} if (m.group(1) != null) {
return m.group(1)!;
}
// Otherwise, wrap the bare URL // Otherwise, wrap the bare URL
final url = m.group(2)!; final url = m.group(2)!;
return "<a href=\"$url\">$url</a>"; return "<a href=\"$url\">$url</a>";
}, },
),
)
: Linkify(
text: body,
maxLines: maxLines,
options: LinkifyOptions(
humanize: false,
),
onOpen: (link) => ref
.watch(LaunchHelper.provider)
.launchUrl(Uri.parse(link.url)),
linkStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.primary,
),
), ),
if (event.lastEditRowId != null) )
Text( : Linkify(
"(edited)", text: body,
style: theme.textTheme.labelSmall, maxLines: maxLines,
options: LinkifyOptions(
humanize: false,
),
onOpen: (link) => ref
.watch(LaunchHelper.provider)
.launchUrl(Uri.parse(link.url)),
linkStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.primary,
),
),
if (event.content case ImageMessageContent(
:final url,
:final info,
))
switch (url?.mxcToHttps(
ref.watch(
ClientStateController.provider.select(
(value) => value!.homeserverUrl!,
),
), ),
if (RegExp( )) {
r'''https?://[^\s"'<>]+''', final url? => ExpandableImage(
).allMatches(body).firstOrNull?.group(0) url.toString(),
case final link?) child: ClipRRect(
LinkPreview(link), borderRadius: BorderRadius.all(
], Radius.circular(8),
), ),
child: Image(
image: CachedNetworkImage(
url.toString(),
ref.watch(
CrossCacheController.provider,
),
headers: ref.headers,
),
width: info?.width,
height: info?.height,
loadingBuilder:
(
context,
child,
loadingProgress,
) => switch (info?.blurHash) {
final blurHash? => BlurHash(
hash: blurHash,
),
_ => Loading(),
},
errorBuilder:
(context, error, stackTrace) =>
Center(
child: Text(
"Image Failed to Load",
style: TextStyle(
color: Theme.of(
context,
).colorScheme.error,
),
),
),
),
),
),
_ => Text(
"Nexus currently cannot handle encrypted media",
style: errorStyle,
),
},
if (event.lastEditRowId != null)
Text(
"(edited)",
style: theme.textTheme.labelSmall,
),
if (RegExp(
r'''https?://[^\s"'<>]+''',
).allMatches(body).firstOrNull?.group(0)
case final link?)
LinkPreview(link),
],
),
_ => _ =>
textOnly textOnly
? Text( ? Text(
@ -206,27 +273,3 @@ class EventText extends ConsumerWidget {
); );
} }
} }
// ExpandableImage(
// url,
// child: ClipRRect(
// borderRadius: BorderRadius.all(Radius.circular(8)),
// child: Image(
// image: CachedNetworkImage(
// url,
// ref.watch(CrossCacheController.provider),
// headers: ref.headers,
// ),
// width: width,
// height: height,
// loadingBuilder: (context, child, loadingProgress) =>
// blurHash == null ? Loading() : BlurHash(hash: blurHash!),
// errorBuilder: (context, error, stackTrace) => Center(
// child: Text(
// "Image Failed to Load",
// style: TextStyle(color: Theme.of(context).colorScheme.error),
// ),
// ),
// ),
// ),
// )