mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-05-25 13:12:13 +00:00
redesigning register view
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
This commit is contained in:
parent
32231d11c2
commit
ea41158872
9 changed files with 485 additions and 253 deletions
|
|
@ -32,7 +32,6 @@ import 'package:twonly/src/services/background/callback_dispatcher.background.da
|
||||||
import 'package:twonly/src/services/backup.service.dart';
|
import 'package:twonly/src/services/backup.service.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/services/memories/memories.service.dart';
|
import 'package:twonly/src/services/memories/memories.service.dart';
|
||||||
|
|
||||||
import 'package:twonly/src/services/notifications/fcm.notifications.dart';
|
import 'package:twonly/src/services/notifications/fcm.notifications.dart';
|
||||||
import 'package:twonly/src/services/notifications/setup.notifications.dart';
|
import 'package:twonly/src/services/notifications/setup.notifications.dart';
|
||||||
import 'package:twonly/src/services/user.service.dart';
|
import 'package:twonly/src/services/user.service.dart';
|
||||||
|
|
|
||||||
|
|
@ -98,16 +98,10 @@ abstract class AppLocalizations {
|
||||||
Locale('en'),
|
Locale('en'),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// No description provided for @registerTitle.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Welcome to twonly!'**
|
|
||||||
String get registerTitle;
|
|
||||||
|
|
||||||
/// No description provided for @registerSlogan.
|
/// No description provided for @registerSlogan.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'twonly, a privacy friendly way to connect with friends through secure, spontaneous image sharing'**
|
/// **'Stay in touch with friends privately and securely.'**
|
||||||
String get registerSlogan;
|
String get registerSlogan;
|
||||||
|
|
||||||
/// No description provided for @onboardingWelcomeTitle.
|
/// No description provided for @onboardingWelcomeTitle.
|
||||||
|
|
@ -179,7 +173,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @registerUsernameSlogan.
|
/// No description provided for @registerUsernameSlogan.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Please select a username so others can find you!'**
|
/// **'Your public username'**
|
||||||
String get registerUsernameSlogan;
|
String get registerUsernameSlogan;
|
||||||
|
|
||||||
/// No description provided for @registerUsernameDecoration.
|
/// No description provided for @registerUsernameDecoration.
|
||||||
|
|
@ -191,7 +185,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @registerUsernameLimits.
|
/// No description provided for @registerUsernameLimits.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Your username must be at least 3 characters long.'**
|
/// **'At least 3 characters.'**
|
||||||
String get registerUsernameLimits;
|
String get registerUsernameLimits;
|
||||||
|
|
||||||
/// No description provided for @registerProofOfWorkFailed.
|
/// No description provided for @registerProofOfWorkFailed.
|
||||||
|
|
@ -1583,7 +1577,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @twonlySafeRecoverTitle.
|
/// No description provided for @twonlySafeRecoverTitle.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Recovery'**
|
/// **'Restore backup'**
|
||||||
String get twonlySafeRecoverTitle;
|
String get twonlySafeRecoverTitle;
|
||||||
|
|
||||||
/// No description provided for @twonlySafeRecoverDesc.
|
/// No description provided for @twonlySafeRecoverDesc.
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,9 @@ import 'app_localizations.dart';
|
||||||
class AppLocalizationsDe extends AppLocalizations {
|
class AppLocalizationsDe extends AppLocalizations {
|
||||||
AppLocalizationsDe([String locale = 'de']) : super(locale);
|
AppLocalizationsDe([String locale = 'de']) : super(locale);
|
||||||
|
|
||||||
@override
|
|
||||||
String get registerTitle => 'Willkommen bei twonly!';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get registerSlogan =>
|
String get registerSlogan =>
|
||||||
'twonly, eine private und sichere Möglichkeit um mit Freunden in Kontakt zu bleiben.';
|
'Privat und sicher mit Freunden in Kontakt bleiben.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get onboardingWelcomeTitle => 'Willkommen bei twonly!';
|
String get onboardingWelcomeTitle => 'Willkommen bei twonly!';
|
||||||
|
|
@ -55,15 +52,13 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get onboardingGetStartedTitle => 'Auf geht\'s';
|
String get onboardingGetStartedTitle => 'Auf geht\'s';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get registerUsernameSlogan =>
|
String get registerUsernameSlogan => 'Dein öffentlicher Benutzername';
|
||||||
'Bitte wähle einen Benutzernamen, damit dich andere finden können!';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get registerUsernameDecoration => 'Benutzername';
|
String get registerUsernameDecoration => 'Benutzername';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get registerUsernameLimits =>
|
String get registerUsernameLimits => 'Mindestens 3 Zeichen.';
|
||||||
'Der Benutzername muss mindestens 3 Zeichen lang sein.';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get registerProofOfWorkFailed =>
|
String get registerProofOfWorkFailed =>
|
||||||
|
|
@ -816,7 +811,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get backupChangePassword => 'Password ändern';
|
String get backupChangePassword => 'Password ändern';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get twonlySafeRecoverTitle => 'Recovery';
|
String get twonlySafeRecoverTitle => 'Backup wiederherstellen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get twonlySafeRecoverDesc =>
|
String get twonlySafeRecoverDesc =>
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,9 @@ import 'app_localizations.dart';
|
||||||
class AppLocalizationsEn extends AppLocalizations {
|
class AppLocalizationsEn extends AppLocalizations {
|
||||||
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
||||||
|
|
||||||
@override
|
|
||||||
String get registerTitle => 'Welcome to twonly!';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get registerSlogan =>
|
String get registerSlogan =>
|
||||||
'twonly, a privacy friendly way to connect with friends through secure, spontaneous image sharing';
|
'Stay in touch with friends privately and securely.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get onboardingWelcomeTitle => 'Welcome to twonly!';
|
String get onboardingWelcomeTitle => 'Welcome to twonly!';
|
||||||
|
|
@ -54,15 +51,13 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get onboardingGetStartedTitle => 'Let\'s go!';
|
String get onboardingGetStartedTitle => 'Let\'s go!';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get registerUsernameSlogan =>
|
String get registerUsernameSlogan => 'Your public username';
|
||||||
'Please select a username so others can find you!';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get registerUsernameDecoration => 'Username';
|
String get registerUsernameDecoration => 'Username';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get registerUsernameLimits =>
|
String get registerUsernameLimits => 'At least 3 characters.';
|
||||||
'Your username must be at least 3 characters long.';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get registerProofOfWorkFailed =>
|
String get registerProofOfWorkFailed =>
|
||||||
|
|
@ -810,7 +805,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get backupChangePassword => 'Change password';
|
String get backupChangePassword => 'Change password';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get twonlySafeRecoverTitle => 'Recovery';
|
String get twonlySafeRecoverTitle => 'Restore backup';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get twonlySafeRecoverDesc =>
|
String get twonlySafeRecoverDesc =>
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit c41aaeff47007ddd5484fe1124bb15b905c3b247
|
Subproject commit 8660b9b11721adda59e387a129f9017bb9e72c67
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
import 'dart:math' as math;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
|
class LinkLogoAnimation extends StatefulWidget {
|
||||||
|
const LinkLogoAnimation({
|
||||||
|
super.key,
|
||||||
|
this.size = 130,
|
||||||
|
this.color = Colors.white,
|
||||||
|
});
|
||||||
|
|
||||||
|
final double size;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LinkLogoAnimation> createState() => _LinkLogoAnimationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LinkLogoAnimationState extends State<LinkLogoAnimation>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController _controller;
|
||||||
|
late Animation<double> _rotation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 1200),
|
||||||
|
vsync: this,
|
||||||
|
)..repeat(reverse: true);
|
||||||
|
|
||||||
|
_rotation =
|
||||||
|
Tween<double>(
|
||||||
|
begin: -2.0 * (math.pi / 180.0),
|
||||||
|
end: 2.0 * (math.pi / 180.0),
|
||||||
|
).animate(
|
||||||
|
CurvedAnimation(
|
||||||
|
parent: _controller,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
const originalViewportSize = 640.0;
|
||||||
|
|
||||||
|
const path1 =
|
||||||
|
'M451.5 160C434.9 160 418.8 164.5 404.7 172.7C388.9 156.7 370.5 143.3 350.2 133.2C378.4 109.2 414.3 96 451.5 96C537.9 96 608 166 608 252.5C608 294 591.5 333.8 562.2 363.1L491.1 434.2C461.8 463.5 422 480 380.5 480C294.1 480 224 410 224 323.5C224 322 224 320.5 224.1 319C224.6 301.3 239.3 287.4 257 287.9C274.7 288.4 288.6 303.1 288.1 320.8C288.1 321.7 288.1 322.6 288.1 323.4C288.1 374.5 329.5 415.9 380.6 415.9C405.1 415.9 428.6 406.2 446 388.8L517.1 317.7C534.4 300.4 544.2 276.8 544.2 252.3C544.2 201.2 502.8 159.8 451.7 159.8z';
|
||||||
|
const path2 =
|
||||||
|
'M307.2 237.3C305.3 236.5 303.4 235.4 301.7 234.2C289.1 227.7 274.7 224 259.6 224C235.1 224 211.6 233.7 194.2 251.1L123.1 322.2C105.8 339.5 96 363.1 96 387.6C96 438.7 137.4 480.1 188.5 480.1C205 480.1 221.1 475.7 235.2 467.5C251 483.5 269.4 496.9 289.8 507C261.6 530.9 225.8 544.2 188.5 544.2C102.1 544.2 32 474.2 32 387.7C32 346.2 48.5 306.4 77.8 277.1L148.9 206C178.2 176.7 218 160.2 259.5 160.2C346.1 160.2 416 230.8 416 317.1C416 318.4 416 319.7 416 321C415.6 338.7 400.9 352.6 383.2 352.2C365.5 351.8 351.6 337.1 352 319.4C352 318.6 352 317.9 352 317.1C352 283.4 334 253.8 307.2 237.5z';
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
width: widget.size,
|
||||||
|
height: widget.size,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _rotation,
|
||||||
|
builder: (context, child) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fill(
|
||||||
|
child: Transform(
|
||||||
|
alignment: const Alignment(
|
||||||
|
(416 * 2 / originalViewportSize) - 1,
|
||||||
|
(288 * 2 / originalViewportSize) - 1,
|
||||||
|
),
|
||||||
|
transform: Matrix4.identity()..rotateZ(_rotation.value),
|
||||||
|
child: SvgPicture.string(
|
||||||
|
'<svg viewBox="0 0 640 640"><path d="$path1" fill="${_toHex(widget.color)}"/></svg>',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
child: Transform(
|
||||||
|
alignment: const Alignment(
|
||||||
|
(224 * 2 / originalViewportSize) - 1,
|
||||||
|
(352 * 2 / originalViewportSize) - 1,
|
||||||
|
),
|
||||||
|
transform: Matrix4.identity()..rotateZ(-_rotation.value),
|
||||||
|
child: SvgPicture.string(
|
||||||
|
'<svg viewBox="0 0 640 640"><path d="$path2" fill="${_toHex(widget.color)}"/></svg>',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _toHex(Color color) {
|
||||||
|
return '#${color.toARGB32().toRadixString(16).substring(2)}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/visual/themes/light.dart';
|
||||||
|
|
||||||
|
class OnboardingWrapper extends StatelessWidget {
|
||||||
|
const OnboardingWrapper({
|
||||||
|
required this.children,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
final List<Widget> children;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor: primaryColor,
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
top: -100,
|
||||||
|
right: -100,
|
||||||
|
child: Container(
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Colors.white.withValues(alpha: 0.1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: -50,
|
||||||
|
left: -50,
|
||||||
|
child: Container(
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Colors.black.withValues(alpha: 0.05),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SafeArea(
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minHeight: constraints.maxHeight,
|
||||||
|
),
|
||||||
|
child: IntrinsicHeight(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: children,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,9 @@ import 'package:twonly/src/services/backup.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
||||||
import 'package:twonly/src/visual/components/snackbar.dart';
|
import 'package:twonly/src/visual/components/snackbar.dart';
|
||||||
import 'package:twonly/src/visual/decorations/input_text.decoration.dart';
|
import 'package:twonly/src/visual/themes/light.dart';
|
||||||
|
import 'package:twonly/src/visual/views/onboarding/components/link_logo_animation.dart';
|
||||||
|
import 'package:twonly/src/visual/views/onboarding/components/onboarding_wrapper.dart';
|
||||||
|
|
||||||
class BackupRecoveryView extends StatefulWidget {
|
class BackupRecoveryView extends StatefulWidget {
|
||||||
const BackupRecoveryView({super.key});
|
const BackupRecoveryView({super.key});
|
||||||
|
|
@ -64,66 +66,112 @@ class _BackupRecoveryViewState extends State<BackupRecoveryView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return OnboardingWrapper(
|
||||||
appBar: AppBar(
|
children: [
|
||||||
title: Text('twonly Backup ${context.lang.twonlySafeRecoverTitle}'),
|
Row(
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await showAlertDialog(
|
|
||||||
context,
|
|
||||||
'twonly Backup',
|
|
||||||
context.lang.backupTwonlySafeLongDesc,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: const FaIcon(FontAwesomeIcons.circleInfo),
|
|
||||||
iconSize: 18,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsetsGeometry.symmetric(
|
|
||||||
vertical: 40,
|
|
||||||
horizontal: 40,
|
|
||||||
),
|
|
||||||
child: ListView(
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
IconButton(
|
||||||
context.lang.twonlySafeRecoverDesc,
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
textAlign: TextAlign.center,
|
icon: const Icon(
|
||||||
),
|
Icons.arrow_back_ios_new_rounded,
|
||||||
const SizedBox(height: 30),
|
|
||||||
TextField(
|
|
||||||
controller: usernameCtrl,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
style: const TextStyle(fontSize: 17),
|
|
||||||
decoration: getInputDecoration(
|
|
||||||
context,
|
|
||||||
context.lang.registerUsernameDecoration,
|
|
||||||
),
|
),
|
||||||
|
color: Colors.white,
|
||||||
|
iconSize: 20,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const Spacer(),
|
||||||
Stack(
|
IconButton(
|
||||||
children: [
|
onPressed: () async {
|
||||||
TextField(
|
await showAlertDialog(
|
||||||
controller: passwordCtrl,
|
context,
|
||||||
onChanged: (value) {
|
'twonly Backup',
|
||||||
setState(() {});
|
context.lang.backupTwonlySafeLongDesc,
|
||||||
},
|
);
|
||||||
style: const TextStyle(fontSize: 17),
|
},
|
||||||
obscureText: obscureText,
|
icon: const FaIcon(FontAwesomeIcons.circleInfo),
|
||||||
decoration: getInputDecoration(
|
color: Colors.white,
|
||||||
context,
|
iconSize: 20,
|
||||||
context.lang.password,
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
const Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(20),
|
||||||
|
child: LinkLogoAnimation(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: Text(
|
||||||
|
context.lang.twonlySafeRecoverTitle,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
color: Colors.white,
|
||||||
|
letterSpacing: -0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 48),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(32),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.1),
|
||||||
|
blurRadius: 20,
|
||||||
|
offset: const Offset(0, 10),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: usernameCtrl,
|
||||||
|
onChanged: (value) => setState(() {}),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: context.lang.registerUsernameDecoration,
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.grey[100],
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
),
|
||||||
|
prefixIcon: const Icon(
|
||||||
|
Icons.alternate_email,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
),
|
||||||
right: 0,
|
const SizedBox(height: 16),
|
||||||
top: 0,
|
TextField(
|
||||||
bottom: 0,
|
controller: passwordCtrl,
|
||||||
child: IconButton(
|
onChanged: (value) => setState(() {}),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
obscureText: obscureText,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: context.lang.password,
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.grey[100],
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
),
|
||||||
|
prefixIcon: const Icon(
|
||||||
|
Icons.lock_outline_rounded,
|
||||||
|
),
|
||||||
|
suffixIcon: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
obscureText = !obscureText;
|
obscureText = !obscureText;
|
||||||
|
|
@ -137,25 +185,42 @@ class _BackupRecoveryViewState extends State<BackupRecoveryView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Center(
|
|
||||||
child: FilledButton.icon(
|
|
||||||
onPressed: (!isLoading) ? _recoverTwonlySafe : null,
|
|
||||||
icon: isLoading
|
|
||||||
? const SizedBox(
|
|
||||||
height: 12,
|
|
||||||
width: 12,
|
|
||||||
child: CircularProgressIndicator(strokeWidth: 1),
|
|
||||||
)
|
|
||||||
: const Icon(Icons.lock_clock_rounded),
|
|
||||||
label: Text(context.lang.twonlySafeRecoverBtn),
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 32),
|
||||||
],
|
FilledButton(
|
||||||
|
onPressed: (!isLoading) ? _recoverTwonlySafe : null,
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor: primaryColor,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
minimumSize: const Size.fromHeight(60),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(18),
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
child: isLoading
|
||||||
|
? const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
strokeWidth: 3,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
context.lang.twonlySafeRecoverBtn,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const Spacer(),
|
||||||
|
const SizedBox(height: 40),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// ignore_for_file: avoid_dynamic_calls
|
// ignore_for_file: avoid_dynamic_calls
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
@ -16,7 +17,10 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/pow.dart';
|
import 'package:twonly/src/utils/pow.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
||||||
|
import 'package:twonly/src/visual/themes/light.dart';
|
||||||
import 'package:twonly/src/visual/views/groups/group.view.dart';
|
import 'package:twonly/src/visual/views/groups/group.view.dart';
|
||||||
|
import 'package:twonly/src/visual/views/onboarding/components/link_logo_animation.dart';
|
||||||
|
import 'package:twonly/src/visual/views/onboarding/components/onboarding_wrapper.dart';
|
||||||
import 'package:twonly/src/visual/views/onboarding/setup.view.dart';
|
import 'package:twonly/src/visual/views/onboarding/setup.view.dart';
|
||||||
|
|
||||||
class RegisterView extends StatefulWidget {
|
class RegisterView extends StatefulWidget {
|
||||||
|
|
@ -134,7 +138,7 @@ class _RegisterViewState extends State<RegisterView> {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
username: username,
|
username: username,
|
||||||
displayName: username,
|
displayName: username,
|
||||||
subscriptionPlan: 'Preview',
|
subscriptionPlan: 'Free',
|
||||||
currentSetupPage: SetupPages.profile.name,
|
currentSetupPage: SetupPages.profile.name,
|
||||||
)..appVersion = AppState.latestAppVersionId;
|
)..appVersion = AppState.latestAppVersionId;
|
||||||
|
|
||||||
|
|
@ -146,174 +150,184 @@ class _RegisterViewState extends State<RegisterView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (_registrationDisabled) {
|
return OnboardingWrapper(
|
||||||
return Scaffold(
|
children: [
|
||||||
body: Padding(
|
const SizedBox(height: 40),
|
||||||
padding: const EdgeInsets.all(10),
|
Center(
|
||||||
child: Padding(
|
child: Container(
|
||||||
padding: const EdgeInsets.only(left: 10, right: 10),
|
padding: const EdgeInsets.all(20),
|
||||||
child: ListView(
|
child: const LinkLogoAnimation(),
|
||||||
children: [
|
),
|
||||||
const SizedBox(height: 50),
|
),
|
||||||
Text(
|
const SizedBox(height: 16),
|
||||||
context.lang.registerTitle,
|
Padding(
|
||||||
textAlign: TextAlign.center,
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
style: const TextStyle(fontSize: 30),
|
child: Text(
|
||||||
),
|
context.lang.registerSlogan,
|
||||||
Padding(
|
textAlign: TextAlign.center,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
style: TextStyle(
|
||||||
child: Text(
|
fontSize: 16,
|
||||||
context.lang.registerSlogan,
|
color: Colors.white.withValues(alpha: 0.9),
|
||||||
textAlign: TextAlign.center,
|
fontWeight: FontWeight.w500,
|
||||||
style: const TextStyle(fontSize: 12),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 130),
|
const SizedBox(height: 48),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(32),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.1),
|
||||||
|
blurRadius: 20,
|
||||||
|
offset: const Offset(0, 10),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
if (_registrationDisabled) ...[
|
||||||
|
const SizedBox(height: 24),
|
||||||
Text(
|
Text(
|
||||||
context.lang.registrationClosed,
|
context.lang.registrationClosed,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
const SizedBox(height: 48),
|
||||||
),
|
] else ...[
|
||||||
),
|
Text(
|
||||||
),
|
context.lang.registerUsernameSlogan,
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
InputDecoration getInputDecoration(String hintText) {
|
|
||||||
return InputDecoration(hintText: hintText, fillColor: Colors.grey[400]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text(''),
|
|
||||||
),
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.all(10),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 10, right: 10),
|
|
||||||
child: ListView(
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 50),
|
|
||||||
Text(
|
|
||||||
context.lang.registerTitle,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: const TextStyle(fontSize: 30),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
|
||||||
child: Text(
|
|
||||||
context.lang.registerSlogan,
|
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(fontSize: 12),
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.grey[800],
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 20),
|
||||||
const SizedBox(height: 60),
|
TextField(
|
||||||
Center(
|
controller: usernameController,
|
||||||
child: Padding(
|
onChanged: (value) {
|
||||||
padding: const EdgeInsets.only(left: 10, right: 10),
|
usernameController.text = value.toLowerCase();
|
||||||
child: Text(
|
usernameController.selection = TextSelection.fromPosition(
|
||||||
context.lang.registerUsernameSlogan,
|
TextPosition(
|
||||||
|
offset: usernameController.text.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
_isValidUserName = usernameController.text.length >= 3;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
inputFormatters: [
|
||||||
|
LengthLimitingTextInputFormatter(12),
|
||||||
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp('[a-z0-9A-Z._]'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: context.lang.registerUsernameDecoration,
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.grey[100],
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
),
|
||||||
|
prefixIcon: const Icon(
|
||||||
|
Icons.alternate_email,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_showUserNameError &&
|
||||||
|
usernameController.text.length < 3) ...[
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
context.lang.registerUsernameLimits,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.red,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(fontSize: 15),
|
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
TextField(
|
|
||||||
controller: usernameController,
|
|
||||||
onChanged: (value) {
|
|
||||||
usernameController.text = value.toLowerCase();
|
|
||||||
usernameController.selection = TextSelection.fromPosition(
|
|
||||||
TextPosition(offset: usernameController.text.length),
|
|
||||||
);
|
|
||||||
setState(() {
|
|
||||||
_isValidUserName = usernameController.text.length >= 3;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
inputFormatters: [
|
|
||||||
LengthLimitingTextInputFormatter(12),
|
|
||||||
FilteringTextInputFormatter.allow(RegExp('[a-z0-9A-Z._]')),
|
|
||||||
],
|
],
|
||||||
style: const TextStyle(fontSize: 17),
|
if (_showProofOfWorkError) ...[
|
||||||
decoration: getInputDecoration(
|
const SizedBox(height: 8),
|
||||||
context.lang.registerUsernameDecoration,
|
Text(
|
||||||
),
|
context.lang.registerProofOfWorkFailed,
|
||||||
),
|
style: const TextStyle(
|
||||||
const SizedBox(height: 10),
|
color: Colors.red,
|
||||||
Text(
|
fontSize: 13,
|
||||||
context.lang.registerUsernameLimits,
|
fontWeight: FontWeight.w500,
|
||||||
style: TextStyle(
|
),
|
||||||
color: _showUserNameError ? Colors.red : Colors.transparent,
|
textAlign: TextAlign.center,
|
||||||
fontSize: 12,
|
),
|
||||||
),
|
],
|
||||||
textAlign: TextAlign.center,
|
const SizedBox(height: 24),
|
||||||
),
|
FilledButton(
|
||||||
const SizedBox(height: 10),
|
onPressed: _isTryingToRegister ? null : createNewUser,
|
||||||
Text(
|
style: FilledButton.styleFrom(
|
||||||
context.lang.registerProofOfWorkFailed,
|
backgroundColor: primaryColor,
|
||||||
style: TextStyle(
|
foregroundColor: Colors.white,
|
||||||
color: _showProofOfWorkError
|
minimumSize: const Size.fromHeight(60),
|
||||||
? Colors.red
|
shape: RoundedRectangleBorder(
|
||||||
: Colors.transparent,
|
borderRadius: BorderRadius.circular(18),
|
||||||
fontSize: 12,
|
),
|
||||||
),
|
elevation: 0,
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
),
|
child: _isTryingToRegister
|
||||||
const SizedBox(height: 10),
|
? const SizedBox(
|
||||||
Column(
|
width: 24,
|
||||||
children: [
|
height: 24,
|
||||||
FilledButton.icon(
|
child: CircularProgressIndicator(
|
||||||
icon: _isTryingToRegister
|
color: Colors.white,
|
||||||
? const SizedBox(
|
strokeWidth: 3,
|
||||||
width: 18,
|
),
|
||||||
height: 18,
|
)
|
||||||
child: CircularProgressIndicator(
|
: Text(
|
||||||
color: Colors.black,
|
context.lang.registerSubmitButton,
|
||||||
strokeWidth: 2,
|
style: const TextStyle(
|
||||||
),
|
fontSize: 18,
|
||||||
)
|
fontWeight: FontWeight.bold,
|
||||||
: const Icon(Icons.group),
|
),
|
||||||
onPressed: createNewUser,
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: WidgetStateProperty.all<EdgeInsets>(
|
|
||||||
const EdgeInsets.symmetric(
|
|
||||||
vertical: 10,
|
|
||||||
horizontal: 30,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
backgroundColor: _isTryingToRegister
|
const SizedBox(height: 16),
|
||||||
? WidgetStateProperty.all<MaterialColor>(
|
],
|
||||||
Colors.grey,
|
TextButton(
|
||||||
)
|
onPressed: () => context.push(
|
||||||
: null,
|
Routes.settingsBackupRecovery,
|
||||||
),
|
),
|
||||||
label: Text(
|
style: TextButton.styleFrom(
|
||||||
context.lang.registerSubmitButton,
|
minimumSize: const Size.fromHeight(50),
|
||||||
style: const TextStyle(fontSize: 17),
|
foregroundColor: Colors.grey[600],
|
||||||
),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(18),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
),
|
||||||
Row(
|
child: Text(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
context.lang.twonlySafeRecoverBtn,
|
||||||
children: [
|
style: const TextStyle(
|
||||||
OutlinedButton.icon(
|
fontSize: 15,
|
||||||
onPressed: () =>
|
fontWeight: FontWeight.w600,
|
||||||
context.push(Routes.settingsBackupRecovery),
|
|
||||||
label: Text(context.lang.twonlySafeRecoverBtn),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const Spacer(),
|
||||||
|
const SizedBox(height: 40),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue