From bfb9ee735133cc9899d158dea8228cff3842a038 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Nov 2023 17:36:00 +0100 Subject: [PATCH 01/20] feat: add button and redirect screen for media account --- assets/l10n/intl_en.arb | 23 ++- lib/pages/add_bridge/add_bridge_body.dart | 235 ++++++++++++++++++++++ lib/pages/chat_list/add_chat_network.dart | 45 +++++ lib/pages/chat_list/chat_list_body.dart | 9 + lib/widgets/layouts/empty_page.dart | 7 + pubspec.lock | 8 + pubspec.yaml | 3 + 7 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 lib/pages/add_bridge/add_bridge_body.dart create mode 100644 lib/pages/chat_list/add_chat_network.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 0c625e7478..849adfb7ec 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2550,5 +2550,26 @@ "editTodo": "Edit todo", "pleaseAddATitle": "Please add a title", "todoListChangedError": "Oops... The todo list has been changed while you edited it.", - "todosUnencrypted": "Please notice that todos are visible by everyone in the chat and are not end to end encrypted." + "todosUnencrypted": "Please notice that todos are visible by everyone in the chat and are not end to end encrypted.", + + + + + + + + "youDonTHaveConversation": "You don't have a conversation yet.", + "connectChatNetworks": "Connect your chat networks", + "addSocialMessagingAccounts": "Add your social messaging accounts", + "addSocialMessagingAccountsText": "Connect all your discussions!", + "connected": "Connected", + "notConnected": "Not connected", + "connectYourSocialAccount": "Login to your account", + "enterYourDetails": "Enter your details :", + "username": "Username", + "pleaseEnterYourUsername": "Please enter your username", + "password": "Password", + "pleaseEnterPassword": "Please enter your password", + "cancel": "Cancel", + "login": "Login" } diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart new file mode 100644 index 0000000000..109c8d28a0 --- /dev/null +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -0,0 +1,235 @@ +import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:icons_plus/icons_plus.dart'; +import 'package:matrix/matrix.dart'; + + +class SocialNetwork { + final Widget logo; // The path to social media image + final String name; // Social media name + final String chatBot; // ChatBot for send demand + bool connected; // An indicator if the user is logged in or not + + SocialNetwork({ + required this.logo, + required this.name, + required this.chatBot, + this.connected = false, + }); +} + + +class AddBridgeBody extends StatefulWidget { + final Client client; + const AddBridgeBody({super.key, required this.client}); + + @override + State createState() => _AddBridgeBodyState(); +} + + + +class _AddBridgeBodyState extends State { + + + @override + Widget build(BuildContext context) { + + final List socialNetwork = [ + SocialNetwork( + logo: Logo(Logos.facebook_messenger), + name: "Facebook Messenger", + chatBot: "", + ), + + SocialNetwork( + logo: Logo(Logos.instagram), + name: "Instagram", + chatBot: "@instagrambot:loveto.party", + ), + + SocialNetwork( + logo: Logo(Logos.whatsapp), + name: "Whatsapp", + chatBot: "", + ), + ]; + + void showNetworkDialog(BuildContext context, SocialNetwork network) { + String? username; + String? password; + final GlobalKey formKey = GlobalKey(); + + showDialog( + context: context, + builder: (BuildContext context) { + return SingleChildScrollView( + child: + AlertDialog( + title: Text( + "${L10n.of(context)!.connectYourSocialAccount} ${network.name}", + style: TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.bold, + color: Color(0xFFFAAB22), + ), + ), + content: Form( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(L10n.of(context)!.enterYourDetails), + const SizedBox( + height: 5, + ), + TextFormField( + decoration: InputDecoration(labelText: L10n.of(context)!.username), + validator: (value) { + if (value!.isEmpty) { + return L10n.of(context)!.pleaseEnterYourUsername; + } + return null; + }, + onSaved: (value) { + setState(() { + username = value; + }); + }, + ), + const SizedBox( + height: 10, + ), + TextFormField( + decoration: InputDecoration(labelText: L10n.of(context)!.password), + obscureText: true, + enableSuggestions: false, + autocorrect: false, + validator: (value) { + if (value!.isEmpty) { + return L10n.of(context)!.pleaseEnterPassword; + } + return null; + }, + onSaved: (value) { + setState(() { + password = value; + }); + }, + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text(L10n.of(context)!.cancel), + ), + TextButton( + onPressed: () async { + if (formKey.currentState!.validate()) { + try { + + Navigator.of(context).pop(); // To close ShowDialog + } catch (e) { + + Navigator.of(context).pop(); + // Display an error message + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Error'), + content: Text('An error has occurred: $e'), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('OK'), + ), + ], + ); + }, + ); + } + } + }, + child: Text(L10n.of(context)!.login), + ), + ], + ), + ); + }, + ); + } + + return Scaffold( + appBar: AppBar(), + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + L10n.of(context)!.addSocialMessagingAccounts, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 24.0, + fontWeight: FontWeight.bold, + color: Color(0xFFFAAB22), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + L10n.of(context)!.addSocialMessagingAccountsText, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16.0), + ), + ), + Center( + child: SizedBox( + width: PlatformInfos.isWeb ?MediaQuery.of(context).size.width/2 :null, + child: ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: socialNetwork.length, + itemBuilder: (BuildContext context, int index) { + return ListTile( + leading: socialNetwork[index].logo, + title: Text( + socialNetwork[index].name, + ), + subtitle: Text( + socialNetwork[index].connected + ?L10n.of(context)!.connected :L10n.of(context)!.notConnected, + style: TextStyle( + color: socialNetwork[index].connected + ?Colors.green :Colors.grey + ), + ), + trailing: const Icon( + CupertinoIcons.right_chevron, + ), + onTap: (){ + showNetworkDialog(context, socialNetwork[index]); + }, + ); + }, + ), + ), + ), + ], + ), + ), + ); + } +} + diff --git a/lib/pages/chat_list/add_chat_network.dart b/lib/pages/chat_list/add_chat_network.dart new file mode 100644 index 0000000000..aeb81249c9 --- /dev/null +++ b/lib/pages/chat_list/add_chat_network.dart @@ -0,0 +1,45 @@ +import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import '../../widgets/matrix.dart'; +import '../add_bridge/add_bridge_body.dart'; + +class AddChatNetwork extends StatelessWidget { + const AddChatNetwork({super.key}); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: MediaQuery.of(context).size.height / 3.5, + width: PlatformInfos.isWeb ?MediaQuery.of(context).size.width/2 :null, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + if(!PlatformInfos.isWeb) + Text( + L10n.of(context)!.youDonTHaveConversation, + ), + ElevatedButton( + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) { + return AddBridgeBody(client: Matrix.of(context).client,); + }, + ), + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFFEEA77), + ), + child: Text( + L10n.of(context)!.connectChatNetworks, + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart index 520adaaa3d..527227eb7a 100644 --- a/lib/pages/chat_list/chat_list_body.dart +++ b/lib/pages/chat_list/chat_list_body.dart @@ -1,4 +1,6 @@ +import 'package:fluffychat/pages/chat_list/add_chat_network.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:animations/animations.dart'; @@ -18,6 +20,7 @@ import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/public_room_bottom_sheet.dart'; import '../../config/themes.dart'; +import '../../utils/platform_infos.dart'; import '../../widgets/connection_status_header.dart'; import '../../widgets/matrix.dart'; import 'chat_list_header.dart'; @@ -295,6 +298,12 @@ class ChatListViewBody extends StatelessWidget { childCount: rooms.length, ), ), + if( + client.prevBatch != null && !PlatformInfos.isWeb && client.rooms.isEmpty + ) //Change to client.rooms.isEmpty for final version !!!!!!!!! + const SliverToBoxAdapter( + child: AddChatNetwork(), + ), ], ), ); diff --git a/lib/widgets/layouts/empty_page.dart b/lib/widgets/layouts/empty_page.dart index a43f96df20..3313adebe2 100644 --- a/lib/widgets/layouts/empty_page.dart +++ b/lib/widgets/layouts/empty_page.dart @@ -1,7 +1,10 @@ import 'dart:math'; +import 'package:fluffychat/pages/chat_list/add_chat_network.dart'; import 'package:flutter/material.dart'; +import '../../utils/platform_infos.dart'; + class EmptyPage extends StatelessWidget { final bool loading; static const double _width = 300; @@ -38,6 +41,10 @@ class EmptyPage extends StatelessWidget { child: const LinearProgressIndicator(), ), ), + + // Button for add bridge when no conversation + if(PlatformInfos.isWeb) + const AddChatNetwork(), ], ), ); diff --git a/pubspec.lock b/pubspec.lock index 818c6e09d8..34976a4f88 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -863,6 +863,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + icons_plus: + dependency: "direct main" + description: + name: icons_plus + sha256: b613ec1deac96e9ef5c8065f2d7325967818bc3f4e118a375e50292761849ea6 + url: "https://pub.dev" + source: hosted + version: "4.0.0" image: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b0bc68a818..5eec3434a8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -91,6 +91,9 @@ dependencies: wakelock_plus: ^1.1.3 webrtc_interface: ^1.0.13 + # Add for Tawkie + icons_plus: ^4.0.0 + dev_dependencies: dart_code_metrics: ^5.7.5 flutter_lints: ^3.0.0 From 534800960aadfe034fd53c582a00ba8d07569979 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Nov 2023 09:27:04 +0100 Subject: [PATCH 02/20] faet: add button redirect screen format fix --- lib/pages/add_bridge/add_bridge_body.dart | 40 ++++++++++------------- lib/pages/chat_list/add_chat_network.dart | 10 +++--- lib/pages/chat_list/chat_list_body.dart | 7 ++-- lib/widgets/layouts/empty_page.dart | 3 +- 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index 109c8d28a0..c91bd5d2b8 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:icons_plus/icons_plus.dart'; import 'package:matrix/matrix.dart'; +import '../../widgets/matrix.dart'; class SocialNetwork { final Widget logo; // The path to social media image @@ -20,7 +21,6 @@ class SocialNetwork { }); } - class AddBridgeBody extends StatefulWidget { final Client client; const AddBridgeBody({super.key, required this.client}); @@ -29,27 +29,20 @@ class AddBridgeBody extends StatefulWidget { State createState() => _AddBridgeBodyState(); } - - class _AddBridgeBodyState extends State { - - @override Widget build(BuildContext context) { - final List socialNetwork = [ SocialNetwork( logo: Logo(Logos.facebook_messenger), name: "Facebook Messenger", chatBot: "", ), - SocialNetwork( logo: Logo(Logos.instagram), name: "Instagram", chatBot: "@instagrambot:loveto.party", ), - SocialNetwork( logo: Logo(Logos.whatsapp), name: "Whatsapp", @@ -66,8 +59,7 @@ class _AddBridgeBodyState extends State { context: context, builder: (BuildContext context) { return SingleChildScrollView( - child: - AlertDialog( + child: AlertDialog( title: Text( "${L10n.of(context)!.connectYourSocialAccount} ${network.name}", style: TextStyle( @@ -86,7 +78,8 @@ class _AddBridgeBodyState extends State { height: 5, ), TextFormField( - decoration: InputDecoration(labelText: L10n.of(context)!.username), + decoration: InputDecoration( + labelText: L10n.of(context)!.username), validator: (value) { if (value!.isEmpty) { return L10n.of(context)!.pleaseEnterYourUsername; @@ -103,7 +96,8 @@ class _AddBridgeBodyState extends State { height: 10, ), TextFormField( - decoration: InputDecoration(labelText: L10n.of(context)!.password), + decoration: InputDecoration( + labelText: L10n.of(context)!.password), obscureText: true, enableSuggestions: false, autocorrect: false, @@ -133,18 +127,16 @@ class _AddBridgeBodyState extends State { onPressed: () async { if (formKey.currentState!.validate()) { try { - Navigator.of(context).pop(); // To close ShowDialog } catch (e) { - Navigator.of(context).pop(); // Display an error message showDialog( context: context, builder: (BuildContext context) { return AlertDialog( - title: Text('Error'), - content: Text('An error has occurred: $e'), + title: Text('Erreur'), + content: Text('Une erreur s\'est produite : $e'), actions: [ TextButton( onPressed: () { @@ -196,7 +188,9 @@ class _AddBridgeBodyState extends State { ), Center( child: SizedBox( - width: PlatformInfos.isWeb ?MediaQuery.of(context).size.width/2 :null, + width: PlatformInfos.isWeb + ? MediaQuery.of(context).size.width / 2 + : null, child: ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -209,16 +203,17 @@ class _AddBridgeBodyState extends State { ), subtitle: Text( socialNetwork[index].connected - ?L10n.of(context)!.connected :L10n.of(context)!.notConnected, + ? L10n.of(context)!.connected + : L10n.of(context)!.notConnected, style: TextStyle( - color: socialNetwork[index].connected - ?Colors.green :Colors.grey - ), + color: socialNetwork[index].connected + ? Colors.green + : Colors.grey), ), trailing: const Icon( CupertinoIcons.right_chevron, ), - onTap: (){ + onTap: () { showNetworkDialog(context, socialNetwork[index]); }, ); @@ -232,4 +227,3 @@ class _AddBridgeBodyState extends State { ); } } - diff --git a/lib/pages/chat_list/add_chat_network.dart b/lib/pages/chat_list/add_chat_network.dart index aeb81249c9..1c64b1aebb 100644 --- a/lib/pages/chat_list/add_chat_network.dart +++ b/lib/pages/chat_list/add_chat_network.dart @@ -11,12 +11,12 @@ class AddChatNetwork extends StatelessWidget { Widget build(BuildContext context) { return SizedBox( height: MediaQuery.of(context).size.height / 3.5, - width: PlatformInfos.isWeb ?MediaQuery.of(context).size.width/2 :null, + width: PlatformInfos.isWeb ? MediaQuery.of(context).size.width / 2 : null, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - if(!PlatformInfos.isWeb) + if (!PlatformInfos.isWeb) Text( L10n.of(context)!.youDonTHaveConversation, ), @@ -25,7 +25,9 @@ class AddChatNetwork extends StatelessWidget { Navigator.of(context).push( MaterialPageRoute( builder: (context) { - return AddBridgeBody(client: Matrix.of(context).client,); + return AddBridgeBody( + client: Matrix.of(context).client, + ); }, ), ); @@ -42,4 +44,4 @@ class AddChatNetwork extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart index 527227eb7a..7b39166329 100644 --- a/lib/pages/chat_list/chat_list_body.dart +++ b/lib/pages/chat_list/chat_list_body.dart @@ -298,9 +298,10 @@ class ChatListViewBody extends StatelessWidget { childCount: rooms.length, ), ), - if( - client.prevBatch != null && !PlatformInfos.isWeb && client.rooms.isEmpty - ) //Change to client.rooms.isEmpty for final version !!!!!!!!! + if (client.prevBatch != null && + !PlatformInfos.isWeb && + client.rooms + .isEmpty) //Change to client.rooms.isEmpty for final version const SliverToBoxAdapter( child: AddChatNetwork(), ), diff --git a/lib/widgets/layouts/empty_page.dart b/lib/widgets/layouts/empty_page.dart index 3313adebe2..c7faf62064 100644 --- a/lib/widgets/layouts/empty_page.dart +++ b/lib/widgets/layouts/empty_page.dart @@ -43,8 +43,7 @@ class EmptyPage extends StatelessWidget { ), // Button for add bridge when no conversation - if(PlatformInfos.isWeb) - const AddChatNetwork(), + if (PlatformInfos.isWeb) const AddChatNetwork(), ], ), ); From 6466a7a5871d6c94541de6143b4436a10034319d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Nov 2023 15:31:47 +0100 Subject: [PATCH 03/20] feat: delete laudry skip arb --- assets/l10n/intl_en.arb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 849adfb7ec..3c4968f0b9 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2551,13 +2551,6 @@ "pleaseAddATitle": "Please add a title", "todoListChangedError": "Oops... The todo list has been changed while you edited it.", "todosUnencrypted": "Please notice that todos are visible by everyone in the chat and are not end to end encrypted.", - - - - - - - "youDonTHaveConversation": "You don't have a conversation yet.", "connectChatNetworks": "Connect your chat networks", "addSocialMessagingAccounts": "Add your social messaging accounts", From 5a9b8f45d31d8fcb5c7412d6004200f4008c07f6 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Nov 2023 17:56:47 +0100 Subject: [PATCH 04/20] feat: add first connextion logic insta --- assets/l10n/intl_en.arb | 10 +- lib/config/app_config.dart | 2 +- lib/pages/add_bridge/add_bridge_body.dart | 268 ++++++------------ lib/pages/add_bridge/add_bridge_header.dart | 30 ++ .../add_bridge/connection_bridge_dialog.dart | 140 +++++++++ .../add_bridge/error_message_dialog.dart | 99 +++++++ .../add_bridge/model/social_network.dart | 34 +++ .../service/bot_bridge_connection.dart | 213 ++++++++++++++ lib/pages/add_bridge/show_bottom_sheet.dart | 60 ++++ lib/pages/chat_list/add_chat_network.dart | 7 +- pubspec.lock | 2 +- pubspec.yaml | 1 + 12 files changed, 678 insertions(+), 188 deletions(-) create mode 100644 lib/pages/add_bridge/add_bridge_header.dart create mode 100644 lib/pages/add_bridge/connection_bridge_dialog.dart create mode 100644 lib/pages/add_bridge/error_message_dialog.dart create mode 100644 lib/pages/add_bridge/model/social_network.dart create mode 100644 lib/pages/add_bridge/service/bot_bridge_connection.dart create mode 100644 lib/pages/add_bridge/show_bottom_sheet.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 3c4968f0b9..68469c65d7 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2559,10 +2559,10 @@ "notConnected": "Not connected", "connectYourSocialAccount": "Login to your account", "enterYourDetails": "Enter your details :", - "username": "Username", - "pleaseEnterYourUsername": "Please enter your username", - "password": "Password", "pleaseEnterPassword": "Please enter your password", - "cancel": "Cancel", - "login": "Login" + "usernameNotFound": "The username you entered doesn't appear to belong to an account. Please check your username and try again.", + "passwordIncorrect": "Password incorrect", + "rateLimit": "An error has occurred, please wait a few minutes before try again", + "err_": "Error", + "err_desc": "An error has occurred:" } diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 75fa52a1ba..8958ecedfb 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -3,7 +3,7 @@ import 'dart:ui'; import 'package:matrix/matrix.dart'; abstract class AppConfig { - static String _applicationName = 'FluffyChat'; + static String _applicationName = 'Tawkie'; static String get applicationName => _applicationName; static String? _applicationWelcomeMessage; static String? get applicationWelcomeMessage => _applicationWelcomeMessage; diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index c91bd5d2b8..0ddd7b0929 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -1,26 +1,17 @@ +import 'package:fluffychat/pages/add_bridge/service/bot_bridge_connection.dart'; +import 'package:fluffychat/pages/add_bridge/show_bottom_sheet.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:icons_plus/icons_plus.dart'; import 'package:matrix/matrix.dart'; -import '../../widgets/matrix.dart'; - -class SocialNetwork { - final Widget logo; // The path to social media image - final String name; // Social media name - final String chatBot; // ChatBot for send demand - bool connected; // An indicator if the user is logged in or not - - SocialNetwork({ - required this.logo, - required this.name, - required this.chatBot, - this.connected = false, - }); -} +import 'add_bridge_header.dart'; +import 'connection_bridge_dialog.dart'; +import 'model/social_network.dart'; +// Page offering brigde bot connections to social network chats +// Takes the user's Client ( Matrix ) as parameter class AddBridgeBody extends StatefulWidget { final Client client; const AddBridgeBody({super.key, required this.client}); @@ -30,135 +21,29 @@ class AddBridgeBody extends StatefulWidget { } class _AddBridgeBodyState extends State { + + bool instagramConnected = false; + bool loadingInstagram = true; + + late BotBridgeConnection botConnection; + @override - Widget build(BuildContext context) { - final List socialNetwork = [ - SocialNetwork( - logo: Logo(Logos.facebook_messenger), - name: "Facebook Messenger", - chatBot: "", - ), - SocialNetwork( - logo: Logo(Logos.instagram), - name: "Instagram", - chatBot: "@instagrambot:loveto.party", - ), - SocialNetwork( - logo: Logo(Logos.whatsapp), - name: "Whatsapp", - chatBot: "", - ), - ]; + void initState() { + botConnection = BotBridgeConnection(client: widget.client); + super.initState(); + _initStateAsync(); + } - void showNetworkDialog(BuildContext context, SocialNetwork network) { - String? username; - String? password; - final GlobalKey formKey = GlobalKey(); + // Online status update when page is opened + Future _initStateAsync() async { + instagramConnected = await botConnection.instagramPing(); + setState(() { + loadingInstagram = false; + }); + } - showDialog( - context: context, - builder: (BuildContext context) { - return SingleChildScrollView( - child: AlertDialog( - title: Text( - "${L10n.of(context)!.connectYourSocialAccount} ${network.name}", - style: TextStyle( - fontSize: 20.0, - fontWeight: FontWeight.bold, - color: Color(0xFFFAAB22), - ), - ), - content: Form( - key: formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text(L10n.of(context)!.enterYourDetails), - const SizedBox( - height: 5, - ), - TextFormField( - decoration: InputDecoration( - labelText: L10n.of(context)!.username), - validator: (value) { - if (value!.isEmpty) { - return L10n.of(context)!.pleaseEnterYourUsername; - } - return null; - }, - onSaved: (value) { - setState(() { - username = value; - }); - }, - ), - const SizedBox( - height: 10, - ), - TextFormField( - decoration: InputDecoration( - labelText: L10n.of(context)!.password), - obscureText: true, - enableSuggestions: false, - autocorrect: false, - validator: (value) { - if (value!.isEmpty) { - return L10n.of(context)!.pleaseEnterPassword; - } - return null; - }, - onSaved: (value) { - setState(() { - password = value; - }); - }, - ), - ], - ), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text(L10n.of(context)!.cancel), - ), - TextButton( - onPressed: () async { - if (formKey.currentState!.validate()) { - try { - Navigator.of(context).pop(); // To close ShowDialog - } catch (e) { - Navigator.of(context).pop(); - // Display an error message - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text('Erreur'), - content: Text('Une erreur s\'est produite : $e'), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text('OK'), - ), - ], - ); - }, - ); - } - } - }, - child: Text(L10n.of(context)!.login), - ), - ], - ), - ); - }, - ); - } + @override + Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), @@ -166,56 +51,30 @@ class _AddBridgeBodyState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: Text( - L10n.of(context)!.addSocialMessagingAccounts, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 24.0, - fontWeight: FontWeight.bold, - color: Color(0xFFFAAB22), - ), - ), - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: Text( - L10n.of(context)!.addSocialMessagingAccountsText, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 16.0), - ), - ), + buildHeaderBridgeText(context), + buildHeaderBridgeSubText(context), Center( child: SizedBox( - width: PlatformInfos.isWeb - ? MediaQuery.of(context).size.width / 2 - : null, + width: PlatformInfos.isWeb ?MediaQuery.of(context).size.width/2 :null, child: ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: socialNetwork.length, itemBuilder: (BuildContext context, int index) { + return ListTile( leading: socialNetwork[index].logo, title: Text( socialNetwork[index].name, ), - subtitle: Text( - socialNetwork[index].connected - ? L10n.of(context)!.connected - : L10n.of(context)!.notConnected, - style: TextStyle( - color: socialNetwork[index].connected - ? Colors.green - : Colors.grey), - ), + // Different build of subtle depending on the social network, for now only Instagram + subtitle: buildSubtitle(socialNetwork[index]), trailing: const Icon( CupertinoIcons.right_chevron, ), - onTap: () { - showNetworkDialog(context, socialNetwork[index]); - }, + + // Different ways of connecting and disconnecting depending on the social network + onTap: () => handleSocialNetworkAction(socialNetwork[index]), ); }, ), @@ -226,4 +85,57 @@ class _AddBridgeBodyState extends State { ), ); } -} + + // Different ways of connecting and disconnecting depending on the social network, for now only Instagram + void handleSocialNetworkAction(SocialNetwork network) async { + if(network.name == "Instagram"){ + if (loadingInstagram == false) { + if (instagramConnected != true) { + // Trying to connect to Instagram + final bool success = await connectToInstagram(context, network, botConnection); + if (success) { + setState(() { + instagramConnected = true; + }); + } + } else { + // Disconnect button, for the moment only this choice + final bool success = await showBottomSheetBridge( + context, + network, + botConnection, + ); + + if (success) { + setState(() { + instagramConnected = false; + }); + } + } + } + } + } + + // Different build of subtle depending on the social network, for now only Instagram + Widget buildSubtitle(SocialNetwork network) { + if (loadingInstagram && network.name == "Instagram") { + return const Align( + alignment: Alignment.centerLeft, + child: CircularProgressIndicator( + color: Colors.grey, + ), + ); + } else { + return Text( + network.name == "Instagram" && instagramConnected + ? L10n.of(context)!.connected + : L10n.of(context)!.notConnected, + style: TextStyle( + color: network.name == "Instagram" && instagramConnected + ? Colors.green + : Colors.grey, + ), + ); + } + } +} \ No newline at end of file diff --git a/lib/pages/add_bridge/add_bridge_header.dart b/lib/pages/add_bridge/add_bridge_header.dart new file mode 100644 index 0000000000..e3a83a89ba --- /dev/null +++ b/lib/pages/add_bridge/add_bridge_header.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +// AddBridge page title and subtitle + +Widget buildHeaderBridgeText(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + L10n.of(context)!.addSocialMessagingAccounts, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 24.0, + fontWeight: FontWeight.bold, + // color: Color(0xFFFAAB22), + ), + ), + ); +} + +Widget buildHeaderBridgeSubText(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + L10n.of(context)!.addSocialMessagingAccountsText, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16.0), + ), + ); +} \ No newline at end of file diff --git a/lib/pages/add_bridge/connection_bridge_dialog.dart b/lib/pages/add_bridge/connection_bridge_dialog.dart new file mode 100644 index 0000000000..e3009d799b --- /dev/null +++ b/lib/pages/add_bridge/connection_bridge_dialog.dart @@ -0,0 +1,140 @@ +import 'dart:async'; + +import 'package:fluffychat/pages/add_bridge/service/bot_bridge_connection.dart'; +import 'package:flutter/material.dart'; +import 'package:future_loading_dialog/future_loading_dialog.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'error_message_dialog.dart'; +import 'model/social_network.dart'; + +// Creation of a FormKey for entering identifiers for Connection ShowDialog +GlobalKey formKey = GlobalKey(); + +// ShowDialog for Instagram connection +Future connectToInstagram(BuildContext context, SocialNetwork network, BotBridgeConnection botConnection) async { + String? username; + String? password; + + final Completer completer = Completer(); + + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return Center( + child: SingleChildScrollView( + child: AlertDialog( + title: Text( + "${L10n.of(context)!.connectYourSocialAccount} ${network.name}", + style: const TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.bold, + // color: Color(0xFFFAAB22), + ), + ), + content: Form( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(L10n.of(context)!.enterYourDetails), + const SizedBox(height: 5), + TextFormField( + decoration: InputDecoration(labelText: L10n.of(context)!.username), + validator: (value) { + if (value!.isEmpty) { + return L10n.of(context)!.pleaseEnterYourUsername; + } + return null; + }, + onSaved: (value) { + username = value; // Saves the value in the username variable + }, + ), + const SizedBox(height: 10), + TextFormField( + decoration: InputDecoration(labelText: L10n.of(context)!.password), + obscureText: true, + enableSuggestions: false, + autocorrect: false, + validator: (value) { + if (value!.isEmpty) { + return L10n.of(context)!.pleaseEnterPassword; + } + return null; + }, + onSaved: (value) { + password = value; // Saves the value in the password variable + }, + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + completer.complete(false); + }, + child: Text(L10n.of(context)!.cancel), + ), + TextButton( + onPressed: () async { + if (formKey.currentState!.validate()) { + formKey.currentState!.save(); // Save form values + + try { + String result = ""; // Variable to store the result of the connection + + // To show Loading while executing the function + await showFutureLoadingDialog( + context: context, + future: () async { + + if (network.name == "Instagram"){ + result = await botConnection.createBridgeInstagram(username!, password!); + } + }, + ); + + if(result == "success"){ + Navigator.of(context).pop(); + completer.complete(true); // returns True if the connection is successful + + }else if(result == "errorUsername"){ + // Display a showDialog with an error message related to the identifier + showErrorUsernameDialog(context); + + }else if(result == "errorPassword"){ + // Display a showDialog with an error message related to the password + showErrorPasswordDialog(context); + + }else if(result == "rateLimitError"){ + // Display a showDialog with an error message related to the rate limit + showRateLimitDialog(context); + + } + } catch (e) { + Navigator.of(context).pop(); + //To view other catch-related errors + showCatchErrorDialog(context, e); + } + } + }, + child: Text( + L10n.of(context)!.login, + style: const TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ), + ); + }, + ); + + return completer.future; +} diff --git a/lib/pages/add_bridge/error_message_dialog.dart b/lib/pages/add_bridge/error_message_dialog.dart new file mode 100644 index 0000000000..ff0224ca48 --- /dev/null +++ b/lib/pages/add_bridge/error_message_dialog.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +// Display a showDialog with an error message related to the identifier +void showErrorUsernameDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: Text( + L10n.of(context)!.usernameNotFound, + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text( + L10n.of(context)!.ok, + ), + ), + ], + ); + }, + ); +} + +// Display a showDialog with an error message related to the password +void showErrorPasswordDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: Text( + L10n.of(context)!.passwordIncorrect, + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text( + L10n.of(context)!.ok, + ), + ), + ], + ); + }, + ); +} + +// Display a showDialog with an error message related to the rate limit +void showRateLimitDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: Text( + L10n.of(context)!.rateLimit, + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text( + L10n.of(context)!.ok, + ), + ), + ], + ); + }, + ); +} + +// To view other catch-related errors +void showCatchErrorDialog(BuildContext context, Object e) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + L10n.of(context)!.err_, + ), + content: Text('${L10n.of(context)!.err_desc} $e'), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text( + L10n.of(context)!.ok, + ), + ), + ], + ); + }, + ); +} diff --git a/lib/pages/add_bridge/model/social_network.dart b/lib/pages/add_bridge/model/social_network.dart new file mode 100644 index 0000000000..94d15eeaf4 --- /dev/null +++ b/lib/pages/add_bridge/model/social_network.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:icons_plus/icons_plus.dart'; + +class SocialNetwork { + final Widget logo; // The path to social media image + final String name; // Social media name + final String chatBot; // ChatBot for send demand + + SocialNetwork({ + required this.logo, + required this.name, + required this.chatBot, + }); +} + +final List socialNetwork = [ + SocialNetwork( + logo: Logo(Logos.facebook_messenger), + name: "Facebook Messenger", + chatBot: "", + ), + + SocialNetwork( + logo: Logo(Logos.instagram), + name: "Instagram", + chatBot: "@instagrambot:loveto.party", + ), + + SocialNetwork( + logo: Logo(Logos.whatsapp), + name: "Whatsapp", + chatBot: "", + ), +]; \ No newline at end of file diff --git a/lib/pages/add_bridge/service/bot_bridge_connection.dart b/lib/pages/add_bridge/service/bot_bridge_connection.dart new file mode 100644 index 0000000000..a7f1fa0f8a --- /dev/null +++ b/lib/pages/add_bridge/service/bot_bridge_connection.dart @@ -0,0 +1,213 @@ +import 'package:matrix/matrix.dart'; +import 'package:uuid/uuid.dart'; + +class BotBridgeConnection { + Client client; + + BotBridgeConnection({ + required this.client, + }); + + // Ping to find out if we're connected to Instagram + Future instagramPing() async { + const String botUserId = '@instagrambot:loveto.party'; + + // Message to spot when we're online + final RegExp onlineMatch = RegExp(r"MQTT connection is active"); + final RegExp successfullyMatch = RegExp(r"Successfully logged in"); + final RegExp alreadySuccessMatch = RegExp(r"You're already logged in"); + + // Message to spot when we're not online + final RegExp notLoggedMatch = RegExp(r"You're not logged into Instagram"); + + // Add a direct chat with the Instagram bot (if you haven't already) + String? directChat = client.getDirectChatFromUserId(botUserId); + directChat ??= await client.startDirectChat(botUserId); + + bool result = false; // Variable to track the result of the connection + + // Get the latest messages from the room (limited to the specified number) + while (true) { + final GetRoomEventsResponse response = await client.getRoomEvents( + directChat, + Direction.b, // To get the latest messages + limit: 1, // Number of messages to obtain + ); + + final List latestMessages = response.chunk ?? []; + + if (latestMessages.isNotEmpty) { + final String latestMessage = latestMessages.first.content['body'].toString() ?? + ''; + + // to find out if we're connected + if (!onlineMatch.hasMatch(latestMessage) && !notLoggedMatch.hasMatch(latestMessage) && !alreadySuccessMatch.hasMatch(latestMessage) && !successfullyMatch.hasMatch(latestMessage)) { + + // Send the "ping" message to the bot + final Map messageBody = { + 'msgtype': 'm.text', + 'body': "ping", + }; + await client.sendMessage( + directChat, + 'm.room.message', + const Uuid().v4(),// Generate random txnId + messageBody, + ); + await Future.delayed(const Duration(seconds: 1)); // Wait 2 sec + + } else if(onlineMatch.hasMatch(latestMessage) || alreadySuccessMatch.hasMatch(latestMessage) || successfullyMatch.hasMatch(latestMessage)){ + + print("You're logged"); + + result = true; + + break; // Exit the loop if bridge is connected + }else if(notLoggedMatch.hasMatch(latestMessage)){ + print('Not connected'); + + break; // Exit the loop if bridge is disconnected + } + } + } + return result; + } + + // Function for create and login bridge with bot + Future createBridgeInstagram(String username, String password) async { + const String botUserId = '@instagrambot:loveto.party'; + + // Success phrases to spot + final RegExp successMatch = RegExp(r"Successfully logged in"); + final RegExp alreadySuccessMatch = RegExp(r"You're already logged in"); + + // Error phrase to spot + final RegExp usernameErrorMatch = RegExp(r"Please check your username and try again"); + final RegExp passwordErrorMatch = RegExp(r"Incorrect password"); + final RegExp rateLimitErrorMatch = RegExp(r"rate_limit_error"); + + // Add a direct chat with the Instagram bot (if you haven't already) + String? directChat = client.getDirectChatFromUserId(botUserId); + directChat ??= await client.startDirectChat(botUserId); + + String result = ""; // Variable to track the result of the connection + + + // Get the latest messages from the room (limited to the specified number) + while (true) { + + // Send the "login" message to the bot + final Map messageBody = { + 'msgtype': 'm.text', + 'body': "login $username $password", + }; + await client.sendMessage( + directChat, + 'm.room.message', + const Uuid().v4(),// Generate random txnId + messageBody, + ); + await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec + + final GetRoomEventsResponse response = await client.getRoomEvents( + directChat, + Direction.b, // To get the latest messages + limit: 1, // Number of messages to obtain + ); + + final List latestMessages = response.chunk ?? []; + final String latestMessage = latestMessages.first.content['body'].toString() ?? + ''; + + if (latestMessages.isNotEmpty) { + + if(successMatch.hasMatch(latestMessage) || alreadySuccessMatch.hasMatch(latestMessage)){ + print("You're logged to Instagram"); + + result = "success"; + + break; // Exit the loop once the "login" message has been sent and is success + + }else if(!successMatch.hasMatch(latestMessage) && usernameErrorMatch.hasMatch(latestMessage)){ + print("Login cannot be found"); + + result = "errorUsername"; + + break; + + }else if(passwordErrorMatch.hasMatch(latestMessage)){ + + print("Password incorrect"); + + result = "errorPassword"; + + break; + + }else if(rateLimitErrorMatch.hasMatch(latestMessage)){ + + print("rate limit error"); + + result = "rateLimitError"; + + break; + + } + } + } + return result; + } + + // To disconnect from Instagram + Future disconnectToInstagram() async { + const String botUserId = '@instagrambot:loveto.party'; + + final RegExp successMatch = RegExp(r"Successfully logged out"); + + // Add a direct chat with the Instagram bot (if you haven't already) + String? directChat = client.getDirectChatFromUserId(botUserId); + directChat ??= await client.startDirectChat(botUserId); + + bool result = true; // Variable to track the result of the connection + + // Get the latest messages from the room (limited to the specified number) + while (true) { + final GetRoomEventsResponse response = await client.getRoomEvents( + directChat, + Direction.b, // To get the latest messages + limit: 1, // Number of messages to obtain + ); + + final List latestMessages = response.chunk ?? []; + + if (latestMessages.isNotEmpty) { + final String latestMessage = latestMessages.first.content['body'].toString() ?? + ''; + + // to find out if we're connected + if (!successMatch.hasMatch(latestMessage)) { + + // Send the "logout" message to the bot + final Map messageBody = { + 'msgtype': 'm.text', + 'body': "logout", + }; + await client.sendMessage( + directChat, + 'm.room.message', + const Uuid().v4(),// Generate random txnId + messageBody, + ); + await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec + + } else if(successMatch.hasMatch(latestMessage)){ + print("You're disconnected"); + + result = false; + break; // Exit the loop if bridge is connected + } + } + } + return result; + } + +} \ No newline at end of file diff --git a/lib/pages/add_bridge/show_bottom_sheet.dart b/lib/pages/add_bridge/show_bottom_sheet.dart new file mode 100644 index 0000000000..0f67bc9f57 --- /dev/null +++ b/lib/pages/add_bridge/show_bottom_sheet.dart @@ -0,0 +1,60 @@ +import 'dart:async'; + +import 'package:fluffychat/pages/add_bridge/service/bot_bridge_connection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:future_loading_dialog/future_loading_dialog.dart'; +import 'error_message_dialog.dart'; +import 'model/social_network.dart'; + +// Disconnect button display +Future showBottomSheetBridge( + BuildContext context, + SocialNetwork network, + BotBridgeConnection botConnection, + ) async { + + final Completer completer = Completer(); + + showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + title: Text( + L10n.of(context)!.logout, + style: const TextStyle( + color: Colors.red, + ), + ), + onTap: () async { + try{ + bool result = true; + Navigator.pop(context); + + await showFutureLoadingDialog( + context: context, + future: () async { + if(network.name == "Instagram") result = await botConnection.disconnectToInstagram(); + + // Returns True if disconnection has been made + completer.complete(true); + }, + ); + }catch (e) { + Navigator.of(context).pop(); + //To view other catch-related errors + showCatchErrorDialog(context, e); + } + }, + ), + ], + ); + }, + ); + + return completer.future; +} + diff --git a/lib/pages/chat_list/add_chat_network.dart b/lib/pages/chat_list/add_chat_network.dart index 1c64b1aebb..d6ee43c1a0 100644 --- a/lib/pages/chat_list/add_chat_network.dart +++ b/lib/pages/chat_list/add_chat_network.dart @@ -4,6 +4,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import '../../widgets/matrix.dart'; import '../add_bridge/add_bridge_body.dart'; +// Shows a button redirecting to the bridge bots social network chat connection page class AddChatNetwork extends StatelessWidget { const AddChatNetwork({super.key}); @@ -32,9 +33,9 @@ class AddChatNetwork extends StatelessWidget { ), ); }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFFEEA77), - ), + // style: ElevatedButton.styleFrom( + // backgroundColor: const Color(0xFFFEEA77), + // ), child: Text( L10n.of(context)!.connectChatNetworks, ), diff --git a/pubspec.lock b/pubspec.lock index 34976a4f88..e522b0406d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1931,7 +1931,7 @@ packages: source: hosted version: "3.0.7" uuid: - dependency: transitive + dependency: "direct main" description: name: uuid sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" diff --git a/pubspec.yaml b/pubspec.yaml index 5eec3434a8..0619aca839 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -93,6 +93,7 @@ dependencies: # Add for Tawkie icons_plus: ^4.0.0 + uuid: ^3.0.7 dev_dependencies: dart_code_metrics: ^5.7.5 From c9a9575bb010cf32498aa0ef7eb2083c06e7084d Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Nov 2023 11:03:13 +0100 Subject: [PATCH 05/20] feat: add menu item to go bridge bot page when already conv --- assets/l10n/intl_en.arb | 3 ++- lib/config/routes.dart | 13 +++++++++++++ lib/pages/add_bridge/add_bridge_body.dart | 15 +++++++++------ lib/pages/chat_list/add_chat_network.dart | 12 +++--------- lib/pages/chat_list/client_chooser_button.dart | 17 +++++++++++++++++ 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 68469c65d7..b182dd5ffe 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2564,5 +2564,6 @@ "passwordIncorrect": "Password incorrect", "rateLimit": "An error has occurred, please wait a few minutes before try again", "err_": "Error", - "err_desc": "An error has occurred:" + "err_desc": "An error has occurred:", + "bridgeBot_menuItemTitle" : "Connected social networks" } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 9d6b837e03..187a408a9c 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -37,6 +37,8 @@ import 'package:fluffychat/widgets/layouts/two_column_layout.dart'; import 'package:fluffychat/widgets/log_view.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import '../pages/add_bridge/add_bridge_body.dart'; + abstract class AppRoutes { static FutureOr loggedInRedirect( BuildContext context, @@ -263,6 +265,17 @@ abstract class AppRoutes { ), ], ), + + // Route to social networking page via chat bot + // The entire path is: /rooms/settings/addbridgebot + GoRoute( + path: 'addbridgebot', + pageBuilder: (context, state) => defaultPageBuilder( + context, + const AddBridgeBody(), + ), + redirect: loggedOutRedirect, + ), GoRoute( path: 'security', redirect: loggedOutRedirect, diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index 0ddd7b0929..1e814959bc 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -4,17 +4,15 @@ import 'package:fluffychat/utils/platform_infos.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:matrix/matrix.dart'; +import '../../widgets/matrix.dart'; import 'add_bridge_header.dart'; import 'connection_bridge_dialog.dart'; import 'model/social_network.dart'; // Page offering brigde bot connections to social network chats -// Takes the user's Client ( Matrix ) as parameter class AddBridgeBody extends StatefulWidget { - final Client client; - const AddBridgeBody({super.key, required this.client}); + const AddBridgeBody({super.key,}); @override State createState() => _AddBridgeBodyState(); @@ -29,7 +27,8 @@ class _AddBridgeBodyState extends State { @override void initState() { - botConnection = BotBridgeConnection(client: widget.client); + final client = Matrix.of(context).client; + botConnection = BotBridgeConnection(client: client); super.initState(); _initStateAsync(); } @@ -46,11 +45,15 @@ class _AddBridgeBodyState extends State { Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(), + // The "go back" provided by the AppBar may no longer be useful now that this page opens with the settings page on Web + appBar: !PlatformInfos.isWeb ?AppBar() :null, body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ + // Small space for the web version + PlatformInfos.isWeb + ?const SizedBox(height: 20,) :Container(), buildHeaderBridgeText(context), buildHeaderBridgeSubText(context), Center( diff --git a/lib/pages/chat_list/add_chat_network.dart b/lib/pages/chat_list/add_chat_network.dart index d6ee43c1a0..45b121f54f 100644 --- a/lib/pages/chat_list/add_chat_network.dart +++ b/lib/pages/chat_list/add_chat_network.dart @@ -1,6 +1,7 @@ import 'package:fluffychat/utils/platform_infos.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:go_router/go_router.dart'; import '../../widgets/matrix.dart'; import '../add_bridge/add_bridge_body.dart'; @@ -23,15 +24,8 @@ class AddChatNetwork extends StatelessWidget { ), ElevatedButton( onPressed: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) { - return AddBridgeBody( - client: Matrix.of(context).client, - ); - }, - ), - ); + // Redirect to bot social network connection page via route + context.go('/rooms/settings/addbridgebot'); }, // style: ElevatedButton.styleFrom( // backgroundColor: const Color(0xFFFEEA77), diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index f2394bb693..aaa369a23c 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -162,6 +162,18 @@ class ClientChooserButton extends StatelessWidget { ], ), ), + + // PopupMenuItem to redirect to the social network connections page via bot bridge + PopupMenuItem( + value: SettingsAction.addBridgeBot, + child: Row( + children: [ + const Icon(Icons.account_tree_outlined), + const SizedBox(width: 18), + Text(L10n.of(context)!.bridgeBot_menuItemTitle), + ], + ), + ), ]; } @@ -278,6 +290,10 @@ class ClientChooserButton extends StatelessWidget { case SettingsAction.archive: context.go('/rooms/archive'); break; + // Redirect to bot social network connection page + case SettingsAction.addBridgeBot: + context.go('/rooms/settings/addbridgebot'); + break; } } } @@ -353,6 +369,7 @@ class ClientChooserButton extends StatelessWidget { } enum SettingsAction { + addBridgeBot, addAccount, newStory, newGroup, From c03eca667d69fc58206a207596c3db2ab100a508 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Nov 2023 14:05:15 +0100 Subject: [PATCH 06/20] feat: proposal to delete room after disconnected bridge --- assets/l10n/intl_en.arb | 4 +- lib/pages/add_bridge/add_bridge_body.dart | 32 +++++--- lib/pages/add_bridge/add_bridge_header.dart | 2 +- .../add_bridge/connection_bridge_dialog.dart | 39 ++++----- .../add_bridge/error_message_dialog.dart | 2 +- .../add_bridge/model/social_network.dart | 4 +- .../service/bot_bridge_connection.dart | 80 +++++++++++-------- lib/pages/add_bridge/show_bottom_sheet.dart | 18 ++--- .../show_delete_conversation_dialog.dart | 51 ++++++++++++ 9 files changed, 155 insertions(+), 77 deletions(-) create mode 100644 lib/pages/add_bridge/show_delete_conversation_dialog.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index b182dd5ffe..059ab372cf 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2565,5 +2565,7 @@ "rateLimit": "An error has occurred, please wait a few minutes before try again", "err_": "Error", "err_desc": "An error has occurred:", - "bridgeBot_menuItemTitle" : "Connected social networks" + "bridgeBot_menuItemTitle" : "Connected social networks", + "bridgeBot_deleteConvTitle": "Delete Conversation", + "bridgeBot_deleteConvDescription": "Do you want to delete the conversation with the bot?" } diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index 1e814959bc..a620a61eab 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -1,5 +1,6 @@ import 'package:fluffychat/pages/add_bridge/service/bot_bridge_connection.dart'; import 'package:fluffychat/pages/add_bridge/show_bottom_sheet.dart'; +import 'package:fluffychat/pages/add_bridge/show_delete_conversation_dialog.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter/cupertino.dart'; @@ -12,14 +13,15 @@ import 'model/social_network.dart'; // Page offering brigde bot connections to social network chats class AddBridgeBody extends StatefulWidget { - const AddBridgeBody({super.key,}); + const AddBridgeBody({ + super.key, + }); @override State createState() => _AddBridgeBodyState(); } class _AddBridgeBodyState extends State { - bool instagramConnected = false; bool loadingInstagram = true; @@ -43,28 +45,31 @@ class _AddBridgeBodyState extends State { @override Widget build(BuildContext context) { - return Scaffold( // The "go back" provided by the AppBar may no longer be useful now that this page opens with the settings page on Web - appBar: !PlatformInfos.isWeb ?AppBar() :null, + appBar: !PlatformInfos.isWeb ? AppBar() : null, body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Small space for the web version PlatformInfos.isWeb - ?const SizedBox(height: 20,) :Container(), + ? const SizedBox( + height: 20, + ) + : Container(), buildHeaderBridgeText(context), buildHeaderBridgeSubText(context), Center( child: SizedBox( - width: PlatformInfos.isWeb ?MediaQuery.of(context).size.width/2 :null, + width: PlatformInfos.isWeb + ? MediaQuery.of(context).size.width / 2 + : null, child: ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: socialNetwork.length, itemBuilder: (BuildContext context, int index) { - return ListTile( leading: socialNetwork[index].logo, title: Text( @@ -77,7 +82,8 @@ class _AddBridgeBodyState extends State { ), // Different ways of connecting and disconnecting depending on the social network - onTap: () => handleSocialNetworkAction(socialNetwork[index]), + onTap: () => + handleSocialNetworkAction(socialNetwork[index]), ); }, ), @@ -91,11 +97,12 @@ class _AddBridgeBodyState extends State { // Different ways of connecting and disconnecting depending on the social network, for now only Instagram void handleSocialNetworkAction(SocialNetwork network) async { - if(network.name == "Instagram"){ + if (network.name == "Instagram") { if (loadingInstagram == false) { if (instagramConnected != true) { // Trying to connect to Instagram - final bool success = await connectToInstagram(context, network, botConnection); + final bool success = + await connectToInstagram(context, network, botConnection); if (success) { setState(() { instagramConnected = true; @@ -114,6 +121,9 @@ class _AddBridgeBodyState extends State { instagramConnected = false; }); } + + // Show the dialog for deleting the conversation + await showDeleteConversationDialog(context, network, botConnection); } } } @@ -141,4 +151,4 @@ class _AddBridgeBodyState extends State { ); } } -} \ No newline at end of file +} diff --git a/lib/pages/add_bridge/add_bridge_header.dart b/lib/pages/add_bridge/add_bridge_header.dart index e3a83a89ba..b99725aa61 100644 --- a/lib/pages/add_bridge/add_bridge_header.dart +++ b/lib/pages/add_bridge/add_bridge_header.dart @@ -27,4 +27,4 @@ Widget buildHeaderBridgeSubText(BuildContext context) { style: const TextStyle(fontSize: 16.0), ), ); -} \ No newline at end of file +} diff --git a/lib/pages/add_bridge/connection_bridge_dialog.dart b/lib/pages/add_bridge/connection_bridge_dialog.dart index e3009d799b..e8bfa42347 100644 --- a/lib/pages/add_bridge/connection_bridge_dialog.dart +++ b/lib/pages/add_bridge/connection_bridge_dialog.dart @@ -11,7 +11,8 @@ import 'model/social_network.dart'; GlobalKey formKey = GlobalKey(); // ShowDialog for Instagram connection -Future connectToInstagram(BuildContext context, SocialNetwork network, BotBridgeConnection botConnection) async { +Future connectToInstagram(BuildContext context, SocialNetwork network, + BotBridgeConnection botConnection) async { String? username; String? password; @@ -40,7 +41,8 @@ Future connectToInstagram(BuildContext context, SocialNetwork network, Bot Text(L10n.of(context)!.enterYourDetails), const SizedBox(height: 5), TextFormField( - decoration: InputDecoration(labelText: L10n.of(context)!.username), + decoration: + InputDecoration(labelText: L10n.of(context)!.username), validator: (value) { if (value!.isEmpty) { return L10n.of(context)!.pleaseEnterYourUsername; @@ -48,12 +50,14 @@ Future connectToInstagram(BuildContext context, SocialNetwork network, Bot return null; }, onSaved: (value) { - username = value; // Saves the value in the username variable + username = + value; // Saves the value in the username variable }, ), const SizedBox(height: 10), TextFormField( - decoration: InputDecoration(labelText: L10n.of(context)!.password), + decoration: + InputDecoration(labelText: L10n.of(context)!.password), obscureText: true, enableSuggestions: false, autocorrect: false, @@ -64,7 +68,8 @@ Future connectToInstagram(BuildContext context, SocialNetwork network, Bot return null; }, onSaved: (value) { - password = value; // Saves the value in the password variable + password = + value; // Saves the value in the password variable }, ), ], @@ -84,35 +89,33 @@ Future connectToInstagram(BuildContext context, SocialNetwork network, Bot formKey.currentState!.save(); // Save form values try { - String result = ""; // Variable to store the result of the connection + String result = + ""; // Variable to store the result of the connection // To show Loading while executing the function await showFutureLoadingDialog( context: context, future: () async { - - if (network.name == "Instagram"){ - result = await botConnection.createBridgeInstagram(username!, password!); + if (network.name == "Instagram") { + result = await botConnection.createBridgeInstagram( + username!, password!); } }, ); - if(result == "success"){ + if (result == "success") { Navigator.of(context).pop(); - completer.complete(true); // returns True if the connection is successful - - }else if(result == "errorUsername"){ + completer.complete( + true); // returns True if the connection is successful + } else if (result == "errorUsername") { // Display a showDialog with an error message related to the identifier showErrorUsernameDialog(context); - - }else if(result == "errorPassword"){ + } else if (result == "errorPassword") { // Display a showDialog with an error message related to the password showErrorPasswordDialog(context); - - }else if(result == "rateLimitError"){ + } else if (result == "rateLimitError") { // Display a showDialog with an error message related to the rate limit showRateLimitDialog(context); - } } catch (e) { Navigator.of(context).pop(); diff --git a/lib/pages/add_bridge/error_message_dialog.dart b/lib/pages/add_bridge/error_message_dialog.dart index ff0224ca48..bfc2318cd8 100644 --- a/lib/pages/add_bridge/error_message_dialog.dart +++ b/lib/pages/add_bridge/error_message_dialog.dart @@ -89,7 +89,7 @@ void showCatchErrorDialog(BuildContext context, Object e) { Navigator.of(context).pop(); }, child: Text( - L10n.of(context)!.ok, + L10n.of(context)!.ok, ), ), ], diff --git a/lib/pages/add_bridge/model/social_network.dart b/lib/pages/add_bridge/model/social_network.dart index 94d15eeaf4..5143231f78 100644 --- a/lib/pages/add_bridge/model/social_network.dart +++ b/lib/pages/add_bridge/model/social_network.dart @@ -19,16 +19,14 @@ final List socialNetwork = [ name: "Facebook Messenger", chatBot: "", ), - SocialNetwork( logo: Logo(Logos.instagram), name: "Instagram", chatBot: "@instagrambot:loveto.party", ), - SocialNetwork( logo: Logo(Logos.whatsapp), name: "Whatsapp", chatBot: "", ), -]; \ No newline at end of file +]; diff --git a/lib/pages/add_bridge/service/bot_bridge_connection.dart b/lib/pages/add_bridge/service/bot_bridge_connection.dart index a7f1fa0f8a..19a54b723e 100644 --- a/lib/pages/add_bridge/service/bot_bridge_connection.dart +++ b/lib/pages/add_bridge/service/bot_bridge_connection.dart @@ -1,6 +1,8 @@ import 'package:matrix/matrix.dart'; import 'package:uuid/uuid.dart'; +// For all bot bridge conversations +// For the moment, rooms are DirectChat class BotBridgeConnection { Client client; @@ -19,6 +21,7 @@ class BotBridgeConnection { // Message to spot when we're not online final RegExp notLoggedMatch = RegExp(r"You're not logged into Instagram"); + final RegExp disconnectMatch = RegExp(r"Successfully logged out"); // Add a direct chat with the Instagram bot (if you haven't already) String? directChat = client.getDirectChatFromUserId(botUserId); @@ -37,12 +40,15 @@ class BotBridgeConnection { final List latestMessages = response.chunk ?? []; if (latestMessages.isNotEmpty) { - final String latestMessage = latestMessages.first.content['body'].toString() ?? - ''; + final String latestMessage = + latestMessages.first.content['body'].toString() ?? ''; // to find out if we're connected - if (!onlineMatch.hasMatch(latestMessage) && !notLoggedMatch.hasMatch(latestMessage) && !alreadySuccessMatch.hasMatch(latestMessage) && !successfullyMatch.hasMatch(latestMessage)) { - + if (!onlineMatch.hasMatch(latestMessage) && + !notLoggedMatch.hasMatch(latestMessage) && + !alreadySuccessMatch.hasMatch(latestMessage) && + !successfullyMatch.hasMatch(latestMessage) && + !disconnectMatch.hasMatch(latestMessage)) { // Send the "ping" message to the bot final Map messageBody = { 'msgtype': 'm.text', @@ -51,19 +57,20 @@ class BotBridgeConnection { await client.sendMessage( directChat, 'm.room.message', - const Uuid().v4(),// Generate random txnId + const Uuid().v4(), // Generate random txnId messageBody, ); await Future.delayed(const Duration(seconds: 1)); // Wait 2 sec - - } else if(onlineMatch.hasMatch(latestMessage) || alreadySuccessMatch.hasMatch(latestMessage) || successfullyMatch.hasMatch(latestMessage)){ - + } else if (onlineMatch.hasMatch(latestMessage) || + alreadySuccessMatch.hasMatch(latestMessage) || + successfullyMatch.hasMatch(latestMessage)) { print("You're logged"); result = true; break; // Exit the loop if bridge is connected - }else if(notLoggedMatch.hasMatch(latestMessage)){ + } else if (notLoggedMatch.hasMatch(latestMessage) || + disconnectMatch.hasMatch(latestMessage)) { print('Not connected'); break; // Exit the loop if bridge is disconnected @@ -82,7 +89,8 @@ class BotBridgeConnection { final RegExp alreadySuccessMatch = RegExp(r"You're already logged in"); // Error phrase to spot - final RegExp usernameErrorMatch = RegExp(r"Please check your username and try again"); + final RegExp usernameErrorMatch = + RegExp(r"Please check your username and try again"); final RegExp passwordErrorMatch = RegExp(r"Incorrect password"); final RegExp rateLimitErrorMatch = RegExp(r"rate_limit_error"); @@ -92,10 +100,8 @@ class BotBridgeConnection { String result = ""; // Variable to track the result of the connection - // Get the latest messages from the room (limited to the specified number) while (true) { - // Send the "login" message to the bot final Map messageBody = { 'msgtype': 'm.text', @@ -104,7 +110,7 @@ class BotBridgeConnection { await client.sendMessage( directChat, 'm.room.message', - const Uuid().v4(),// Generate random txnId + const Uuid().v4(), // Generate random txnId messageBody, ); await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec @@ -116,41 +122,36 @@ class BotBridgeConnection { ); final List latestMessages = response.chunk ?? []; - final String latestMessage = latestMessages.first.content['body'].toString() ?? - ''; + final String latestMessage = + latestMessages.first.content['body'].toString() ?? ''; if (latestMessages.isNotEmpty) { - - if(successMatch.hasMatch(latestMessage) || alreadySuccessMatch.hasMatch(latestMessage)){ + if (successMatch.hasMatch(latestMessage) || + alreadySuccessMatch.hasMatch(latestMessage)) { print("You're logged to Instagram"); result = "success"; break; // Exit the loop once the "login" message has been sent and is success - - }else if(!successMatch.hasMatch(latestMessage) && usernameErrorMatch.hasMatch(latestMessage)){ + } else if (!successMatch.hasMatch(latestMessage) && + usernameErrorMatch.hasMatch(latestMessage)) { print("Login cannot be found"); result = "errorUsername"; break; - - }else if(passwordErrorMatch.hasMatch(latestMessage)){ - + } else if (passwordErrorMatch.hasMatch(latestMessage)) { print("Password incorrect"); result = "errorPassword"; break; - - }else if(rateLimitErrorMatch.hasMatch(latestMessage)){ - + } else if (rateLimitErrorMatch.hasMatch(latestMessage)) { print("rate limit error"); result = "rateLimitError"; break; - } } } @@ -180,12 +181,11 @@ class BotBridgeConnection { final List latestMessages = response.chunk ?? []; if (latestMessages.isNotEmpty) { - final String latestMessage = latestMessages.first.content['body'].toString() ?? - ''; + final String latestMessage = + latestMessages.first.content['body'].toString() ?? ''; // to find out if we're connected if (!successMatch.hasMatch(latestMessage)) { - // Send the "logout" message to the bot final Map messageBody = { 'msgtype': 'm.text', @@ -194,12 +194,11 @@ class BotBridgeConnection { await client.sendMessage( directChat, 'm.room.message', - const Uuid().v4(),// Generate random txnId + const Uuid().v4(), // Generate random txnId messageBody, ); await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec - - } else if(successMatch.hasMatch(latestMessage)){ + } else if (successMatch.hasMatch(latestMessage)) { print("You're disconnected"); result = false; @@ -210,4 +209,19 @@ class BotBridgeConnection { return result; } -} \ No newline at end of file + // Function to delete a conversation with a bot + Future deleteConversation(String botUserId) async { + try { + final roomId = client.getDirectChatFromUserId(botUserId); + final room = client.getRoomById(roomId!); + if (room != null) { + await room.leave(); // To leave and delete the room (DirectChat only) + print('Conversation deleted successfully'); + } else { + print('Room not found'); + } + } catch (e) { + print('Error deleting conversation: $e'); + } + } +} diff --git a/lib/pages/add_bridge/show_bottom_sheet.dart b/lib/pages/add_bridge/show_bottom_sheet.dart index 0f67bc9f57..0ec6c7695b 100644 --- a/lib/pages/add_bridge/show_bottom_sheet.dart +++ b/lib/pages/add_bridge/show_bottom_sheet.dart @@ -9,11 +9,10 @@ import 'model/social_network.dart'; // Disconnect button display Future showBottomSheetBridge( - BuildContext context, - SocialNetwork network, - BotBridgeConnection botConnection, - ) async { - + BuildContext context, + SocialNetwork network, + BotBridgeConnection botConnection, +) async { final Completer completer = Completer(); showModalBottomSheet( @@ -30,20 +29,22 @@ Future showBottomSheetBridge( ), ), onTap: () async { - try{ + try { bool result = true; Navigator.pop(context); await showFutureLoadingDialog( context: context, future: () async { - if(network.name == "Instagram") result = await botConnection.disconnectToInstagram(); + if (network.name == "Instagram") { + result = await botConnection.disconnectToInstagram(); + } // Returns True if disconnection has been made completer.complete(true); }, ); - }catch (e) { + } catch (e) { Navigator.of(context).pop(); //To view other catch-related errors showCatchErrorDialog(context, e); @@ -57,4 +58,3 @@ Future showBottomSheetBridge( return completer.future; } - diff --git a/lib/pages/add_bridge/show_delete_conversation_dialog.dart b/lib/pages/add_bridge/show_delete_conversation_dialog.dart new file mode 100644 index 0000000000..0561b94e5f --- /dev/null +++ b/lib/pages/add_bridge/show_delete_conversation_dialog.dart @@ -0,0 +1,51 @@ +import 'package:fluffychat/pages/add_bridge/model/social_network.dart'; +import 'package:fluffychat/pages/add_bridge/service/bot_bridge_connection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:future_loading_dialog/future_loading_dialog.dart'; + +// ShowDialog to offer the user the option of cancelling the conversation with the bot after disconnection +Future showDeleteConversationDialog(BuildContext context, + SocialNetwork network, BotBridgeConnection botConnection) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + L10n.of(context)!.bridgeBot_deleteConvTitle, + ), + content: Text( + L10n.of(context)!.bridgeBot_deleteConvDescription, + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text(L10n.of(context)!.cancel), + ), + TextButton( + onPressed: () async { + // Action to delete the conversation + await showFutureLoadingDialog( + context: context, + future: () async { + await botConnection.deleteConversation(network.chatBot); + }, + ); + Navigator.of(context).pop(); // Close the dialog + }, + child: Text( + L10n.of(context)!.delete, + style: const TextStyle( + color: Colors.red, + fontSize: 20.0, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ); + }, + ); +} From 2da7e55ff10028217a03b657d470f517e637e21b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Nov 2023 14:34:50 +0100 Subject: [PATCH 07/20] feat: adaptation and format fix --- lib/pages/add_bridge/add_bridge_body.dart | 9 +-------- lib/pages/settings/settings_view.dart | 7 +++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index a620a61eab..d6c1be2cae 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -46,18 +46,11 @@ class _AddBridgeBodyState extends State { @override Widget build(BuildContext context) { return Scaffold( - // The "go back" provided by the AppBar may no longer be useful now that this page opens with the settings page on Web - appBar: !PlatformInfos.isWeb ? AppBar() : null, + appBar: AppBar(), body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - // Small space for the web version - PlatformInfos.isWeb - ? const SizedBox( - height: 20, - ) - : Container(), buildHeaderBridgeText(context), buildHeaderBridgeSubText(context), Center( diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index 05af6fda95..a6acb954a5 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -152,6 +152,13 @@ class SettingsView extends StatelessWidget { onChanged: controller.firstRunBootstrapAction, ), const Divider(thickness: 1), + // ListTile redirects to bots bridges page + ListTile( + leading: const Icon(Icons.account_tree_outlined), + title: Text(L10n.of(context)!.bridgeBot_menuItemTitle), + onTap: () => context.go('/rooms/settings/addbridgebot'), + trailing: const Icon(Icons.chevron_right_outlined), + ), ListTile( leading: const Icon(Icons.format_paint_outlined), title: Text(L10n.of(context)!.changeTheme), From 9ed959781372528c007205565235c598e98847fc Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Nov 2023 09:01:18 +0100 Subject: [PATCH 08/20] feat: L10n English correction --- assets/l10n/intl_en.arb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 059ab372cf..a2ff898069 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2551,10 +2551,10 @@ "pleaseAddATitle": "Please add a title", "todoListChangedError": "Oops... The todo list has been changed while you edited it.", "todosUnencrypted": "Please notice that todos are visible by everyone in the chat and are not end to end encrypted.", - "youDonTHaveConversation": "You don't have a conversation yet.", + "youDonTHaveConversation": "You don't have any conversation yet.", "connectChatNetworks": "Connect your chat networks", "addSocialMessagingAccounts": "Add your social messaging accounts", - "addSocialMessagingAccountsText": "Connect all your discussions!", + "addSocialMessagingAccountsText": "Connect all your conversation!", "connected": "Connected", "notConnected": "Not connected", "connectYourSocialAccount": "Login to your account", @@ -2562,7 +2562,7 @@ "pleaseEnterPassword": "Please enter your password", "usernameNotFound": "The username you entered doesn't appear to belong to an account. Please check your username and try again.", "passwordIncorrect": "Password incorrect", - "rateLimit": "An error has occurred, please wait a few minutes before try again", + "rateLimit": "An error has occurred, please wait a few minutes before trying again", "err_": "Error", "err_desc": "An error has occurred:", "bridgeBot_menuItemTitle" : "Connected social networks", From 958b713d88997888ffd6815fa3bdde6062164eca Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Nov 2023 10:08:16 +0100 Subject: [PATCH 09/20] feat: ping timeout or wrong answer --- assets/l10n/intl_en.arb | 6 +- lib/pages/add_bridge/add_bridge_body.dart | 96 +++++++++++++------ .../add_bridge/error_message_dialog.dart | 9 +- .../add_bridge/model/social_network.dart | 14 ++- 4 files changed, 91 insertions(+), 34 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index a2ff898069..70096705c2 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2565,7 +2565,11 @@ "rateLimit": "An error has occurred, please wait a few minutes before trying again", "err_": "Error", "err_desc": "An error has occurred:", + "err_toConnect": "A problem occurred when connecting to", + "err_timeOut": "A network problem has been detected", "bridgeBot_menuItemTitle" : "Connected social networks", "bridgeBot_deleteConvTitle": "Delete Conversation", - "bridgeBot_deleteConvDescription": "Do you want to delete the conversation with the bot?" + "bridgeBot_deleteConvDescription": "Do you want to delete the conversation with the bot?", + "instagram": "Instagram", + "whatsApp": "WhatsApp" } diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index d6c1be2cae..93dda00e37 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:fluffychat/pages/add_bridge/service/bot_bridge_connection.dart'; import 'package:fluffychat/pages/add_bridge/show_bottom_sheet.dart'; import 'package:fluffychat/pages/add_bridge/show_delete_conversation_dialog.dart'; @@ -9,6 +11,7 @@ import 'package:flutter/material.dart'; import '../../widgets/matrix.dart'; import 'add_bridge_header.dart'; import 'connection_bridge_dialog.dart'; +import 'error_message_dialog.dart'; import 'model/social_network.dart'; // Page offering brigde bot connections to social network chats @@ -22,11 +25,10 @@ class AddBridgeBody extends StatefulWidget { } class _AddBridgeBodyState extends State { - bool instagramConnected = false; - bool loadingInstagram = true; - late BotBridgeConnection botConnection; + bool timeoutErrorOccurred = false; + @override void initState() { final client = Matrix.of(context).client; @@ -35,12 +37,44 @@ class _AddBridgeBodyState extends State { _initStateAsync(); } - // Online status update when page is opened +// Online status update when page is opened Future _initStateAsync() async { - instagramConnected = await botConnection.instagramPing(); - setState(() { - loadingInstagram = false; - }); + try { + + final instagramConnected = await _pingWithTimeout(botConnection.instagramPing()); + setState(() { + socialNetwork.firstWhere((element) => element.name == "Instagram").connected = instagramConnected; + socialNetwork.firstWhere((element) => element.name == "Instagram").loading = false; + }); + } on TimeoutException { + // To indicate that the time-out error has occurred + timeoutErrorOccurred = true; + } catch (error) { + print("Error pinging Instagram: $error"); + await Future.delayed(const Duration(seconds: 1)); // Precaution to let the page load + if (!timeoutErrorOccurred) { + showCatchErrorDialog(context, "${L10n.of(context)!.err_toConnect} ${L10n.of(context)!.instagram}"); + } + } + + } + +// Function to manage missed deadlines + Future _pingWithTimeout(Future pingFunction) async { + try { + // Future.timeout to define a maximum waiting time + return await pingFunction.timeout(const Duration(seconds: 15)); + } on TimeoutException { + print("Ping timeout"); + + // Display error message to warn user + showCatchErrorDialog(context, L10n.of(context)!.err_timeOut); + + throw TimeoutException("Ping timeout"); + } catch (error) { + print("Error pinging: $error"); + rethrow; + } } @override @@ -90,41 +124,41 @@ class _AddBridgeBodyState extends State { // Different ways of connecting and disconnecting depending on the social network, for now only Instagram void handleSocialNetworkAction(SocialNetwork network) async { - if (network.name == "Instagram") { - if (loadingInstagram == false) { - if (instagramConnected != true) { + if (network.loading == false) { + if (network.connected != true) { + if(network.name == "Instagram"){ // Trying to connect to Instagram final bool success = - await connectToInstagram(context, network, botConnection); + await connectToInstagram(context, network, botConnection); if (success) { setState(() { - instagramConnected = true; - }); - } - } else { - // Disconnect button, for the moment only this choice - final bool success = await showBottomSheetBridge( - context, - network, - botConnection, - ); - - if (success) { - setState(() { - instagramConnected = false; + network.connected = true; }); } + } + } else { + // Disconnect button, for the moment only this choice + final bool success = await showBottomSheetBridge( + context, + network, + botConnection, + ); - // Show the dialog for deleting the conversation - await showDeleteConversationDialog(context, network, botConnection); + if (success) { + setState(() { + network.connected = false; + }); } + + // Show the dialog for deleting the conversation + await showDeleteConversationDialog(context, network, botConnection); } } } // Different build of subtle depending on the social network, for now only Instagram Widget buildSubtitle(SocialNetwork network) { - if (loadingInstagram && network.name == "Instagram") { + if (network.loading == true) { return const Align( alignment: Alignment.centerLeft, child: CircularProgressIndicator( @@ -133,11 +167,11 @@ class _AddBridgeBodyState extends State { ); } else { return Text( - network.name == "Instagram" && instagramConnected + network.connected == true ? L10n.of(context)!.connected : L10n.of(context)!.notConnected, style: TextStyle( - color: network.name == "Instagram" && instagramConnected + color: network.connected == true ? Colors.green : Colors.grey, ), diff --git a/lib/pages/add_bridge/error_message_dialog.dart b/lib/pages/add_bridge/error_message_dialog.dart index bfc2318cd8..77591408d0 100644 --- a/lib/pages/add_bridge/error_message_dialog.dart +++ b/lib/pages/add_bridge/error_message_dialog.dart @@ -82,7 +82,14 @@ void showCatchErrorDialog(BuildContext context, Object e) { title: Text( L10n.of(context)!.err_, ), - content: Text('${L10n.of(context)!.err_desc} $e'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(L10n.of(context)!.err_desc), + Text(e.toString()), + ], + ), actions: [ TextButton( onPressed: () { diff --git a/lib/pages/add_bridge/model/social_network.dart b/lib/pages/add_bridge/model/social_network.dart index 5143231f78..ab9ff2f7c0 100644 --- a/lib/pages/add_bridge/model/social_network.dart +++ b/lib/pages/add_bridge/model/social_network.dart @@ -5,11 +5,15 @@ class SocialNetwork { final Widget logo; // The path to social media image final String name; // Social media name final String chatBot; // ChatBot for send demand + bool? loading; // To find out if state is loading + bool? connected; // To find out if state is disconnected SocialNetwork({ required this.logo, required this.name, required this.chatBot, + this.loading = true, // Default value true for loading + this.connected = false, // Default value false for connected }); } @@ -27,6 +31,14 @@ final List socialNetwork = [ SocialNetwork( logo: Logo(Logos.whatsapp), name: "Whatsapp", - chatBot: "", + chatBot: "@whatsappbot:loveto.party", ), ]; + +class WhatsAppResult { + final String result; + final String? code; + final String? qrCode; + + WhatsAppResult(this.result, this.code, this.qrCode); +} \ No newline at end of file From 19dc1f543d95a656ebe58c179f5eeba50939923c Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Nov 2023 10:37:32 +0100 Subject: [PATCH 10/20] feat: size var --- lib/pages/add_bridge/add_bridge_body.dart | 3 ++- lib/pages/chat_list/add_chat_network.dart | 3 ++- lib/utils/platform_size.dart | 9 +++++++++ lib/widgets/fluffy_chat_app.dart | 2 ++ pubspec.yaml | 2 +- 5 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 lib/utils/platform_size.dart diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index 93dda00e37..f00813bc71 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -4,6 +4,7 @@ import 'package:fluffychat/pages/add_bridge/service/bot_bridge_connection.dart'; import 'package:fluffychat/pages/add_bridge/show_bottom_sheet.dart'; import 'package:fluffychat/pages/add_bridge/show_delete_conversation_dialog.dart'; import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/utils/platform_size.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -90,7 +91,7 @@ class _AddBridgeBodyState extends State { Center( child: SizedBox( width: PlatformInfos.isWeb - ? MediaQuery.of(context).size.width / 2 + ? PlatformWidth.webWidth : null, child: ListView.builder( shrinkWrap: true, diff --git a/lib/pages/chat_list/add_chat_network.dart b/lib/pages/chat_list/add_chat_network.dart index 45b121f54f..2331127bb0 100644 --- a/lib/pages/chat_list/add_chat_network.dart +++ b/lib/pages/chat_list/add_chat_network.dart @@ -2,6 +2,7 @@ import 'package:fluffychat/utils/platform_infos.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:go_router/go_router.dart'; +import '../../utils/platform_size.dart'; import '../../widgets/matrix.dart'; import '../add_bridge/add_bridge_body.dart'; @@ -13,7 +14,7 @@ class AddChatNetwork extends StatelessWidget { Widget build(BuildContext context) { return SizedBox( height: MediaQuery.of(context).size.height / 3.5, - width: PlatformInfos.isWeb ? MediaQuery.of(context).size.width / 2 : null, + width: PlatformInfos.isWeb ? PlatformWidth.webWidth : null, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, diff --git a/lib/utils/platform_size.dart b/lib/utils/platform_size.dart new file mode 100644 index 0000000000..cfdcf4b525 --- /dev/null +++ b/lib/utils/platform_size.dart @@ -0,0 +1,9 @@ +import 'package:flutter/widgets.dart'; + +class PlatformWidth { + static double? webWidth; + + static void initialize(BuildContext context) { + webWidth = MediaQuery.of(context).size.width / 2; + } +} diff --git a/lib/widgets/fluffy_chat_app.dart b/lib/widgets/fluffy_chat_app.dart index d9d2f042af..5d54100157 100644 --- a/lib/widgets/fluffy_chat_app.dart +++ b/lib/widgets/fluffy_chat_app.dart @@ -11,6 +11,7 @@ import 'package:fluffychat/widgets/app_lock.dart'; import 'package:fluffychat/widgets/theme_builder.dart'; import '../config/app_config.dart'; import '../utils/custom_scroll_behaviour.dart'; +import '../utils/platform_size.dart'; import 'matrix.dart'; class FluffyChatApp extends StatelessWidget { @@ -38,6 +39,7 @@ class FluffyChatApp extends StatelessWidget { @override Widget build(BuildContext context) { + PlatformWidth.initialize(context); // To initialize size variables according to platform return ThemeBuilder( builder: (context, themeMode, primaryColor) => MaterialApp.router( title: AppConfig.applicationName, diff --git a/pubspec.yaml b/pubspec.yaml index 0619aca839..c8253394db 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,7 +31,7 @@ dependencies: flutter: sdk: flutter flutter_app_badger: ^1.5.0 - flutter_blurhash: ^0.7.0 + flutter_blurhash: ^0.8.2 flutter_cache_manager: ^3.3.0 flutter_foreground_task: ^6.0.0+1 flutter_highlighter: ^0.1.1 From 1c646a7065eaafcfcdaa0d36bc6d3372616e2916 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Nov 2023 14:19:13 +0100 Subject: [PATCH 11/20] feat: show error for disconnected --- lib/pages/add_bridge/add_bridge_body.dart | 29 +++------ .../service/bot_bridge_connection.dart | 61 ++++++++++++++----- lib/pages/add_bridge/show_bottom_sheet.dart | 13 +++- 3 files changed, 63 insertions(+), 40 deletions(-) diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index f00813bc71..3af176fb13 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -42,7 +42,7 @@ class _AddBridgeBodyState extends State { Future _initStateAsync() async { try { - final instagramConnected = await _pingWithTimeout(botConnection.instagramPing()); + final instagramConnected = await botConnection.pingWithTimeout(context, botConnection.instagramPing()); setState(() { socialNetwork.firstWhere((element) => element.name == "Instagram").connected = instagramConnected; socialNetwork.firstWhere((element) => element.name == "Instagram").loading = false; @@ -60,24 +60,6 @@ class _AddBridgeBodyState extends State { } -// Function to manage missed deadlines - Future _pingWithTimeout(Future pingFunction) async { - try { - // Future.timeout to define a maximum waiting time - return await pingFunction.timeout(const Duration(seconds: 15)); - } on TimeoutException { - print("Ping timeout"); - - // Display error message to warn user - showCatchErrorDialog(context, L10n.of(context)!.err_timeOut); - - throw TimeoutException("Ping timeout"); - } catch (error) { - print("Error pinging: $error"); - rethrow; - } - } - @override Widget build(BuildContext context) { return Scaffold( @@ -149,10 +131,13 @@ class _AddBridgeBodyState extends State { setState(() { network.connected = false; }); - } - // Show the dialog for deleting the conversation - await showDeleteConversationDialog(context, network, botConnection); + // Show the dialog for deleting the conversation + await showDeleteConversationDialog(context, network, botConnection); + }else{ + // Display error message to warn user + showCatchErrorDialog(context, L10n.of(context)!.err_timeOut); + } } } } diff --git a/lib/pages/add_bridge/service/bot_bridge_connection.dart b/lib/pages/add_bridge/service/bot_bridge_connection.dart index 19a54b723e..878535416d 100644 --- a/lib/pages/add_bridge/service/bot_bridge_connection.dart +++ b/lib/pages/add_bridge/service/bot_bridge_connection.dart @@ -1,5 +1,11 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; import 'package:matrix/matrix.dart'; import 'package:uuid/uuid.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +import '../error_message_dialog.dart'; // For all bot bridge conversations // For the moment, rooms are DirectChat @@ -163,6 +169,7 @@ class BotBridgeConnection { const String botUserId = '@instagrambot:loveto.party'; final RegExp successMatch = RegExp(r"Successfully logged out"); + final RegExp aldreadyLogoutMatch = RegExp(r"That command requires you to be logged in."); // Add a direct chat with the Instagram bot (if you haven't already) String? directChat = client.getDirectChatFromUserId(botUserId); @@ -170,8 +177,21 @@ class BotBridgeConnection { bool result = true; // Variable to track the result of the connection - // Get the latest messages from the room (limited to the specified number) while (true) { + // Send the "logout" message to the bot + final Map messageBody = { + 'msgtype': 'm.text', + 'body': "logout", + }; + await client.sendMessage( + directChat, + 'm.room.message', + const Uuid().v4(), // Generate random txnId + messageBody, + ); + await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec + + // Get the latest messages from the room (limited to the specified number) final GetRoomEventsResponse response = await client.getRoomEvents( directChat, Direction.b, // To get the latest messages @@ -185,20 +205,13 @@ class BotBridgeConnection { latestMessages.first.content['body'].toString() ?? ''; // to find out if we're connected - if (!successMatch.hasMatch(latestMessage)) { - // Send the "logout" message to the bot - final Map messageBody = { - 'msgtype': 'm.text', - 'body': "logout", - }; - await client.sendMessage( - directChat, - 'm.room.message', - const Uuid().v4(), // Generate random txnId - messageBody, - ); - await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec - } else if (successMatch.hasMatch(latestMessage)) { + if (!successMatch.hasMatch(latestMessage) && + !aldreadyLogoutMatch.hasMatch(latestMessage)) { + print("You're always connected"); + result = true; + break; + } else if (successMatch.hasMatch(latestMessage) + || aldreadyLogoutMatch.hasMatch(latestMessage)) { print("You're disconnected"); result = false; @@ -224,4 +237,22 @@ class BotBridgeConnection { print('Error deleting conversation: $e'); } } + + // Function to manage missed deadlines + Future pingWithTimeout(BuildContext context, Future pingFunction) async { + try { + // Future.timeout to define a maximum waiting time + return await pingFunction.timeout(const Duration(seconds: 15)); + } on TimeoutException { + print("Ping timeout"); + + // Display error message to warn user + showCatchErrorDialog(context, L10n.of(context)!.err_timeOut); + + throw TimeoutException("Ping timeout"); + } catch (error) { + print("Error pinging: $error"); + rethrow; + } + } } diff --git a/lib/pages/add_bridge/show_bottom_sheet.dart b/lib/pages/add_bridge/show_bottom_sheet.dart index 0ec6c7695b..6f633ca991 100644 --- a/lib/pages/add_bridge/show_bottom_sheet.dart +++ b/lib/pages/add_bridge/show_bottom_sheet.dart @@ -36,18 +36,25 @@ Future showBottomSheetBridge( await showFutureLoadingDialog( context: context, future: () async { + if (network.name == "Instagram") { result = await botConnection.disconnectToInstagram(); } - // Returns True if disconnection has been made - completer.complete(true); + if(result != false){ + completer.complete(false); + } }, ); + + completer.complete(true); + } catch (e) { + print("error: $e"); + Navigator.of(context).pop(); //To view other catch-related errors - showCatchErrorDialog(context, e); + showCatchErrorDialog(context, L10n.of(context)!.err_timeOut); } }, ), From e213aecde1bf1df7f3691c1d831a800dbfd3834a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Nov 2023 14:29:02 +0100 Subject: [PATCH 12/20] feat: switch statement --- lib/pages/add_bridge/add_bridge_body.dart | 23 ++++++++++++++------- lib/pages/add_bridge/show_bottom_sheet.dart | 7 +++++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index 3af176fb13..7323883259 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -109,15 +109,22 @@ class _AddBridgeBodyState extends State { void handleSocialNetworkAction(SocialNetwork network) async { if (network.loading == false) { if (network.connected != true) { - if(network.name == "Instagram"){ + bool success = false; + switch (network.name) { + + case "Instagram": // Trying to connect to Instagram - final bool success = - await connectToInstagram(context, network, botConnection); - if (success) { - setState(() { - network.connected = true; - }); - } + success = + await connectToInstagram(context, network, botConnection); + break; + + // For other networks + + } + if (success) { + setState(() { + network.connected = true; + }); } } else { // Disconnect button, for the moment only this choice diff --git a/lib/pages/add_bridge/show_bottom_sheet.dart b/lib/pages/add_bridge/show_bottom_sheet.dart index 6f633ca991..e7add6a888 100644 --- a/lib/pages/add_bridge/show_bottom_sheet.dart +++ b/lib/pages/add_bridge/show_bottom_sheet.dart @@ -37,8 +37,11 @@ Future showBottomSheetBridge( context: context, future: () async { - if (network.name == "Instagram") { - result = await botConnection.disconnectToInstagram(); + switch (network.name) { + case "Instagram": + result = await botConnection.disconnectToInstagram(); + break; + // For other networks } if(result != false){ From da146ff64ef63e165ccd7204d98b755a50b693be Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Nov 2023 14:46:10 +0100 Subject: [PATCH 13/20] feat: delete uuid --- .../service/bot_bridge_connection.dart | 55 ++++++------------- pubspec.yaml | 1 - 2 files changed, 17 insertions(+), 39 deletions(-) diff --git a/lib/pages/add_bridge/service/bot_bridge_connection.dart b/lib/pages/add_bridge/service/bot_bridge_connection.dart index 878535416d..6a15f15eb0 100644 --- a/lib/pages/add_bridge/service/bot_bridge_connection.dart +++ b/lib/pages/add_bridge/service/bot_bridge_connection.dart @@ -33,10 +33,18 @@ class BotBridgeConnection { String? directChat = client.getDirectChatFromUserId(botUserId); directChat ??= await client.startDirectChat(botUserId); + final Room? roomBot = client.getRoomById(directChat); + bool result = false; // Variable to track the result of the connection // Get the latest messages from the room (limited to the specified number) while (true) { + + // Send the "ping" message to the bot + await roomBot?.sendTextEvent("ping"); + await Future.delayed(const Duration(seconds: 2)); // Wait 2 sec + + // To take latest message final GetRoomEventsResponse response = await client.getRoomEvents( directChat, Direction.b, // To get the latest messages @@ -50,24 +58,7 @@ class BotBridgeConnection { latestMessages.first.content['body'].toString() ?? ''; // to find out if we're connected - if (!onlineMatch.hasMatch(latestMessage) && - !notLoggedMatch.hasMatch(latestMessage) && - !alreadySuccessMatch.hasMatch(latestMessage) && - !successfullyMatch.hasMatch(latestMessage) && - !disconnectMatch.hasMatch(latestMessage)) { - // Send the "ping" message to the bot - final Map messageBody = { - 'msgtype': 'm.text', - 'body': "ping", - }; - await client.sendMessage( - directChat, - 'm.room.message', - const Uuid().v4(), // Generate random txnId - messageBody, - ); - await Future.delayed(const Duration(seconds: 1)); // Wait 2 sec - } else if (onlineMatch.hasMatch(latestMessage) || + if (onlineMatch.hasMatch(latestMessage) || alreadySuccessMatch.hasMatch(latestMessage) || successfullyMatch.hasMatch(latestMessage)) { print("You're logged"); @@ -104,21 +95,15 @@ class BotBridgeConnection { String? directChat = client.getDirectChatFromUserId(botUserId); directChat ??= await client.startDirectChat(botUserId); + final Room? roomBot = client.getRoomById(directChat); + String result = ""; // Variable to track the result of the connection // Get the latest messages from the room (limited to the specified number) while (true) { + // Send the "login" message to the bot - final Map messageBody = { - 'msgtype': 'm.text', - 'body': "login $username $password", - }; - await client.sendMessage( - directChat, - 'm.room.message', - const Uuid().v4(), // Generate random txnId - messageBody, - ); + await roomBot?.sendTextEvent("login $username $password"); await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec final GetRoomEventsResponse response = await client.getRoomEvents( @@ -175,20 +160,14 @@ class BotBridgeConnection { String? directChat = client.getDirectChatFromUserId(botUserId); directChat ??= await client.startDirectChat(botUserId); + final Room? roomBot = client.getRoomById(directChat); + bool result = true; // Variable to track the result of the connection while (true) { + // Send the "logout" message to the bot - final Map messageBody = { - 'msgtype': 'm.text', - 'body': "logout", - }; - await client.sendMessage( - directChat, - 'm.room.message', - const Uuid().v4(), // Generate random txnId - messageBody, - ); + await roomBot?.sendTextEvent("logout"); await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec // Get the latest messages from the room (limited to the specified number) diff --git a/pubspec.yaml b/pubspec.yaml index c8253394db..21d378a435 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -93,7 +93,6 @@ dependencies: # Add for Tawkie icons_plus: ^4.0.0 - uuid: ^3.0.7 dev_dependencies: dart_code_metrics: ^5.7.5 From 95db0508eb1d670a1540490613b2446c756b0fc8 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Nov 2023 15:45:38 +0100 Subject: [PATCH 14/20] feat: fix apk error and format --- android/app/build.gradle | 8 ++++ lib/pages/add_bridge/add_bridge_body.dart | 38 +++++++++---------- .../add_bridge/model/social_network.dart | 2 +- .../service/bot_bridge_connection.dart | 13 +++---- lib/pages/add_bridge/show_bottom_sheet.dart | 6 +-- lib/widgets/fluffy_chat_app.dart | 3 +- pubspec.yaml | 2 +- 7 files changed, 38 insertions(+), 34 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 8eb605e94f..0c8cdf85f8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -72,6 +72,14 @@ android { signingConfig signingConfigs.release } } + + // https://stackoverflow.com/a/77494454/8222484 + packagingOptions { + pickFirst 'lib/x86/libc++_shared.so' + pickFirst 'lib/x86_64/libc++_shared.so' + pickFirst 'lib/armeabi-v7a/libc++_shared.so' + pickFirst 'lib/arm64-v8a/libc++_shared.so' + } } flutter { diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index 7323883259..71ca4cd28b 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -41,23 +41,28 @@ class _AddBridgeBodyState extends State { // Online status update when page is opened Future _initStateAsync() async { try { - - final instagramConnected = await botConnection.pingWithTimeout(context, botConnection.instagramPing()); + final instagramConnected = await botConnection.pingWithTimeout( + context, botConnection.instagramPing()); setState(() { - socialNetwork.firstWhere((element) => element.name == "Instagram").connected = instagramConnected; - socialNetwork.firstWhere((element) => element.name == "Instagram").loading = false; + socialNetwork + .firstWhere((element) => element.name == "Instagram") + .connected = instagramConnected; + socialNetwork + .firstWhere((element) => element.name == "Instagram") + .loading = false; }); } on TimeoutException { // To indicate that the time-out error has occurred timeoutErrorOccurred = true; } catch (error) { print("Error pinging Instagram: $error"); - await Future.delayed(const Duration(seconds: 1)); // Precaution to let the page load + await Future.delayed( + const Duration(seconds: 1)); // Precaution to let the page load if (!timeoutErrorOccurred) { - showCatchErrorDialog(context, "${L10n.of(context)!.err_toConnect} ${L10n.of(context)!.instagram}"); + showCatchErrorDialog(context, + "${L10n.of(context)!.err_toConnect} ${L10n.of(context)!.instagram}"); } } - } @override @@ -72,9 +77,7 @@ class _AddBridgeBodyState extends State { buildHeaderBridgeSubText(context), Center( child: SizedBox( - width: PlatformInfos.isWeb - ? PlatformWidth.webWidth - : null, + width: PlatformInfos.isWeb ? PlatformWidth.webWidth : null, child: ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -111,15 +114,12 @@ class _AddBridgeBodyState extends State { if (network.connected != true) { bool success = false; switch (network.name) { - case "Instagram": - // Trying to connect to Instagram - success = - await connectToInstagram(context, network, botConnection); + // Trying to connect to Instagram + success = await connectToInstagram(context, network, botConnection); break; - // For other networks - + // For other networks } if (success) { setState(() { @@ -141,7 +141,7 @@ class _AddBridgeBodyState extends State { // Show the dialog for deleting the conversation await showDeleteConversationDialog(context, network, botConnection); - }else{ + } else { // Display error message to warn user showCatchErrorDialog(context, L10n.of(context)!.err_timeOut); } @@ -164,9 +164,7 @@ class _AddBridgeBodyState extends State { ? L10n.of(context)!.connected : L10n.of(context)!.notConnected, style: TextStyle( - color: network.connected == true - ? Colors.green - : Colors.grey, + color: network.connected == true ? Colors.green : Colors.grey, ), ); } diff --git a/lib/pages/add_bridge/model/social_network.dart b/lib/pages/add_bridge/model/social_network.dart index ab9ff2f7c0..8e98bde402 100644 --- a/lib/pages/add_bridge/model/social_network.dart +++ b/lib/pages/add_bridge/model/social_network.dart @@ -41,4 +41,4 @@ class WhatsAppResult { final String? qrCode; WhatsAppResult(this.result, this.code, this.qrCode); -} \ No newline at end of file +} diff --git a/lib/pages/add_bridge/service/bot_bridge_connection.dart b/lib/pages/add_bridge/service/bot_bridge_connection.dart index 6a15f15eb0..537e9667d1 100644 --- a/lib/pages/add_bridge/service/bot_bridge_connection.dart +++ b/lib/pages/add_bridge/service/bot_bridge_connection.dart @@ -39,7 +39,6 @@ class BotBridgeConnection { // Get the latest messages from the room (limited to the specified number) while (true) { - // Send the "ping" message to the bot await roomBot?.sendTextEvent("ping"); await Future.delayed(const Duration(seconds: 2)); // Wait 2 sec @@ -101,7 +100,6 @@ class BotBridgeConnection { // Get the latest messages from the room (limited to the specified number) while (true) { - // Send the "login" message to the bot await roomBot?.sendTextEvent("login $username $password"); await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec @@ -154,7 +152,8 @@ class BotBridgeConnection { const String botUserId = '@instagrambot:loveto.party'; final RegExp successMatch = RegExp(r"Successfully logged out"); - final RegExp aldreadyLogoutMatch = RegExp(r"That command requires you to be logged in."); + final RegExp aldreadyLogoutMatch = + RegExp(r"That command requires you to be logged in."); // Add a direct chat with the Instagram bot (if you haven't already) String? directChat = client.getDirectChatFromUserId(botUserId); @@ -165,7 +164,6 @@ class BotBridgeConnection { bool result = true; // Variable to track the result of the connection while (true) { - // Send the "logout" message to the bot await roomBot?.sendTextEvent("logout"); await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec @@ -189,8 +187,8 @@ class BotBridgeConnection { print("You're always connected"); result = true; break; - } else if (successMatch.hasMatch(latestMessage) - || aldreadyLogoutMatch.hasMatch(latestMessage)) { + } else if (successMatch.hasMatch(latestMessage) || + aldreadyLogoutMatch.hasMatch(latestMessage)) { print("You're disconnected"); result = false; @@ -218,7 +216,8 @@ class BotBridgeConnection { } // Function to manage missed deadlines - Future pingWithTimeout(BuildContext context, Future pingFunction) async { + Future pingWithTimeout( + BuildContext context, Future pingFunction) async { try { // Future.timeout to define a maximum waiting time return await pingFunction.timeout(const Duration(seconds: 15)); diff --git a/lib/pages/add_bridge/show_bottom_sheet.dart b/lib/pages/add_bridge/show_bottom_sheet.dart index e7add6a888..bd12b0ee5d 100644 --- a/lib/pages/add_bridge/show_bottom_sheet.dart +++ b/lib/pages/add_bridge/show_bottom_sheet.dart @@ -36,22 +36,20 @@ Future showBottomSheetBridge( await showFutureLoadingDialog( context: context, future: () async { - switch (network.name) { case "Instagram": result = await botConnection.disconnectToInstagram(); break; - // For other networks + // For other networks } - if(result != false){ + if (result != false) { completer.complete(false); } }, ); completer.complete(true); - } catch (e) { print("error: $e"); diff --git a/lib/widgets/fluffy_chat_app.dart b/lib/widgets/fluffy_chat_app.dart index 5d54100157..df6da162e5 100644 --- a/lib/widgets/fluffy_chat_app.dart +++ b/lib/widgets/fluffy_chat_app.dart @@ -39,7 +39,8 @@ class FluffyChatApp extends StatelessWidget { @override Widget build(BuildContext context) { - PlatformWidth.initialize(context); // To initialize size variables according to platform + PlatformWidth.initialize( + context); // To initialize size variables according to platform return ThemeBuilder( builder: (context, themeMode, primaryColor) => MaterialApp.router( title: AppConfig.applicationName, diff --git a/pubspec.yaml b/pubspec.yaml index 21d378a435..9c8663e0a8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,7 +42,7 @@ dependencies: flutter_localizations: sdk: flutter flutter_map: ^4.0.0 - flutter_math_fork: ^0.7.1 + flutter_math_fork: ^0.7.2 flutter_olm: ^1.2.0 flutter_openssl_crypto: ^0.1.0 flutter_ringtone_player: ^3.1.1 From 8fa9248253083c6af485d6f3fc1216077538775e Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Nov 2023 17:32:43 +0100 Subject: [PATCH 15/20] feat: correction L10n --- assets/l10n/intl_en.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 70096705c2..ed0994e183 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2554,7 +2554,7 @@ "youDonTHaveConversation": "You don't have any conversation yet.", "connectChatNetworks": "Connect your chat networks", "addSocialMessagingAccounts": "Add your social messaging accounts", - "addSocialMessagingAccountsText": "Connect all your conversation!", + "addSocialMessagingAccountsText": "Connect all your conversations!", "connected": "Connected", "notConnected": "Not connected", "connectYourSocialAccount": "Login to your account", From 30188e89e50b4b7c757c1178b95e5352f8886927 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Nov 2023 20:36:29 +0100 Subject: [PATCH 16/20] feat: loop limit and error message --- assets/l10n/intl_en.arb | 1 + lib/pages/add_bridge/add_bridge_body.dart | 39 +++++++---- .../add_bridge/connection_bridge_dialog.dart | 17 ++++- .../add_bridge/error_message_dialog.dart | 3 + .../service/bot_bridge_connection.dart | 65 +++++++++++++++---- lib/pages/add_bridge/show_bottom_sheet.dart | 35 ++++++---- 6 files changed, 120 insertions(+), 40 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index ed0994e183..53b8af6c99 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2567,6 +2567,7 @@ "err_desc": "An error has occurred:", "err_toConnect": "A problem occurred when connecting to", "err_timeOut": "A network problem has been detected", + "err_tryAgain": "Please try again", "bridgeBot_menuItemTitle" : "Connected social networks", "bridgeBot_deleteConvTitle": "Delete Conversation", "bridgeBot_deleteConvDescription": "Do you want to delete the conversation with the bot?", diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index 71ca4cd28b..8cd22f455d 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -9,6 +9,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import '../../config/app_config.dart'; import '../../widgets/matrix.dart'; import 'add_bridge_header.dart'; import 'connection_bridge_dialog.dart'; @@ -27,7 +28,6 @@ class AddBridgeBody extends StatefulWidget { class _AddBridgeBodyState extends State { late BotBridgeConnection botConnection; - bool timeoutErrorOccurred = false; @override @@ -38,19 +38,30 @@ class _AddBridgeBodyState extends State { _initStateAsync(); } + @override + void dispose() { + super.dispose(); + } + // Online status update when page is opened Future _initStateAsync() async { try { final instagramConnected = await botConnection.pingWithTimeout( - context, botConnection.instagramPing()); - setState(() { - socialNetwork - .firstWhere((element) => element.name == "Instagram") - .connected = instagramConnected; - socialNetwork - .firstWhere((element) => element.name == "Instagram") - .loading = false; - }); + context, botConnection.instagramPing(),); + if(instagramConnected != 'error' && mounted){ + setState(() { + socialNetwork + .firstWhere((element) => element.name == "Instagram") + .connected = instagramConnected == 'Connected' ? true : false; + socialNetwork + .firstWhere((element) => element.name == "Instagram") + .loading = false; + }); + }else{ + showCatchErrorDialog( + context, + "${L10n.of(context)!.err_toConnect} ${L10n.of(context)!.instagram}",); + } } on TimeoutException { // To indicate that the time-out error has occurred timeoutErrorOccurred = true; @@ -58,9 +69,11 @@ class _AddBridgeBodyState extends State { print("Error pinging Instagram: $error"); await Future.delayed( const Duration(seconds: 1)); // Precaution to let the page load - if (!timeoutErrorOccurred) { - showCatchErrorDialog(context, - "${L10n.of(context)!.err_toConnect} ${L10n.of(context)!.instagram}"); + if (mounted && !timeoutErrorOccurred) { + showCatchErrorDialog( + context, + "${L10n.of(context)!.err_toConnect} ${L10n.of(context)!.instagram}", + ); } } } diff --git a/lib/pages/add_bridge/connection_bridge_dialog.dart b/lib/pages/add_bridge/connection_bridge_dialog.dart index e8bfa42347..9bef9ae523 100644 --- a/lib/pages/add_bridge/connection_bridge_dialog.dart +++ b/lib/pages/add_bridge/connection_bridge_dialog.dart @@ -96,9 +96,14 @@ Future connectToInstagram(BuildContext context, SocialNetwork network, await showFutureLoadingDialog( context: context, future: () async { - if (network.name == "Instagram") { - result = await botConnection.createBridgeInstagram( - username!, password!); + switch (network.name) { + case "Instagram": + result = await botConnection.createBridgeInstagram( + username!, + password!, + ); + break; + // Other network } }, ); @@ -116,6 +121,12 @@ Future connectToInstagram(BuildContext context, SocialNetwork network, } else if (result == "rateLimitError") { // Display a showDialog with an error message related to the rate limit showRateLimitDialog(context); + } else if (result == "error") { + // Display a showDialog with an unknown error message + showCatchErrorDialog( + context, + L10n.of(context)!.err_tryAgain, + ); } } catch (e) { Navigator.of(context).pop(); diff --git a/lib/pages/add_bridge/error_message_dialog.dart b/lib/pages/add_bridge/error_message_dialog.dart index 77591408d0..e07c48fa04 100644 --- a/lib/pages/add_bridge/error_message_dialog.dart +++ b/lib/pages/add_bridge/error_message_dialog.dart @@ -81,6 +81,9 @@ void showCatchErrorDialog(BuildContext context, Object e) { return AlertDialog( title: Text( L10n.of(context)!.err_, + style: const TextStyle( + color: Colors.red, + ), ), content: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/add_bridge/service/bot_bridge_connection.dart b/lib/pages/add_bridge/service/bot_bridge_connection.dart index 537e9667d1..6fb6f69f01 100644 --- a/lib/pages/add_bridge/service/bot_bridge_connection.dart +++ b/lib/pages/add_bridge/service/bot_bridge_connection.dart @@ -17,7 +17,7 @@ class BotBridgeConnection { }); // Ping to find out if we're connected to Instagram - Future instagramPing() async { + Future instagramPing() async { const String botUserId = '@instagrambot:loveto.party'; // Message to spot when we're online @@ -35,13 +35,18 @@ class BotBridgeConnection { final Room? roomBot = client.getRoomById(directChat); - bool result = false; // Variable to track the result of the connection + String result = ''; // Variable to track the result of the connection + + // variable for loop limit + const int maxIterations = 2; + int currentIteration = 0; // Get the latest messages from the room (limited to the specified number) - while (true) { + while (currentIteration < maxIterations) { + // Send the "ping" message to the bot await roomBot?.sendTextEvent("ping"); - await Future.delayed(const Duration(seconds: 2)); // Wait 2 sec + await Future.delayed(const Duration(seconds: 3)); // Wait 2 sec // To take latest message final GetRoomEventsResponse response = await client.getRoomEvents( @@ -62,17 +67,26 @@ class BotBridgeConnection { successfullyMatch.hasMatch(latestMessage)) { print("You're logged"); - result = true; + result = 'Connected'; break; // Exit the loop if bridge is connected } else if (notLoggedMatch.hasMatch(latestMessage) || disconnectMatch.hasMatch(latestMessage)) { print('Not connected'); + result = 'Not Connected'; break; // Exit the loop if bridge is disconnected } } + currentIteration++; + } + + if(currentIteration == maxIterations){ + print("Maximum iterations reached, setting result to 'error'"); + + result = 'error'; } + return result; } @@ -98,8 +112,13 @@ class BotBridgeConnection { String result = ""; // Variable to track the result of the connection + // variable for loop limit + const int maxIterations = 5; + int currentIteration = 0; + // Get the latest messages from the room (limited to the specified number) - while (true) { + while (currentIteration < maxIterations) { + // Send the "login" message to the bot await roomBot?.sendTextEvent("login $username $password"); await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec @@ -143,12 +162,20 @@ class BotBridgeConnection { break; } } + currentIteration++; } + + if(currentIteration == maxIterations){ + print("Maximum iterations reached, setting result to 'error'"); + + result = 'error'; + } + return result; } // To disconnect from Instagram - Future disconnectToInstagram() async { + Future disconnectToInstagram() async { const String botUserId = '@instagrambot:loveto.party'; final RegExp successMatch = RegExp(r"Successfully logged out"); @@ -161,9 +188,13 @@ class BotBridgeConnection { final Room? roomBot = client.getRoomById(directChat); - bool result = true; // Variable to track the result of the connection + String result = "Connected"; // Variable to track the result of the connection + + // variable for loop limit + const int maxIterations = 5; + int currentIteration = 0; - while (true) { + while (currentIteration < maxIterations) { // Send the "logout" message to the bot await roomBot?.sendTextEvent("logout"); await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec @@ -185,17 +216,25 @@ class BotBridgeConnection { if (!successMatch.hasMatch(latestMessage) && !aldreadyLogoutMatch.hasMatch(latestMessage)) { print("You're always connected"); - result = true; + result = 'Connected'; break; } else if (successMatch.hasMatch(latestMessage) || aldreadyLogoutMatch.hasMatch(latestMessage)) { print("You're disconnected"); - result = false; + result = 'Not Connected'; break; // Exit the loop if bridge is connected } } + currentIteration++; } + + if(currentIteration == maxIterations){ + print("Maximum iterations reached, setting result to 'error'"); + + result = 'error'; + } + return result; } @@ -216,8 +255,8 @@ class BotBridgeConnection { } // Function to manage missed deadlines - Future pingWithTimeout( - BuildContext context, Future pingFunction) async { + Future pingWithTimeout( + BuildContext context, Future pingFunction) async { try { // Future.timeout to define a maximum waiting time return await pingFunction.timeout(const Duration(seconds: 15)); diff --git a/lib/pages/add_bridge/show_bottom_sheet.dart b/lib/pages/add_bridge/show_bottom_sheet.dart index bd12b0ee5d..8fb2b92956 100644 --- a/lib/pages/add_bridge/show_bottom_sheet.dart +++ b/lib/pages/add_bridge/show_bottom_sheet.dart @@ -30,9 +30,12 @@ Future showBottomSheetBridge( ), onTap: () async { try { - bool result = true; - Navigator.pop(context); + Navigator.of(context).pop(); + + String result = + ""; // Variable to store the result of the connection + // To show Loading while executing the function await showFutureLoadingDialog( context: context, future: () async { @@ -40,23 +43,33 @@ Future showBottomSheetBridge( case "Instagram": result = await botConnection.disconnectToInstagram(); break; - // For other networks - } - - if (result != false) { - completer.complete(false); + // For other networks } }, ); - completer.complete(true); - } catch (e) { - print("error: $e"); + if (result == "Not Connected") { + completer.complete( + true,); // returns true if is not connected + + } else if (result == "error" || result == 'Connected') { + completer.complete( + false,); + // Display a showDialog with an unknown error message + showCatchErrorDialog( + context, + L10n.of(context)!.err_tryAgain, + ); + } + } catch (e) { Navigator.of(context).pop(); + print(('Error: $e')); + //To view other catch-related errors - showCatchErrorDialog(context, L10n.of(context)!.err_timeOut); + showCatchErrorDialog(context, e); } + }, ), ], From 7e985432c04becabef60cdf8958ff14c1e9e5eb0 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Nov 2023 10:05:37 +0100 Subject: [PATCH 17/20] feat: stop loop when leaving page --- lib/pages/add_bridge/add_bridge_body.dart | 31 +++++++++++++------ .../service/bot_bridge_connection.dart | 15 +++++++-- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index 8cd22f455d..4fbafa8843 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -9,7 +9,6 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import '../../config/app_config.dart'; import '../../widgets/matrix.dart'; import 'add_bridge_header.dart'; import 'connection_bridge_dialog.dart'; @@ -40,15 +39,21 @@ class _AddBridgeBodyState extends State { @override void dispose() { + botConnection.stopProcess(); super.dispose(); } -// Online status update when page is opened + + // Online status update when page is opened Future _initStateAsync() async { try { final instagramConnected = await botConnection.pingWithTimeout( - context, botConnection.instagramPing(),); - if(instagramConnected != 'error' && mounted){ + context, + botConnection.instagramPing(), + ); + if (!mounted) return; // Check if the widget is still mounted + + if (instagramConnected != 'error') { setState(() { socialNetwork .firstWhere((element) => element.name == "Instagram") @@ -57,18 +62,23 @@ class _AddBridgeBodyState extends State { .firstWhere((element) => element.name == "Instagram") .loading = false; }); - }else{ - showCatchErrorDialog( - context, - "${L10n.of(context)!.err_toConnect} ${L10n.of(context)!.instagram}",); + } else { + if (mounted) { + showCatchErrorDialog( + context, + "${L10n.of(context)!.err_toConnect} ${L10n.of(context)!.instagram}", + ); + } } } on TimeoutException { // To indicate that the time-out error has occurred - timeoutErrorOccurred = true; + if (mounted) { + timeoutErrorOccurred = true; + } } catch (error) { print("Error pinging Instagram: $error"); await Future.delayed( - const Duration(seconds: 1)); // Precaution to let the page load + const Duration(seconds: 1),); // Precaution to let the page load if (mounted && !timeoutErrorOccurred) { showCatchErrorDialog( context, @@ -78,6 +88,7 @@ class _AddBridgeBodyState extends State { } } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/pages/add_bridge/service/bot_bridge_connection.dart b/lib/pages/add_bridge/service/bot_bridge_connection.dart index 6fb6f69f01..97980ef5b8 100644 --- a/lib/pages/add_bridge/service/bot_bridge_connection.dart +++ b/lib/pages/add_bridge/service/bot_bridge_connection.dart @@ -11,11 +11,17 @@ import '../error_message_dialog.dart'; // For the moment, rooms are DirectChat class BotBridgeConnection { Client client; + bool continueProcess = true; BotBridgeConnection({ required this.client, }); + // To stop loops (when leaving the page) + void stopProcess() { + continueProcess = false; + } + // Ping to find out if we're connected to Instagram Future instagramPing() async { const String botUserId = '@instagrambot:loveto.party'; @@ -38,15 +44,15 @@ class BotBridgeConnection { String result = ''; // Variable to track the result of the connection // variable for loop limit - const int maxIterations = 2; + const int maxIterations = 5; int currentIteration = 0; // Get the latest messages from the room (limited to the specified number) - while (currentIteration < maxIterations) { + while (continueProcess && currentIteration < maxIterations) { // Send the "ping" message to the bot await roomBot?.sendTextEvent("ping"); - await Future.delayed(const Duration(seconds: 3)); // Wait 2 sec + await Future.delayed(const Duration(seconds: 2)); // Wait sec // To take latest message final GetRoomEventsResponse response = await client.getRoomEvents( @@ -85,6 +91,9 @@ class BotBridgeConnection { print("Maximum iterations reached, setting result to 'error'"); result = 'error'; + }else if(!continueProcess){ + print(('ping stoping')); + result = 'stop'; } return result; From a034ed64f4eb7bc2cdf7f4e0981d6cc4f6d30ac7 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Nov 2023 10:47:40 +0100 Subject: [PATCH 18/20] feat: automatic hostname extract for bot --- lib/pages/add_bridge/add_bridge_body.dart | 11 ++++++---- .../add_bridge/connection_bridge_dialog.dart | 5 +++-- .../add_bridge/model/social_network.dart | 6 ++--- .../service/bot_bridge_connection.dart | 22 +++++++++---------- lib/pages/add_bridge/service/hostname.dart | 19 ++++++++++++++++ lib/pages/add_bridge/show_bottom_sheet.dart | 11 +++++----- 6 files changed, 48 insertions(+), 26 deletions(-) create mode 100644 lib/pages/add_bridge/service/hostname.dart diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index 4fbafa8843..8fef16064d 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:fluffychat/pages/add_bridge/service/bot_bridge_connection.dart'; +import 'package:fluffychat/pages/add_bridge/service/hostname.dart'; import 'package:fluffychat/pages/add_bridge/show_bottom_sheet.dart'; import 'package:fluffychat/pages/add_bridge/show_delete_conversation_dialog.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -27,12 +28,15 @@ class AddBridgeBody extends StatefulWidget { class _AddBridgeBodyState extends State { late BotBridgeConnection botConnection; + late String hostname; bool timeoutErrorOccurred = false; @override void initState() { final client = Matrix.of(context).client; - botConnection = BotBridgeConnection(client: client); + String fullUrl = client.homeserver!.host; + hostname = extractHostName(fullUrl); + botConnection = BotBridgeConnection(client: client, hostname: hostname); super.initState(); _initStateAsync(); } @@ -43,7 +47,6 @@ class _AddBridgeBodyState extends State { super.dispose(); } - // Online status update when page is opened Future _initStateAsync() async { try { @@ -78,7 +81,8 @@ class _AddBridgeBodyState extends State { } catch (error) { print("Error pinging Instagram: $error"); await Future.delayed( - const Duration(seconds: 1),); // Precaution to let the page load + const Duration(seconds: 1), + ); // Precaution to let the page load if (mounted && !timeoutErrorOccurred) { showCatchErrorDialog( context, @@ -88,7 +92,6 @@ class _AddBridgeBodyState extends State { } } - @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/pages/add_bridge/connection_bridge_dialog.dart b/lib/pages/add_bridge/connection_bridge_dialog.dart index 9bef9ae523..98bf5cc8de 100644 --- a/lib/pages/add_bridge/connection_bridge_dialog.dart +++ b/lib/pages/add_bridge/connection_bridge_dialog.dart @@ -98,12 +98,13 @@ Future connectToInstagram(BuildContext context, SocialNetwork network, future: () async { switch (network.name) { case "Instagram": - result = await botConnection.createBridgeInstagram( + result = + await botConnection.createBridgeInstagram( username!, password!, ); break; - // Other network + // Other network } }, ); diff --git a/lib/pages/add_bridge/model/social_network.dart b/lib/pages/add_bridge/model/social_network.dart index 8e98bde402..44a8a8f892 100644 --- a/lib/pages/add_bridge/model/social_network.dart +++ b/lib/pages/add_bridge/model/social_network.dart @@ -21,17 +21,17 @@ final List socialNetwork = [ SocialNetwork( logo: Logo(Logos.facebook_messenger), name: "Facebook Messenger", - chatBot: "", + chatBot: "@facebookbot:", ), SocialNetwork( logo: Logo(Logos.instagram), name: "Instagram", - chatBot: "@instagrambot:loveto.party", + chatBot: "@instagrambot:", ), SocialNetwork( logo: Logo(Logos.whatsapp), name: "Whatsapp", - chatBot: "@whatsappbot:loveto.party", + chatBot: "@whatsappbot:", ), ]; diff --git a/lib/pages/add_bridge/service/bot_bridge_connection.dart b/lib/pages/add_bridge/service/bot_bridge_connection.dart index 97980ef5b8..1b966868c9 100644 --- a/lib/pages/add_bridge/service/bot_bridge_connection.dart +++ b/lib/pages/add_bridge/service/bot_bridge_connection.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:matrix/matrix.dart'; -import 'package:uuid/uuid.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import '../error_message_dialog.dart'; @@ -11,10 +10,12 @@ import '../error_message_dialog.dart'; // For the moment, rooms are DirectChat class BotBridgeConnection { Client client; + String hostname; bool continueProcess = true; BotBridgeConnection({ required this.client, + required this.hostname, }); // To stop loops (when leaving the page) @@ -24,7 +25,7 @@ class BotBridgeConnection { // Ping to find out if we're connected to Instagram Future instagramPing() async { - const String botUserId = '@instagrambot:loveto.party'; + final String botUserId = '@instagrambot:$hostname'; // Message to spot when we're online final RegExp onlineMatch = RegExp(r"MQTT connection is active"); @@ -49,7 +50,6 @@ class BotBridgeConnection { // Get the latest messages from the room (limited to the specified number) while (continueProcess && currentIteration < maxIterations) { - // Send the "ping" message to the bot await roomBot?.sendTextEvent("ping"); await Future.delayed(const Duration(seconds: 2)); // Wait sec @@ -87,11 +87,11 @@ class BotBridgeConnection { currentIteration++; } - if(currentIteration == maxIterations){ + if (currentIteration == maxIterations) { print("Maximum iterations reached, setting result to 'error'"); result = 'error'; - }else if(!continueProcess){ + } else if (!continueProcess) { print(('ping stoping')); result = 'stop'; } @@ -101,7 +101,7 @@ class BotBridgeConnection { // Function for create and login bridge with bot Future createBridgeInstagram(String username, String password) async { - const String botUserId = '@instagrambot:loveto.party'; + final String botUserId = '@instagrambot:$hostname'; // Success phrases to spot final RegExp successMatch = RegExp(r"Successfully logged in"); @@ -127,7 +127,6 @@ class BotBridgeConnection { // Get the latest messages from the room (limited to the specified number) while (currentIteration < maxIterations) { - // Send the "login" message to the bot await roomBot?.sendTextEvent("login $username $password"); await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec @@ -174,7 +173,7 @@ class BotBridgeConnection { currentIteration++; } - if(currentIteration == maxIterations){ + if (currentIteration == maxIterations) { print("Maximum iterations reached, setting result to 'error'"); result = 'error'; @@ -185,7 +184,7 @@ class BotBridgeConnection { // To disconnect from Instagram Future disconnectToInstagram() async { - const String botUserId = '@instagrambot:loveto.party'; + final String botUserId = '@instagrambot:$hostname'; final RegExp successMatch = RegExp(r"Successfully logged out"); final RegExp aldreadyLogoutMatch = @@ -197,7 +196,8 @@ class BotBridgeConnection { final Room? roomBot = client.getRoomById(directChat); - String result = "Connected"; // Variable to track the result of the connection + String result = + "Connected"; // Variable to track the result of the connection // variable for loop limit const int maxIterations = 5; @@ -238,7 +238,7 @@ class BotBridgeConnection { currentIteration++; } - if(currentIteration == maxIterations){ + if (currentIteration == maxIterations) { print("Maximum iterations reached, setting result to 'error'"); result = 'error'; diff --git a/lib/pages/add_bridge/service/hostname.dart b/lib/pages/add_bridge/service/hostname.dart new file mode 100644 index 0000000000..1d3862df35 --- /dev/null +++ b/lib/pages/add_bridge/service/hostname.dart @@ -0,0 +1,19 @@ +String extractHostName(String fullUrl) { + const String prefixToRemove = "https://"; + const String prefixToRemoveTwo = "matrix."; + + // Check if the string begins with "https://". + if (fullUrl.startsWith(prefixToRemove)) { + // replaceFirst to remove the prefix + return fullUrl.replaceFirst(prefixToRemove, ''); + } + + // Check if the string begins with "matrix.". + if (fullUrl.startsWith(prefixToRemoveTwo)) { + // replaceFirst to remove the prefix + return fullUrl.replaceFirst(prefixToRemoveTwo, ''); + } + + // If the prefix is not found, the string remains unchanged. + return fullUrl; +} diff --git a/lib/pages/add_bridge/show_bottom_sheet.dart b/lib/pages/add_bridge/show_bottom_sheet.dart index 8fb2b92956..20c28e98d7 100644 --- a/lib/pages/add_bridge/show_bottom_sheet.dart +++ b/lib/pages/add_bridge/show_bottom_sheet.dart @@ -43,19 +43,19 @@ Future showBottomSheetBridge( case "Instagram": result = await botConnection.disconnectToInstagram(); break; - // For other networks + // For other networks } }, ); if (result == "Not Connected") { - completer.complete( - true,); // returns true if is not connected - + true, + ); // returns true if is not connected } else if (result == "error" || result == 'Connected') { completer.complete( - false,); + false, + ); // Display a showDialog with an unknown error message showCatchErrorDialog( context, @@ -69,7 +69,6 @@ Future showBottomSheetBridge( //To view other catch-related errors showCatchErrorDialog(context, e); } - }, ), ], From 874602f055747ed763f2e29ade3ee803e89d2ae8 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Nov 2023 18:07:26 +0100 Subject: [PATCH 19/20] feat: correction condition --- lib/pages/add_bridge/add_bridge_body.dart | 4 +--- lib/pages/add_bridge/show_bottom_sheet.dart | 12 ++++-------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index 8fef16064d..599f8e1634 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -65,14 +65,12 @@ class _AddBridgeBodyState extends State { .firstWhere((element) => element.name == "Instagram") .loading = false; }); - } else { - if (mounted) { + } else if (mounted) { showCatchErrorDialog( context, "${L10n.of(context)!.err_toConnect} ${L10n.of(context)!.instagram}", ); } - } } on TimeoutException { // To indicate that the time-out error has occurred if (mounted) { diff --git a/lib/pages/add_bridge/show_bottom_sheet.dart b/lib/pages/add_bridge/show_bottom_sheet.dart index 20c28e98d7..9d2d536d4e 100644 --- a/lib/pages/add_bridge/show_bottom_sheet.dart +++ b/lib/pages/add_bridge/show_bottom_sheet.dart @@ -48,14 +48,10 @@ Future showBottomSheetBridge( }, ); - if (result == "Not Connected") { - completer.complete( - true, - ); // returns true if is not connected - } else if (result == "error" || result == 'Connected') { - completer.complete( - false, - ); + // returns true if is not connected + completer.complete(result == "Not Connected"); + + if (result == "error" || result == 'Connected') { // Display a showDialog with an unknown error message showCatchErrorDialog( context, From ad3cae3b28599213705f190c6cb918f17f6e0b09 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Nov 2023 16:30:15 +0100 Subject: [PATCH 20/20] fix: message login not delete by bot --- lib/pages/add_bridge/add_bridge_body.dart | 10 +++---- .../service/bot_bridge_connection.dart | 29 ++++++++++--------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/pages/add_bridge/add_bridge_body.dart b/lib/pages/add_bridge/add_bridge_body.dart index 599f8e1634..e0a0052eb9 100644 --- a/lib/pages/add_bridge/add_bridge_body.dart +++ b/lib/pages/add_bridge/add_bridge_body.dart @@ -66,11 +66,11 @@ class _AddBridgeBodyState extends State { .loading = false; }); } else if (mounted) { - showCatchErrorDialog( - context, - "${L10n.of(context)!.err_toConnect} ${L10n.of(context)!.instagram}", - ); - } + showCatchErrorDialog( + context, + "${L10n.of(context)!.err_toConnect} ${L10n.of(context)!.instagram}", + ); + } } on TimeoutException { // To indicate that the time-out error has occurred if (mounted) { diff --git a/lib/pages/add_bridge/service/bot_bridge_connection.dart b/lib/pages/add_bridge/service/bot_bridge_connection.dart index 1b966868c9..c91ce9623e 100644 --- a/lib/pages/add_bridge/service/bot_bridge_connection.dart +++ b/lib/pages/add_bridge/service/bot_bridge_connection.dart @@ -42,6 +42,10 @@ class BotBridgeConnection { final Room? roomBot = client.getRoomById(directChat); + // Send the "ping" message to the bot + await roomBot?.sendTextEvent("ping"); + await Future.delayed(const Duration(seconds: 2)); // Wait sec + String result = ''; // Variable to track the result of the connection // variable for loop limit @@ -50,10 +54,6 @@ class BotBridgeConnection { // Get the latest messages from the room (limited to the specified number) while (continueProcess && currentIteration < maxIterations) { - // Send the "ping" message to the bot - await roomBot?.sendTextEvent("ping"); - await Future.delayed(const Duration(seconds: 2)); // Wait sec - // To take latest message final GetRoomEventsResponse response = await client.getRoomEvents( directChat, @@ -108,9 +108,10 @@ class BotBridgeConnection { final RegExp alreadySuccessMatch = RegExp(r"You're already logged in"); // Error phrase to spot - final RegExp usernameErrorMatch = - RegExp(r"Please check your username and try again"); + final RegExp usernameErrorMatch = RegExp(r"Invalid username"); final RegExp passwordErrorMatch = RegExp(r"Incorrect password"); + final RegExp nameOrPasswordErrorMatch = + RegExp(r"Incorrect username or password"); final RegExp rateLimitErrorMatch = RegExp(r"rate_limit_error"); // Add a direct chat with the Instagram bot (if you haven't already) @@ -119,6 +120,10 @@ class BotBridgeConnection { final Room? roomBot = client.getRoomById(directChat); + // Send the "login" message to the bot + await roomBot?.sendTextEvent("login $username $password"); + await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec + String result = ""; // Variable to track the result of the connection // variable for loop limit @@ -127,10 +132,6 @@ class BotBridgeConnection { // Get the latest messages from the room (limited to the specified number) while (currentIteration < maxIterations) { - // Send the "login" message to the bot - await roomBot?.sendTextEvent("login $username $password"); - await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec - final GetRoomEventsResponse response = await client.getRoomEvents( directChat, Direction.b, // To get the latest messages @@ -196,6 +197,10 @@ class BotBridgeConnection { final Room? roomBot = client.getRoomById(directChat); + // Send the "logout" message to the bot + await roomBot?.sendTextEvent("logout"); + await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec + String result = "Connected"; // Variable to track the result of the connection @@ -204,10 +209,6 @@ class BotBridgeConnection { int currentIteration = 0; while (currentIteration < maxIterations) { - // Send the "logout" message to the bot - await roomBot?.sendTextEvent("logout"); - await Future.delayed(const Duration(seconds: 5)); // Wait 5 sec - // Get the latest messages from the room (limited to the specified number) final GetRoomEventsResponse response = await client.getRoomEvents( directChat,