From edc295540823061e706dded174951b2306416d06 Mon Sep 17 00:00:00 2001 From: TheOneWithTheBraid Date: Wed, 23 Mar 2022 14:09:35 +0100 Subject: [PATCH] feat: allow to create widgets - supported widget types: therpad, jitsi, video, custom - update Matrix SDK Signed-off-by: TheOneWithTheBraid --- assets/l10n/intl_en.arb | 12 ++- lib/pages/chat/add_widget_tile.dart | 85 +++++++++++++++++++ lib/pages/chat/add_widget_tile_view.dart | 71 ++++++++++++++++ .../chat/cupertino_widgets_bottom_sheet.dart | 13 ++- lib/pages/chat/edit_widgets_dialog.dart | 31 +++++++ lib/pages/chat/widgets_bottom_sheet.dart | 14 ++- lib/widgets/chat_settings_popup_menu.dart | 29 ++++--- pubspec.lock | 2 +- pubspec.yaml | 2 +- 9 files changed, 241 insertions(+), 18 deletions(-) create mode 100644 lib/pages/chat/add_widget_tile.dart create mode 100644 lib/pages/chat/add_widget_tile_view.dart create mode 100644 lib/pages/chat/edit_widgets_dialog.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index e55a5267..5c60f73b 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2759,5 +2759,15 @@ } }, "nextAccount": "Next account", - "previousAccount": "Previous account" + "previousAccount": "Previous account", + "editWidgets": "Edit widgets", + "addWidget": "Add widget", + "widgetVideo": "Video", + "widgetEtherpad": "Text note", + "widgetJitsi": "Jitsi Meet", + "widgetCustom": "Custom", + "widgetName": "Name", + "widgetUrlError": "This is not a valid URL.", + "widgetNameError": "Please provide a display name.", + "errorAddingWidget": "Error adding the widget." } diff --git a/lib/pages/chat/add_widget_tile.dart b/lib/pages/chat/add_widget_tile.dart new file mode 100644 index 00000000..917a13b3 --- /dev/null +++ b/lib/pages/chat/add_widget_tile.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix.dart'; +import 'package:matrix/widget.dart'; + +import 'package:fluffychat/pages/chat/add_widget_tile_view.dart'; + +class AddWidgetTile extends StatefulWidget { + final Room room; + + const AddWidgetTile({Key? key, required this.room}) : super(key: key); + + @override + State createState() => AddWidgetTileState(); +} + +class AddWidgetTileState extends State { + final TextEditingController urlController = TextEditingController(); + final TextEditingController nameController = TextEditingController(); + String widgetType = 'm.etherpad'; + + late final bool initiallyExpanded; + + String? nameError; + String? urlError; + + @override + void initState() { + initiallyExpanded = widget.room.widgets.isEmpty; + super.initState(); + } + + void setWidgetType(String value) => setState(() => widgetType = value); + + void addWidget() { + try { + nameError = null; + urlError = null; + + final room = widget.room; + final name = nameController.text; + final uri = Uri.tryParse(urlController.text); + + if (name.length < 3) { + setState(() { + nameError = L10n.of(context)!.widgetNameError; + }); + return; + } + + if (uri == null || uri.scheme != 'https') { + setState(() { + urlError = L10n.of(context)!.widgetUrlError; + }); + return; + } + setState(() {}); + + late MatrixWidget matrixWidget; + switch (widgetType) { + case 'm.etherpad': + matrixWidget = MatrixWidget.etherpad(room, name, uri); + break; + case 'm.jitsi': + matrixWidget = MatrixWidget.jitsi(room, name, uri); + break; + case 'm.video': + matrixWidget = MatrixWidget.video(room, name, uri); + break; + default: + matrixWidget = MatrixWidget.custom(room, name, uri); + break; + } + widget.room.addWidget(matrixWidget); + Navigator.of(context).pop(); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(L10n.of(context)!.errorAddingWidget))); + } + } + + @override + Widget build(BuildContext context) => AddWidgetTileView(controller: this); +} diff --git a/lib/pages/chat/add_widget_tile_view.dart b/lib/pages/chat/add_widget_tile_view.dart new file mode 100644 index 00000000..0f98b7cc --- /dev/null +++ b/lib/pages/chat/add_widget_tile_view.dart @@ -0,0 +1,71 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +import 'package:fluffychat/pages/chat/add_widget_tile.dart'; + +class AddWidgetTileView extends StatelessWidget { + final AddWidgetTileState controller; + + const AddWidgetTileView({Key? key, required this.controller}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return ExpansionTile( + title: Text(L10n.of(context)!.addWidget), + leading: const Icon(Icons.add), + initiallyExpanded: controller.initiallyExpanded, + children: [ + CupertinoSegmentedControl( + groupValue: controller.widgetType, + padding: const EdgeInsets.all(8), + children: { + 'm.etherpad': Text(L10n.of(context)!.widgetEtherpad), + 'm.jitsi': Text(L10n.of(context)!.widgetJitsi), + 'm.video': Text(L10n.of(context)!.widgetVideo), + 'm.custom': Text(L10n.of(context)!.widgetCustom), + }.map((key, value) => MapEntry( + key, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: value, + ))), + onValueChanged: controller.setWidgetType, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: controller.nameController, + autofocus: true, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.label), + label: Text(L10n.of(context)!.widgetName), + errorText: controller.nameError, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: controller.urlController, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.add_link), + label: Text(L10n.of(context)!.link), + errorText: controller.urlError, + ), + ), + ), + ButtonBar( + children: [ + TextButton( + onPressed: controller.addWidget, + child: Text(L10n.of(context)!.addWidget), + ), + ], + ) + ], + ); + } +} diff --git a/lib/pages/chat/cupertino_widgets_bottom_sheet.dart b/lib/pages/chat/cupertino_widgets_bottom_sheet.dart index c0e077af..16428f3c 100644 --- a/lib/pages/chat/cupertino_widgets_bottom_sheet.dart +++ b/lib/pages/chat/cupertino_widgets_bottom_sheet.dart @@ -4,11 +4,14 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; import 'package:url_launcher/link.dart'; +import 'edit_widgets_dialog.dart'; + class CupertinoWidgetsBottomSheet extends StatelessWidget { final Room room; const CupertinoWidgetsBottomSheet({Key? key, required this.room}) : super(key: key); + @override Widget build(BuildContext context) { return CupertinoActionSheet( @@ -27,8 +30,14 @@ class CupertinoWidgetsBottomSheet extends StatelessWidget { ), ), CupertinoActionSheetAction( - child: Text(L10n.of(context)!.integrationsNotImplemented), - onPressed: () {}, + child: Text(L10n.of(context)!.editWidgets), + onPressed: () { + Navigator.of(context).pop(); + showCupertinoDialog( + context: context, + builder: (context) => EditWidgetsDialog(room: room), + ); + }, ), CupertinoActionSheetAction( child: Text(L10n.of(context)!.cancel), diff --git a/lib/pages/chat/edit_widgets_dialog.dart b/lib/pages/chat/edit_widgets_dialog.dart new file mode 100644 index 00000000..8d351913 --- /dev/null +++ b/lib/pages/chat/edit_widgets_dialog.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix.dart'; + +import 'add_widget_tile.dart'; + +class EditWidgetsDialog extends StatelessWidget { + final Room room; + + const EditWidgetsDialog({Key? key, required this.room}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SimpleDialog( + title: Text(L10n.of(context)!.editWidgets), + children: [ + ...room.widgets.map((e) => ListTile( + title: Text(e.name ?? e.type), + leading: IconButton( + onPressed: () { + room.deleteWidget(e.id!); + Navigator.of(context).pop(); + }, + icon: const Icon(Icons.delete)), + )), + AddWidgetTile(room: room), + ], + ); + } +} diff --git a/lib/pages/chat/widgets_bottom_sheet.dart b/lib/pages/chat/widgets_bottom_sheet.dart index 7c187284..f88797df 100644 --- a/lib/pages/chat/widgets_bottom_sheet.dart +++ b/lib/pages/chat/widgets_bottom_sheet.dart @@ -4,10 +4,13 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; import 'package:url_launcher/link.dart'; +import 'edit_widgets_dialog.dart'; + class WidgetsBottomSheet extends StatelessWidget { final Room room; const WidgetsBottomSheet({Key? key, required this.room}) : super(key: key); + @override Widget build(BuildContext context) { return ListView.builder( @@ -15,8 +18,15 @@ class WidgetsBottomSheet extends StatelessWidget { itemBuilder: (context, index) { if (index == room.widgets.length) { return ListTile( - title: Text(L10n.of(context)!.integrationsNotImplemented), - leading: const Icon(Icons.info), + leading: const Icon(Icons.edit), + title: Text(L10n.of(context)!.editWidgets), + onTap: () { + Navigator.of(context).pop(); + showDialog( + context: context, + builder: (context) => EditWidgetsDialog(room: room), + ); + }, ); } final widget = room.widgets[index]; diff --git a/lib/widgets/chat_settings_popup_menu.dart b/lib/widgets/chat_settings_popup_menu.dart index db06f7ac..9ac0b860 100644 --- a/lib/widgets/chat_settings_popup_menu.dart +++ b/lib/widgets/chat_settings_popup_menu.dart @@ -12,6 +12,7 @@ import 'package:matrix/matrix.dart'; import 'package:vrouter/vrouter.dart'; import 'package:fluffychat/pages/chat/cupertino_widgets_bottom_sheet.dart'; +import 'package:fluffychat/pages/chat/edit_widgets_dialog.dart'; import 'package:fluffychat/pages/chat/widgets_bottom_sheet.dart'; import 'matrix.dart'; @@ -46,17 +47,16 @@ class _ChatSettingsPopupMenuState extends State { (u) => setState(() {}), ); final items = >[ - if (widget.room.widgets.isNotEmpty) - PopupMenuItem( - value: 'widgets', - child: Row( - children: [ - const Icon(Icons.widgets_outlined), - const SizedBox(width: 12), - Text(L10n.of(context)!.matrixWidgets), - ], - ), + PopupMenuItem( + value: 'widgets', + child: Row( + children: [ + const Icon(Icons.widgets_outlined), + const SizedBox(width: 12), + Text(L10n.of(context)!.matrixWidgets), + ], ), + ), widget.room.pushRuleState == PushRuleState.notify ? PopupMenuItem( value: 'mute', @@ -129,7 +129,14 @@ class _ChatSettingsPopupMenuState extends State { onSelected: (String choice) async { switch (choice) { case 'widgets': - _showWidgets(); + if (widget.room.widgets.isNotEmpty) { + _showWidgets(); + } else { + showDialog( + context: context, + builder: (context) => EditWidgetsDialog(room: widget.room), + ); + } break; case 'leave': final confirmed = await showOkCancelAlertDialog( diff --git a/pubspec.lock b/pubspec.lock index 5d691605..451b43a6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -990,7 +990,7 @@ packages: name: matrix url: "https://pub.dartlang.org" source: hosted - version: "0.8.13" + version: "0.8.14" matrix_api_lite: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7720408c..79e30b86 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,7 +58,7 @@ dependencies: keyboard_shortcuts: ^0.1.4 localstorage: ^4.0.0+1 lottie: ^1.2.2 - matrix: ^0.8.13 + matrix: ^0.8.14 matrix_link_text: ^1.0.2 open_noti_settings: ^0.4.0 package_info_plus: ^1.3.0