diff --git a/assets/images/app_store.svg b/assets/images/app_store.svg
new file mode 100644
index 0000000000..7aaeb5cd12
--- /dev/null
+++ b/assets/images/app_store.svg
@@ -0,0 +1,35 @@
+
diff --git a/assets/images/google_play.svg b/assets/images/google_play.svg
new file mode 100644
index 0000000000..e83dc0cf84
--- /dev/null
+++ b/assets/images/google_play.svg
@@ -0,0 +1,52 @@
+
diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb
index 01a399137b..95a1a60719 100644
--- a/assets/l10n/intl_en.arb
+++ b/assets/l10n/intl_en.arb
@@ -3097,5 +3097,6 @@
"disable_notifications": "Disable notifications",
"logoutDialogWarning": "You will lose access to encrypted messages. We recommend that you enable chat backups before logging out",
"copyNumber": "Copy number",
- "callViaCarrier": "Call via Carrier"
+ "callViaCarrier": "Call via Carrier",
+ "scanQrCodeToJoin": "Installation of the mobile application will allow you to contact people from your phone's address book, your chats will be synchronised between devices"
}
diff --git a/config.sample.json b/config.sample.json
index 27ce4416f8..e1afcdc800 100644
--- a/config.sample.json
+++ b/config.sample.json
@@ -13,5 +13,6 @@
"homeserver": "https://example.com/",
"platform": "platform",
"default_max_upload_avatar_size_in_bytes": 1000000,
- "dev_mode": false
+ "dev_mode": false,
+ "qr_code_download_url": ""
}
diff --git a/docs/configurations/config_web_app_for_public_platform.md b/docs/configurations/config_web_app_for_public_platform.md
index 09bef71ac2..024332e6c7 100644
--- a/docs/configurations/config_web_app_for_public_platform.md
+++ b/docs/configurations/config_web_app_for_public_platform.md
@@ -28,7 +28,8 @@ in [config.sample.json](https://github.com/linagora/twake-on-matrix/blob/main/co
"homeserver": "https://example.com/",
"platform": "platform"
"default_max_upload_avatar_size_in_bytes": 1000000,
- "dev_mode": false
+ "dev_mode": false,
+ "qr_code_download_url": "https://example.com/"
}
```
@@ -46,6 +47,7 @@ in [config.sample.json](https://github.com/linagora/twake-on-matrix/blob/main/co
- `homeserver`: Homeserver
- `platform`: Platform, `saas` for the case of public platform
- `default_max_upload_avatar_size_in_bytes`: Default max upload avatar size
-- `dev_mode`: Enable to run app in IDE
+- `dev_mode`: Enable to run app in IDE,
+- `qr_code_download_url`: URL generate QR code to download app
If you want to disable it, please change the value or remove this from `config.sample.json`
\ No newline at end of file
diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart
index 0837e4097f..9f1b712f6d 100644
--- a/lib/config/app_config.dart
+++ b/lib/config/app_config.dart
@@ -52,6 +52,14 @@ abstract class AppConfig {
static String appTermsOfUse = 'https://twake.app/terms';
+ static String qrCodeDownloadUrl = '';
+
+ static String twakeChatAppleStore =
+ 'https://apps.apple.com/us/app/twake-chat/id6473384641';
+
+ static String twakeChatGooglePlay =
+ 'https://play.google.com/store/apps/details?id=app.twake.android.chat';
+
static double toolbarHeight(BuildContext context) =>
responsive.isMobile(context) ? 48 : 56;
static const Color chatColor = primaryColor;
@@ -250,5 +258,8 @@ abstract class AppConfig {
if (json['dev_mode'] is bool) {
devMode = json['dev_mode'];
}
+ if (json['qr_code_download_url'] is String) {
+ qrCodeDownloadUrl = json['qr_code_download_url'];
+ }
}
}
diff --git a/lib/pages/chat_blank/chat_blank.dart b/lib/pages/chat_blank/chat_blank.dart
index 2fc9be4280..380f484253 100644
--- a/lib/pages/chat_blank/chat_blank.dart
+++ b/lib/pages/chat_blank/chat_blank.dart
@@ -1,5 +1,7 @@
+import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/first_column_inner_routes.dart';
import 'package:fluffychat/pages/chat_blank/chat_blank_style.dart';
+import 'package:fluffychat/pages/chat_blank/chat_qr_code.dart';
import 'package:fluffychat/presentation/mixins/go_to_group_chat_mixin.dart';
import 'package:fluffychat/resource/image_paths.dart';
import 'package:fluffychat/utils/extension/build_context_extension.dart';
@@ -13,9 +15,7 @@ import 'package:go_router/go_router.dart';
import 'package:linagora_design_flutter/linagora_design_flutter.dart';
class ChatBlank extends StatelessWidget {
- final bool loading;
-
- const ChatBlank({this.loading = false, super.key});
+ const ChatBlank({super.key});
@override
Widget build(BuildContext context) {
@@ -34,9 +34,15 @@ class ChatBlank extends StatelessWidget {
if (value) {
return const SizedBox.shrink();
}
- return loading
- ? _ChatBlankLoading(context: context)
- : _ChatBlankNotChat(context: context);
+ return ValueListenableBuilder(
+ valueListenable: Matrix.of(context).showQrCodeDownload,
+ builder: (context, value, _) {
+ if (value && AppConfig.qrCodeDownloadUrl.isNotEmpty) {
+ return const ChatQrCode();
+ }
+ return _ChatBlankNotChat(context: context);
+ },
+ );
},
),
);
@@ -172,21 +178,3 @@ class _ChatBlankRichText extends StatelessWidget with GoToGroupChatMixin {
}
}
}
-
-class _ChatBlankLoading extends StatelessWidget {
- const _ChatBlankLoading({
- required this.context,
- });
-
- final BuildContext context;
-
- @override
- Widget build(BuildContext context) {
- return Center(
- child: SizedBox(
- width: ChatBlankStyle.width(context),
- child: const LinearProgressIndicator(),
- ),
- );
- }
-}
diff --git a/lib/pages/chat_blank/chat_qr_code.dart b/lib/pages/chat_blank/chat_qr_code.dart
new file mode 100644
index 0000000000..9b643ee810
--- /dev/null
+++ b/lib/pages/chat_blank/chat_qr_code.dart
@@ -0,0 +1,135 @@
+import 'package:fluffychat/config/app_config.dart';
+import 'package:fluffychat/resource/image_paths.dart';
+import 'package:fluffychat/utils/responsive/responsive_utils.dart';
+import 'package:fluffychat/utils/url_launcher.dart';
+import 'package:fluffychat/widgets/matrix.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:linagora_design_flutter/colors/linagora_sys_colors.dart';
+import 'package:pretty_qr_code/pretty_qr_code.dart';
+import 'package:flutter_gen/gen_l10n/l10n.dart';
+
+class ChatQrCode extends StatefulWidget {
+ const ChatQrCode({super.key});
+
+ @override
+ State createState() => _ChatQrCodeState();
+}
+
+class _ChatQrCodeState extends State {
+ @protected
+ late QrCode qrCode;
+
+ @protected
+ late QrImage qrImage;
+
+ @protected
+ late PrettyQrDecoration decoration;
+
+ @override
+ void initState() {
+ super.initState();
+
+ qrCode = QrCode.fromData(
+ data: AppConfig.qrCodeDownloadUrl,
+ errorCorrectLevel: QrErrorCorrectLevel.H,
+ );
+
+ qrImage = QrImage(qrCode);
+
+ decoration = PrettyQrDecoration(
+ image: PrettyQrDecorationImage(
+ image: AssetImage(ImagePaths.logoPng),
+ position: PrettyQrDecorationImagePosition.embedded,
+ ),
+ );
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ Matrix.of(context).resetFirstLogin();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ width: double.infinity,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(
+ bottom: 36,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ InkWell(
+ onTap: () {
+ UrlLauncher(
+ context,
+ url: AppConfig.twakeChatGooglePlay,
+ ).launchUrl();
+ },
+ child: SvgPicture.asset(
+ ImagePaths.googlePlay,
+ ),
+ ),
+ const SizedBox(width: 24),
+ InkWell(
+ onTap: () {
+ UrlLauncher(
+ context,
+ url: AppConfig.twakeChatAppleStore,
+ ).launchUrl();
+ },
+ child: SvgPicture.asset(
+ ImagePaths.appStore,
+ ),
+ ),
+ ],
+ ),
+ ),
+ SizedBox(
+ width: 200,
+ height: 200,
+ child: TweenAnimationBuilder(
+ tween: PrettyQrDecorationTween(
+ begin: decoration,
+ end: decoration,
+ ),
+ curve: Curves.ease,
+ duration: const Duration(
+ milliseconds: 240,
+ ),
+ builder: (context, decoration, child) {
+ return PrettyQrView(
+ qrImage: qrImage,
+ decoration: decoration,
+ );
+ },
+ ),
+ ),
+ Container(
+ constraints: const BoxConstraints(
+ maxWidth: ResponsiveUtils.bodyRadioWidth,
+ ),
+ padding: const EdgeInsets.only(
+ top: 16,
+ left: 24,
+ right: 24,
+ ),
+ child: Text(
+ L10n.of(context)!.scanQrCodeToJoin,
+ style: Theme.of(context).textTheme.titleMedium?.copyWith(
+ color: LinagoraSysColors.material().onSurface,
+ ),
+ textAlign: TextAlign.center,
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/resource/image_paths.dart b/lib/resource/image_paths.dart
index 71091fa7d9..c6d62de316 100644
--- a/lib/resource/image_paths.dart
+++ b/lib/resource/image_paths.dart
@@ -53,6 +53,9 @@ class ImagePaths {
static String get icPersonCheck => _getImagePath('ic_person_check.svg');
static String get icTwakeImageLogoBeta =>
_getImagePath('ic_twake_image_beta.svg');
+ static String get logoPng => _getAssetPath('logo.png');
+ static String get appStore => _getImagePath('app_store.svg');
+ static String get googlePlay => _getImagePath('google_play.svg');
static String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
diff --git a/lib/widgets/layouts/loading_view.dart b/lib/widgets/layouts/loading_view.dart
deleted file mode 100644
index 1c75e0f730..0000000000
--- a/lib/widgets/layouts/loading_view.dart
+++ /dev/null
@@ -1,30 +0,0 @@
-import 'package:fluffychat/pages/chat_blank/chat_blank.dart';
-import 'package:flutter/material.dart';
-import 'package:go_router/go_router.dart';
-
-import 'package:matrix/matrix.dart';
-import 'package:fluffychat/utils/update_checker_no_store.dart';
-import 'package:fluffychat/widgets/matrix.dart';
-
-class LoadingView extends StatelessWidget {
- const LoadingView({super.key});
-
- @override
- Widget build(BuildContext context) {
- WidgetsBinding.instance.addPostFrameCallback(
- (_) async {
- await UpdateCheckerNoStore(context).checkUpdate();
- context.go(
- Matrix.of(context).widget.clients.any(
- (client) =>
- client.onLoginStateChanged.value == LoginState.loggedIn,
- )
- ? '/rooms'
- : '/home',
- extra: GoRouterState.of(context).pathParameters,
- );
- },
- );
- return const ChatBlank(loading: true);
- }
-}
diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart
index d912db9df9..ce4234d564 100644
--- a/lib/widgets/matrix.dart
+++ b/lib/widgets/matrix.dart
@@ -91,6 +91,10 @@ class MatrixState extends State
bool waitForFirstSync = false;
+ bool firstLogin = false;
+
+ ValueNotifier showQrCodeDownload = ValueNotifier(false);
+
ValueNotifier showToMBootstrap = ValueNotifier(false);
bool get twakeSupported {
@@ -308,6 +312,7 @@ class MatrixState extends State
} else {
initConfigMobile().then((_) => initSettings());
}
+ listenShowToMBootstrap();
});
}
@@ -424,6 +429,7 @@ class MatrixState extends State
LoginState loginState,
) async {
waitForFirstSync = false;
+ markFirstLogin();
await setUpToMServicesInLogin(newActiveClient);
await _storePersistActiveAccount(newActiveClient);
matrixState.reSyncContacts();
@@ -830,6 +836,25 @@ class MatrixState extends State
_contactsManager.reSyncContacts();
}
+ void markFirstLogin() {
+ firstLogin = true;
+ }
+
+ void resetFirstLogin() {
+ firstLogin = false;
+ handleShowQrCodeDownload(firstLogin);
+ }
+
+ void handleShowQrCodeDownload(bool show) {
+ showQrCodeDownload.value = show;
+ }
+
+ void listenShowToMBootstrap() {
+ showToMBootstrap.addListener(() {
+ handleShowQrCodeDownload(firstLogin);
+ });
+ }
+
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
Logs().i('didChangeAppLifecycleState: AppLifecycleState = $state');
@@ -909,7 +934,7 @@ class MatrixState extends State
backgroundPush?.onRoomSync?.cancel();
showToMBootstrap.dispose();
linuxNotifications?.close();
-
+ showQrCodeDownload.dispose();
super.dispose();
}
diff --git a/pubspec.lock b/pubspec.lock
index e175271b50..87ff185715 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -2332,6 +2332,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.1"
+ pretty_qr_code:
+ dependency: "direct main"
+ description:
+ name: pretty_qr_code
+ sha256: cbdb4af29da1c1fa21dd76f809646c591320ab9e435d3b0eab867492d43607d5
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.3.0"
process:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 4d1fae9222..6c991cf796 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -212,6 +212,7 @@ dependencies:
flutter_avif: 2.4.1
heif_converter: 1.0.0
pull_down_button: 0.10.2
+ pretty_qr_code: 3.3.0
dev_dependencies:
build_runner: 2.4.12