improved login screen; improvements & bugfixes

This commit is contained in:
Myzel394 2022-08-25 08:39:42 +02:00
parent 7ba7652917
commit 2e90e6343c
6 changed files with 117 additions and 134 deletions

View File

@ -46,11 +46,10 @@
"loginScreenHelpText": "Melde dich an. Wenn du noch keinen Account besitzt, erstellen wir automatisch einen für dich.", "loginScreenHelpText": "Melde dich an. Wenn du noch keinen Account besitzt, erstellen wir automatisch einen für dich.",
"loginScreenFormEmailLabel": "E-Mail", "loginScreenFormEmailLabel": "E-Mail",
"loginScreenFormPasswordLabel": "Passwort", "loginScreenFormPasswordLabel": "Passwort",
"loginScreenFormSubmitButton": "Anmelden", "loginScreenFormLoginButton": "Anmelden",
"loginScreenFormSignUpButton": "Registrieren",
"loginScreenLoginFailed": "E-Mail oder Passwort inkorrekt", "loginScreenLoginFailed": "E-Mail oder Passwort inkorrekt",
"loginScreenSignUpDialogTitle": "Registrieren", "loginScreenSignUpFailed": "Registrierung fehlgeschlagen. Hast du eventuell schon einen Account?",
"loginScreenSignUpDialogExplanation": "Wir haben deinen Account nicht gefunden. Möchtest du dich jetzt registrieren?",
"loginScreenSignUpDialogAffirmationLabel": "Jetzt registrieren",
"grantPermissionScreenTitle": "Berechtigungen erteilen", "grantPermissionScreenTitle": "Berechtigungen erteilen",

View File

@ -66,11 +66,10 @@
"loginScreenHelpText": "Sign in to your account. If you do not have one already, we will automatically set up one for you.", "loginScreenHelpText": "Sign in to your account. If you do not have one already, we will automatically set up one for you.",
"loginScreenFormEmailLabel": "Email", "loginScreenFormEmailLabel": "Email",
"loginScreenFormPasswordLabel": "Password", "loginScreenFormPasswordLabel": "Password",
"loginScreenFormSubmitButton": "Login", "loginScreenFormLoginButton": "Login",
"loginScreenFormSignUpButton": "Sign up",
"loginScreenLoginFailed": "Email or password incorrect", "loginScreenLoginFailed": "Email or password incorrect",
"loginScreenSignUpDialogTitle": "Sign up", "loginScreenSignUpFailed": "Registration failed. Maybe you already have an account?",
"loginScreenSignUpDialogExplanation": "We didn't find your account. Do you want to sign up now?",
"loginScreenSignUpDialogAffirmationLabel": "Sign me up now",
"grantPermissionScreenTitle": "Grant Permissions", "grantPermissionScreenTitle": "Grant Permissions",

View File

@ -46,20 +46,22 @@ class _LoginScreenState extends AuthState<LoginScreen> with Loadable {
super.dispose(); super.dispose();
} }
Future<void> _signUp() async { Future<void> _onAuthenticated() async {
final response = await supabase.auth.signUp( if (!mounted) {
emailController.text.trim(), return;
passwordController.text,
);
final error = response.error;
if (error != null) {
throw Exception(error);
} }
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ServerLoadingScreen(),
),
);
} }
Future<void> _signIn() async { Future<void> signIn() async {
final localizations = AppLocalizations.of(context)!;
final response = await supabase.auth.signIn( final response = await supabase.auth.signIn(
email: emailController.text.trim(), email: emailController.text.trim(),
password: passwordController.text, password: passwordController.text,
@ -67,63 +69,46 @@ class _LoginScreenState extends AuthState<LoginScreen> with Loadable {
final error = response.error; final error = response.error;
if (!mounted) {
return;
}
if (error != null) { if (error != null) {
throw Exception(error); context.showLongErrorSnackBar(
} message: localizations.loginScreenLoginFailed,
}
Future<bool> _askWhetherToSignUp() {
final localizations = AppLocalizations.of(context)!;
return showPlatformDialog(
context: context,
builder: (_) => PlatformAlertDialog(
title: Text(localizations.loginScreenSignUpDialogTitle),
content: Text(localizations.loginScreenSignUpDialogExplanation),
actions: <Widget>[
PlatformDialogAction(
child: Text(localizations.generalCancelButtonLabel),
onPressed: () => Navigator.pop(context, false),
),
PlatformDialogAction(
child: Text(localizations.loginScreenSignUpDialogAffirmationLabel),
onPressed: () => Navigator.pop(context, true),
),
],
),
) as Future<bool>;
}
Future<void> signIn() async {
final localizations = AppLocalizations.of(context)!;
try {
await _signIn();
} catch (error) {
if (await _askWhetherToSignUp()) {
try {
await _signUp();
} catch (error) {
if (mounted) {
context.showLongErrorSnackBar(
message: localizations.loginScreenLoginFailed,
);
passwordController.clear();
}
return;
}
}
}
if (mounted) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ServerLoadingScreen(),
),
); );
passwordController.clear();
return;
} }
_onAuthenticated();
}
Future<void> signUp() async {
final localizations = AppLocalizations.of(context)!;
final response = await supabase.auth.signUp(
emailController.text.trim(),
passwordController.text,
);
final error = response.error;
if (!mounted) {
return;
}
if (error != null) {
context.showLongErrorSnackBar(
message: localizations.loginScreenSignUpFailed,
);
passwordController.clear();
return;
}
_onAuthenticated();
} }
@override @override
@ -148,16 +133,18 @@ class _LoginScreenState extends AuthState<LoginScreen> with Loadable {
cupertino: (data) => data.textTheme.navLargeTitleTextStyle, cupertino: (data) => data.textTheme.navLargeTitleTextStyle,
), ),
), ),
const SizedBox(height: LARGE_SPACE), const Flexible(child: SizedBox(height: LARGE_SPACE)),
Text( Flexible(
localizations.loginScreenHelpText, child: Text(
style: platformThemeData( localizations.loginScreenHelpText,
context, style: platformThemeData(
material: (data) => data.textTheme.bodyText1, context,
cupertino: (data) => data.textTheme.textStyle, material: (data) => data.textTheme.bodyText1,
cupertino: (data) => data.textTheme.textStyle,
),
), ),
), ),
const SizedBox(height: MEDIUM_SPACE), const Flexible(child: SizedBox(height: MEDIUM_SPACE)),
PlatformTextField( PlatformTextField(
controller: emailController, controller: emailController,
autofocus: true, autofocus: true,
@ -191,13 +178,25 @@ class _LoginScreenState extends AuthState<LoginScreen> with Loadable {
), ),
onSubmitted: (value) => callWithLoading(signIn), onSubmitted: (value) => callWithLoading(signIn),
), ),
const SizedBox(height: MEDIUM_SPACE), const Flexible(child: SizedBox(height: MEDIUM_SPACE)),
PlatformElevatedButton( Row(
onPressed: isLoading ? null : () => callWithLoading(signIn), mainAxisAlignment: MainAxisAlignment.spaceEvenly,
child: IconButtonChild( children: <Widget>[
icon: Icon(context.platformIcons.forward), PlatformElevatedButton(
label: Text(localizations.loginScreenFormSubmitButton), onPressed: isLoading ? null : () => callWithLoading(signIn),
), child: IconButtonChild(
icon: Icon(context.platformIcons.forward),
label: Text(localizations.loginScreenFormLoginButton),
),
),
PlatformTextButton(
onPressed: isLoading ? null : () => callWithLoading(signUp),
child: IconButtonChild(
icon: Icon(context.platformIcons.add),
label: Text(localizations.loginScreenFormSignUpButton),
),
),
],
), ),
], ],
), ),

View File

@ -1,4 +1,4 @@
import 'dart:typed_data'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -25,7 +25,7 @@ class TodayPhotoButton extends StatefulWidget {
} }
class _TodayPhotoButtonState extends State<TodayPhotoButton> { class _TodayPhotoButtonState extends State<TodayPhotoButton> {
Uint8List? data; File? file;
MemoryType? type; MemoryType? type;
@override @override
@ -59,7 +59,7 @@ class _TodayPhotoButtonState extends State<TodayPhotoButton> {
if (memories.memories.isEmpty) { if (memories.memories.isEmpty) {
setState(() { setState(() {
data = null; file = null;
type = null; type = null;
}); });
return; return;
@ -67,12 +67,11 @@ class _TodayPhotoButtonState extends State<TodayPhotoButton> {
final lastMemory = memories.memories.first; final lastMemory = memories.memories.first;
final file = await lastMemory.downloadToFile(); final downloadedFile = await lastMemory.downloadToFile();
final memoryData = await file.readAsBytes();
setState(() { setState(() {
data = memoryData;
type = lastMemory.type; type = lastMemory.type;
file = downloadedFile;
}); });
} }
@ -107,10 +106,10 @@ class _TodayPhotoButtonState extends State<TodayPhotoButton> {
), ),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(SMALL_SPACE), borderRadius: BorderRadius.circular(SMALL_SPACE),
child: (data == null || type == null) child: (file == null || type == null)
? const SizedBox.shrink() ? const SizedBox.shrink()
: RawMemoryDisplay( : RawMemoryDisplay(
data: data!, file: file!,
type: type!, type: type!,
), ),
), ),

View File

@ -1,5 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:typed_data'; import 'dart:io';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -39,7 +39,7 @@ class MemoryView extends StatefulWidget {
class _MemoryViewState extends State<MemoryView> { class _MemoryViewState extends State<MemoryView> {
MemoryFetchStatus status = MemoryFetchStatus.downloading; MemoryFetchStatus status = MemoryFetchStatus.downloading;
Uint8List? data; File? file;
Timer? _nextMemoryTimer; Timer? _nextMemoryTimer;
@override @override
@ -64,17 +64,15 @@ class _MemoryViewState extends State<MemoryView> {
}); });
try { try {
final file = await widget.memory.downloadToFile(); final downloadedFile = await widget.memory.downloadToFile();
if (!mounted) { if (!mounted) {
return; return;
} }
final fileData = await file.readAsBytes();
setState(() { setState(() {
status = MemoryFetchStatus.done; status = MemoryFetchStatus.done;
data = fileData; file = downloadedFile;
}); });
if (widget.onFileDownloaded != null) { if (widget.onFileDownloaded != null) {
@ -128,16 +126,14 @@ class _MemoryViewState extends State<MemoryView> {
ImageFiltered( ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30), imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
child: RawMemoryDisplay( child: RawMemoryDisplay(
filename: widget.memory.filename, file: file!,
data: data!,
type: widget.memory.type, type: widget.memory.type,
loopVideo: widget.loopVideo, loopVideo: widget.loopVideo,
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
), ),
RawMemoryDisplay( RawMemoryDisplay(
filename: widget.memory.filename, file: file!,
data: data!,
type: widget.memory.type, type: widget.memory.type,
fit: BoxFit.contain, fit: BoxFit.contain,
loopVideo: widget.loopVideo, loopVideo: widget.loopVideo,

View File

@ -2,27 +2,29 @@ import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:quid_faciam_hodie/enums.dart'; import 'package:quid_faciam_hodie/enums.dart';
import 'package:video_player/video_player.dart'; import 'package:video_player/video_player.dart';
class RawMemoryDisplay extends StatefulWidget { class RawMemoryDisplay extends StatefulWidget {
final Uint8List data; final File? file;
final Uint8List? data;
final MemoryType type; final MemoryType type;
final bool loopVideo; final bool loopVideo;
final String? filename;
final void Function(VideoPlayerController)? onVideoControllerInitialized; final void Function(VideoPlayerController)? onVideoControllerInitialized;
final BoxFit? fit; final BoxFit? fit;
const RawMemoryDisplay({ const RawMemoryDisplay({
Key? key, Key? key,
required this.data,
required this.type, required this.type,
this.loopVideo = false, this.loopVideo = false,
this.fit = BoxFit.cover, this.fit = BoxFit.cover,
this.filename,
this.onVideoControllerInitialized, this.onVideoControllerInitialized,
}) : super(key: key); this.file,
this.data,
}) : assert(data != null || file != null),
assert((type == MemoryType.photo) ||
(type == MemoryType.video && data != null)),
super(key: key);
@override @override
State<RawMemoryDisplay> createState() => _RawMemoryDisplayState(); State<RawMemoryDisplay> createState() => _RawMemoryDisplayState();
@ -40,27 +42,9 @@ class _RawMemoryDisplayState extends State<RawMemoryDisplay> {
} }
} }
Future<File> createTempVideo() async {
final tempDirectory = await getTemporaryDirectory();
final path = '${tempDirectory.path}/${widget.filename ?? 'video.mp4'}';
final file = File(path);
if (await file.exists()) {
// File already exists, so just return it
return file;
}
// File needs to be created
await file.create();
await file.writeAsBytes(widget.data);
return file;
}
Future<void> initializeVideo() async { Future<void> initializeVideo() async {
final file = await createTempVideo(); // `file` MUST be defined for videos.
videoController = VideoPlayerController.file(widget.file!);
videoController = VideoPlayerController.file(file);
videoController!.initialize().then((value) { videoController!.initialize().then((value) {
if (!mounted) { if (!mounted) {
return; return;
@ -87,8 +71,15 @@ class _RawMemoryDisplayState extends State<RawMemoryDisplay> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
switch (widget.type) { switch (widget.type) {
case MemoryType.photo: case MemoryType.photo:
return Image.memory( if (widget.data != null) {
widget.data, return Image.memory(
widget.data!,
fit: widget.fit,
);
}
return Image.file(
widget.file!,
fit: widget.fit, fit: widget.fit,
); );
case MemoryType.video: case MemoryType.video: