mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-27 14:59:29 +01:00
feat: allow to create widgets
- supported widget types: therpad, jitsi, video, custom - update Matrix SDK Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
This commit is contained in:
parent
d1185e8499
commit
edc2955408
@ -2759,5 +2759,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nextAccount": "Next account",
|
"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."
|
||||||
}
|
}
|
||||||
|
85
lib/pages/chat/add_widget_tile.dart
Normal file
85
lib/pages/chat/add_widget_tile.dart
Normal file
@ -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<AddWidgetTile> createState() => AddWidgetTileState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class AddWidgetTileState extends State<AddWidgetTile> {
|
||||||
|
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);
|
||||||
|
}
|
71
lib/pages/chat/add_widget_tile_view.dart
Normal file
71
lib/pages/chat/add_widget_tile_view.dart
Normal file
@ -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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,11 +4,14 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:url_launcher/link.dart';
|
import 'package:url_launcher/link.dart';
|
||||||
|
|
||||||
|
import 'edit_widgets_dialog.dart';
|
||||||
|
|
||||||
class CupertinoWidgetsBottomSheet extends StatelessWidget {
|
class CupertinoWidgetsBottomSheet extends StatelessWidget {
|
||||||
final Room room;
|
final Room room;
|
||||||
|
|
||||||
const CupertinoWidgetsBottomSheet({Key? key, required this.room})
|
const CupertinoWidgetsBottomSheet({Key? key, required this.room})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CupertinoActionSheet(
|
return CupertinoActionSheet(
|
||||||
@ -27,8 +30,14 @@ class CupertinoWidgetsBottomSheet extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
CupertinoActionSheetAction(
|
CupertinoActionSheetAction(
|
||||||
child: Text(L10n.of(context)!.integrationsNotImplemented),
|
child: Text(L10n.of(context)!.editWidgets),
|
||||||
onPressed: () {},
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
showCupertinoDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => EditWidgetsDialog(room: room),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
CupertinoActionSheetAction(
|
CupertinoActionSheetAction(
|
||||||
child: Text(L10n.of(context)!.cancel),
|
child: Text(L10n.of(context)!.cancel),
|
||||||
|
31
lib/pages/chat/edit_widgets_dialog.dart
Normal file
31
lib/pages/chat/edit_widgets_dialog.dart
Normal file
@ -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),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,10 +4,13 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:url_launcher/link.dart';
|
import 'package:url_launcher/link.dart';
|
||||||
|
|
||||||
|
import 'edit_widgets_dialog.dart';
|
||||||
|
|
||||||
class WidgetsBottomSheet extends StatelessWidget {
|
class WidgetsBottomSheet extends StatelessWidget {
|
||||||
final Room room;
|
final Room room;
|
||||||
|
|
||||||
const WidgetsBottomSheet({Key? key, required this.room}) : super(key: key);
|
const WidgetsBottomSheet({Key? key, required this.room}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
@ -15,8 +18,15 @@ class WidgetsBottomSheet extends StatelessWidget {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (index == room.widgets.length) {
|
if (index == room.widgets.length) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(L10n.of(context)!.integrationsNotImplemented),
|
leading: const Icon(Icons.edit),
|
||||||
leading: const Icon(Icons.info),
|
title: Text(L10n.of(context)!.editWidgets),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => EditWidgetsDialog(room: room),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final widget = room.widgets[index];
|
final widget = room.widgets[index];
|
||||||
|
@ -12,6 +12,7 @@ import 'package:matrix/matrix.dart';
|
|||||||
import 'package:vrouter/vrouter.dart';
|
import 'package:vrouter/vrouter.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/pages/chat/cupertino_widgets_bottom_sheet.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 'package:fluffychat/pages/chat/widgets_bottom_sheet.dart';
|
||||||
import 'matrix.dart';
|
import 'matrix.dart';
|
||||||
|
|
||||||
@ -46,17 +47,16 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
|
|||||||
(u) => setState(() {}),
|
(u) => setState(() {}),
|
||||||
);
|
);
|
||||||
final items = <PopupMenuEntry<String>>[
|
final items = <PopupMenuEntry<String>>[
|
||||||
if (widget.room.widgets.isNotEmpty)
|
PopupMenuItem<String>(
|
||||||
PopupMenuItem<String>(
|
value: 'widgets',
|
||||||
value: 'widgets',
|
child: Row(
|
||||||
child: Row(
|
children: [
|
||||||
children: [
|
const Icon(Icons.widgets_outlined),
|
||||||
const Icon(Icons.widgets_outlined),
|
const SizedBox(width: 12),
|
||||||
const SizedBox(width: 12),
|
Text(L10n.of(context)!.matrixWidgets),
|
||||||
Text(L10n.of(context)!.matrixWidgets),
|
],
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
widget.room.pushRuleState == PushRuleState.notify
|
widget.room.pushRuleState == PushRuleState.notify
|
||||||
? PopupMenuItem<String>(
|
? PopupMenuItem<String>(
|
||||||
value: 'mute',
|
value: 'mute',
|
||||||
@ -129,7 +129,14 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
|
|||||||
onSelected: (String choice) async {
|
onSelected: (String choice) async {
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
case 'widgets':
|
case 'widgets':
|
||||||
_showWidgets();
|
if (widget.room.widgets.isNotEmpty) {
|
||||||
|
_showWidgets();
|
||||||
|
} else {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => EditWidgetsDialog(room: widget.room),
|
||||||
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'leave':
|
case 'leave':
|
||||||
final confirmed = await showOkCancelAlertDialog(
|
final confirmed = await showOkCancelAlertDialog(
|
||||||
|
@ -990,7 +990,7 @@ packages:
|
|||||||
name: matrix
|
name: matrix
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.13"
|
version: "0.8.14"
|
||||||
matrix_api_lite:
|
matrix_api_lite:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -58,7 +58,7 @@ dependencies:
|
|||||||
keyboard_shortcuts: ^0.1.4
|
keyboard_shortcuts: ^0.1.4
|
||||||
localstorage: ^4.0.0+1
|
localstorage: ^4.0.0+1
|
||||||
lottie: ^1.2.2
|
lottie: ^1.2.2
|
||||||
matrix: ^0.8.13
|
matrix: ^0.8.14
|
||||||
matrix_link_text: ^1.0.2
|
matrix_link_text: ^1.0.2
|
||||||
open_noti_settings: ^0.4.0
|
open_noti_settings: ^0.4.0
|
||||||
package_info_plus: ^1.3.0
|
package_info_plus: ^1.3.0
|
||||||
|
Loading…
Reference in New Issue
Block a user