From 81241f6582802e771d0668a38d6a9be48441018c Mon Sep 17 00:00:00 2001 From: Airyzz <36567925+Airyzz@users.noreply.github.com> Date: Wed, 9 Sep 2026 23:29:56 +0930 Subject: [PATCH] Start working on UX for adding widgets --- .../lib/ui/atoms/drag_drop_file_target.dart | 3 +- commet/lib/ui/organisms/chat/chat.dart | 73 +++++++++++++++++++ .../room_widgets/room_widgets_view.dart | 21 ++++++ commet/lib/utils/custom_uri.dart | 42 +++++++++++ commet/pubspec.yaml | 2 +- pubspec.lock | 4 +- 6 files changed, 141 insertions(+), 4 deletions(-) diff --git a/commet/lib/ui/atoms/drag_drop_file_target.dart b/commet/lib/ui/atoms/drag_drop_file_target.dart index e69d991d5..8ce6ddf2a 100644 --- a/commet/lib/ui/atoms/drag_drop_file_target.dart +++ b/commet/lib/ui/atoms/drag_drop_file_target.dart @@ -21,7 +21,8 @@ class _DragDropFileTargetState extends State { Widget build(BuildContext context) { return IgnorePointer( child: DropTarget( - onDragEntered: (_) { + onDragEntered: (ev) { + print(ev); setState(() { isFileHovered = true; }); diff --git a/commet/lib/ui/organisms/chat/chat.dart b/commet/lib/ui/organisms/chat/chat.dart index 69a3f5f5f..4b7d043b9 100644 --- a/commet/lib/ui/organisms/chat/chat.dart +++ b/commet/lib/ui/organisms/chat/chat.dart @@ -12,6 +12,8 @@ import 'package:commet/client/components/gif/gif_search_result.dart'; import 'package:commet/client/components/read_receipts/read_receipt_component.dart'; import 'package:commet/client/components/threads/thread_component.dart'; import 'package:commet/client/components/typing_indicators/typing_indicator_component.dart'; +import 'package:commet/client/matrix/matrix_client.dart'; +import 'package:commet/client/matrix/matrix_mxc_image_provider.dart'; import 'package:commet/client/timeline_events/timeline_event.dart'; import 'package:commet/client/timeline_events/timeline_event_message.dart'; import 'package:commet/client/timeline_events/timeline_event_sticker.dart'; @@ -21,6 +23,7 @@ import 'package:commet/main.dart'; import 'package:commet/ui/organisms/attachment_processor/attachment_processor.dart'; import 'package:commet/ui/navigation/adaptive_dialog.dart'; import 'package:commet/ui/organisms/chat/chat_view.dart'; +import 'package:commet/utils/custom_uri.dart'; import 'package:commet/utils/debounce.dart'; import 'package:commet/utils/error_utils.dart'; import 'package:commet/utils/event_bus.dart'; @@ -399,6 +402,76 @@ class ChatState extends State { } void onFileDropped(DropDoneDetails event) async { + var path = event.rawText; + + print(path); + if (path != null) { + var custom = CustomURI.parse(path); + + if (custom case AddWidgetURI widgetUri) { + AdaptiveDialog.show(context, builder: (dialogContext) { + return SizedBox( + width: 500, + child: Column( + spacing: 8, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.all(4.0), + child: Row( + spacing: 8, + children: [ + if (widgetUri.widgetAvatarMxc != null) + SizedBox( + width: 30, + height: 30, + child: Image( + fit: BoxFit.cover, + image: MatrixMxcImage( + Uri.parse(widgetUri.widgetAvatarMxc!), + doThumbnail: false, + doFullres: true, + (widget.room.client as MatrixClient) + .matrixClient)), + ), + tiamat.Text.label( + "Add the widget '${widgetUri.widgetName ?? "Custom"}' to ${widget.room.displayName}?"), + ], + ), + ), + + tiamat.Text.labelLow( + "Host: ${Uri.parse(widgetUri.widgetUrl).host}"), + + if (widgetUri.previewMxc != null) + Padding( + padding: const EdgeInsets.all(8.0), + child: ClipRRect( + borderRadius: BorderRadiusGeometry.circular(20), + child: SizedBox( + height: 300, + child: Image( + fit: BoxFit.cover, + image: MatrixMxcImage( + Uri.parse(widgetUri.previewMxc!), + doThumbnail: false, + doFullres: true, + (widget.room.client as MatrixClient) + .matrixClient)), + ), + ), + ), + tiamat.Button(text: "Add Widget",), + tiamat.Button.secondary(text: "Cancel",), + tiamat.Text.error( + "Widgets are hosted on external websites, and the code can be changed without notice. Do not use the widget if you do not trust the host.") + ], + ), + ); + }, title: 'Add "${widgetUri.widgetName ?? "Widget"}"?'); + } + } + for (var file in event.files) { var size = await file.length(); Uint8List? data; diff --git a/commet/lib/ui/organisms/room_widgets/room_widgets_view.dart b/commet/lib/ui/organisms/room_widgets/room_widgets_view.dart index 3fd56e85b..7d1ea30e5 100644 --- a/commet/lib/ui/organisms/room_widgets/room_widgets_view.dart +++ b/commet/lib/ui/organisms/room_widgets/room_widgets_view.dart @@ -2,6 +2,7 @@ import 'package:commet/client/components/widgets/widget_component.dart'; import 'package:commet/client/room.dart'; import 'package:commet/main.dart'; import 'package:commet/ui/atoms/adaptive_context_menu.dart'; +import 'package:commet/utils/links/link_utils.dart'; import 'package:flutter/material.dart'; import 'package:tiamat/tiamat.dart' as tiamat; @@ -57,6 +58,11 @@ class _RoomWidgetsViewState extends State { return tiamat.Tile.low( child: Column( children: [ + if (widgets.isEmpty) + Padding( + padding: const EdgeInsets.all(8.0), + child: tiamat.Text.labelLow("No widgets have been added to this room"), + ), Flexible( child: ListView.builder( padding: EdgeInsets.all(0), @@ -108,6 +114,21 @@ class _RoomWidgetsViewState extends State { }, ), ), + Padding( + padding: const EdgeInsets.all(4.0), + child: SizedBox( + height: 50, + child: tiamat.TextButton( + "Browse Widgets", + icon: Icons.open_in_browser, + onTap: () { + LinkUtils.open( + Uri.parse("https://commet.chat/widgets"), + context: context, + ); + }, + )), + ), ], ), ); diff --git a/commet/lib/utils/custom_uri.dart b/commet/lib/utils/custom_uri.dart index 375661766..73338414d 100644 --- a/commet/lib/utils/custom_uri.dart +++ b/commet/lib/utils/custom_uri.dart @@ -1,4 +1,5 @@ import 'package:commet/config/build_config.dart'; +import 'package:tiamat/config/style/theme_json_converter.dart'; class CustomURI { static CustomURI? parse(String text) { @@ -26,6 +27,24 @@ class CustomURI { clientId: uri.queryParameters["client_id"]!); } + if (uri.host == "add_widget") { + var url = uri.queryParameters.tryGet("url"); + var avatar = uri.queryParameters.tryGet("avatar"); + var widgetType = uri.queryParameters.tryGet("type"); + var widgetName = uri.queryParameters.tryGet("name"); + var preview = uri.queryParameters.tryGet("preview"); + if (url != null) { + return AddWidgetURI( + widgetUrl: Uri.decodeComponent(url), + widgetAvatarMxc: + avatar != null ? Uri.decodeComponent(avatar) : null, + widgetName: widgetName != null ? Uri.decodeComponent(widgetName) : null, + previewMxc: preview != null && preview.startsWith("mxc") == true ? Uri.decodeComponent(preview) : null, + widgetType: + widgetType != null ? Uri.decodeComponent(widgetType) : null); + } + } + if (uri.host == "accept_call") { if ([ "room_id", @@ -75,6 +94,29 @@ class OpenRoomURI implements CustomURI { } } +class AddWidgetURI implements CustomURI { + final String widgetUrl; + final String? widgetType; + final String? widgetAvatarMxc; + final String? widgetName; + final String? previewMxc; + + AddWidgetURI( + {required this.widgetUrl, this.previewMxc, this.widgetName, this.widgetType, this.widgetAvatarMxc}); + + @override + String toString() { + return Uri( + scheme: BuildConfig.appSchema, + host: "add_widget", + queryParameters: { + "url": widgetUrl, + if (widgetType != null) "type": widgetType, + if (widgetAvatarMxc != null) "avatar": widgetAvatarMxc!, + }).toString(); + } +} + class AcceptCallUri implements CustomURI { final String roomId; final String clientId; diff --git a/commet/pubspec.yaml b/commet/pubspec.yaml index e9529d93d..336ad5d3b 100644 --- a/commet/pubspec.yaml +++ b/commet/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: matrix_widget_api: path: ../widgets/matrix_widget_api crypto: ^3.0.2 - desktop_drop: ^0.4.1 + desktop_drop: ^0.8.4 desktop_notifications: ^0.6.3 device_info_plus: ^12.2.0 flutter_background_service: ^5.0.5 diff --git a/pubspec.lock b/pubspec.lock index 0c6c4103a..0b1da48bc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -374,10 +374,10 @@ packages: dependency: transitive description: name: desktop_drop - sha256: d55a010fe46c8e8fcff4ea4b451a9ff84a162217bdb3b2a0aa1479776205e15d + sha256: "47ded09c85909edfed644fd4cbb5051b040d0aed5c79e7e062ea4b3b7156dbe4" url: "https://pub.dev" source: hosted - version: "0.4.4" + version: "0.8.4" desktop_notifications: dependency: transitive description: