Skip to content

Commit

Permalink
Associate email
Browse files Browse the repository at this point in the history
  • Loading branch information
Ziedelth committed May 22, 2024
1 parent 498f3f7 commit 5cbe745
Show file tree
Hide file tree
Showing 15 changed files with 587 additions and 268 deletions.
41 changes: 41 additions & 0 deletions lib/components/accounts/account_card.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:flutter/material.dart';

class AccountCard extends StatelessWidget {
final String label;
final String value;

const AccountCard({
super.key,
required this.label,
required this.value,
});

@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(8),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(
value,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(
label,
style: const TextStyle(color: Colors.white),
),
],
),
),
);
}
}
175 changes: 175 additions & 0 deletions lib/components/accounts/associate_email.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import 'package:application/controllers/member_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:vibration/vibration.dart';

class AssociateEmail extends StatefulWidget {
const AssociateEmail({super.key});

@override
State<StatefulWidget> createState() => _AssociateEmailState();
}

class _AssociateEmailState extends State<AssociateEmail> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _codeController = TextEditingController();
bool _isInvalidEmailError = false;
bool _isConflictEmailError = false;
bool _isCodeInError = false;
bool _isLoading = false;
String? _actionUuid;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.associateEmail),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
decoration: InputDecoration(
labelText: AppLocalizations.of(context)!.email,
errorText: errorText(context),
),
controller: _emailController,
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: AppLocalizations.of(context)!.code,
errorText: _isCodeInError
? AppLocalizations.of(context)!.invalidCode
: null,
),
controller: _codeController,
),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: _isLoading
? null
: () {
saveEmail();
},
child: Text(AppLocalizations.of(context)!.sendCode),
),
],
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: _isLoading
? null
: () {
validateAction(context);
},
child: Text(AppLocalizations.of(context)!.save),
),
],
),
),
);
}

String? errorText(BuildContext context) {
if (_isInvalidEmailError) {
return AppLocalizations.of(context)!.invalidEmail;
}

if (_isConflictEmailError) {
return AppLocalizations.of(context)!.conflictEmail;
}

return null;
}

Future<void> saveEmail() async {
if (_emailController.text.isEmpty) {
vibrate();
return;
}

if (!isValidEmail(_emailController.text)) {
vibrate();
updateState(invalidEmail: true);
return;
}

if (_emailController.text == MemberController.instance.member?.email) {
vibrate();
updateState(conflictEmail: true);
return;
}

updateState(invalidEmail: false, conflictEmail: false, isLoading: true);

try {
_actionUuid =
await MemberController.instance.associateEmail(_emailController.text);
} on ConflictEmailException {
vibrate();
updateState(conflictEmail: true, invalidEmail: false);
} catch (e) {
vibrate();
updateState(conflictEmail: false, invalidEmail: true);
} finally {
updateState(isLoading: false);
}
}

void vibrate() {
Vibration.vibrate(duration: 200, amplitude: 255);
}

bool isValidEmail(String email) {
return RegExp(r'^[A-Za-z0-9+_.-]+@(.+)$').hasMatch(email);
}

void updateState({bool? invalidEmail, bool? conflictEmail, bool? isLoading}) {
if (context.mounted) {
setState(() {
if (invalidEmail != null) _isInvalidEmailError = invalidEmail;
if (conflictEmail != null) _isConflictEmailError = conflictEmail;
if (isLoading != null) _isLoading = isLoading;
});
}
}

Future<void> validateAction(BuildContext context) async {
if (_codeController.text.isEmpty || _actionUuid == null) {
Vibration.vibrate(duration: 200, amplitude: 255);
return;
}

try {
await MemberController.instance
.validateAction(_actionUuid!, _codeController.text);

if (context.mounted) {
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'OK',
textAlign: TextAlign.center,
),
),
);
}
} catch (e) {
vibrate();

if (context.mounted) {
setState(() {
_isCodeInError = true;
});
}
}
}
}
81 changes: 81 additions & 0 deletions lib/components/accounts/edit_identifier.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import 'package:application/controllers/member_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:vibration/vibration.dart';

class EditIdentifier extends StatelessWidget {
final TextEditingController _controller = TextEditingController();

EditIdentifier({super.key});

@override
Widget build(BuildContext context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
AppLocalizations.of(context)!.enterNewIdentifier,
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 16),
TextField(
decoration: InputDecoration(
labelText: AppLocalizations.of(context)!.identifier,
),
controller: _controller,
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(AppLocalizations.of(context)!.cancel),
),
ElevatedButton(
onPressed: () {
saveIdentifier(context);
},
child: Text(AppLocalizations.of(context)!.save),
),
],
);
}

Future<void> saveIdentifier(BuildContext context) async {
if (_controller.text.isEmpty) {
Vibration.vibrate(duration: 200, amplitude: 255);
return;
}

final oldIdentifier = MemberController.instance.identifier;

try {
await MemberController.instance.testLogin(_controller.text);
await MemberController.instance.login(identifier: _controller.text);
} catch (e) {
Vibration.vibrate(duration: 200, amplitude: 255);

if (MemberController.instance.identifier != oldIdentifier) {
await MemberController.instance.login(identifier: oldIdentifier);
}

if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context)!.invalidIdentifier,
textAlign: TextAlign.center,
),
),
);
}
} finally {
if (context.mounted) {
Navigator.of(context).pop();
}
}
}
}
7 changes: 4 additions & 3 deletions lib/components/followed_stream_builder.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:application/controllers/member_controller.dart';
import 'package:application/dtos/anime_dto.dart';
import 'package:application/dtos/episode_mapping_dto.dart';
import 'package:application/dtos/member_dto.dart';
import 'package:flutter/material.dart';

class FollowedStreamBuilder extends StatelessWidget {
Expand All @@ -17,11 +18,11 @@ class FollowedStreamBuilder extends StatelessWidget {

@override
Widget build(BuildContext context) {
return StreamBuilder(
return StreamBuilder<MemberDto>(
stream: MemberController.instance.streamController.stream,
initialData: null,
initialData: MemberController.instance.member,
builder: (context, snapshot) {
final memberDto = MemberController.instance.member;
final memberDto = snapshot.data;

final containsAnime =
anime != null && memberDto!.followedAnimes.contains(anime?.uuid);
Expand Down
Loading

0 comments on commit 5cbe745

Please sign in to comment.