invitedRooms,
diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart
index b15f6d4..bd41d51 100644
--- a/lib/pages/login_page.dart
+++ b/lib/pages/login_page.dart
@@ -97,6 +97,7 @@ class LoginPage extends HookConsumerWidget {
),
),
IconButton.filled(
+ tooltip: "Confirm homeserver choice",
onPressed: isLoading.value
? null
: () => setHomeserver(Uri.tryParse(homeserverUrl.text)),
@@ -143,6 +144,7 @@ class LoginPage extends HookConsumerWidget {
? null
: () => setHomeserver(homeserver.url),
trailing: IconButton(
+ tooltip: "Launch homeserver info page",
onPressed: () => launch(homeserver.url),
icon: Icon(Icons.info_outline),
),
diff --git a/lib/pages/settings_page.dart b/lib/pages/settings_page.dart
index b348aac..505904c 100644
--- a/lib/pages/settings_page.dart
+++ b/lib/pages/settings_page.dart
@@ -1,18 +1,11 @@
import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
-import "package:nexus/controllers/secure_storage_controller.dart";
class SettingsPage extends ConsumerWidget {
const SettingsPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
- return Scaffold(
- appBar: AppBar(title: Text("Settings")),
- body: ElevatedButton(
- onPressed: ref.watch(SecureStorageController.provider.notifier).clear,
- child: Text("Log out"),
- ),
- );
+ return Placeholder();
}
}
diff --git a/lib/widgets/appbar.dart b/lib/widgets/appbar.dart
index 00b0e4c..5b14244 100644
--- a/lib/widgets/appbar.dart
+++ b/lib/widgets/appbar.dart
@@ -49,10 +49,15 @@ class Appbar extends StatelessWidget implements PreferredSizeWidget {
if (!(Platform.isAndroid || Platform.isIOS)) ...[
if (!Platform.isLinux)
IconButton(
+ tooltip: "Maximize window",
onPressed: maximize,
icon: const Icon(Icons.fullscreen),
),
- IconButton(onPressed: () => exit(0), icon: const Icon(Icons.close)),
+ IconButton(
+ tooltip: "Close window",
+ onPressed: () => exit(0),
+ icon: const Icon(Icons.close),
+ ),
],
],
),
diff --git a/lib/widgets/avatar_or_hash.dart b/lib/widgets/avatar_or_hash.dart
index a47bbb5..8e93b6b 100644
--- a/lib/widgets/avatar_or_hash.dart
+++ b/lib/widgets/avatar_or_hash.dart
@@ -40,7 +40,7 @@ class AvatarOrHash extends ConsumerWidget {
smallSize: 12,
backgroundColor: Theme.of(context).colorScheme.primary,
child: ClipRRect(
- borderRadius: BorderRadius.all(Radius.circular(4)),
+ borderRadius: BorderRadius.all(Radius.circular((height - 8) / 2.5)),
child: SizedBox(
width: height,
height: height,
diff --git a/lib/widgets/chat_page/chat_box.dart b/lib/widgets/chat_page/chat_box.dart
index 885ddd9..b9e7dbb 100644
--- a/lib/widgets/chat_page/chat_box.dart
+++ b/lib/widgets/chat_page/chat_box.dart
@@ -29,19 +29,13 @@ class ChatBox extends HookConsumerWidget {
final theme = Theme.of(context);
final controller = useRef(FlutterTaggerController());
final triggerCharacter = useState("");
+ final shouldMention = useState(true);
final query = useState("");
if (relationType == RelationType.edit &&
relatedMessage is TextMessage &&
controller.value.text.isEmpty) {
- final text = (relatedMessage as TextMessage).text;
- final splitText = relatedMessage?.replyToMessageId == null
- ? text
- : text.split("\n\n").sublist(1).join("\n\n");
- final notEmpty = splitText.isEmpty ? text : splitText;
- controller.value.text = notEmpty.startsWith("* ")
- ? notEmpty.substring(2)
- : notEmpty;
+ controller.value.text = relatedMessage?.metadata?["editSource"] ?? "";
}
void send() {
@@ -50,6 +44,7 @@ class ChatBox extends HookConsumerWidget {
.watch(RoomChatController.provider(room.metadata!.id).notifier)
.send(
controller.value.formattedText,
+ shouldMention: shouldMention.value,
relation: relatedMessage,
relationType: relationType,
tags: controller.value.tags,
@@ -91,6 +86,9 @@ class ChatBox extends HookConsumerWidget {
child: Column(
children: [
RelationPreview(
+ shouldMention: shouldMention.value,
+ toggleShouldMention: () =>
+ shouldMention.value = !shouldMention.value,
relatedMessage: relatedMessage,
relationType: relationType,
onDismiss: onDismiss,
@@ -102,7 +100,27 @@ class ChatBox extends HookConsumerWidget {
spacing: 8,
children: [
PopupMenuButton(
- itemBuilder: (context) => [],
+ tooltip: "Add media",
+ itemBuilder: (context) => [
+ PopupMenuItem(
+ child: ListTile(
+ title: Text("Camera"),
+ leading: Icon(Icons.add_a_photo),
+ ),
+ ),
+ PopupMenuItem(
+ child: ListTile(
+ title: Text("Gallery"),
+ leading: Icon(Icons.add_photo_alternate),
+ ),
+ ),
+ PopupMenuItem(
+ child: ListTile(
+ title: Text("Files"),
+ leading: Icon(Icons.attachment),
+ ),
+ ),
+ ],
icon: Icon(Icons.add),
// enabled: room.canSendDefaultMessages, TODO: Permissions check
),
@@ -129,9 +147,10 @@ class ChatBox extends HookConsumerWidget {
maxLines: 12,
minLines: 1,
decoration: InputDecoration(
- // hintText: room.canSendDefaultMessages
- // ? "Your message here..."
- // : "You don't have permission to send messages in this room...",
+ hintText:
+ true // TODO: room.canSendDefaultMessages
+ ? "Your message here..."
+ : "You don't have permission to send messages in this room...",
border: InputBorder.none,
),
controller: controller.value,
@@ -145,6 +164,7 @@ class ChatBox extends HookConsumerWidget {
onPressed: send,
// onPressed: room.canSendDefaultMessages ? send : null,
icon: Icon(Icons.send),
+ tooltip: "Send message",
),
],
),
diff --git a/lib/widgets/chat_page/html/code_block.dart b/lib/widgets/chat_page/html/code_block.dart
index fe5b492..80950ce 100644
--- a/lib/widgets/chat_page/html/code_block.dart
+++ b/lib/widgets/chat_page/html/code_block.dart
@@ -41,6 +41,8 @@ class CodeBlock extends StatelessWidget {
padding: EdgeInsets.all(8),
child: SelectableText(
code,
+ minLines: 1,
+ maxLines: 99,
style: TextStyle(fontFamily: "monospace"),
),
),
diff --git a/lib/widgets/chat_page/html/html.dart b/lib/widgets/chat_page/html/html.dart
index 18edf4a..1e1ab82 100644
--- a/lib/widgets/chat_page/html/html.dart
+++ b/lib/widgets/chat_page/html/html.dart
@@ -14,11 +14,13 @@ import "package:nexus/widgets/chat_page/html/quoted.dart";
class Html extends ConsumerWidget {
final String html;
- const Html(this.html, {super.key});
+ final TextStyle? textStyle;
+ const Html(this.html, {this.textStyle, super.key});
@override
Widget build(BuildContext context, WidgetRef ref) => HtmlWidget(
html,
+ textStyle: textStyle,
customWidgetBuilder: (element) {
if (element.attributes.keys.contains("data-mx-spoiler")) {
return InlineCustomWidget(child: SpoilerText(text: element.text));
@@ -30,10 +32,14 @@ class Html extends ConsumerWidget {
return switch (element.localName) {
"code" =>
element.parent?.localName == "pre"
- ? CodeBlock(
- element.text,
- lang: element.className.replaceAll("language-", ""),
- )
+ ? element.outerHtml.contains("
")
+ ? Html(
+ """${element.outerHtml.replaceAll("
", "\n")}""",
+ )
+ : CodeBlock(
+ element.text,
+ lang: element.className.replaceAll("language-", ""),
+ )
: null,
"blockquote" => Quoted(Html(element.innerHtml)),
@@ -45,8 +51,9 @@ class Html extends ConsumerWidget {
"img" =>
element.attributes["src"] == null
- ? null
+ ? SizedBox.shrink()
: InlineCustomWidget(
+ alignment: PlaceholderAlignment.middle,
child: Image.network(
Uri.parse(element.attributes["src"]!)
.mxcToHttps(
@@ -119,9 +126,7 @@ class Html extends ConsumerWidget {
.mapTo?>(
(key, value) => switch (key) {
"data-mx-color" => MapEntry("color", value),
-
"data-mx-bg-color" => MapEntry("background-color", value),
-
_ => null,
},
)
diff --git a/lib/widgets/chat_page/image_message.dart b/lib/widgets/chat_page/image_message.dart
new file mode 100644
index 0000000..103fdd2
--- /dev/null
+++ b/lib/widgets/chat_page/image_message.dart
@@ -0,0 +1,58 @@
+import "dart:math";
+import "package:cross_cache/cross_cache.dart";
+import "package:flutter/material.dart";
+import "package:flutter_chat_core/flutter_chat_core.dart";
+import "package:flutter_riverpod/flutter_riverpod.dart";
+import "package:flyer_chat_image_message/flyer_chat_image_message.dart";
+import "package:nexus/controllers/cross_cache_controller.dart";
+import "package:nexus/helpers/extensions/get_headers.dart";
+
+class ExpandableImageMessage extends ConsumerWidget {
+ final ImageMessage message;
+ final int index;
+
+ const ExpandableImageMessage(this.message, {required this.index, super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) => InkWell(
+ onTap: () => showDialog(
+ context: context,
+ builder: (_) => LayoutBuilder(
+ builder: (context, constraints) => Dialog(
+ backgroundColor: Colors.transparent,
+ insetPadding: EdgeInsets.all(constraints.maxWidth / 100),
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ minWidth: min(constraints.maxWidth, 1000),
+ ),
+ child: InteractiveViewer(
+ child: Image(
+ fit: BoxFit.contain,
+ image: CachedNetworkImage(
+ message.source,
+ ref.watch(CrossCacheController.provider),
+ headers: ref.headers,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ child: FlyerChatImageMessage(
+ customImageProvider: CachedNetworkImage(
+ message.source,
+ ref.watch(CrossCacheController.provider),
+ headers: ref.headers,
+ ),
+ errorBuilder: (context, error, stackTrace) => Center(
+ child: Text(
+ "Image Failed to Load",
+ style: TextStyle(color: Theme.of(context).colorScheme.error),
+ ),
+ ),
+ message: message,
+ index: index,
+ ),
+ );
+}
diff --git a/lib/widgets/chat_page/member_list.dart b/lib/widgets/chat_page/member_list.dart
index 5e1f3bf..24d22e4 100644
--- a/lib/widgets/chat_page/member_list.dart
+++ b/lib/widgets/chat_page/member_list.dart
@@ -1,7 +1,6 @@
import "package:flutter/material.dart";
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";
@@ -10,49 +9,49 @@ class MemberList extends ConsumerWidget {
const MemberList(this.room, {super.key});
@override
- Widget build(BuildContext context, WidgetRef ref) => Drawer(
- shape: Border(),
- child: ref
- .watch(MembersController.provider(room))
- .betterWhen(
- 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,
- ),
+ Widget build(BuildContext context, WidgetRef ref) {
+ final members = ref.watch(MembersController.provider(room));
+ return Drawer(
+ shape: Border(),
+ child: 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),
+ tooltip: "Close member list",
),
- ),
],
),
- ),
- );
+ ...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.stateKey ?? "Unknown User",
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
}
diff --git a/lib/widgets/chat_page/mention_overlay.dart b/lib/widgets/chat_page/mention_overlay.dart
index b2f2d9d..9858574 100644
--- a/lib/widgets/chat_page/mention_overlay.dart
+++ b/lib/widgets/chat_page/mention_overlay.dart
@@ -2,7 +2,6 @@ import "package:flutter/material.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
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";
@@ -32,51 +31,55 @@ class MentionOverlay extends ConsumerWidget {
color: Theme.of(context).colorScheme.surfaceContainerHigh,
padding: EdgeInsets.all(8),
child: switch (triggerCharacter) {
- "@" =>
- ref
- .watch(MembersController.provider(room))
- .betterWhen(
- data: (members) => ListView(
- children:
- (query.isEmpty
- ? members
- : members.where(
- (member) =>
- member.authorId
- .toLowerCase()
- .contains(query.toLowerCase()) ||
- (member.content["displayname"]
- as String?)
- ?.toLowerCase()
- .contains(
- query.toLowerCase(),
- ) ==
- true,
- ))
- .map(
- (member) => ListTile(
- leading: AvatarOrHash(
- Uri.tryParse(
- member.content["avatar_url"] ?? "",
- ),
- member.content["displayname"] ?? "",
- ),
- title: Text(
- member.content["displayname"] as String? ??
- member.authorId,
- ),
- onTap: () => addTag(
- id: "[@${member.content["displayname"]}](https://matrix.to/#/${member.authorId})",
- name: member.authorId
- .substring(1)
- .split(":")
- .first,
- ),
+ "@" => Consumer(
+ builder: (_, ref, _) {
+ final members = ref.watch(MembersController.provider(room));
+ return ListView(
+ children:
+ (query.isEmpty
+ ? members
+ : members.where(
+ (member) =>
+ member.stateKey?.toLowerCase().contains(
+ query.toLowerCase(),
+ ) ==
+ true ||
+ (member.content["displayname"] as String?)
+ ?.toLowerCase()
+ .contains(query.toLowerCase()) ==
+ true,
+ ))
+ .map(
+ (member) => ListTile(
+ leading: AvatarOrHash(
+ Uri.tryParse(
+ member.content["avatar_url"] ?? "",
),
- )
- .toList(),
- ),
- ),
+ member.content["displayname"] ?? "",
+ ),
+ title: Text(
+ member.content["displayname"] as String? ??
+ member.stateKey ??
+ "Unknown User",
+ ),
+ subtitle: member.stateKey != null
+ ? Text(member.stateKey!)
+ : null,
+ onTap: () => addTag(
+ id: "[@${member.content["displayname"]}](https://matrix.to/#/${member.stateKey})",
+ name:
+ member.stateKey
+ ?.substring(1)
+ .split(":")
+ .first ??
+ "Unknown User",
+ ),
+ ),
+ )
+ .toList(),
+ );
+ },
+ ),
"#" => ListView(
children:
(query.isEmpty
diff --git a/lib/widgets/chat_page/message_wrapper.dart b/lib/widgets/chat_page/message_wrapper.dart
new file mode 100644
index 0000000..da53be0
--- /dev/null
+++ b/lib/widgets/chat_page/message_wrapper.dart
@@ -0,0 +1,54 @@
+import "package:flutter/material.dart";
+import "package:flutter_chat_core/flutter_chat_core.dart";
+import "package:nexus/widgets/avatar_or_hash.dart";
+
+class MessageWrapper extends StatelessWidget {
+ final Message message;
+ final Widget child;
+ final MessageGroupStatus? groupStatus;
+ const MessageWrapper(this.message, this.child, this.groupStatus, {super.key});
+
+ @override
+ Widget build(BuildContext context) => ClipRRect(
+ borderRadius: BorderRadius.all(Radius.circular(12)),
+ child: AnimatedContainer(
+ padding: message.metadata?["flashing"] == true
+ ? EdgeInsets.all(8)
+ : EdgeInsets.all(0),
+ color: message.metadata?["flashing"] == true
+ ? Theme.of(context).colorScheme.onSurface.withAlpha(50)
+ : Colors.transparent,
+ duration: Duration(milliseconds: 250),
+ child: Row(
+ spacing: 8,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ groupStatus?.isFirst != false
+ ? AvatarOrHash(
+ Uri.parse(message.metadata?["avatarUrl"] ?? ""),
+ height: 40,
+ message.metadata?["displayName"] ?? "",
+ )
+ : SizedBox(width: 40),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ spacing: 4,
+ children: [
+ if (groupStatus?.isFirst != false)
+ Text(
+ message.metadata?["displayName"] ?? message.authorId,
+ overflow: TextOverflow.ellipsis,
+ style: Theme.of(context).textTheme.titleMedium?.copyWith(
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ child,
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+}
diff --git a/lib/widgets/chat_page/relation_preview.dart b/lib/widgets/chat_page/relation_preview.dart
index 9918b35..7aa3ae8 100644
--- a/lib/widgets/chat_page/relation_preview.dart
+++ b/lib/widgets/chat_page/relation_preview.dart
@@ -2,15 +2,20 @@ import "package:flutter/material.dart";
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/models/relation_type.dart";
+import "package:nexus/widgets/avatar_or_hash.dart";
class RelationPreview extends ConsumerWidget {
final Message? relatedMessage;
final RelationType relationType;
final VoidCallback onDismiss;
+ final bool shouldMention;
+ final VoidCallback toggleShouldMention;
const RelationPreview({
required this.relatedMessage,
required this.relationType,
required this.onDismiss,
+ required this.shouldMention,
+ required this.toggleShouldMention,
super.key,
});
@@ -31,18 +36,11 @@ class RelationPreview extends ConsumerWidget {
"Editing message:",
style: TextStyle(fontWeight: FontWeight.bold),
),
- // AvatarOrHash(
- // ref
- // .watch(
- // AvatarController.provider(
- // relatedMessage!.metadata!["avatarUrl"],
- // ),
- // )
- // .whenOrNull(data: (data) => data),
- // relatedMessage!.metadata!["displayName"].toString(),
- // headers: room.client.headers,
- // height: 16,
- // ),
+ AvatarOrHash(
+ Uri.tryParse(relatedMessage?.metadata?["avatarUrl"] ?? ""),
+ relatedMessage?.metadata?["displayName"]?.toString() ?? "",
+ height: 16,
+ ),
Text(
relatedMessage!.metadata?["displayName"] ??
relatedMessage!.authorId,
@@ -52,16 +50,28 @@ class RelationPreview extends ConsumerWidget {
),
Expanded(
child: Text(
- (relatedMessage is TextMessage)
- ? (relatedMessage as TextMessage).text
- : relatedMessage?.metadata?["body"] ??
- relatedMessage?.metadata?["eventType"],
+ relatedMessage?.metadata?["body"] ??
+ relatedMessage?.metadata?["eventType"],
overflow: TextOverflow.ellipsis,
style: theme.textTheme.labelMedium,
maxLines: 1,
),
),
+
+ if (relationType == RelationType.reply)
+ TextButton(
+ onPressed: toggleShouldMention,
+ child: Text(
+ shouldMention ? "@On" : "@Off",
+ style: TextStyle(
+ fontWeight: FontWeight.w900,
+ color: shouldMention ? null : Theme.of(context).disabledColor,
+ ),
+ ),
+ ),
IconButton(
+ tooltip:
+ "Cancel ${relationType == RelationType.edit ? "edit" : "reply"}",
onPressed: onDismiss,
icon: Icon(Icons.close),
iconSize: 20,
diff --git a/lib/widgets/chat_page/reply_widget.dart b/lib/widgets/chat_page/reply_widget.dart
new file mode 100644
index 0000000..cd30acc
--- /dev/null
+++ b/lib/widgets/chat_page/reply_widget.dart
@@ -0,0 +1,146 @@
+import "dart:math";
+import "package:flutter/material.dart";
+import "package:flutter_chat_core/flutter_chat_core.dart";
+import "package:flutter_riverpod/flutter_riverpod.dart";
+import "package:nexus/controllers/event_controller.dart";
+import "package:nexus/controllers/message_controller.dart";
+import "package:nexus/helpers/extensions/better_when.dart";
+import "package:nexus/models/message_config.dart";
+import "package:nexus/models/requests/get_event_request.dart";
+import "package:nexus/models/room.dart";
+import "package:nexus/widgets/avatar_or_hash.dart";
+import "package:nexus/widgets/chat_page/html/quoted.dart";
+
+typedef OnTapReply = void Function(Message message)?;
+
+class ReplyWidget extends ConsumerWidget {
+ final Message message;
+ final bool alwaysShow;
+ final Room room;
+ final MessageGroupStatus? groupStatus;
+ final OnTapReply onTapReply;
+ const ReplyWidget(
+ this.message, {
+ required this.room,
+ required this.groupStatus,
+ this.onTapReply,
+ this.alwaysShow = false,
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) =>
+ message.replyToMessageId == null
+ ? SizedBox.shrink()
+ : Padding(
+ padding: EdgeInsets.only(bottom: 12),
+ child: Quoted(
+ ref
+ .watch(
+ EventController.provider(
+ GetEventRequest(
+ room: room,
+ eventId: message.replyToMessageId!,
+ ),
+ ),
+ )
+ .betterWhen(
+ loading: () => Text("Fetching event..."),
+ data: (event) => event == null
+ ? SizedBox.shrink()
+ : ref
+ .watch(
+ MessageController.provider(
+ MessageConfig(room: room, event: event),
+ ),
+ )
+ .betterWhen(
+ loading: () => Text("Parsing message..."),
+ data: (replyMessage) {
+ if (replyMessage == null) {
+ return SizedBox.shrink();
+ }
+
+ final smallerText =
+ message is TextMessage &&
+ replyMessage.metadata?["body"] != null
+ ? replyMessage.metadata!["body"].substring(
+ 0,
+ min(
+ max(
+ max(
+ (message as TextMessage)
+ .text
+ .length -
+ (replyMessage
+ .metadata?["displayName"]
+ as String)
+ .length -
+ 5,
+ message
+ .metadata?["displayName"]
+ .length,
+ ),
+ 5,
+ ),
+ replyMessage.metadata!["body"].length,
+ ),
+ )
+ : null;
+ final replyText =
+ (smallerText == null ||
+ smallerText.length ==
+ replyMessage
+ .metadata!["body"]
+ .length)
+ ? replyMessage.metadata!["body"]
+ : "$smallerText...";
+
+ return InkWell(
+ onTap: () => onTapReply?.call(replyMessage),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ spacing: 8,
+ children: [
+ AvatarOrHash(
+ Uri.tryParse(
+ replyMessage.metadata?["avatarUrl"] ??
+ "",
+ ),
+ replyMessage.metadata?["displayName"] ??
+ "",
+ height: 16,
+ ),
+ Flexible(
+ child: Text(
+ replyMessage
+ .metadata?["displayName"] ??
+ replyMessage.authorId,
+ style: Theme.of(context)
+ .textTheme
+ .labelMedium
+ ?.copyWith(
+ fontWeight: FontWeight.bold,
+ ),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ Flexible(
+ child: Text(
+ replyText,
+ overflow: TextOverflow.ellipsis,
+ style: Theme.of(
+ context,
+ ).textTheme.labelMedium,
+ maxLines: 1,
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ ),
+ ),
+ ),
+ );
+}
diff --git a/lib/widgets/chat_page/room_appbar.dart b/lib/widgets/chat_page/room_appbar.dart
index 21aa4ae..436bcb9 100644
--- a/lib/widgets/chat_page/room_appbar.dart
+++ b/lib/widgets/chat_page/room_appbar.dart
@@ -52,9 +52,14 @@ class RoomAppbar extends StatelessWidget implements PreferredSizeWidget {
],
),
actions: [
- IconButton(onPressed: () {}, icon: Icon(Icons.push_pin)),
+ IconButton(
+ onPressed: null,
+ icon: Icon(Icons.push_pin),
+ tooltip: "Open pinned messages",
+ ),
IconButton(
onPressed: () => onOpenMemberList(context),
+ tooltip: "Open member list",
icon: Icon(Icons.people),
),
RoomMenu(room),
diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart
index 6adc013..839109f 100644
--- a/lib/widgets/chat_page/room_chat.dart
+++ b/lib/widgets/chat_page/room_chat.dart
@@ -1,29 +1,25 @@
-import "package:cross_cache/cross_cache.dart";
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_hooks/flutter_hooks.dart";
-import "package:flutter_link_previewer/flutter_link_previewer.dart";
import "package:flyer_chat_file_message/flyer_chat_file_message.dart";
-import "package:flyer_chat_image_message/flyer_chat_image_message.dart";
import "package:flyer_chat_system_message/flyer_chat_system_message.dart";
-import "package:flyer_chat_text_message/flyer_chat_text_message.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/client_controller.dart";
import "package:nexus/controllers/client_state_controller.dart";
-import "package:nexus/controllers/cross_cache_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart";
import "package:nexus/controllers/room_chat_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart";
-import "package:nexus/helpers/extensions/get_headers.dart";
import "package:nexus/helpers/extensions/show_context_menu.dart";
import "package:nexus/models/relation_type.dart";
import "package:nexus/models/requests/report_request.dart";
import "package:nexus/widgets/chat_page/chat_box.dart";
-import "package:nexus/widgets/chat_page/html/html.dart";
+import "package:nexus/widgets/chat_page/image_message.dart";
import "package:nexus/widgets/chat_page/member_list.dart";
+import "package:nexus/widgets/chat_page/message_wrapper.dart";
import "package:nexus/widgets/chat_page/room_appbar.dart";
-import "package:nexus/widgets/chat_page/top_widget.dart";
+import "package:nexus/widgets/chat_page/text_message_wrapper.dart";
+import "package:nexus/widgets/chat_page/reply_widget.dart";
import "package:nexus/widgets/form_text_input.dart";
import "package:nexus/widgets/loading.dart";
// import "package:dynamic_polls/dynamic_polls.dart";
@@ -185,6 +181,13 @@ class RoomChat extends HookConsumerWidget {
];
}
+ final chatTheme = ChatTheme.fromThemeData(theme).copyWith(
+ colors: ChatColors.fromThemeData(theme).copyWith(
+ primary: theme.colorScheme.primaryContainer,
+ onPrimary: theme.colorScheme.onPrimaryContainer,
+ ),
+ );
+
return Scaffold(
appBar: RoomAppbar(
room,
@@ -206,12 +209,7 @@ class RoomChat extends HookConsumerWidget {
.betterWhen(
data: (controller) => Chat(
currentUserId: userId,
- theme: ChatTheme.fromThemeData(theme).copyWith(
- colors: ChatColors.fromThemeData(theme).copyWith(
- primary: theme.colorScheme.primaryContainer,
- onPrimary: theme.colorScheme.onPrimaryContainer,
- ),
- ),
+ theme: chatTheme,
onMessageSecondaryTap:
(
context,
@@ -234,36 +232,9 @@ class RoomChat extends HookConsumerWidget {
globalPosition: details.globalPosition,
children: getMessageOptions(message),
),
- onMessageTap:
- (
- context,
- message, {
- required details,
- required index,
- }) {
- if (message is ImageMessage) {
- showDialog(
- context: context,
- builder: (_) => Dialog(
- backgroundColor: Colors.transparent,
- insetPadding: EdgeInsets.all(64),
- child: InteractiveViewer(
- child: Image(
- image: CachedNetworkImage(
- message.source,
- ref.watch(
- CrossCacheController.provider,
- ),
- headers: ref.headers,
- ),
- ),
- ),
- ),
- );
- }
- },
builders: Builders(
loadMoreBuilder: (_) => Loading(),
+
chatAnimatedListBuilder: (_, itemBuilder) =>
ChatAnimatedList(
itemBuilder: itemBuilder,
@@ -273,6 +244,7 @@ class RoomChat extends HookConsumerWidget {
onStartReached: () => client.markRead(room),
bottomPadding: 72,
),
+
composerBuilder: (_) => ChatBox(
relationType: relationType.value,
relatedMessage: replyToMessage.value,
@@ -280,104 +252,6 @@ class RoomChat extends HookConsumerWidget {
room: room,
),
- // TODO: Polls
- // customMessageBuilder:
- // (
- // context,
- // message,
- // index, {
- // required bool isSentByMe,
- // MessageGroupStatus? groupStatus,
- // }) {
- // final poll =
- // message.metadata?["poll"]
- // as PollStartContent;
- // final responses =
- // (message.metadata?["responses"]
- // as Map<
- // String,
- // Set
- // >)
- // .values
- // .expand((set) => set)
- // .fold({}, (
- // acc,
- // value,
- // ) {
- // acc[value] =
- // (acc[value] ?? 0) + 1;
- // return acc;
- // });
-
- // return Column(
- // crossAxisAlignment:
- // CrossAxisAlignment.start,
- // spacing: 4,
- // children: [
- // TopWidget(
- // message,
- // headers: room
- // .roomData
- // .client
- // .headers,
- // groupStatus: groupStatus,
- // ),
-
- // DynamicPolls(
- // startDate: DateTime.now(),
- // endDate: DateTime.now(),
- // private:
- // poll.kind ==
- // PollKind.undisclosed,
- // allowReselection: true,
- // backgroundDecoration:
- // BoxDecoration(
- // borderRadius:
- // BorderRadius.all(
- // Radius.circular(16),
- // ),
- // border: Border.all(
- // color: theme
- // .colorScheme
- // .primaryContainer,
- // width: 4,
- // ),
- // ),
- // allStyle: Styles(
- // titleStyle: TitleStyle(
- // style: theme
- // .textTheme
- // .headlineSmall,
- // ),
- // optionStyle: OptionStyle(
- // fillColor: theme
- // .colorScheme
- // .primaryContainer,
- // selectedBorderColor: theme
- // .colorScheme
- // .primary,
- // borderColor: theme
- // .colorScheme
- // .primary,
- // unselectedBorderColor:
- // Colors.transparent,
- // textSelectColor: theme
- // .colorScheme
- // .primary,
- // ),
- // ),
- // onOptionSelected:
- // (int index) {},
- // title: poll.question.mText,
- // options: poll.answers
- // .map(
- // (option) => option.mText,
- // )
- // .toList(),
- // ),
- // ],
- // );
- // },
textMessageBuilder:
(
context,
@@ -385,98 +259,37 @@ class RoomChat extends HookConsumerWidget {
index, {
required bool isSentByMe,
MessageGroupStatus? groupStatus,
- }) => FlyerChatTextMessage(
- customWidget: Column(
- crossAxisAlignment:
- CrossAxisAlignment.start,
- children: [
- Html(
- (message.metadata?["formatted"]
- as String)
- .replaceAllMapped(
- RegExp(
- "(]*>.*?<\\/a>)|(\\bhttps?:\\/\\/[^\\s<]+)",
- caseSensitive: false,
- ),
- (m) {
- // If it's already an tag, leave it unchanged
- if (m.group(1) != null) {
- return m.group(1)!;
- }
+ }) => TextMessageWrapper(
+ room: room,
+ message,
+ content: message.text,
+ groupStatus: groupStatus,
+ onTapReply: notifier.scrollToMessage,
+ updateMessage: controller.updateMessage,
+ isSentByMe: isSentByMe,
+ ),
- // Otherwise, wrap the bare URL
- final url = m.group(2)!;
- return "$url";
- },
- )
- .replaceAll("\n", "
"),
- ),
- if (message.editedAt != null)
- Text(
- "(edited)",
- style: theme.textTheme.labelSmall,
- ),
- ],
- ),
- topWidget: TopWidget(
- message,
- groupStatus: groupStatus,
- ),
- message: message,
- showTime: true,
- index: index,
- ),
- linkPreviewBuilder: (_, message, isSentByMe) =>
- LinkPreview(
- text: message.text,
- backgroundColor: isSentByMe
- ? theme.colorScheme.inversePrimary
- : theme.colorScheme.surfaceContainerLow,
- insidePadding: EdgeInsets.symmetric(
- vertical: 8,
- horizontal: 16,
- ),
- linkPreviewData: message.linkPreviewData,
- onLinkPreviewDataFetched: (linkPreviewData) =>
- notifier.updateMessage(
- message,
- message.copyWith(
- linkPreviewData: linkPreviewData,
- ),
- ),
- ),
imageMessageBuilder:
(
- _,
+ context,
message,
index, {
required bool isSentByMe,
MessageGroupStatus? groupStatus,
- }) => FlyerChatImageMessage(
- topWidget: TopWidget(
+ }) => TextMessageWrapper(
+ message,
+ room: room,
+ content: message.text,
+ groupStatus: groupStatus,
+ onTapReply: notifier.scrollToMessage,
+ updateMessage: controller.updateMessage,
+ isSentByMe: isSentByMe,
+ extra: ExpandableImageMessage(
message,
- groupStatus: groupStatus,
- alwaysShow: true,
+ index: index,
),
- customImageProvider: CachedNetworkImage(
- message.source,
- ref.watch(CrossCacheController.provider),
- headers: ref.headers,
- ),
- errorBuilder: (context, error, stackTrace) =>
- Center(
- child: Text(
- "Image Failed to Load",
- style: TextStyle(
- color: Theme.of(
- context,
- ).colorScheme.error,
- ),
- ),
- ),
- message: message,
- index: index,
),
+
fileMessageBuilder:
(
_,
@@ -484,22 +297,31 @@ class RoomChat extends HookConsumerWidget {
index, {
required bool isSentByMe,
MessageGroupStatus? groupStatus,
- }) => InkWell(
- onTap: () => showDialog(
- context: context,
- builder: (_) => Dialog(
- child: Text("TODO: Download Attachments"),
+ }) => MessageWrapper(
+ message,
+ InkWell(
+ onTap: () => showDialog(
+ context: context,
+ builder: (_) => Dialog(
+ child: Text(
+ "TODO: Download Attachments",
+ ),
+ ),
+ ),
+ child: FlyerChatFileMessage(
+ topWidget: ReplyWidget(
+ room: room,
+ message,
+ onTapReply: notifier.scrollToMessage,
+ groupStatus: groupStatus,
+ ),
+ message: message,
+ index: index,
),
),
- child: FlyerChatFileMessage(
- topWidget: TopWidget(
- message,
- groupStatus: groupStatus,
- ),
- message: message,
- index: index,
- ),
+ groupStatus,
),
+
systemMessageBuilder:
(
_,
@@ -511,6 +333,7 @@ class RoomChat extends HookConsumerWidget {
message: message,
index: index,
),
+
unsupportedMessageBuilder:
(
_,
diff --git a/lib/widgets/chat_page/sidebar.dart b/lib/widgets/chat_page/sidebar.dart
index 341dd60..4642a58 100644
--- a/lib/widgets/chat_page/sidebar.dart
+++ b/lib/widgets/chat_page/sidebar.dart
@@ -155,6 +155,7 @@ class Sidebar extends HookConsumerWidget {
icon: Icon(Icons.add),
),
IconButton(
+ tooltip: "Explore other rooms",
onPressed: () => showDialog(
context: context,
builder: (context) => AlertDialog(title: Text("To-do")),
@@ -162,6 +163,7 @@ class Sidebar extends HookConsumerWidget {
icon: Icon(Icons.explore),
),
IconButton(
+ tooltip: "Open settings",
onPressed: () => Navigator.of(
context,
).push(MaterialPageRoute(builder: (_) => SettingsPage())),
diff --git a/lib/widgets/chat_page/text_message_wrapper.dart b/lib/widgets/chat_page/text_message_wrapper.dart
new file mode 100644
index 0000000..9734a34
--- /dev/null
+++ b/lib/widgets/chat_page/text_message_wrapper.dart
@@ -0,0 +1,114 @@
+import "package:flutter/material.dart";
+import "package:flutter_chat_core/flutter_chat_core.dart";
+import "package:flutter_link_previewer/flutter_link_previewer.dart";
+import "package:nexus/models/room.dart";
+import "package:nexus/widgets/chat_page/html/html.dart";
+import "package:nexus/widgets/chat_page/message_wrapper.dart";
+import "package:nexus/widgets/chat_page/reply_widget.dart";
+
+class TextMessageWrapper extends StatelessWidget {
+ final Message message;
+ final String? content;
+ final Room room;
+ final MessageGroupStatus? groupStatus;
+ final Future Function(Message oldMessage, Message newMessage)
+ updateMessage;
+ final bool isSentByMe;
+ final Widget? extra;
+ final OnTapReply onTapReply;
+
+ const TextMessageWrapper(
+ this.message, {
+ this.content,
+ this.onTapReply,
+ required this.room,
+ required this.updateMessage,
+ required this.groupStatus,
+ required this.isSentByMe,
+ this.extra,
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ final colorScheme = theme.colorScheme;
+ final textMessage = message is TextMessage ? message as TextMessage : null;
+
+ return MessageWrapper(
+ message,
+ ClipRRect(
+ borderRadius: BorderRadius.all(Radius.circular(8)),
+ child: Container(
+ padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
+ decoration: BoxDecoration(
+ color: isSentByMe
+ ? colorScheme.primaryContainer
+ : colorScheme.surfaceContainer,
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ ReplyWidget(
+ message,
+ room: room,
+ groupStatus: groupStatus,
+ onTapReply: onTapReply,
+ ),
+ if (content != null)
+ Html(
+ textStyle: message.metadata?["big"] == true
+ ? TextStyle(fontSize: 32)
+ : null,
+ content!
+ .replaceAllMapped(
+ RegExp(
+ "(]*>.*?<\\/a>)|(\\bhttps?:\\/\\/[^\\s<]+)",
+ caseSensitive: false,
+ ),
+ (m) {
+ // If it's already an tag, leave it unchanged
+ if (m.group(1) != null) {
+ return m.group(1)!;
+ }
+
+ // Otherwise, wrap the bare URL
+ final url = m.group(2)!;
+ return "$url";
+ },
+ )
+ .replaceAll("\n", "
"),
+ ),
+ if (textMessage?.editedAt != null)
+ Text("(edited)", style: theme.textTheme.labelSmall),
+ if (textMessage != null)
+ LinkPreview(
+ text: textMessage.text,
+ backgroundColor: isSentByMe
+ ? colorScheme.inversePrimary
+ : colorScheme.surfaceContainerLow,
+ outsidePadding: EdgeInsets.only(top: 4),
+ insidePadding: EdgeInsets.symmetric(
+ vertical: 8,
+ horizontal: 16,
+ ),
+ linkPreviewData: message.metadata?["linkPreviewData"],
+ onLinkPreviewDataFetched: (linkPreviewData) => updateMessage(
+ message,
+ message.copyWith(
+ metadata: {
+ ...(message.metadata ?? {}),
+ "linkPreviewData": linkPreviewData,
+ },
+ ),
+ ),
+ ),
+ if (extra != null) extra!,
+ ],
+ ),
+ ),
+ ),
+ groupStatus,
+ );
+ }
+}
diff --git a/lib/widgets/chat_page/top_widget.dart b/lib/widgets/chat_page/top_widget.dart
deleted file mode 100644
index cfba6fc..0000000
--- a/lib/widgets/chat_page/top_widget.dart
+++ /dev/null
@@ -1,124 +0,0 @@
-import "dart:math";
-import "package:flutter/material.dart";
-import "package:flutter_chat_core/flutter_chat_core.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 {
- final Message message;
- final bool alwaysShow;
- final MessageGroupStatus? groupStatus;
- const TopWidget(
- this.message, {
- required this.groupStatus,
- this.alwaysShow = false,
- super.key,
- });
-
- @override
- Widget build(BuildContext context, WidgetRef ref) => Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Builder(
- builder: (_) {
- final replyMessage = message.metadata?["reply"] as TextMessage?;
-
- if (replyMessage == null) return SizedBox.shrink();
- final smallerText = message is TextMessage
- ? replyMessage.text.substring(
- 0,
- min(
- max(
- max(
- (message as TextMessage).text.length - 20,
- message.metadata?["displayName"].length,
- ),
- 5,
- ),
- replyMessage.text.length,
- ),
- )
- : null;
- final replyText =
- (smallerText == null ||
- smallerText.length == replyMessage.text.length)
- ? replyMessage.text
- : "$smallerText...";
-
- return Padding(
- padding: EdgeInsets.only(bottom: 12),
- child: InkWell(
- onTap: () => showDialog(
- context: context,
- builder: (_) => Dialog(
- child: Text("TODO: Scroll to original message"),
- ), // TODO
- ),
- child: Quoted(
- Row(
- mainAxisSize: MainAxisSize.min,
- spacing: 8,
- children: [
- AvatarOrHash(
- Uri.tryParse(replyMessage.metadata?["avatarUrl"] ?? ""),
- replyMessage.metadata?["displayName"] ?? "",
- height: 16,
- ),
- Flexible(
- child: Text(
- replyMessage.metadata?["displayName"] ??
- replyMessage.authorId,
- style: Theme.of(context).textTheme.labelMedium
- ?.copyWith(fontWeight: FontWeight.bold),
- overflow: TextOverflow.ellipsis,
- ),
- ),
- Flexible(
- child: Text(
- replyText,
- overflow: TextOverflow.ellipsis,
- style: Theme.of(context).textTheme.labelMedium,
- maxLines: 1,
- ),
- ),
- ],
- ),
- ),
- ),
- );
- },
- ),
- if (alwaysShow ||
- groupStatus?.isFirst != false ||
- message.metadata?["reply"] != null)
- InkWell(
- onTap: () => showDialog(
- context: context,
- builder: (_) =>
- Dialog(child: Text("TODO: Show user profile")), // TODO
- ),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- spacing: 8,
- children: [
- AvatarOrHash(
- Uri.parse(message.metadata?["avatarUrl"] ?? ""),
- message.metadata?["displayName"] ?? "",
- ),
- Flexible(
- child: Text(
- message.metadata?["displayName"] ?? message.authorId,
- overflow: TextOverflow.ellipsis,
- style: Theme.of(context).textTheme.titleMedium?.copyWith(
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- ],
- ),
- ),
- SizedBox(height: 4),
- ],
- );
-}
diff --git a/lib/widgets/loading.dart b/lib/widgets/loading.dart
index aadc43c..9bb2858 100644
--- a/lib/widgets/loading.dart
+++ b/lib/widgets/loading.dart
@@ -1,13 +1,14 @@
import "package:flutter/material.dart";
class Loading extends StatelessWidget {
- const Loading({super.key});
+ final double? height;
+ const Loading({this.height, super.key});
@override
- Widget build(BuildContext context) => const Center(
- child: Padding(
- padding: EdgeInsets.all(16),
- child: CircularProgressIndicator(),
- ),
- );
+ Widget build(BuildContext context) => Center(
+ child: Padding(
+ padding: EdgeInsets.all(16),
+ child: SizedBox(height: height, child: CircularProgressIndicator()),
+ ),
+ );
}
diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc
index dffacff..f70fb6e 100644
--- a/linux/flutter/generated_plugin_registrant.cc
+++ b/linux/flutter/generated_plugin_registrant.cc
@@ -8,7 +8,6 @@
#include
#include
-#include
#include
#include
#include
@@ -21,9 +20,6 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
- g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
- fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
- flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake
index 8b658f4..78dcf40 100644
--- a/linux/flutter/generated_plugins.cmake
+++ b/linux/flutter/generated_plugins.cmake
@@ -5,7 +5,6 @@
list(APPEND FLUTTER_PLUGIN_LIST
dynamic_system_colors
file_selector_linux
- flutter_secure_storage_linux
screen_retriever_linux
url_launcher_linux
window_manager
diff --git a/nix/fake-rustup.sh b/nix/fake-rustup.sh
deleted file mode 100644
index 7884c05..0000000
--- a/nix/fake-rustup.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env bash
-# Fake rustup for nix-managed Rust toolchains
-
-case "$1" in
- run)
- if [[ "$2" == "stable" ]]; then
- shift 2
- if [[ $# -eq 0 ]]; then
- echo "fake rustup: no command given" >&2
- exit 1
- fi
- exec "$@"
- exit 0
- fi
- ;;
-
- toolchain)
- if [[ "$2" == "list" ]]; then
- echo "stable (default)"
- exit 0
- fi
- ;;
-
- target)
- if [[ "$2" == "list" && "$3" == "--toolchain" && "$4" == "stable" && "$5" == "--installed" ]]; then
- echo "x86_64-unknown-linux-gnu"
- exit 0
- fi
- ;;
-esac
-
-echo "fake rustup: the command:" >&2
-echo " rustup $*" >&2
-echo "…is not mocked yet" >&2
-exit 1
\ No newline at end of file
diff --git a/pubspec.lock b/pubspec.lock
index 222e779..da5de89 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -509,14 +509,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
- flutter_math_fork:
- dependency: transitive
- description:
- name: flutter_math_fork
- sha256: "6d5f2f1aa57ae539ffb0a04bb39d2da67af74601d685a161aff7ce5bda5fa407"
- url: "https://pub.dev"
- source: hosted
- version: "0.7.4"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@@ -533,54 +525,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.0"
- flutter_secure_storage:
- dependency: "direct main"
- description:
- name: flutter_secure_storage
- sha256: da922f2aab2d733db7e011a6bcc4a825b844892d4edd6df83ff156b09a9b2e40
- url: "https://pub.dev"
- source: hosted
- version: "10.0.0"
- flutter_secure_storage_darwin:
- dependency: transitive
- description:
- name: flutter_secure_storage_darwin
- sha256: "8878c25136a79def1668c75985e8e193d9d7d095453ec28730da0315dc69aee3"
- url: "https://pub.dev"
- source: hosted
- version: "0.2.0"
- flutter_secure_storage_linux:
- dependency: transitive
- description:
- name: flutter_secure_storage_linux
- sha256: "2b5c76dce569ab752d55a1cee6a2242bcc11fdba927078fb88c503f150767cda"
- url: "https://pub.dev"
- source: hosted
- version: "3.0.0"
- flutter_secure_storage_platform_interface:
- dependency: transitive
- description:
- name: flutter_secure_storage_platform_interface
- sha256: "8ceea1223bee3c6ac1a22dabd8feefc550e4729b3675de4b5900f55afcb435d6"
- url: "https://pub.dev"
- source: hosted
- version: "2.0.1"
- flutter_secure_storage_web:
- dependency: transitive
- description:
- name: flutter_secure_storage_web
- sha256: "6a1137df62b84b54261dca582c1c09ea72f4f9a4b2fcee21b025964132d5d0c3"
- url: "https://pub.dev"
- source: hosted
- version: "2.1.0"
- flutter_secure_storage_windows:
- dependency: transitive
- description:
- name: flutter_secure_storage_windows
- sha256: "3b7c8e068875dfd46719ff57c90d8c459c87f2302ed6b00ff006b3c9fcad1613"
- url: "https://pub.dev"
- source: hosted
- version: "4.1.0"
flutter_svg:
dependency: "direct main"
description:
@@ -639,15 +583,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
- flyer_chat_text_message:
- dependency: "direct main"
- description:
- path: "packages/flyer_chat_text_message"
- ref: HEAD
- resolved-ref: "03be67c8c81c8f637672ee03dd8f082d2c223627"
- url: "https://github.com/Henry-Hiles/flutter_chat_ui"
- source: git
- version: "2.6.0"
freezed:
dependency: "direct dev"
description:
@@ -688,14 +623,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.3"
- gpt_markdown:
- dependency: transitive
- description:
- name: gpt_markdown
- sha256: "9b88dfaffea644070b648c204ca4a55745a49f4ad0b58ed0ab70913ad593c7a1"
- url: "https://pub.dev"
- source: hosted
- version: "1.1.5"
graphs:
dependency: transitive
description:
@@ -928,14 +855,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.11.1"
- mention_tag_text_field:
- dependency: "direct main"
- description:
- name: mention_tag_text_field
- sha256: ba7b9d8003e0f340a65c6dcdb7770f4340f653ae1612a9e31e11d12f7f1dd80f
- url: "https://pub.dev"
- source: hosted
- version: "0.0.9"
meta:
dependency: transitive
description:
@@ -1437,14 +1356,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.1.0+1"
- tuple:
- dependency: transitive
- description:
- name: tuple
- sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
- url: "https://pub.dev"
- source: hosted
- version: "2.0.2"
typed_data:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 7893653..3c0198d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -42,10 +42,6 @@ dependencies:
flyer_chat_image_message: ^2.2.2
flyer_chat_system_message: ^2.1.13
flyer_chat_file_message: ^2.3.1
- flyer_chat_text_message:
- git:
- url: https://github.com/Henry-Hiles/flutter_chat_ui
- path: packages/flyer_chat_text_message
flutter_chat_ui:
git:
url: https://github.com/Henry-Hiles/flutter_chat_ui
@@ -59,9 +55,7 @@ dependencies:
flutter_svg: ^2.2.2
json_annotation: ^4.9.0
shared_preferences: ^2.5.3
- mention_tag_text_field: ^0.0.9
fluttertagger: ^2.3.1
- flutter_secure_storage: ^10.0.0
dynamic_polls: ^0.0.6
flutter_hooks: ^0.21.3+1
cross_cache: ^1.1.0
diff --git a/scripts/generate.dart b/scripts/generate.dart
index 9806603..b240d98 100644
--- a/scripts/generate.dart
+++ b/scripts/generate.dart
@@ -25,6 +25,8 @@ void main(List args) async {
}
print("Generating FFI Bindings...");
+
+ final libclangPath = Platform.environment["LIBCLANG_PATH"];
FfiGenerator(
output: Output(
dartFile: Platform.script.resolve("../lib/src/third_party/gomuks.g.dart"),
@@ -34,6 +36,10 @@ void main(List args) async {
compilerOptions: ["--no-warnings"],
),
functions: Functions.includeAll,
- ).generate();
+ ).generate(
+ libclangDylib: libclangPath == null
+ ? null
+ : Uri.file(join(libclangPath, "libclang.so")),
+ );
print("Done!");
}
diff --git a/scripts/generate.sh b/scripts/generate.sh
index faafd29..6076ab8 100755
--- a/scripts/generate.sh
+++ b/scripts/generate.sh
@@ -1,9 +1,9 @@
#!/usr/bin/env bash
-pushd "$(dirname "$(readlink -f "$0")")"/.. || exit
+pushd "$(dirname "$(readlink -f "$0")")"/.. > /dev/null || exit
mkdir -p build
touch build/lock
dart scripts/generate.dart
rm build/lock
-popd || exit
\ No newline at end of file
+popd > /dev/null || exit
\ No newline at end of file
diff --git a/src/gomuks/libgomuks.h b/src/gomuks/libgomuks.h
deleted file mode 100644
index 962d281..0000000
--- a/src/gomuks/libgomuks.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/* Code generated by cmd/cgo; DO NOT EDIT. */
-
-/* package go.mau.fi/gomuks/pkg/ffi */
-
-
-#line 1 "cgo-builtin-export-prolog"
-
-#include
-
-#ifndef GO_CGO_EXPORT_PROLOGUE_H
-#define GO_CGO_EXPORT_PROLOGUE_H
-
-#ifndef GO_CGO_GOSTRING_TYPEDEF
-typedef struct { const char *p; ptrdiff_t n; } _GoString_;
-extern size_t _GoStringLen(_GoString_ s);
-extern const char *_GoStringPtr(_GoString_ s);
-#endif
-
-#endif
-
-/* Start of preamble from import "C" comments. */
-
-
-#line 9 "ffi.go"
-
-#include "gomuksffi.h"
-#include
-
-static inline void _gomuks_callEventCallback(EventCallback cb, const char *command, int64_t request_id, GomuksOwnedBuffer data) {
- cb(command, request_id, data);
-}
-
-#line 1 "cgo-generated-wrapper"
-
-
-/* End of preamble from import "C" comments. */
-
-
-/* Start of boilerplate cgo prologue. */
-#line 1 "cgo-gcc-export-header-prolog"
-
-#ifndef GO_CGO_PROLOGUE_H
-#define GO_CGO_PROLOGUE_H
-
-typedef signed char GoInt8;
-typedef unsigned char GoUint8;
-typedef short GoInt16;
-typedef unsigned short GoUint16;
-typedef int GoInt32;
-typedef unsigned int GoUint32;
-typedef long long GoInt64;
-typedef unsigned long long GoUint64;
-typedef GoInt64 GoInt;
-typedef GoUint64 GoUint;
-typedef size_t GoUintptr;
-typedef float GoFloat32;
-typedef double GoFloat64;
-#ifdef _MSC_VER
-#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
-#include
-typedef _Fcomplex GoComplex64;
-typedef _Dcomplex GoComplex128;
-#else
-#include
-typedef std::complex GoComplex64;
-typedef std::complex GoComplex128;
-#endif
-#else
-typedef float _Complex GoComplex64;
-typedef double _Complex GoComplex128;
-#endif
-
-/*
- static assertion to make sure the file is being used on architecture
- at least with matching size of GoInt.
-*/
-typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
-
-#ifndef GO_CGO_GOSTRING_TYPEDEF
-typedef _GoString_ GoString;
-#endif
-typedef void *GoMap;
-typedef void *GoChan;
-typedef struct { void *t; void *v; } GoInterface;
-typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
-
-#endif
-
-/* End of boilerplate cgo prologue. */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern GomuksHandle GomuksInit(void);
-extern int GomuksStart(GomuksHandle handle, EventCallback callback);
-extern void GomuksDestroy(GomuksHandle handle);
-extern GomuksResponse GomuksSubmitCommand(GomuksHandle handle, char* command, GomuksBorrowedBuffer data);
-extern GomuksAccountInfo GomuksGetAccountInfo(GomuksHandle handle);
-extern void GomuksFreeAccountInfo(GomuksAccountInfo info);
-extern void GomuksFreeBuffer(GomuksOwnedBuffer buf);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/src/gomuks/libgomuks.so b/src/gomuks/libgomuks.so
deleted file mode 100644
index c4987c5..0000000
Binary files a/src/gomuks/libgomuks.so and /dev/null differ
diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc
index b12edca..55fb066 100644
--- a/windows/flutter/generated_plugin_registrant.cc
+++ b/windows/flutter/generated_plugin_registrant.cc
@@ -8,7 +8,6 @@
#include
#include
-#include
#include
#include
#include
@@ -19,8 +18,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
- FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
- registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar(
diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake
index 8967b80..9333a2f 100644
--- a/windows/flutter/generated_plugins.cmake
+++ b/windows/flutter/generated_plugins.cmake
@@ -5,7 +5,6 @@
list(APPEND FLUTTER_PLUGIN_LIST
dynamic_system_colors
file_selector_windows
- flutter_secure_storage_windows
screen_retriever_windows
url_launcher_windows
window_manager