Working images

This commit is contained in:
Henry Hiles 2026-01-30 16:50:25 +01:00
commit 2372ecd141
No known key found for this signature in database
20 changed files with 388 additions and 375 deletions

View file

@ -2,7 +2,10 @@ 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_controller.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/widgets/chat_page/html/mention_chip.dart";
import "package:nexus/widgets/chat_page/html/spoiler_text.dart";
@ -40,53 +43,36 @@ class Html extends ConsumerWidget {
? null
: InlineCustomWidget(child: MentionChip(element.text)),
// "img" => TODO: Img support
// element.attributes["src"] == null
// ? null
// : Consumer(
// builder: (_, ref, _) => ref
// .watch(
// ThumbnailController.provider(
// ImageData(
// uri: element.attributes["src"]!,
// height: height,
// width: width,
// ),
// ),
// )
// .when(
// data: (uri) {
// if (uri == null) return SizedBox.shrink();
// return InlineCustomWidget(
// child: Image.network(
// uri,
// headers: client.headers,
// errorBuilder: (_, error, _) => Text(
// "Image Failed to Load",
// style: TextStyle(
// color: Theme.of(context).colorScheme.error,
// ),
// ),
// height: height.toDouble(),
// width: width?.toDouble(),
// loadingBuilder: (_, child, loadingProgress) =>
// loadingProgress == null
// ? child
// : CircularProgressIndicator(),
// ),
// );
// },
// error: ErrorDialog.new,
// loading: () => InlineCustomWidget(
// child: SizedBox(
// width: width?.toDouble(),
// height: height.toDouble(),
// child: CircularProgressIndicator(),
// ),
// ),
// ),
// ),
"img" =>
element.attributes["src"] == null
? null
: InlineCustomWidget(
child: Image.network(
Uri.parse(element.attributes["src"]!)
.mxcToHttps(
ref.watch(
ClientStateController.provider.select(
(value) => value?.homeserverUrl,
),
) ??
"",
)
.toString(),
headers: ref.headers,
errorBuilder: (_, error, _) => Text(
"Image Failed to Load",
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
),
height: height.toDouble(),
width: width?.toDouble(),
loadingBuilder: (_, child, loadingProgress) =>
loadingProgress == null
? child
: CircularProgressIndicator(),
),
),
("del" ||
"h1" ||
"h2" ||

View file

@ -19,7 +19,7 @@ class MentionChip extends StatelessWidget {
context: context,
builder: (_) => Dialog(
child: Text("TODO: Open room or join room dialog, or user popover"),
), // TODO
),
),
);
}

View file

@ -3,6 +3,7 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/members_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/models/room.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
class MemberList extends ConsumerWidget {
final Room room;
@ -14,58 +15,44 @@ class MemberList extends ConsumerWidget {
child: ref
.watch(MembersController.provider(room))
.betterWhen(
data: (members) {
final joined = members.where(
(membership) =>
membership.content["membership"] ==
"join", // TODO: Show invites seperately
);
return ListView(
children: [
AppBar(
scrolledUnderElevation: 0,
leading: Icon(Icons.people),
title: Text("Members (${joined.length})"),
actionsPadding: EdgeInsets.only(right: 4),
actions: [
if (Scaffold.of(context).hasEndDrawer)
IconButton(
onPressed: Scaffold.of(context).closeEndDrawer,
icon: Icon(Icons.close),
),
],
),
...joined.map(
(member) => ListTile(
onTap: () => showDialog(
context: context,
builder: (context) =>
Dialog(child: Text("TODO: Open member popover")),
),
// leading: AvatarOrHash( TODO
// ref
// .watch(
// AvatarController.provider(
// member.content["avatar_url"].toString(),
// ),
// )
// .whenOrNull(data: (data) => data),
// member.content["displayname"].toString(),
// headers: room.client.headers,
// ),
title: Text(
member.content["displayname"].toString(),
overflow: TextOverflow.ellipsis,
),
subtitle: Text(
member.authorId,
overflow: TextOverflow.ellipsis,
data: (members) => ListView(
children: [
AppBar(
scrolledUnderElevation: 0,
leading: Icon(Icons.people),
title: Text("Members (${members.length})"),
actionsPadding: EdgeInsets.only(right: 4),
actions: [
if (Scaffold.of(context).hasEndDrawer)
IconButton(
onPressed: Scaffold.of(context).closeEndDrawer,
icon: Icon(Icons.close),
),
],
),
...members.map(
(member) => ListTile(
onTap: () => showDialog(
context: context,
builder: (context) =>
Dialog(child: Text("TODO: Open member popover")),
),
leading: AvatarOrHash(
Uri.tryParse(member.content["avatar_url"] ?? ""),
member.content["displayname"].toString(),
),
title: Text(
member.content["displayname"].toString(),
overflow: TextOverflow.ellipsis,
),
subtitle: Text(
member.authorId,
overflow: TextOverflow.ellipsis,
),
),
],
);
},
),
],
),
),
);
}

View file

@ -4,6 +4,7 @@ import "package:nexus/controllers/members_controller.dart";
import "package:nexus/controllers/rooms_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/models/room.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
import "package:nexus/widgets/loading.dart";
class MentionOverlay extends ConsumerWidget {
@ -54,18 +55,12 @@ class MentionOverlay extends ConsumerWidget {
))
.map(
(member) => ListTile(
// leading: AvatarOrHash( TODO: Images
// ref
// .watch(
// AvatarController.provider(
// member.content["avatar_url"]
// .toString(),
// ),
// )
// .whenOrNull(data: (data) => data),
// member.content["displayname"].toString(),
// headers: room.client.headers,
// ),
leading: AvatarOrHash(
Uri.tryParse(
member.content["avatar_url"] ?? "",
),
member.content["displayname"] ?? "",
),
title: Text(
member.content["displayname"] as String? ??
member.authorId,
@ -93,12 +88,11 @@ class MentionOverlay extends ConsumerWidget {
))
.map(
(room) => ListTile(
// leading: AvatarOrHash( TODO: Images
// room.avatar,
// room.title,
// fallback: Icon(Icons.numbers),
// headers: room.roomData.client.headers,
// ),
leading: AvatarOrHash(
room.metadata?.avatar,
room.metadata?.name ?? "Unnamed Room",
fallback: Icon(Icons.numbers),
),
title: Text(room.metadata?.name ?? "Unnamed Room"),
subtitle: room.metadata?.topic == null
? null

View file

@ -24,14 +24,12 @@ class RoomAppbar extends StatelessWidget implements PreferredSizeWidget {
@override
Widget build(BuildContext context) => Appbar(
leading: isDesktop
? null
// AvatarOrHash( TODO: Images
// room.avatar,
// room.title,
// height: 24,
// fallback: Icon(Icons.numbers),
// headers: room.roomData.client.headers,
// )
? AvatarOrHash(
room.metadata?.avatar,
room.metadata?.name ?? "Unnamed Rooms",
height: 24,
fallback: Icon(Icons.numbers),
)
: DrawerButton(onPressed: () => onOpenDrawer(context)),
scrolledUnderElevation: 0,
title: Column(

View file

@ -488,9 +488,7 @@ class RoomChat extends HookConsumerWidget {
onTap: () => showDialog(
context: context,
builder: (_) => Dialog(
child: Text(
"TODO: Download Attachments", // TODO
),
child: Text("TODO: Download Attachments"),
),
),
child: FlyerChatFileMessage(

View file

@ -61,10 +61,9 @@ class Sidebar extends HookConsumerWidget {
.map(
(space) => NavigationRailDestination(
icon: AvatarOrHash(
null, // TODO: Url
space.room?.metadata?.avatar,
fallback: space.icon == null ? null : Icon(space.icon),
space.title,
headers: {}, // TODO
hasBadge: space.children.any(
(room) => room.metadata?.unreadMessages != 0,
),
@ -177,15 +176,12 @@ class Sidebar extends HookConsumerWidget {
backgroundColor: Colors.transparent,
appBar: AppBar(
leading: AvatarOrHash(
null,
// space.avatar, TODO
selectedSpace.room?.metadata?.avatar,
fallback: selectedSpace.icon == null
? null
: Icon(selectedSpace.icon),
selectedSpace.title,
headers: {},
// space.client.headers, TODO
),
title: Text(
selectedSpace.title,
@ -210,15 +206,13 @@ class Sidebar extends HookConsumerWidget {
(room) => NavigationRailDestination(
label: Text(room.metadata?.name ?? "Unnamed Room"),
icon: AvatarOrHash(
null,
room.metadata?.avatar,
hasBadge: room.metadata?.unreadMessages != 0,
badgeNumber: room.metadata?.unreadNotifications ?? 0,
// room.avatar, TODO
room.metadata?.name ?? "Unnamed Room",
fallback: selectedSpaceId == "dms"
? null
: Icon(Icons.numbers),
headers: {},
// space.client.headers,
),
),

View file

@ -1,8 +1,8 @@
import "dart:math";
import "package:flutter/material.dart";
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_chat_ui/flutter_chat_ui.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
import "package:nexus/widgets/chat_page/html/quoted.dart";
class TopWidget extends ConsumerWidget {
@ -60,11 +60,11 @@ class TopWidget extends ConsumerWidget {
mainAxisSize: MainAxisSize.min,
spacing: 8,
children: [
// Avatar( TODO: images
// userId: replyMessage.authorId,
// headers: headers,
// size: 16,
// ),
AvatarOrHash(
Uri.tryParse(replyMessage.metadata?["avatarUrl"] ?? ""),
replyMessage.metadata?["displayName"] ?? "",
height: 16,
),
Flexible(
child: Text(
replyMessage.metadata?["displayName"] ??
@ -102,7 +102,10 @@ class TopWidget extends ConsumerWidget {
mainAxisSize: MainAxisSize.min,
spacing: 8,
children: [
// Avatar(userId: message.authorId, headers: headers), TODO: images
AvatarOrHash(
Uri.parse(message.metadata?["avatarUrl"] ?? ""),
message.metadata?["displayName"] ?? "",
),
Flexible(
child: Text(
message.metadata?["displayName"] ?? message.authorId,