Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TW-2128: Implement QR code to download app #2130

Merged
merged 3 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions assets/images/app_store.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 52 additions & 0 deletions assets/images/google_play.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion assets/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
3 changes: 2 additions & 1 deletion config.sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": ""
}
6 changes: 4 additions & 2 deletions docs/configurations/config_web_app_for_public_platform.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/"
}
```

Expand All @@ -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`
11 changes: 11 additions & 0 deletions lib/config/app_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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'];
}
}
}
36 changes: 12 additions & 24 deletions lib/pages/chat_blank/chat_blank.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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) {
Expand All @@ -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);
},
);
},
),
);
Expand Down Expand Up @@ -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(),
),
);
}
}
135 changes: 135 additions & 0 deletions lib/pages/chat_blank/chat_qr_code.dart
Original file line number Diff line number Diff line change
@@ -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<ChatQrCode> createState() => _ChatQrCodeState();
}

class _ChatQrCodeState extends State<ChatQrCode> {
@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.twakeChatGooglePlay,
hoangdat marked this conversation as resolved.
Show resolved Hide resolved
).launchUrl();
},
child: SvgPicture.asset(
ImagePaths.appStore,
),
),
],
),
),
SizedBox(
width: 200,
height: 200,
child: TweenAnimationBuilder<PrettyQrDecoration>(
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,
),
),
],
),
);
}
}
3 changes: 3 additions & 0 deletions lib/resource/image_paths.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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');
hoangdat marked this conversation as resolved.
Show resolved Hide resolved
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;
Expand Down
30 changes: 0 additions & 30 deletions lib/widgets/layouts/loading_view.dart

This file was deleted.

27 changes: 26 additions & 1 deletion lib/widgets/matrix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ class MatrixState extends State<Matrix>

bool waitForFirstSync = false;

bool firstLogin = false;

ValueNotifier<bool> showQrCodeDownload = ValueNotifier(false);
hoangdat marked this conversation as resolved.
Show resolved Hide resolved

ValueNotifier<bool> showToMBootstrap = ValueNotifier(false);

bool get twakeSupported {
Expand Down Expand Up @@ -308,6 +312,7 @@ class MatrixState extends State<Matrix>
} else {
initConfigMobile().then((_) => initSettings());
}
listenShowToMBootstrap();
});
}

Expand Down Expand Up @@ -424,6 +429,7 @@ class MatrixState extends State<Matrix>
LoginState loginState,
) async {
waitForFirstSync = false;
markFirstLogin();
await setUpToMServicesInLogin(newActiveClient);
await _storePersistActiveAccount(newActiveClient);
matrixState.reSyncContacts();
Expand Down Expand Up @@ -830,6 +836,25 @@ class MatrixState extends State<Matrix>
_contactsManager.reSyncContacts();
}

void markFirstLogin() {
firstLogin = true;
}

void resetFirstLogin() {
hoangdat marked this conversation as resolved.
Show resolved Hide resolved
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');
Expand Down Expand Up @@ -909,7 +934,7 @@ class MatrixState extends State<Matrix>
backgroundPush?.onRoomSync?.cancel();
showToMBootstrap.dispose();
linuxNotifications?.close();

showQrCodeDownload.dispose();
super.dispose();
}

Expand Down
8 changes: 8 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down