v1 release

This commit is contained in:
Sarthak Kimtani 2024-02-17 20:32:01 +05:30
commit cb24b68069
116 changed files with 4913 additions and 0 deletions

View file

@ -0,0 +1,67 @@
import "package:auto_route/auto_route.dart";
import "package:dio/dio.dart";
import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:flash_mail/data/providers/providers.dart";
import "package:flash_mail/presentation/widgets/primary_button.dart";
import "package:flash_mail/presentation/widgets/secondary_button.dart";
import "package:flash_mail/presentation/widgets/small_loading_spinner.dart";
class AccountButtons extends StatelessWidget {
const AccountButtons({super.key});
void showErrorSnackBar(BuildContext ctx, String message) {
ScaffoldMessenger.of(ctx).showSnackBar(
SnackBar(
content: Text(message),
behavior: SnackBarBehavior.floating,
),
);
}
void _handleSubmit(BuildContext context, WidgetRef ref) async {
try {
await ref.read(accountProvider.notifier).createAccount();
if (context.mounted) {
AutoRouter.of(context).replaceNamed("/inbox");
}
} on DioError catch (err) {
if (context.mounted) {
showErrorSnackBar(context, err.message);
}
} catch (err) {
if (context.mounted) {
showErrorSnackBar(context, "Something went wrong!");
}
}
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text(
"Your Inbox, Your Privacy, Zero Ads.",
style: Theme.of(context).textTheme.labelSmall,
),
const SizedBox(height: 15),
Consumer(
builder: (context, ref, child) {
return PrimaryButton(
child: ref.watch(accountProvider).isLoading
? const SmallLoadingSpinner()
: const Text("Create New Account"),
onPressed: () => _handleSubmit(context, ref),
);
},
),
const SizedBox(height: 5),
SecondaryButton(
text: "Use Existing",
onPressed: () => AutoRouter.of(context).pushNamed("/login"),
),
],
);
}
}

View file

@ -0,0 +1,58 @@
import "package:flutter/material.dart";
class InputField extends StatelessWidget {
final String label;
final String placeholder;
final TextInputAction inputAction;
final String? Function(String?) validator;
final void Function(String?) onSaved;
final TextInputType? inputType;
final bool? obscureText;
const InputField({
super.key,
required this.label,
required this.placeholder,
required this.inputAction,
required this.validator,
required this.onSaved,
this.inputType,
this.obscureText,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
label,
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Theme.of(context).primaryColor,
),
),
const SizedBox(height: 10),
TextFormField(
keyboardType: inputType,
textInputAction: inputAction,
obscureText: obscureText ?? false,
validator: validator,
onSaved: onSaved,
style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 18),
decoration: InputDecoration(
filled: true,
fillColor: const Color.fromRGBO(22, 39, 56, 1),
hintText: placeholder,
hintStyle: const TextStyle(
color: Color.fromRGBO(127, 133, 141, 1),
),
errorStyle: const TextStyle(
fontFamily: "Inter",
fontSize: 16,
),
),
),
],
);
}
}

View file

@ -0,0 +1,107 @@
import "package:auto_route/auto_route.dart";
import "package:dio/dio.dart";
import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:flash_mail/data/providers/providers.dart";
import "package:flash_mail/presentation/widgets/auth/input_field.dart";
import "package:flash_mail/presentation/widgets/primary_button.dart";
import "package:flash_mail/presentation/widgets/small_loading_spinner.dart";
import "package:flash_mail/shared/utils/regex_patterns.dart";
class LoginForm extends ConsumerWidget {
final GlobalKey<FormState> _formKey = GlobalKey();
LoginForm({super.key});
String? _validateEmail(String? value) {
if (value!.isEmpty || !emailRegex.hasMatch(value)) {
return "Invalid email address";
}
return null;
}
String? _validatePassword(String? value) {
if (value!.isEmpty || value.length < 8) {
return "Password too short";
}
return null;
}
void showErrorSnackBar(BuildContext ctx, String message) {
ScaffoldMessenger.of(ctx).showSnackBar(
SnackBar(
content: Text(message),
behavior: SnackBarBehavior.floating,
),
);
}
void _handleSubmit(BuildContext context, WidgetRef ref) async {
if (!_formKey.currentState!.validate()) {
return;
}
FocusScope.of(context).unfocus();
_formKey.currentState!.save();
try {
String? email = ref.read(emailProvider.notifier).state;
String? password = ref.read(passwordProvider.notifier).state;
await ref.read(accountProvider.notifier).login(email, password);
if (context.mounted) {
AutoRouter.of(context).replaceNamed("/inbox");
}
} on DioError catch (err) {
if (context.mounted) {
showErrorSnackBar(context, err.message);
}
} catch (_) {
if (context.mounted) {
showErrorSnackBar(context, "Something went wrong!");
}
}
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final account = ref.watch(accountProvider);
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InputField(
label: "Email Address",
placeholder: "Enter email",
inputType: TextInputType.emailAddress,
inputAction: TextInputAction.next,
validator: _validateEmail,
onSaved: (value) => ref.read(emailProvider.notifier).state = value,
),
const SizedBox(height: 25),
InputField(
label: "Password",
placeholder: "Enter password",
obscureText: true,
inputAction: TextInputAction.done,
validator: _validatePassword,
onSaved: (value) =>
ref.read(passwordProvider.notifier).state = value,
),
const SizedBox(height: 30),
Center(
child: PrimaryButton(
onPressed: () => _handleSubmit(context, ref),
child: account.isLoading
? const SmallLoadingSpinner()
: const Text("Login"),
),
),
],
),
);
}
}