From 16b2e3a0c764daf8377d8123ba995f5f6d542524 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 19 Sep 2024 20:34:04 +0800 Subject: [PATCH] :sparkles: Terms that show up let user accept --- assets/locales/en_us.json | 10 +++- assets/locales/zh_cn.json | 10 +++- lib/screens/about.dart | 12 ++++- lib/screens/auth/signin.dart | 102 +++++++++++++++++++++++++---------- lib/screens/auth/signup.dart | 72 +++++++++++++++++++++---- lib/theme.dart | 3 ++ 6 files changed, 166 insertions(+), 43 deletions(-) diff --git a/assets/locales/en_us.json b/assets/locales/en_us.json index 3bc035d..61a13d8 100644 --- a/assets/locales/en_us.json +++ b/assets/locales/en_us.json @@ -432,5 +432,13 @@ "update": "Update", "updateCheckStrictly": "Strict mode", "updateCheckStrictlyDesc": "If enabled, the app will ask for updating once the local version is different from remote one.", - "updateMayAvailable": "App version @version is available, you can update from app store or our website." + "updateMayAvailable": "App version @version is available, you can update from app store or our website.", + "termAccept": "I've read and agree to Solar Network's Terms", + "termAcceptDesc": "Including but not limited to \"User Agreement\" and \"Privacy Policy\"", + "termAcceptLink": "View terms", + "termAcceptNextWithAgree": "By clicking the \"Next\", it means you agree to our terms and its updates. You should already agreed with them while you sign up.", + "termRelated": "Related Terms", + "appDetails": "App Details", + "projectWebsite": "Project Website", + "iAmNotRobot": "I'm not a Robot" } diff --git a/assets/locales/zh_cn.json b/assets/locales/zh_cn.json index 53be8fb..2065bed 100644 --- a/assets/locales/zh_cn.json +++ b/assets/locales/zh_cn.json @@ -428,5 +428,13 @@ "update": "更新", "updateCheckStrictly": "严格模式", "updateCheckStrictlyDesc": "如果启用,应用程序将会在本地版本与远程版本不同时询问更新,而不会检查版本号大小。", - "updateMayAvailable": "版本 @version 现已可用,你可以前往应用商店或是我们的官网下载更新。" + "updateMayAvailable": "版本 @version 现已可用,你可以前往应用商店或是我们的官网下载更新。", + "termAccept": "我已阅读并同意 Solar Network 各项条款", + "termAcceptDesc": "包括但不限于《用户守则》和《隐私政策》", + "termAcceptLink": "浏览条款", + "termAcceptNextWithAgree": "点击 “下一步”,即表示你同意我们的各项条款,包括其之后的更新。你应该在注册时已经同意过了。", + "termRelated": "相关条款", + "projectWebsite": "项目网站", + "appDetails": "应用详情", + "iAmNotRobot": "我不是机器人" } diff --git a/lib/screens/about.dart b/lib/screens/about.dart index 0ac995a..a2cfac4 100644 --- a/lib/screens/about.dart +++ b/lib/screens/about.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:get/get.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -49,7 +50,7 @@ class AboutScreen extends StatelessWidget { const Gap(16), TextButton( style: denseButtonStyle, - child: const Text('App Details'), + child: Text('appDetails'.tr), onPressed: () async { final info = await PackageInfo.fromPlatform(); @@ -68,11 +69,18 @@ class AboutScreen extends StatelessWidget { ), TextButton( style: denseButtonStyle, - child: const Text('Project Website'), + child: Text('projectWebsite'.tr), onPressed: () { launchUrlString('https://solsynth.dev/products/solar-network'); }, ), + TextButton( + style: denseButtonStyle, + child: Text('termRelated'.tr), + onPressed: () { + launchUrlString('https://solsynth.dev/terms'); + }, + ), const Gap(16), const Text( 'Open-sourced under AGPLv3', diff --git a/lib/screens/auth/signin.dart b/lib/screens/auth/signin.dart index e78bd68..6eec356 100644 --- a/lib/screens/auth/signin.dart +++ b/lib/screens/auth/signin.dart @@ -13,6 +13,7 @@ import 'package:solian/providers/relation.dart'; import 'package:solian/providers/websocket.dart'; import 'package:solian/services.dart'; import 'package:solian/widgets/sized_container.dart'; +import 'package:url_launcher/url_launcher_string.dart'; class SignInScreen extends StatefulWidget { const SignInScreen({super.key}); @@ -167,7 +168,6 @@ class _SignInScreenState extends State { final result = AuthResult.fromJson(resp.body); _currentTicket = result.ticket; - _passwordController.clear(); // Finish sign in if possible if (result.isFinished) { @@ -185,11 +185,13 @@ class _SignInScreenState extends State { autoStartBackgroundNotificationService(); Navigator.pop(context, true); + _passwordController.clear(); }); } else { // Skip the first step _factorPicked = null; _factorPickedType = null; + _passwordController.clear(); setState(() => _period += 2); } } catch (e) { @@ -210,9 +212,8 @@ class _SignInScreenState extends State { case 2: _passwordController.clear(); _factorPickedType = null; - default: - setState(() => _period--); } + setState(() => _period--); } @override @@ -235,16 +236,18 @@ class _SignInScreenState extends State { ); }, child: switch (_period % 3) { - 1 => Column( + 1 => ListView( + shrinkWrap: true, key: const ValueKey(1), - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, children: [ - ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(8)), - child: - Image.asset('assets/logo.png', width: 64, height: 64), - ).paddingOnly(bottom: 8, left: 4), + Align( + alignment: Alignment.centerLeft, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(8)), + child: + Image.asset('assets/logo.png', width: 64, height: 64), + ).paddingOnly(bottom: 8, left: 4), + ), Text( 'signinPickFactor'.tr, style: const TextStyle( @@ -323,16 +326,18 @@ class _SignInScreenState extends State { ), ], ), - 2 => Column( + 2 => ListView( key: const ValueKey(2), - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, + shrinkWrap: true, children: [ - ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(8)), - child: - Image.asset('assets/logo.png', width: 64, height: 64), - ).paddingOnly(bottom: 8, left: 4), + Align( + alignment: Alignment.centerLeft, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(8)), + child: + Image.asset('assets/logo.png', width: 64, height: 64), + ).paddingOnly(bottom: 8, left: 4), + ), Text( 'signinEnterPassword'.tr, style: const TextStyle( @@ -396,16 +401,18 @@ class _SignInScreenState extends State { ), ], ), - _ => Column( + _ => ListView( key: const ValueKey(0), - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, + shrinkWrap: true, children: [ - ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(8)), - child: - Image.asset('assets/logo.png', width: 64, height: 64), - ).paddingOnly(bottom: 8, left: 4), + Align( + alignment: Alignment.centerLeft, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(8)), + child: + Image.asset('assets/logo.png', width: 64, height: 64), + ).paddingOnly(bottom: 8, left: 4), + ), Text( 'signinGreeting'.tr, style: const TextStyle( @@ -451,11 +458,50 @@ class _SignInScreenState extends State { ), ], ), + const Gap(12), + Align( + alignment: Alignment.centerRight, + child: Container( + constraints: const BoxConstraints(maxWidth: 290), + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + 'termAcceptNextWithAgree'.tr, + textAlign: TextAlign.end, + style: + Theme.of(context).textTheme.bodySmall!.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.75), + ), + ), + Material( + color: Colors.transparent, + child: InkWell( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text('termAcceptLink'.tr), + const Gap(4), + const Icon(Icons.launch, size: 14), + ], + ), + onTap: () { + launchUrlString('https://solsynth.dev/terms'); + }, + ), + ), + ], + ), + ).paddingSymmetric(horizontal: 16), + ), ], ), }, ), - ), + ).paddingAll(24), ); } } diff --git a/lib/screens/auth/signup.dart b/lib/screens/auth/signup.dart index b5bafd9..9c65b2a 100644 --- a/lib/screens/auth/signup.dart +++ b/lib/screens/auth/signup.dart @@ -4,6 +4,7 @@ import 'package:get/get.dart'; import 'package:solian/exts.dart'; import 'package:solian/services.dart'; import 'package:solian/widgets/sized_container.dart'; +import 'package:url_launcher/url_launcher_string.dart'; class SignUpScreen extends StatefulWidget { const SignUpScreen({super.key}); @@ -18,7 +19,7 @@ class _SignUpScreenState extends State { final _nicknameController = TextEditingController(); final _passwordController = TextEditingController(); - void performAction(BuildContext context) async { + void _performAction(BuildContext context) async { final email = _emailController.value.text; final username = _usernameController.value.text; final nickname = _nicknameController.value.text; @@ -60,20 +61,24 @@ class _SignUpScreenState extends State { } } + bool _isTermAccepted = false; + @override Widget build(BuildContext context) { return Material( color: Theme.of(context).colorScheme.surface, child: CenteredContainer( maxWidth: 360, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + child: ListView( + shrinkWrap: true, children: [ - ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(8)), - child: Image.asset('assets/logo.png', width: 64, height: 64), - ).paddingOnly(bottom: 8, left: 4), + Align( + alignment: Alignment.centerLeft, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(8)), + child: Image.asset('assets/logo.png', width: 64, height: 64), + ).paddingOnly(bottom: 8, left: 4), + ), Text( 'signupGreeting'.tr, style: const TextStyle( @@ -136,12 +141,58 @@ class _SignUpScreenState extends State { ), onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), - onSubmitted: (_) => performAction(context), + onSubmitted: (_) => _performAction(context), + ), + const Gap(8), + CheckboxListTile( + value: _isTermAccepted, + title: Text('termAccept'.tr), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(8), + ), + ), + subtitle: RichText( + text: TextSpan( + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.75), + ), + children: [ + TextSpan(text: 'termAcceptDesc'.tr), + WidgetSpan( + child: Material( + color: Colors.transparent, + child: InkWell( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text('termAcceptLink'.tr), + const Gap(4), + const Icon(Icons.launch, size: 14), + ], + ), + onTap: () { + launchUrlString('https://solsynth.dev/terms'); + }, + ), + ), + ), + ], + ), + ), + onChanged: (value) { + setState(() => _isTermAccepted = value ?? false); + }, ), const Gap(16), Align( alignment: Alignment.centerRight, child: TextButton( + onPressed: + !_isTermAccepted ? null : () => _performAction(context), child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -149,12 +200,11 @@ class _SignUpScreenState extends State { const Icon(Icons.chevron_right), ], ), - onPressed: () => performAction(context), ), ) ], ), - ), + ).paddingAll(24), ); } } diff --git a/lib/theme.dart b/lib/theme.dart index dd12e72..91c1c14 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -35,6 +35,9 @@ abstract class AppTheme { brightness: brightness, seedColor: seedColor ?? const Color.fromRGBO(154, 98, 91, 1), ), + snackBarTheme: const SnackBarThemeData( + behavior: SnackBarBehavior.floating, + ), fontFamily: 'Comfortaa', fontFamilyFallback: [ 'NotoSansSC',