Terms that show up let user accept

This commit is contained in:
LittleSheep 2024-09-19 20:34:04 +08:00
parent 0cc842c030
commit 16b2e3a0c7
6 changed files with 166 additions and 43 deletions

View File

@ -432,5 +432,13 @@
"update": "Update", "update": "Update",
"updateCheckStrictly": "Strict mode", "updateCheckStrictly": "Strict mode",
"updateCheckStrictlyDesc": "If enabled, the app will ask for updating once the local version is different from remote one.", "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"
} }

View File

@ -428,5 +428,13 @@
"update": "更新", "update": "更新",
"updateCheckStrictly": "严格模式", "updateCheckStrictly": "严格模式",
"updateCheckStrictlyDesc": "如果启用,应用程序将会在本地版本与远程版本不同时询问更新,而不会检查版本号大小。", "updateCheckStrictlyDesc": "如果启用,应用程序将会在本地版本与远程版本不同时询问更新,而不会检查版本号大小。",
"updateMayAvailable": "版本 @version 现已可用,你可以前往应用商店或是我们的官网下载更新。" "updateMayAvailable": "版本 @version 现已可用,你可以前往应用商店或是我们的官网下载更新。",
"termAccept": "我已阅读并同意 Solar Network 各项条款",
"termAcceptDesc": "包括但不限于《用户守则》和《隐私政策》",
"termAcceptLink": "浏览条款",
"termAcceptNextWithAgree": "点击 “下一步”,即表示你同意我们的各项条款,包括其之后的更新。你应该在注册时已经同意过了。",
"termRelated": "相关条款",
"projectWebsite": "项目网站",
"appDetails": "应用详情",
"iAmNotRobot": "我不是机器人"
} }

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:get/get.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
@ -49,7 +50,7 @@ class AboutScreen extends StatelessWidget {
const Gap(16), const Gap(16),
TextButton( TextButton(
style: denseButtonStyle, style: denseButtonStyle,
child: const Text('App Details'), child: Text('appDetails'.tr),
onPressed: () async { onPressed: () async {
final info = await PackageInfo.fromPlatform(); final info = await PackageInfo.fromPlatform();
@ -68,11 +69,18 @@ class AboutScreen extends StatelessWidget {
), ),
TextButton( TextButton(
style: denseButtonStyle, style: denseButtonStyle,
child: const Text('Project Website'), child: Text('projectWebsite'.tr),
onPressed: () { onPressed: () {
launchUrlString('https://solsynth.dev/products/solar-network'); launchUrlString('https://solsynth.dev/products/solar-network');
}, },
), ),
TextButton(
style: denseButtonStyle,
child: Text('termRelated'.tr),
onPressed: () {
launchUrlString('https://solsynth.dev/terms');
},
),
const Gap(16), const Gap(16),
const Text( const Text(
'Open-sourced under AGPLv3', 'Open-sourced under AGPLv3',

View File

@ -13,6 +13,7 @@ import 'package:solian/providers/relation.dart';
import 'package:solian/providers/websocket.dart'; import 'package:solian/providers/websocket.dart';
import 'package:solian/services.dart'; import 'package:solian/services.dart';
import 'package:solian/widgets/sized_container.dart'; import 'package:solian/widgets/sized_container.dart';
import 'package:url_launcher/url_launcher_string.dart';
class SignInScreen extends StatefulWidget { class SignInScreen extends StatefulWidget {
const SignInScreen({super.key}); const SignInScreen({super.key});
@ -167,7 +168,6 @@ class _SignInScreenState extends State<SignInScreen> {
final result = AuthResult.fromJson(resp.body); final result = AuthResult.fromJson(resp.body);
_currentTicket = result.ticket; _currentTicket = result.ticket;
_passwordController.clear();
// Finish sign in if possible // Finish sign in if possible
if (result.isFinished) { if (result.isFinished) {
@ -185,11 +185,13 @@ class _SignInScreenState extends State<SignInScreen> {
autoStartBackgroundNotificationService(); autoStartBackgroundNotificationService();
Navigator.pop(context, true); Navigator.pop(context, true);
_passwordController.clear();
}); });
} else { } else {
// Skip the first step // Skip the first step
_factorPicked = null; _factorPicked = null;
_factorPickedType = null; _factorPickedType = null;
_passwordController.clear();
setState(() => _period += 2); setState(() => _period += 2);
} }
} catch (e) { } catch (e) {
@ -210,9 +212,8 @@ class _SignInScreenState extends State<SignInScreen> {
case 2: case 2:
_passwordController.clear(); _passwordController.clear();
_factorPickedType = null; _factorPickedType = null;
default:
setState(() => _period--);
} }
setState(() => _period--);
} }
@override @override
@ -235,16 +236,18 @@ class _SignInScreenState extends State<SignInScreen> {
); );
}, },
child: switch (_period % 3) { child: switch (_period % 3) {
1 => Column( 1 => ListView(
shrinkWrap: true,
key: const ValueKey<int>(1), key: const ValueKey<int>(1),
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ClipRRect( Align(
borderRadius: const BorderRadius.all(Radius.circular(8)), alignment: Alignment.centerLeft,
child: child: ClipRRect(
Image.asset('assets/logo.png', width: 64, height: 64), borderRadius: const BorderRadius.all(Radius.circular(8)),
).paddingOnly(bottom: 8, left: 4), child:
Image.asset('assets/logo.png', width: 64, height: 64),
).paddingOnly(bottom: 8, left: 4),
),
Text( Text(
'signinPickFactor'.tr, 'signinPickFactor'.tr,
style: const TextStyle( style: const TextStyle(
@ -323,16 +326,18 @@ class _SignInScreenState extends State<SignInScreen> {
), ),
], ],
), ),
2 => Column( 2 => ListView(
key: const ValueKey<int>(2), key: const ValueKey<int>(2),
mainAxisAlignment: MainAxisAlignment.center, shrinkWrap: true,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ClipRRect( Align(
borderRadius: const BorderRadius.all(Radius.circular(8)), alignment: Alignment.centerLeft,
child: child: ClipRRect(
Image.asset('assets/logo.png', width: 64, height: 64), borderRadius: const BorderRadius.all(Radius.circular(8)),
).paddingOnly(bottom: 8, left: 4), child:
Image.asset('assets/logo.png', width: 64, height: 64),
).paddingOnly(bottom: 8, left: 4),
),
Text( Text(
'signinEnterPassword'.tr, 'signinEnterPassword'.tr,
style: const TextStyle( style: const TextStyle(
@ -396,16 +401,18 @@ class _SignInScreenState extends State<SignInScreen> {
), ),
], ],
), ),
_ => Column( _ => ListView(
key: const ValueKey<int>(0), key: const ValueKey<int>(0),
mainAxisAlignment: MainAxisAlignment.center, shrinkWrap: true,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ClipRRect( Align(
borderRadius: const BorderRadius.all(Radius.circular(8)), alignment: Alignment.centerLeft,
child: child: ClipRRect(
Image.asset('assets/logo.png', width: 64, height: 64), borderRadius: const BorderRadius.all(Radius.circular(8)),
).paddingOnly(bottom: 8, left: 4), child:
Image.asset('assets/logo.png', width: 64, height: 64),
).paddingOnly(bottom: 8, left: 4),
),
Text( Text(
'signinGreeting'.tr, 'signinGreeting'.tr,
style: const TextStyle( style: const TextStyle(
@ -451,11 +458,50 @@ class _SignInScreenState extends State<SignInScreen> {
), ),
], ],
), ),
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),
); );
} }
} }

View File

@ -4,6 +4,7 @@ import 'package:get/get.dart';
import 'package:solian/exts.dart'; import 'package:solian/exts.dart';
import 'package:solian/services.dart'; import 'package:solian/services.dart';
import 'package:solian/widgets/sized_container.dart'; import 'package:solian/widgets/sized_container.dart';
import 'package:url_launcher/url_launcher_string.dart';
class SignUpScreen extends StatefulWidget { class SignUpScreen extends StatefulWidget {
const SignUpScreen({super.key}); const SignUpScreen({super.key});
@ -18,7 +19,7 @@ class _SignUpScreenState extends State<SignUpScreen> {
final _nicknameController = TextEditingController(); final _nicknameController = TextEditingController();
final _passwordController = TextEditingController(); final _passwordController = TextEditingController();
void performAction(BuildContext context) async { void _performAction(BuildContext context) async {
final email = _emailController.value.text; final email = _emailController.value.text;
final username = _usernameController.value.text; final username = _usernameController.value.text;
final nickname = _nicknameController.value.text; final nickname = _nicknameController.value.text;
@ -60,20 +61,24 @@ class _SignUpScreenState extends State<SignUpScreen> {
} }
} }
bool _isTermAccepted = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Material( return Material(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
child: CenteredContainer( child: CenteredContainer(
maxWidth: 360, maxWidth: 360,
child: Column( child: ListView(
mainAxisSize: MainAxisSize.min, shrinkWrap: true,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ClipRRect( Align(
borderRadius: const BorderRadius.all(Radius.circular(8)), alignment: Alignment.centerLeft,
child: Image.asset('assets/logo.png', width: 64, height: 64), child: ClipRRect(
).paddingOnly(bottom: 8, left: 4), borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Image.asset('assets/logo.png', width: 64, height: 64),
).paddingOnly(bottom: 8, left: 4),
),
Text( Text(
'signupGreeting'.tr, 'signupGreeting'.tr,
style: const TextStyle( style: const TextStyle(
@ -136,12 +141,58 @@ class _SignUpScreenState extends State<SignUpScreen> {
), ),
onTapOutside: (_) => onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(), 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), const Gap(16),
Align( Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: TextButton( child: TextButton(
onPressed:
!_isTermAccepted ? null : () => _performAction(context),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@ -149,12 +200,11 @@ class _SignUpScreenState extends State<SignUpScreen> {
const Icon(Icons.chevron_right), const Icon(Icons.chevron_right),
], ],
), ),
onPressed: () => performAction(context),
), ),
) )
], ],
), ),
), ).paddingAll(24),
); );
} }
} }

View File

@ -35,6 +35,9 @@ abstract class AppTheme {
brightness: brightness, brightness: brightness,
seedColor: seedColor ?? const Color.fromRGBO(154, 98, 91, 1), seedColor: seedColor ?? const Color.fromRGBO(154, 98, 91, 1),
), ),
snackBarTheme: const SnackBarThemeData(
behavior: SnackBarBehavior.floating,
),
fontFamily: 'Comfortaa', fontFamily: 'Comfortaa',
fontFamilyFallback: [ fontFamilyFallback: [
'NotoSansSC', 'NotoSansSC',