mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-03 22:07:23 +01:00 
			
		
		
		
	Implement jitsi
This commit is contained in:
		
							parent
							
								
									4852fd42ad
								
							
						
					
					
						commit
						7f93dd2c97
					
				@ -6,6 +6,7 @@ import 'package:fluffychat/utils/app_route.dart';
 | 
			
		||||
import 'package:fluffychat/views/chat_details.dart';
 | 
			
		||||
import 'package:fluffychat/views/chat_list.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:url_launcher/url_launcher.dart';
 | 
			
		||||
 | 
			
		||||
import 'dialogs/simple_dialogs.dart';
 | 
			
		||||
import 'matrix.dart';
 | 
			
		||||
@ -29,6 +30,18 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void startCallAction(BuildContext context) async {
 | 
			
		||||
    final url =
 | 
			
		||||
        '${Matrix.of(context).jitsiInstance}${Uri.encodeComponent(widget.room.id.localpart)}';
 | 
			
		||||
    final success = await Matrix.of(context)
 | 
			
		||||
        .tryRequestWithLoadingDialog(widget.room.sendEvent({
 | 
			
		||||
      'msgtype': Matrix.callNamespace,
 | 
			
		||||
      'body': url,
 | 
			
		||||
    }));
 | 
			
		||||
    if (success == false) return;
 | 
			
		||||
    await launch(url);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    notificationChangeSub ??= Matrix.of(context)
 | 
			
		||||
@ -49,6 +62,10 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
 | 
			
		||||
              value: "unmute",
 | 
			
		||||
              child: Text(I18n.of(context).unmuteChat),
 | 
			
		||||
            ),
 | 
			
		||||
      PopupMenuItem<String>(
 | 
			
		||||
        value: "call",
 | 
			
		||||
        child: Text(I18n.of(context).videoCall),
 | 
			
		||||
      ),
 | 
			
		||||
      PopupMenuItem<String>(
 | 
			
		||||
        value: "leave",
 | 
			
		||||
        child: Text(I18n.of(context).leave),
 | 
			
		||||
@ -86,6 +103,9 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
 | 
			
		||||
            await Matrix.of(context).tryRequestWithLoadingDialog(
 | 
			
		||||
                widget.room.setPushRuleState(PushRuleState.notify));
 | 
			
		||||
            break;
 | 
			
		||||
          case "call":
 | 
			
		||||
            startCallAction(context);
 | 
			
		||||
            break;
 | 
			
		||||
          case "details":
 | 
			
		||||
            await Navigator.of(context).push(
 | 
			
		||||
              AppRoute.defaultRoute(
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ class SimpleDialogs {
 | 
			
		||||
        content: TextField(
 | 
			
		||||
          controller: controller,
 | 
			
		||||
          autofocus: true,
 | 
			
		||||
          autocorrect: false,
 | 
			
		||||
          onSubmitted: (s) {
 | 
			
		||||
            input = s;
 | 
			
		||||
            Navigator.of(context).pop();
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
 | 
			
		||||
import 'package:localstorage/localstorage.dart';
 | 
			
		||||
import 'package:path_provider/path_provider.dart';
 | 
			
		||||
import 'package:url_launcher/url_launcher.dart';
 | 
			
		||||
 | 
			
		||||
import '../i18n/i18n.dart';
 | 
			
		||||
import '../utils/app_route.dart';
 | 
			
		||||
@ -18,8 +19,11 @@ import '../utils/event_extension.dart';
 | 
			
		||||
import '../utils/famedlysdk_store.dart';
 | 
			
		||||
import '../utils/room_extension.dart';
 | 
			
		||||
import '../views/chat.dart';
 | 
			
		||||
import 'avatar.dart';
 | 
			
		||||
 | 
			
		||||
class Matrix extends StatefulWidget {
 | 
			
		||||
  static const String callNamespace = 'chat.fluffy.jitsi_call';
 | 
			
		||||
 | 
			
		||||
  final Widget child;
 | 
			
		||||
 | 
			
		||||
  final String clientName;
 | 
			
		||||
@ -52,6 +56,8 @@ class MatrixState extends State<Matrix> {
 | 
			
		||||
  String activeRoomId;
 | 
			
		||||
  File wallpaper;
 | 
			
		||||
 | 
			
		||||
  String jitsiInstance = 'https://meet.jit.si/';
 | 
			
		||||
 | 
			
		||||
  void clean() async {
 | 
			
		||||
    if (!kIsWeb) return;
 | 
			
		||||
 | 
			
		||||
@ -343,12 +349,73 @@ class MatrixState extends State<Matrix> {
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
  StreamSubscription onRoomKeyRequestSub;
 | 
			
		||||
  StreamSubscription onJitsiCallSub;
 | 
			
		||||
 | 
			
		||||
  void onJitsiCall(EventUpdate eventUpdate) {
 | 
			
		||||
    final event = Event.fromJson(
 | 
			
		||||
        eventUpdate.content, client.getRoomById(eventUpdate.roomID));
 | 
			
		||||
    if (DateTime.now().millisecondsSinceEpoch -
 | 
			
		||||
            event.time.millisecondsSinceEpoch >
 | 
			
		||||
        1000 * 60 * 5) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    final senderName = event.sender.calcDisplayname();
 | 
			
		||||
    final senderAvatar = event.sender.avatarUrl;
 | 
			
		||||
    showDialog(
 | 
			
		||||
      context: context,
 | 
			
		||||
      builder: (context) => AlertDialog(
 | 
			
		||||
        title: ListTile(
 | 
			
		||||
          contentPadding: EdgeInsets.all(0),
 | 
			
		||||
          leading: Avatar(senderAvatar, senderName),
 | 
			
		||||
          title: Text(
 | 
			
		||||
            senderName,
 | 
			
		||||
            style: TextStyle(fontSize: 18),
 | 
			
		||||
          ),
 | 
			
		||||
          subtitle:
 | 
			
		||||
              event.room.isDirectChat ? null : Text(event.room.displayname),
 | 
			
		||||
        ),
 | 
			
		||||
        content: Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            Divider(),
 | 
			
		||||
            Row(
 | 
			
		||||
              children: <Widget>[
 | 
			
		||||
                Spacer(),
 | 
			
		||||
                FloatingActionButton(
 | 
			
		||||
                  backgroundColor: Colors.red,
 | 
			
		||||
                  child: Icon(Icons.phone_missed),
 | 
			
		||||
                  onPressed: () => Navigator.of(context).pop(),
 | 
			
		||||
                ),
 | 
			
		||||
                Spacer(),
 | 
			
		||||
                FloatingActionButton(
 | 
			
		||||
                  backgroundColor: Colors.green,
 | 
			
		||||
                  child: Icon(Icons.phone),
 | 
			
		||||
                  onPressed: () {
 | 
			
		||||
                    Navigator.of(context).pop();
 | 
			
		||||
                    launch(event.body);
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
                Spacer(),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    if (widget.client == null) {
 | 
			
		||||
      debugPrint("[Matrix] Init matrix client");
 | 
			
		||||
      client = Client(widget.clientName, debug: false);
 | 
			
		||||
      onJitsiCallSub ??= client.onEvent.stream
 | 
			
		||||
          .where((e) =>
 | 
			
		||||
              e.eventType == 'm.room.message' &&
 | 
			
		||||
              e.content['content']['msgtype'] == Matrix.callNamespace &&
 | 
			
		||||
              e.content['sender'] != client.userID)
 | 
			
		||||
          .listen(onJitsiCall);
 | 
			
		||||
      onRoomKeyRequestSub ??=
 | 
			
		||||
          client.onRoomKeyRequest.stream.listen((RoomKeyRequest request) async {
 | 
			
		||||
        final Room room = request.room;
 | 
			
		||||
@ -368,6 +435,9 @@ class MatrixState extends State<Matrix> {
 | 
			
		||||
      client = widget.client;
 | 
			
		||||
    }
 | 
			
		||||
    if (client.storeAPI != null) {
 | 
			
		||||
      client.storeAPI
 | 
			
		||||
          .getItem("chat.fluffy.jitsi_instance")
 | 
			
		||||
          .then((final instance) => jitsiInstance = instance ?? jitsiInstance);
 | 
			
		||||
      client.storeAPI.getItem("chat.fluffy.wallpaper").then((final path) async {
 | 
			
		||||
        if (path == null) return;
 | 
			
		||||
        final file = File(path);
 | 
			
		||||
@ -382,6 +452,7 @@ class MatrixState extends State<Matrix> {
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    onRoomKeyRequestSub?.cancel();
 | 
			
		||||
    onJitsiCallSub?.cancel();
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -99,6 +99,19 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
          case MessageTypes.Notice:
 | 
			
		||||
          case MessageTypes.Emote:
 | 
			
		||||
          default:
 | 
			
		||||
            if (event.content['msgtype'] == Matrix.callNamespace) {
 | 
			
		||||
              return RaisedButton(
 | 
			
		||||
                color: Theme.of(context).backgroundColor,
 | 
			
		||||
                child: Row(
 | 
			
		||||
                  mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                  children: <Widget>[
 | 
			
		||||
                    Icon(Icons.phone),
 | 
			
		||||
                    Text(I18n.of(context).videoCall),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
                onPressed: () => launch(event.body),
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
            return LinkText(
 | 
			
		||||
              text: event.getLocalizedBody(context, hideReply: true),
 | 
			
		||||
              textStyle: TextStyle(
 | 
			
		||||
 | 
			
		||||
@ -371,6 +371,8 @@ class I18n {
 | 
			
		||||
 | 
			
		||||
  String get isTyping => Intl.message("is typing...");
 | 
			
		||||
 | 
			
		||||
  String get editJitsiInstance => Intl.message('Edit Jitsi instance');
 | 
			
		||||
 | 
			
		||||
  String joinedTheChat(String username) => Intl.message(
 | 
			
		||||
        "$username joined the chat",
 | 
			
		||||
        name: "joinedTheChat",
 | 
			
		||||
@ -714,6 +716,8 @@ class I18n {
 | 
			
		||||
 | 
			
		||||
  String get verify => Intl.message("Verify");
 | 
			
		||||
 | 
			
		||||
  String get videoCall => Intl.message('Video call');
 | 
			
		||||
 | 
			
		||||
  String get visibleForAllParticipants =>
 | 
			
		||||
      Intl.message("Visible for all participants");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,21 @@ class _SettingsState extends State<Settings> {
 | 
			
		||||
        AppRoute.defaultRoute(context, SignUp()), (r) => false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void setJitsiInstanceAction(BuildContext context) async {
 | 
			
		||||
    var jitsi = await SimpleDialogs(context).enterText(
 | 
			
		||||
      titleText: I18n.of(context).editJitsiInstance,
 | 
			
		||||
      hintText: Matrix.of(context).jitsiInstance,
 | 
			
		||||
      labelText: I18n.of(context).editJitsiInstance,
 | 
			
		||||
    );
 | 
			
		||||
    if (jitsi == null) return;
 | 
			
		||||
    if (!jitsi.endsWith('/')) {
 | 
			
		||||
      jitsi += '/';
 | 
			
		||||
    }
 | 
			
		||||
    final MatrixState matrix = Matrix.of(context);
 | 
			
		||||
    await matrix.client.storeAPI.setItem('chat.fluffy.jitsi_instance', jitsi);
 | 
			
		||||
    matrix.jitsiInstance = jitsi;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void setDisplaynameAction(BuildContext context) async {
 | 
			
		||||
    final String displayname = await SimpleDialogs(context).enterText(
 | 
			
		||||
      titleText: I18n.of(context).editDisplayname,
 | 
			
		||||
@ -205,6 +220,12 @@ class _SettingsState extends State<Settings> {
 | 
			
		||||
              subtitle: Text(profile?.displayname ?? client.userID.localpart),
 | 
			
		||||
              onTap: () => setDisplaynameAction(context),
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              trailing: Icon(Icons.phone),
 | 
			
		||||
              title: Text(I18n.of(context).editJitsiInstance),
 | 
			
		||||
              subtitle: Text(Matrix.of(context).jitsiInstance),
 | 
			
		||||
              onTap: () => setJitsiInstanceAction(context),
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              trailing: Icon(Icons.devices_other),
 | 
			
		||||
              title: Text(I18n.of(context).devices),
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										37
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								pubspec.lock
									
									
									
									
									
								
							@ -21,28 +21,28 @@ packages:
 | 
			
		||||
      name: archive
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.0.13"
 | 
			
		||||
    version: "2.0.11"
 | 
			
		||||
  args:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: args
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.6.0"
 | 
			
		||||
    version: "1.5.2"
 | 
			
		||||
  async:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: async
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.4.1"
 | 
			
		||||
    version: "2.4.0"
 | 
			
		||||
  boolean_selector:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: boolean_selector
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.0.0"
 | 
			
		||||
    version: "1.0.5"
 | 
			
		||||
  bubble:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -63,14 +63,14 @@ packages:
 | 
			
		||||
      name: charcode
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.1.3"
 | 
			
		||||
    version: "1.1.2"
 | 
			
		||||
  collection:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: collection
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.14.12"
 | 
			
		||||
    version: "1.14.11"
 | 
			
		||||
  convert:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -91,7 +91,7 @@ packages:
 | 
			
		||||
      name: crypto
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.4"
 | 
			
		||||
    version: "2.1.3"
 | 
			
		||||
  csslib:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -260,7 +260,7 @@ packages:
 | 
			
		||||
      name: image
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.12"
 | 
			
		||||
    version: "2.1.4"
 | 
			
		||||
  image_picker:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -274,7 +274,7 @@ packages:
 | 
			
		||||
      name: intl
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.16.1"
 | 
			
		||||
    version: "0.16.0"
 | 
			
		||||
  intl_translation:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -502,7 +502,7 @@ packages:
 | 
			
		||||
      name: quiver
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.3"
 | 
			
		||||
    version: "2.0.5"
 | 
			
		||||
  receive_sharing_intent:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -570,7 +570,7 @@ packages:
 | 
			
		||||
      name: source_span
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.7.0"
 | 
			
		||||
    version: "1.5.5"
 | 
			
		||||
  sqflite:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -619,21 +619,21 @@ packages:
 | 
			
		||||
      name: test
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.13.0"
 | 
			
		||||
    version: "1.9.4"
 | 
			
		||||
  test_api:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: test_api
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.2.15"
 | 
			
		||||
    version: "0.2.11"
 | 
			
		||||
  test_core:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: test_core
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.3.1"
 | 
			
		||||
    version: "0.2.15"
 | 
			
		||||
  typed_data:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -718,13 +718,6 @@ packages:
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.1.0"
 | 
			
		||||
  webkit_inspection_protocol:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: webkit_inspection_protocol
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.5.0+1"
 | 
			
		||||
  webview_flutter:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -738,7 +731,7 @@ packages:
 | 
			
		||||
      name: xml
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.6.1"
 | 
			
		||||
    version: "3.5.0"
 | 
			
		||||
  yaml:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user