feat(auth): add reactive repo + extra data + router examples + tests
This commit is contained in:
parent
a1e36f84be
commit
d9d45db5c0
@ -149,7 +149,7 @@ class _PasswordInput extends StatelessWidget {
|
|||||||
.read<SignUpCubit>()
|
.read<SignUpCubit>()
|
||||||
.state
|
.state
|
||||||
.data
|
.data
|
||||||
.valueOf<String, ValidationError>(formFieldConfirmedPassword),
|
.valueOf<String>(formFieldConfirmedPassword),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -202,7 +202,7 @@ class _CheckIsProInput extends StatelessWidget {
|
|||||||
trailing: BlocBuilder<SignUpCubit, SignUpState>(
|
trailing: BlocBuilder<SignUpCubit, SignUpState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Checkbox(
|
return Checkbox(
|
||||||
value: state.data.valueOf<bool, ValidationError>(formFieldPro),
|
value: state.data.valueOf<bool>(formFieldPro),
|
||||||
onChanged: (isPro) {
|
onChanged: (isPro) {
|
||||||
final value =
|
final value =
|
||||||
isPro!; // tristate is false, so value can't be null
|
isPro!; // tristate is false, so value can't be null
|
||||||
@ -296,7 +296,7 @@ class SignUpForm extends StatelessWidget {
|
|||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
BlocBuilder<SignUpCubit, SignUpState>(
|
BlocBuilder<SignUpCubit, SignUpState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state.data.valueOf<bool, ValidationError>(formFieldPro)) {
|
if (state.data.valueOf<bool>(formFieldPro)) {
|
||||||
return Column(children: [
|
return Column(children: [
|
||||||
_SirenInput(),
|
_SirenInput(),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
47
packages/wyatt_authentication_bloc/example_router/.gitignore
vendored
Normal file
47
packages/wyatt_authentication_bloc/example_router/.gitignore
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.packages
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Web related
|
||||||
|
lib/generated_plugin_registrant.dart
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
30
packages/wyatt_authentication_bloc/example_router/.metadata
Normal file
30
packages/wyatt_authentication_bloc/example_router/.metadata
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: app
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||||
|
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||||
|
- platform: android
|
||||||
|
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||||
|
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
25
packages/wyatt_authentication_bloc/example_router/.vscode/launch.json
vendored
Normal file
25
packages/wyatt_authentication_bloc/example_router/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Author: Hugo Pointcheval
|
||||||
|
* Email: git@pcl.ovh
|
||||||
|
* -----
|
||||||
|
* File: launch.json
|
||||||
|
* Created Date: 19/08/2022 15:12:25
|
||||||
|
* Last Modified: 19/08/2022 15:22:02
|
||||||
|
* -----
|
||||||
|
* Copyright (c) 2022
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "example_router",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"program": "lib/main.dart",
|
||||||
|
"flutterMode": "debug"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
16
packages/wyatt_authentication_bloc/example_router/README.md
Normal file
16
packages/wyatt_authentication_bloc/example_router/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# example_router
|
||||||
|
|
||||||
|
A new Flutter project.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
|
A few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
|
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||||
|
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||||
|
|
||||||
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
@ -0,0 +1,29 @@
|
|||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at
|
||||||
|
# https://dart-lang.github.io/linter/lints/index.html.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
13
packages/wyatt_authentication_bloc/example_router/android/.gitignore
vendored
Normal file
13
packages/wyatt_authentication_bloc/example_router/android/.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
gradle-wrapper.jar
|
||||||
|
/.gradle
|
||||||
|
/captures/
|
||||||
|
/gradlew
|
||||||
|
/gradlew.bat
|
||||||
|
/local.properties
|
||||||
|
GeneratedPluginRegistrant.java
|
||||||
|
|
||||||
|
# Remember to never publicly share your keystore.
|
||||||
|
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||||
|
key.properties
|
||||||
|
**/*.keystore
|
||||||
|
**/*.jks
|
@ -0,0 +1,74 @@
|
|||||||
|
def localProperties = new Properties()
|
||||||
|
def localPropertiesFile = rootProject.file('local.properties')
|
||||||
|
if (localPropertiesFile.exists()) {
|
||||||
|
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||||
|
localProperties.load(reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||||
|
if (flutterRoot == null) {
|
||||||
|
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
|
if (flutterVersionCode == null) {
|
||||||
|
flutterVersionCode = '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||||
|
if (flutterVersionName == null) {
|
||||||
|
flutterVersionName = '1.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
// START: FlutterFire Configuration
|
||||||
|
apply plugin: 'com.google.gms.google-services'
|
||||||
|
// END: FlutterFire Configuration
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion flutter.compileSdkVersion
|
||||||
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '1.8'
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
|
applicationId "com.example.example_router"
|
||||||
|
// You can update the following values to match your application needs.
|
||||||
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
||||||
|
minSdkVersion 21
|
||||||
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
|
versionCode flutterVersionCode.toInteger()
|
||||||
|
versionName flutterVersionName
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// TODO: Add your own signing config for the release build.
|
||||||
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source '../..'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.example_router">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
@ -0,0 +1,34 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.example_router">
|
||||||
|
<application
|
||||||
|
android:label="example_router"
|
||||||
|
android:name="${applicationName}"
|
||||||
|
android:icon="@mipmap/ic_launcher">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
|
the Android process has started. This theme is visible to the user
|
||||||
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
to determine the Window background behind the Flutter UI. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
|
android:resource="@style/NormalTheme"
|
||||||
|
/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
</application>
|
||||||
|
</manifest>
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.example.example_router
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
class MainActivity: FlutterActivity() {
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="?android:colorBackground" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
Binary file not shown.
After Width: | Height: | Size: 544 B |
Binary file not shown.
After Width: | Height: | Size: 442 B |
Binary file not shown.
After Width: | Height: | Size: 721 B |
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
@ -0,0 +1,8 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.example_router">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
@ -0,0 +1,34 @@
|
|||||||
|
buildscript {
|
||||||
|
ext.kotlin_version = '1.6.10'
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:7.1.2'
|
||||||
|
// START: FlutterFire Configuration
|
||||||
|
classpath 'com.google.gms:google-services:4.3.10'
|
||||||
|
// END: FlutterFire Configuration
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.buildDir = '../build'
|
||||||
|
subprojects {
|
||||||
|
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(':app')
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
@ -0,0 +1,6 @@
|
|||||||
|
#Fri Jun 23 08:50:38 CEST 2017
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
@ -0,0 +1,11 @@
|
|||||||
|
include ':app'
|
||||||
|
|
||||||
|
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||||
|
def properties = new Properties()
|
||||||
|
|
||||||
|
assert localPropertiesFile.exists()
|
||||||
|
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||||
|
|
||||||
|
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||||
|
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
@ -0,0 +1,40 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: bootstrap.dart
|
||||||
|
// Created Date: 19/08/2022 15:05:17
|
||||||
|
// Last Modified: 19/08/2022 15:21:47
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:example_router/core/utils/app_bloc_observer.dart';
|
||||||
|
import 'package:example_router/firebase_options.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
|
||||||
|
await runZonedGuarded(
|
||||||
|
() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
FlutterError.onError = (details) {
|
||||||
|
debugPrint(details.toString());
|
||||||
|
};
|
||||||
|
|
||||||
|
await Firebase.initializeApp(
|
||||||
|
options: DefaultFirebaseOptions.currentPlatform,
|
||||||
|
);
|
||||||
|
|
||||||
|
GoRouter.setUrlPathStrategy(UrlPathStrategy.path);
|
||||||
|
|
||||||
|
Bloc.observer = AppBlocObserver();
|
||||||
|
|
||||||
|
runApp(await builder());
|
||||||
|
},
|
||||||
|
(error, stackTrace) => debugPrint(error.toString()),
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: form_field.dart
|
||||||
|
// Created Date: 19/08/2022 11:52:33
|
||||||
|
// Last Modified: 19/08/2022 16:35:39
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
abstract class AppFormField {
|
||||||
|
static const confirmedPassword = 'confirmedPassword';
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: router.dart
|
||||||
|
// Created Date: 19/08/2022 11:52:22
|
||||||
|
// Last Modified: 19/08/2022 16:39:07
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:example_router/presentation/features/home/home_page.dart';
|
||||||
|
import 'package:example_router/presentation/features/sign_in/sign_in_page.dart';
|
||||||
|
import 'package:example_router/presentation/features/sign_up/sign_up_page.dart';
|
||||||
|
import 'package:example_router/presentation/features/sub/sub_page.dart';
|
||||||
|
import 'package:example_router/presentation/features/welcome/welcome_page.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
class AppRouter {
|
||||||
|
/// Default transition for all pages
|
||||||
|
static Page<void> defaultTransition(
|
||||||
|
BuildContext context,
|
||||||
|
GoRouterState state,
|
||||||
|
Widget child,
|
||||||
|
) =>
|
||||||
|
CupertinoPage<void>(
|
||||||
|
key: state.pageKey,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
|
||||||
|
static final publicRoutes = ['/', '/sign_in', '/sign_up'];
|
||||||
|
|
||||||
|
static final List<GoRoute> routes = [
|
||||||
|
GoRoute(
|
||||||
|
path: '/',
|
||||||
|
name: WelcomePage.pageName,
|
||||||
|
pageBuilder: (context, state) => defaultTransition(
|
||||||
|
context,
|
||||||
|
state,
|
||||||
|
const WelcomePage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/sign_in',
|
||||||
|
name: SignInPage.pageName,
|
||||||
|
pageBuilder: (context, state) => defaultTransition(
|
||||||
|
context,
|
||||||
|
state,
|
||||||
|
const SignInPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/sign_up',
|
||||||
|
name: SignUpPage.pageName,
|
||||||
|
pageBuilder: (context, state) => defaultTransition(
|
||||||
|
context,
|
||||||
|
state,
|
||||||
|
const SignUpPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/home',
|
||||||
|
name: HomePage.pageName,
|
||||||
|
pageBuilder: (context, state) => defaultTransition(
|
||||||
|
context,
|
||||||
|
state,
|
||||||
|
const HomePage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/home/sub',
|
||||||
|
name: SubPage.pageName,
|
||||||
|
pageBuilder: (context, state) => defaultTransition(
|
||||||
|
context,
|
||||||
|
state,
|
||||||
|
const SubPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: app_bloc_observer.dart
|
||||||
|
// Created Date: 19/08/2022 12:02:23
|
||||||
|
// Last Modified: 19/08/2022 12:02:45
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
class AppBlocObserver extends BlocObserver {
|
||||||
|
@override
|
||||||
|
void onEvent(Bloc bloc, Object? event) {
|
||||||
|
super.onEvent(bloc, event);
|
||||||
|
debugPrint(event.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
|
||||||
|
debugPrint(error.toString());
|
||||||
|
super.onError(bloc, error, stackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onChange(BlocBase bloc, Change change) {
|
||||||
|
super.onChange(bloc, change);
|
||||||
|
debugPrint('curr:\t${change.currentState}\nnext:\t${change.nextState}');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onTransition(Bloc bloc, Transition transition) {
|
||||||
|
super.onTransition(bloc, transition);
|
||||||
|
debugPrint(transition.toString());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: forms.dart
|
||||||
|
// Created Date: 19/08/2022 12:00:31
|
||||||
|
// Last Modified: 19/08/2022 16:35:52
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:example_router/core/constants/form_field.dart';
|
||||||
|
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||||
|
|
||||||
|
class Forms {
|
||||||
|
static FormData getNormalData() => const FormData([
|
||||||
|
FormInput(
|
||||||
|
AppFormField.confirmedPassword,
|
||||||
|
ConfirmedPassword.pure(),
|
||||||
|
metadata: FormInputMetadata<void>(export: false),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
// File generated by FlutterFire CLI.
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members
|
||||||
|
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
||||||
|
import 'package:flutter/foundation.dart'
|
||||||
|
show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
||||||
|
|
||||||
|
/// Default [FirebaseOptions] for use with your Firebase apps.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// import 'firebase_options.dart';
|
||||||
|
/// // ...
|
||||||
|
/// await Firebase.initializeApp(
|
||||||
|
/// options: DefaultFirebaseOptions.currentPlatform,
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
class DefaultFirebaseOptions {
|
||||||
|
static FirebaseOptions get currentPlatform {
|
||||||
|
if (kIsWeb) {
|
||||||
|
throw UnsupportedError(
|
||||||
|
'DefaultFirebaseOptions have not been configured for web - '
|
||||||
|
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
switch (defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
return android;
|
||||||
|
case TargetPlatform.iOS:
|
||||||
|
throw UnsupportedError(
|
||||||
|
'DefaultFirebaseOptions have not been configured for ios - '
|
||||||
|
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||||
|
);
|
||||||
|
case TargetPlatform.macOS:
|
||||||
|
throw UnsupportedError(
|
||||||
|
'DefaultFirebaseOptions have not been configured for macos - '
|
||||||
|
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||||
|
);
|
||||||
|
case TargetPlatform.windows:
|
||||||
|
throw UnsupportedError(
|
||||||
|
'DefaultFirebaseOptions have not been configured for windows - '
|
||||||
|
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||||
|
);
|
||||||
|
case TargetPlatform.linux:
|
||||||
|
throw UnsupportedError(
|
||||||
|
'DefaultFirebaseOptions have not been configured for linux - '
|
||||||
|
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
throw UnsupportedError(
|
||||||
|
'DefaultFirebaseOptions are not supported for this platform.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const FirebaseOptions android = FirebaseOptions(
|
||||||
|
apiKey: 'AIzaSyAYS14uXupkS158Q5QAFP1864UrUN_yDSk',
|
||||||
|
appId: '1:136771801992:android:ac3cfeb99fb0763e97203d',
|
||||||
|
messagingSenderId: '136771801992',
|
||||||
|
projectId: 'tchat-beta',
|
||||||
|
databaseURL: 'https://tchat-beta.firebaseio.com',
|
||||||
|
storageBucket: 'tchat-beta.appspot.com',
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
import 'package:example_router/bootstrap.dart';
|
||||||
|
import 'package:example_router/presentation/features/app/app.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
bootstrap(App.new);
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: app.dart
|
||||||
|
// Created Date: 19/08/2022 12:05:38
|
||||||
|
// Last Modified: Fri Aug 26 2022
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:example_router/core/routes/router.dart';
|
||||||
|
import 'package:example_router/core/utils/forms.dart';
|
||||||
|
import 'package:example_router/presentation/features/home/home_page.dart';
|
||||||
|
import 'package:example_router/presentation/features/welcome/welcome_page.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||||
|
|
||||||
|
class App extends StatelessWidget {
|
||||||
|
final AuthenticationRepository authenticationRepository =
|
||||||
|
AuthenticationRepositoryFirebase();
|
||||||
|
|
||||||
|
App({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
AuthenticationState? previous;
|
||||||
|
|
||||||
|
final AuthenticationCubit authenticationCubit = AuthenticationCubit(
|
||||||
|
authenticationRepository: authenticationRepository,
|
||||||
|
onAuthSuccess: (user) async {
|
||||||
|
debugPrint(user.toString());
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final GoRouter router = GoRouter(
|
||||||
|
initialLocation: '/',
|
||||||
|
routes: AppRouter.routes,
|
||||||
|
debugLogDiagnostics: true,
|
||||||
|
errorBuilder: (_, __) => const ColoredBox(
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
refreshListenable: GoRouterRefreshStream(authenticationCubit.stream),
|
||||||
|
redirect: (state) {
|
||||||
|
final authState = authenticationCubit.state;
|
||||||
|
|
||||||
|
if (authState != previous) {
|
||||||
|
previous = authState;
|
||||||
|
// Check if current user is logged in
|
||||||
|
final loggedIn =
|
||||||
|
authState.status == AuthenticationStatus.authenticated;
|
||||||
|
|
||||||
|
// Checking if current path is onboarding or not
|
||||||
|
final isOnboarding = AppRouter.publicRoutes.contains(state.subloc);
|
||||||
|
|
||||||
|
if (!loggedIn) {
|
||||||
|
debugPrint('Not logged');
|
||||||
|
if (isOnboarding) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return state.namedLocation(WelcomePage.pageName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final email = authState.user?.email;
|
||||||
|
debugPrint('Logged as: $email');
|
||||||
|
if (isOnboarding) {
|
||||||
|
return state.namedLocation(HomePage.pageName);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return MultiRepositoryProvider(
|
||||||
|
providers: [
|
||||||
|
RepositoryProvider<AuthenticationRepository>.value(
|
||||||
|
value: authenticationRepository,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: MultiBlocProvider(
|
||||||
|
providers: [
|
||||||
|
BlocProvider<AuthenticationCubit>.value(
|
||||||
|
value: authenticationCubit..init(),
|
||||||
|
),
|
||||||
|
BlocProvider<SignUpCubit>(
|
||||||
|
create: (_) => SignUpCubit(
|
||||||
|
authenticationRepository: authenticationRepository,
|
||||||
|
formData: Forms.getNormalData(),
|
||||||
|
onSignUpSuccess: (state, uid) async {
|
||||||
|
debugPrint(state.toString());
|
||||||
|
debugPrint(uid);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
BlocProvider<SignInCubit>(
|
||||||
|
create: (_) => SignInCubit(
|
||||||
|
authenticationRepository: authenticationRepository,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: MaterialApp.router(
|
||||||
|
title: 'Demo Authentication',
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
routerDelegate: router.routerDelegate,
|
||||||
|
routeInformationParser: router.routeInformationParser,
|
||||||
|
routeInformationProvider: router.routeInformationProvider,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: home_page.dart
|
||||||
|
// Created Date: 19/08/2022 14:38:24
|
||||||
|
// Last Modified: 19/08/2022 16:12:22
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:example_router/presentation/features/sub/sub_page.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||||
|
|
||||||
|
class HomePage extends StatelessWidget {
|
||||||
|
const HomePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
static String pageName = 'Home';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Home'),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => context.read<AuthenticationCubit>().logOut(),
|
||||||
|
icon: const Icon(Icons.logout_rounded))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
BlocBuilder<AuthenticationCubit, AuthenticationState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
final email = state.user?.email;
|
||||||
|
return Text('Logged as $email');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => context.pushNamed(SubPage.pageName),
|
||||||
|
child: const Text('Go to sub page')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: sign_in_page.dart
|
||||||
|
// Created Date: 19/08/2022 12:41:42
|
||||||
|
// Last Modified: 19/08/2022 15:26:36
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:example_router/presentation/features/sign_in/widgets/sign_in_form.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SignInPage extends StatelessWidget {
|
||||||
|
const SignInPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
static String pageName = 'SignIn';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('Sign In')),
|
||||||
|
body: const Padding(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: SignInForm(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: sign_in_form.dart
|
||||||
|
// Created Date: 19/08/2022 15:24:37
|
||||||
|
// Last Modified: 19/08/2022 16:35:01
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||||
|
|
||||||
|
class _EmailInput extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<SignInCubit, SignInState>(
|
||||||
|
buildWhen: (previous, current) => previous.email != current.email,
|
||||||
|
builder: (context, state) {
|
||||||
|
return TextField(
|
||||||
|
onChanged: (email) => context.read<SignInCubit>().emailChanged(email),
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Email',
|
||||||
|
helperText: '',
|
||||||
|
errorText: state.email.invalid ? 'Invalid email' : null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PasswordInput extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<SignInCubit, SignInState>(
|
||||||
|
buildWhen: (previous, current) => previous.password != current.password,
|
||||||
|
builder: (context, state) {
|
||||||
|
return TextField(
|
||||||
|
onChanged: (password) {
|
||||||
|
context.read<SignInCubit>().passwordChanged(password);
|
||||||
|
},
|
||||||
|
obscureText: true,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Password',
|
||||||
|
helperText: '',
|
||||||
|
errorText: state.password.invalid ? 'Invalid password' : null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SignInButton extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<SignInCubit, SignInState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return state.status.isSubmissionInProgress
|
||||||
|
? const CircularProgressIndicator()
|
||||||
|
: ElevatedButton(
|
||||||
|
onPressed: state.status.isValidated
|
||||||
|
? () =>
|
||||||
|
context.read<SignInCubit>().signInWithEmailAndPassword()
|
||||||
|
: null,
|
||||||
|
child: const Text('Sign in'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SignInForm extends StatelessWidget {
|
||||||
|
const SignInForm({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocListener<SignInCubit, SignInState>(
|
||||||
|
listener: (context, state) {
|
||||||
|
if (state.status.isSubmissionSuccess) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
} else if (state.status.isSubmissionFailure) {
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..hideCurrentSnackBar()
|
||||||
|
..showSnackBar(
|
||||||
|
SnackBar(content: Text(state.errorMessage ?? 'Sign In Failure')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_EmailInput(),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_PasswordInput(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
_SignInButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: sign_up_page.dart
|
||||||
|
// Created Date: 19/08/2022 12:41:27
|
||||||
|
// Last Modified: 19/08/2022 14:58:51
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:example_router/presentation/features/sign_up/widgets/sign_up_form.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SignUpPage extends StatelessWidget {
|
||||||
|
const SignUpPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
static String pageName = 'SignUp';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('Sign Up')),
|
||||||
|
body: const Padding(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: SignUpForm(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: sign_up_form.dart
|
||||||
|
// Created Date: 19/08/2022 14:41:08
|
||||||
|
// Last Modified: Fri Aug 26 2022
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:example_router/core/constants/form_field.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||||
|
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||||
|
|
||||||
|
class _EmailInput extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||||
|
buildWhen: (previous, current) => previous.email != current.email,
|
||||||
|
builder: (context, state) {
|
||||||
|
return TextField(
|
||||||
|
onChanged: (email) => context.read<SignUpCubit>().emailChanged(email),
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Email',
|
||||||
|
helperText: '',
|
||||||
|
errorText: state.email.invalid ? 'Invalid email' : null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PasswordInput extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||||
|
buildWhen: (previous, current) => previous.password != current.password,
|
||||||
|
builder: (context, state) {
|
||||||
|
return TextField(
|
||||||
|
onChanged: (password) {
|
||||||
|
context.read<SignUpCubit>().passwordChanged(password);
|
||||||
|
context.read<SignUpCubit>().dataChanged(
|
||||||
|
AppFormField.confirmedPassword,
|
||||||
|
ConfirmedPassword.dirty(
|
||||||
|
password: password,
|
||||||
|
value: context
|
||||||
|
.read<SignUpCubit>()
|
||||||
|
.state
|
||||||
|
.data
|
||||||
|
.valueOf<String>(
|
||||||
|
AppFormField.confirmedPassword),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
obscureText: true,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Password',
|
||||||
|
helperText: '',
|
||||||
|
errorText: state.password.invalid ? 'Invalid password' : null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ConfirmPasswordInput extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return TextField(
|
||||||
|
onChanged: (confirmPassword) => context
|
||||||
|
.read<SignUpCubit>()
|
||||||
|
.dataChanged(
|
||||||
|
AppFormField.confirmedPassword,
|
||||||
|
ConfirmedPassword.dirty(
|
||||||
|
password: context.read<SignUpCubit>().state.password.value,
|
||||||
|
value: confirmPassword,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
obscureText: true,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Confirm password',
|
||||||
|
helperText: '',
|
||||||
|
errorText: state.data.isNotValid(AppFormField.confirmedPassword)
|
||||||
|
? 'Passwords do not match'
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SignUpButton extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<SignUpCubit, SignUpState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return state.status.isSubmissionInProgress
|
||||||
|
? const CircularProgressIndicator()
|
||||||
|
: ElevatedButton(
|
||||||
|
onPressed: state.status.isValidated
|
||||||
|
? () => context.read<SignUpCubit>().signUpFormSubmitted()
|
||||||
|
: null,
|
||||||
|
child: const Text('Sign up'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SignUpForm extends StatelessWidget {
|
||||||
|
const SignUpForm({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocListener<SignUpCubit, SignUpState>(
|
||||||
|
listener: (context, state) {
|
||||||
|
if (state.status.isSubmissionSuccess) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
} else if (state.status.isSubmissionFailure) {
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..hideCurrentSnackBar()
|
||||||
|
..showSnackBar(
|
||||||
|
SnackBar(content: Text(state.errorMessage ?? 'Sign Up Failure')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_EmailInput(),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_PasswordInput(),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_ConfirmPasswordInput(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
_SignUpButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: sub_page.dart
|
||||||
|
// Created Date: 19/08/2022 16:10:05
|
||||||
|
// Last Modified: 19/08/2022 16:10:44
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||||
|
|
||||||
|
class SubPage extends StatelessWidget {
|
||||||
|
const SubPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
static String pageName = 'Sub';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Sub'),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => context.read<AuthenticationCubit>().logOut(),
|
||||||
|
icon: const Icon(Icons.logout_rounded))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: const Padding(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Text('Another page'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: welcome_page.dart
|
||||||
|
// Created Date: 19/08/2022 12:33:21
|
||||||
|
// Last Modified: 19/08/2022 15:56:05
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:example_router/presentation/features/sign_in/sign_in_page.dart';
|
||||||
|
import 'package:example_router/presentation/features/sign_up/sign_up_page.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||||
|
|
||||||
|
class WelcomePage extends StatelessWidget {
|
||||||
|
const WelcomePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
static String pageName = 'Welcome';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Welcome'),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => context.read<AuthenticationCubit>().changeStatus(
|
||||||
|
context.read<AuthenticationCubit>().state.user!),
|
||||||
|
icon: const Icon(Icons.refresh_rounded))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => context.pushNamed(SignUpPage.pageName),
|
||||||
|
child: const Text('Sign Up')),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => context.pushNamed(SignInPage.pageName),
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor:
|
||||||
|
MaterialStateProperty.all<Color>(Colors.white),
|
||||||
|
foregroundColor:
|
||||||
|
MaterialStateProperty.all<Color>(Colors.blue)),
|
||||||
|
child: const Text('Sign In'))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,76 +0,0 @@
|
|||||||
// Copyright (C) 2022 WYATT GROUP
|
|
||||||
// Please see the AUTHORS file for details.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:wyatt_authentication_bloc/src/models/user/user_interface.dart';
|
|
||||||
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
|
|
||||||
|
|
||||||
part 'authentication_state.dart';
|
|
||||||
|
|
||||||
class AuthenticationCubit extends Cubit<AuthenticationState> {
|
|
||||||
final AuthenticationRepositoryInterface _authenticationRepository;
|
|
||||||
|
|
||||||
StreamSubscription<UserInterface>? _userSubscription;
|
|
||||||
|
|
||||||
final Future<Map<String, dynamic>> Function(UserInterface user)?
|
|
||||||
_onAuthSuccess;
|
|
||||||
|
|
||||||
AuthenticationCubit({
|
|
||||||
required AuthenticationRepositoryInterface authenticationRepository,
|
|
||||||
Future<Map<String, dynamic>> Function(UserInterface user)? onAuthSuccess,
|
|
||||||
}) : _authenticationRepository = authenticationRepository,
|
|
||||||
_onAuthSuccess = onAuthSuccess,
|
|
||||||
super(const AuthenticationState.unknown());
|
|
||||||
|
|
||||||
Future<void> init() async {
|
|
||||||
final _firstUser = await _authenticationRepository.user.first;
|
|
||||||
start();
|
|
||||||
return changeStatus(_firstUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start() {
|
|
||||||
_userSubscription = _authenticationRepository.user.listen(changeStatus);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool stop() {
|
|
||||||
_userSubscription?.cancel();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> changeStatus(UserInterface user) async {
|
|
||||||
if (user.isNotEmpty) {
|
|
||||||
final Map<String, dynamic>? userData = await _onAuthSuccess?.call(user);
|
|
||||||
emit(AuthenticationState.authenticated(user, userData));
|
|
||||||
} else {
|
|
||||||
stop();
|
|
||||||
emit(const AuthenticationState.unauthenticated());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void logOut() {
|
|
||||||
unawaited(_authenticationRepository.signOut());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> close() {
|
|
||||||
_userSubscription?.cancel();
|
|
||||||
return super.close();
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,5 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
export 'user_firebase.dart';
|
enum AuthCubitStatus {
|
||||||
export 'user_interface.dart';
|
started,
|
||||||
|
stoped,
|
||||||
|
}
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/core/exceptions/exceptions.dart';
|
||||||
|
|
||||||
class ApplyActionCodeFailureFirebase extends ApplyActionCodeFailureInterface {
|
class ApplyActionCodeFailureFirebase extends ApplyActionCodeFailureInterface {
|
||||||
ApplyActionCodeFailureFirebase([String? code, String? message])
|
ApplyActionCodeFailureFirebase([String? code, String? message])
|
||||||
@ -148,32 +148,28 @@ class SignInWithCredentialFailureFirebase
|
|||||||
class SignInWithGoogleFailureFirebase
|
class SignInWithGoogleFailureFirebase
|
||||||
extends SignInWithCredentialFailureFirebase
|
extends SignInWithCredentialFailureFirebase
|
||||||
implements SignInWithGoogleFailureInterface {
|
implements SignInWithGoogleFailureInterface {
|
||||||
SignInWithGoogleFailureFirebase([String? code, String? message])
|
SignInWithGoogleFailureFirebase([super.code, super.message]);
|
||||||
: super(code, message);
|
SignInWithGoogleFailureFirebase.fromCode(super.code) : super.fromCode();
|
||||||
SignInWithGoogleFailureFirebase.fromCode(String code) : super.fromCode(code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SignInWithFacebookFailureFirebase
|
class SignInWithFacebookFailureFirebase
|
||||||
extends SignInWithCredentialFailureFirebase
|
extends SignInWithCredentialFailureFirebase
|
||||||
implements SignInWithFacebookFailureInterface {
|
implements SignInWithFacebookFailureInterface {
|
||||||
SignInWithFacebookFailureFirebase([String? code, String? message])
|
SignInWithFacebookFailureFirebase([super.code, super.message]);
|
||||||
: super(code, message);
|
SignInWithFacebookFailureFirebase.fromCode(super.code)
|
||||||
SignInWithFacebookFailureFirebase.fromCode(String code)
|
: super.fromCode();
|
||||||
: super.fromCode(code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SignInWithAppleFailureFirebase extends SignInWithCredentialFailureFirebase
|
class SignInWithAppleFailureFirebase extends SignInWithCredentialFailureFirebase
|
||||||
implements SignInWithAppleFailureInterface {
|
implements SignInWithAppleFailureInterface {
|
||||||
SignInWithAppleFailureFirebase([String? code, String? message])
|
SignInWithAppleFailureFirebase([super.code, super.message]);
|
||||||
: super(code, message);
|
SignInWithAppleFailureFirebase.fromCode(super.code) : super.fromCode();
|
||||||
SignInWithAppleFailureFirebase.fromCode(String code) : super.fromCode(code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SignInWithTwitterFailureFirebase extends SignInWithCredentialFailureFirebase
|
class SignInWithTwitterFailureFirebase extends SignInWithCredentialFailureFirebase
|
||||||
implements SignInWithAppleFailureInterface {
|
implements SignInWithAppleFailureInterface {
|
||||||
SignInWithTwitterFailureFirebase([String? code, String? message])
|
SignInWithTwitterFailureFirebase([super.code, super.message]);
|
||||||
: super(code, message);
|
SignInWithTwitterFailureFirebase.fromCode(super.code) : super.fromCode();
|
||||||
SignInWithTwitterFailureFirebase.fromCode(String code) : super.fromCode(code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -233,16 +229,16 @@ class SendEmailVerificationFailureFirebase
|
|||||||
SendEmailVerificationFailureFirebase([String? code, String? message])
|
SendEmailVerificationFailureFirebase([String? code, String? message])
|
||||||
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
||||||
|
|
||||||
SendEmailVerificationFailureFirebase.fromCode(String code)
|
SendEmailVerificationFailureFirebase.fromCode(super.code)
|
||||||
: super.fromCode(code);
|
: super.fromCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
class SendPasswordResetEmailFailureFirebase
|
class SendPasswordResetEmailFailureFirebase
|
||||||
extends SendPasswordResetEmailFailureInterface {
|
extends SendPasswordResetEmailFailureInterface {
|
||||||
SendPasswordResetEmailFailureFirebase([String? code, String? message])
|
SendPasswordResetEmailFailureFirebase([String? code, String? message])
|
||||||
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
||||||
SendPasswordResetEmailFailureFirebase.fromCode(String code)
|
SendPasswordResetEmailFailureFirebase.fromCode(super.code)
|
||||||
: super.fromCode(code);
|
: super.fromCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
class SendSignInLinkEmailFailureFirebase
|
class SendSignInLinkEmailFailureFirebase
|
||||||
@ -250,8 +246,8 @@ class SendSignInLinkEmailFailureFirebase
|
|||||||
SendSignInLinkEmailFailureFirebase([String? code, String? message])
|
SendSignInLinkEmailFailureFirebase([String? code, String? message])
|
||||||
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
||||||
|
|
||||||
SendSignInLinkEmailFailureFirebase.fromCode(String code)
|
SendSignInLinkEmailFailureFirebase.fromCode(super.code)
|
||||||
: super.fromCode(code);
|
: super.fromCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConfirmPasswordResetFailureFirebase
|
class ConfirmPasswordResetFailureFirebase
|
||||||
@ -259,8 +255,8 @@ class ConfirmPasswordResetFailureFirebase
|
|||||||
ConfirmPasswordResetFailureFirebase([String? code, String? message])
|
ConfirmPasswordResetFailureFirebase([String? code, String? message])
|
||||||
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
||||||
|
|
||||||
ConfirmPasswordResetFailureFirebase.fromCode(String code)
|
ConfirmPasswordResetFailureFirebase.fromCode(super.code)
|
||||||
: super.fromCode(code);
|
: super.fromCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
class VerifyPasswordResetCodeFailureFirebase
|
class VerifyPasswordResetCodeFailureFirebase
|
||||||
@ -268,19 +264,19 @@ class VerifyPasswordResetCodeFailureFirebase
|
|||||||
VerifyPasswordResetCodeFailureFirebase([String? code, String? message])
|
VerifyPasswordResetCodeFailureFirebase([String? code, String? message])
|
||||||
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
||||||
|
|
||||||
VerifyPasswordResetCodeFailureFirebase.fromCode(String code)
|
VerifyPasswordResetCodeFailureFirebase.fromCode(super.code)
|
||||||
: super.fromCode(code);
|
: super.fromCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
class RefreshFailureFirebase extends RefreshFailureInterface {
|
class RefreshFailureFirebase extends RefreshFailureInterface {
|
||||||
RefreshFailureFirebase([String? code, String? message])
|
RefreshFailureFirebase([String? code, String? message])
|
||||||
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
||||||
RefreshFailureFirebase.fromCode(String code) : super.fromCode(code);
|
RefreshFailureFirebase.fromCode(super.code) : super.fromCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
class SignOutFailureFirebase extends SignOutFailureInterface {
|
class SignOutFailureFirebase extends SignOutFailureInterface {
|
||||||
SignOutFailureFirebase([String? code, String? message])
|
SignOutFailureFirebase([String? code, String? message])
|
||||||
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
|
||||||
|
|
||||||
SignOutFailureFirebase.fromCode(String code) : super.fromCode(code);
|
SignOutFailureFirebase.fromCode(super.code) : super.fromCode();
|
||||||
}
|
}
|
@ -14,5 +14,9 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
export 'authentication_repository_firebase.dart';
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
export 'authentication_repository_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/data/models/user_firebase.dart';
|
||||||
|
|
||||||
|
extension FirebaseAuthUserX on User {
|
||||||
|
UserFirebase get model => UserFirebase(this);
|
||||||
|
}
|
@ -1,24 +1,23 @@
|
|||||||
// Copyright (C) 2022 WYATT GROUP
|
// Copyright (C) 2022 WYATT GROUP
|
||||||
// Please see the AUTHORS file for details.
|
// Please see the AUTHORS file for details.
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// any later version.
|
// any later version.
|
||||||
//
|
//
|
||||||
// This program is distributed in the hope that it will be useful,
|
// This program is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/src/domain/entities/user.dart' as wyatt;
|
||||||
|
|
||||||
import 'package:wyatt_authentication_bloc/src/models/user/user_interface.dart';
|
class UserFirebase implements wyatt.User {
|
||||||
|
|
||||||
class UserFirebase implements UserInterface {
|
|
||||||
final User? _user;
|
final User? _user;
|
||||||
|
|
||||||
const UserFirebase(User user) : _user = user;
|
const UserFirebase(User user) : _user = user;
|
@ -14,19 +14,24 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
|
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
|
||||||
import 'package:google_sign_in/google_sign_in.dart';
|
import 'package:google_sign_in/google_sign_in.dart';
|
||||||
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
|
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
|
||||||
import 'package:twitter_login/twitter_login.dart';
|
import 'package:twitter_login/twitter_login.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_firebase.dart';
|
import 'package:wyatt_authentication_bloc/src/core/enum/auth_cubit_status.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/models/user/user_firebase.dart';
|
import 'package:wyatt_authentication_bloc/src/core/exceptions/exceptions_firebase.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/models/user/user_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/core/extensions/firebase_auth_user_x.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/core/utils/cryptography.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/utils/cryptography.dart';
|
import 'package:wyatt_authentication_bloc/src/data/models/user_firebase.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/src/domain/entities/user.dart'
|
||||||
|
as wyatt;
|
||||||
|
import 'package:wyatt_authentication_bloc/src/domain/repositories/authentication_repository.dart';
|
||||||
|
|
||||||
class AuthenticationRepositoryFirebase
|
class AuthenticationRepositoryFirebase implements AuthenticationRepository {
|
||||||
implements AuthenticationRepositoryInterface {
|
final _controller = StreamController<AuthCubitStatus>();
|
||||||
final FirebaseAuth _firebaseAuth;
|
final FirebaseAuth _firebaseAuth;
|
||||||
final TwitterLogin? _twitterLogin;
|
final TwitterLogin? _twitterLogin;
|
||||||
|
|
||||||
@ -36,23 +41,30 @@ class AuthenticationRepositoryFirebase
|
|||||||
FirebaseAuth? firebaseAuth,
|
FirebaseAuth? firebaseAuth,
|
||||||
TwitterLogin? twitterLogin,
|
TwitterLogin? twitterLogin,
|
||||||
}) : _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance,
|
}) : _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance,
|
||||||
_twitterLogin = twitterLogin;
|
_twitterLogin = twitterLogin {
|
||||||
|
_controller.sink.add(AuthCubitStatus.stoped);
|
||||||
@override
|
|
||||||
Stream<UserInterface> get user {
|
|
||||||
return _firebaseAuth.userChanges().map((User? firebaseUser) {
|
|
||||||
final UserFirebase user = firebaseUser == null
|
|
||||||
? const UserFirebase.empty()
|
|
||||||
: firebaseUser.model;
|
|
||||||
_userCache = user;
|
|
||||||
return user;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
UserInterface get currentUser {
|
Stream<AuthCubitStatus> get cubitStatus =>
|
||||||
return _userCache;
|
_controller.stream.asBroadcastStream();
|
||||||
}
|
|
||||||
|
@override
|
||||||
|
void changeCubitStatus(AuthCubitStatus status) =>
|
||||||
|
_controller.sink.add(status);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<wyatt.User> get user =>
|
||||||
|
_firebaseAuth.userChanges().map((firebaseUser) {
|
||||||
|
final UserFirebase user = (firebaseUser == null)
|
||||||
|
? const UserFirebase.empty()
|
||||||
|
: firebaseUser.model;
|
||||||
|
_userCache = user;
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
wyatt.User get currentUser => _userCache;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> applyActionCode(String code) async {
|
Future<void> applyActionCode(String code) async {
|
||||||
@ -324,9 +336,3 @@ class AuthenticationRepositoryFirebase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on User {
|
|
||||||
UserFirebase get model {
|
|
||||||
return UserFirebase(this);
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,9 +14,9 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
abstract class UserInterface {
|
abstract class User {
|
||||||
/// The empty user constructor.
|
/// The empty user constructor.
|
||||||
const UserInterface.empty();
|
const User.empty();
|
||||||
|
|
||||||
/// The users display name.
|
/// The users display name.
|
||||||
///
|
///
|
@ -14,22 +14,29 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/core/enum/auth_cubit_status.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/models/user/user_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/core/exceptions/exceptions.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/src/domain/entities/user.dart';
|
||||||
|
|
||||||
/// {@template authentication_repository}
|
/// {@template authentication_repository}
|
||||||
/// Repository which manages user authentication.
|
/// Repository which manages user authentication.
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
abstract class AuthenticationRepositoryInterface {
|
abstract class AuthenticationRepository {
|
||||||
/// Stream of [UserInterface] which will emit the current user when
|
/// Stream of [AuthCubitStatus] wich will emit the current cubit status.
|
||||||
|
Stream<AuthCubitStatus> get cubitStatus;
|
||||||
|
|
||||||
|
/// Changes cubit status.(Useful to start or stop the engine.)
|
||||||
|
void changeCubitStatus(AuthCubitStatus status);
|
||||||
|
|
||||||
|
/// Stream of [User] which will emit the current user when
|
||||||
/// the authentication state changes.
|
/// the authentication state changes.
|
||||||
///
|
///
|
||||||
/// Emits [UserInterface.empty] if the user is not authenticated.
|
/// Emits [User.empty] if the user is not authenticated.
|
||||||
Stream<UserInterface> get user;
|
Stream<User> get user;
|
||||||
|
|
||||||
/// Returns the current cached account.
|
/// Returns the current cached account.
|
||||||
/// Defaults to [UserInterface.empty] if there is no cached user.
|
/// Defaults to [User.empty] if there is no cached user.
|
||||||
UserInterface get currentUser;
|
User get currentUser;
|
||||||
|
|
||||||
/// Applies action code
|
/// Applies action code
|
||||||
///
|
///
|
||||||
@ -122,7 +129,7 @@ abstract class AuthenticationRepositoryInterface {
|
|||||||
Future<void> verifyPasswordResetCode({required String code});
|
Future<void> verifyPasswordResetCode({required String code});
|
||||||
|
|
||||||
/// Signs out the current user which will emit
|
/// Signs out the current user which will emit
|
||||||
/// [UserInterface.empty] from the [user] Stream.
|
/// [User.empty] from the [user] Stream.
|
||||||
Future<void> signOut();
|
Future<void> signOut();
|
||||||
|
|
||||||
/// Refreshes the current user.
|
/// Refreshes the current user.
|
@ -16,41 +16,40 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/authentication/cubit/authentication_cubit.dart';
|
import 'package:wyatt_authentication_bloc/src/domain/entities/user.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/models/user/user_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/features/authentication/cubit/authentication_cubit.dart';
|
||||||
|
|
||||||
class AuthenticationBuilder extends StatelessWidget {
|
class AuthenticationBuilder<Extra> extends StatelessWidget {
|
||||||
const AuthenticationBuilder({
|
const AuthenticationBuilder({
|
||||||
Key? key,
|
|
||||||
required this.authenticated,
|
required this.authenticated,
|
||||||
required this.unauthenticated,
|
required this.unauthenticated,
|
||||||
required this.unknown,
|
required this.unknown,
|
||||||
}) : super(key: key);
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final Widget Function(
|
final Widget Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
UserInterface user,
|
User user,
|
||||||
Map<String, dynamic>? userData,
|
Extra? extra,
|
||||||
) authenticated;
|
) authenticated;
|
||||||
final Widget Function(BuildContext context) unauthenticated;
|
final Widget Function(BuildContext context) unauthenticated;
|
||||||
final Widget Function(BuildContext context) unknown;
|
final Widget Function(BuildContext context) unknown;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) =>
|
||||||
return BlocBuilder<AuthenticationCubit, AuthenticationState>(
|
BlocBuilder<AuthenticationCubit<Extra>, AuthenticationState<Extra>>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state.status == AuthenticationStatus.authenticated) {
|
if (state.status == AuthenticationStatus.authenticated) {
|
||||||
if (state.user != null) {
|
if (state.user != null) {
|
||||||
return authenticated(context, state.user!, state.userData);
|
return authenticated(context, state.user!, state.extra);
|
||||||
} else {
|
} else {
|
||||||
|
return unauthenticated(context);
|
||||||
|
}
|
||||||
|
} else if (state.status == AuthenticationStatus.unauthenticated) {
|
||||||
return unauthenticated(context);
|
return unauthenticated(context);
|
||||||
|
} else {
|
||||||
|
return unknown(context);
|
||||||
}
|
}
|
||||||
} else if (state.status == AuthenticationStatus.unauthenticated) {
|
},
|
||||||
return unauthenticated(context);
|
);
|
||||||
} else {
|
|
||||||
return unknown(context);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
// Copyright (C) 2022 WYATT GROUP
|
||||||
|
// Please see the AUTHORS file for details.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/src/core/enum/auth_cubit_status.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/src/domain/entities/user.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/src/domain/repositories/authentication_repository.dart';
|
||||||
|
|
||||||
|
part 'authentication_state.dart';
|
||||||
|
|
||||||
|
class AuthenticationCubit<Extra> extends Cubit<AuthenticationState<Extra>> {
|
||||||
|
final AuthenticationRepository _authenticationRepository;
|
||||||
|
late final StreamSubscription<AuthCubitStatus> _statusSubscription;
|
||||||
|
|
||||||
|
StreamSubscription<User>? _userSubscription;
|
||||||
|
|
||||||
|
final Future<Extra?> Function(User user)? _onAuthSuccess;
|
||||||
|
|
||||||
|
AuthenticationCubit({
|
||||||
|
required AuthenticationRepository authenticationRepository,
|
||||||
|
Future<Extra?> Function(User user)? onAuthSuccess,
|
||||||
|
}) : _authenticationRepository = authenticationRepository,
|
||||||
|
_onAuthSuccess = onAuthSuccess,
|
||||||
|
super(const AuthenticationState.unknown()) {
|
||||||
|
_subscribeStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<AuthCubitStatus> get status async =>
|
||||||
|
_authenticationRepository.cubitStatus.last;
|
||||||
|
|
||||||
|
void _subscribeStatus() {
|
||||||
|
_statusSubscription = _authenticationRepository.cubitStatus.listen(
|
||||||
|
(status) {
|
||||||
|
switch (status) {
|
||||||
|
case AuthCubitStatus.started:
|
||||||
|
start();
|
||||||
|
break;
|
||||||
|
case AuthCubitStatus.stoped:
|
||||||
|
stop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> init() async {
|
||||||
|
final firstUser = await _authenticationRepository.user.first;
|
||||||
|
_authenticationRepository.changeCubitStatus(AuthCubitStatus.started);
|
||||||
|
return changeStatus(firstUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start() {
|
||||||
|
_userSubscription = _authenticationRepository.user.listen(changeStatus);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stop() {
|
||||||
|
_userSubscription?.cancel();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> changeStatus(User user) async {
|
||||||
|
if (user.isNotEmpty) {
|
||||||
|
final Extra? extra = await _onAuthSuccess?.call(user);
|
||||||
|
emit(AuthenticationState.authenticated(user, extra));
|
||||||
|
} else {
|
||||||
|
_authenticationRepository.changeCubitStatus(AuthCubitStatus.stoped);
|
||||||
|
emit(const AuthenticationState.unauthenticated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void logOut() {
|
||||||
|
unawaited(_authenticationRepository.signOut());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_userSubscription?.cancel();
|
||||||
|
_statusSubscription.cancel();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
}
|
@ -22,37 +22,36 @@ enum AuthenticationStatus {
|
|||||||
unauthenticated,
|
unauthenticated,
|
||||||
}
|
}
|
||||||
|
|
||||||
class AuthenticationState extends Equatable {
|
class AuthenticationState<Extra> extends Equatable {
|
||||||
final AuthenticationStatus status;
|
final AuthenticationStatus status;
|
||||||
final UserInterface? user;
|
final User? user;
|
||||||
final Map<String, dynamic>? userData;
|
final Extra? extra;
|
||||||
|
|
||||||
const AuthenticationState._({
|
const AuthenticationState._({
|
||||||
required this.status,
|
required this.status,
|
||||||
this.user,
|
this.user,
|
||||||
this.userData,
|
this.extra,
|
||||||
});
|
});
|
||||||
|
|
||||||
const AuthenticationState.unknown()
|
const AuthenticationState.unknown()
|
||||||
: this._(status: AuthenticationStatus.unknown);
|
: this._(status: AuthenticationStatus.unknown);
|
||||||
|
|
||||||
const AuthenticationState.authenticated(
|
const AuthenticationState.authenticated(
|
||||||
UserInterface user,
|
User user,
|
||||||
Map<String, dynamic>? userData,
|
Extra? extra,
|
||||||
) : this._(
|
) : this._(
|
||||||
status: AuthenticationStatus.authenticated,
|
status: AuthenticationStatus.authenticated,
|
||||||
user: user,
|
user: user,
|
||||||
userData: userData,
|
extra: extra,
|
||||||
);
|
);
|
||||||
|
|
||||||
const AuthenticationState.unauthenticated()
|
const AuthenticationState.unauthenticated()
|
||||||
: this._(status: AuthenticationStatus.unauthenticated);
|
: this._(status: AuthenticationStatus.unauthenticated);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [status, user, userData];
|
List<Object?> get props => [status, user, extra];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
// ignore: lines_longer_than_80_chars
|
|
||||||
String toString() =>
|
String toString() =>
|
||||||
'AuthenticationState(status: $status, user: $user, userData: $userData)';
|
'AuthenticationState(status: $status, user: $user, extra: $extra)';
|
||||||
}
|
}
|
@ -14,19 +14,21 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/core/exceptions/exceptions.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/src/domain/repositories/authentication_repository.dart';
|
||||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart' show FormStatus;
|
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart' show FormStatus;
|
||||||
|
|
||||||
part 'email_verification_state.dart';
|
part 'email_verification_state.dart';
|
||||||
|
|
||||||
class EmailVerificationCubit extends Cubit<EmailVerificationState> {
|
class EmailVerificationCubit extends Cubit<EmailVerificationState> {
|
||||||
final AuthenticationRepositoryInterface _authenticationRepository;
|
final AuthenticationRepository _authenticationRepository;
|
||||||
|
|
||||||
EmailVerificationCubit(this._authenticationRepository)
|
EmailVerificationCubit({
|
||||||
: super(const EmailVerificationState());
|
required AuthenticationRepository authenticationRepository,
|
||||||
|
}) : _authenticationRepository = authenticationRepository,
|
||||||
|
super(const EmailVerificationState());
|
||||||
|
|
||||||
Future<void> sendEmailVerification() async {
|
Future<void> sendEmailVerification() async {
|
||||||
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
@ -31,13 +31,11 @@ class EmailVerificationState extends Equatable {
|
|||||||
bool? isVerified,
|
bool? isVerified,
|
||||||
FormStatus? status,
|
FormStatus? status,
|
||||||
String? errorMessage,
|
String? errorMessage,
|
||||||
}) {
|
}) => EmailVerificationState(
|
||||||
return EmailVerificationState(
|
|
||||||
isVerified: isVerified ?? this.isVerified,
|
isVerified: isVerified ?? this.isVerified,
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
errorMessage: errorMessage ?? this.errorMessage,
|
errorMessage: errorMessage ?? this.errorMessage,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [isVerified, status];
|
List<Object> get props => [isVerified, status];
|
@ -14,32 +14,40 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/core/exceptions/exceptions.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/src/domain/repositories/authentication_repository.dart';
|
||||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||||
|
|
||||||
part 'password_reset_state.dart';
|
part 'password_reset_state.dart';
|
||||||
|
|
||||||
class PasswordResetCubit extends Cubit<PasswordResetState> {
|
class PasswordResetCubit extends Cubit<PasswordResetState> {
|
||||||
final AuthenticationRepositoryInterface _authenticationRepository;
|
final AuthenticationRepository _authenticationRepository;
|
||||||
|
|
||||||
PasswordResetCubit(this._authenticationRepository)
|
final FormValidator _validationStrategy;
|
||||||
: super(const PasswordResetState());
|
|
||||||
|
PasswordResetCubit({
|
||||||
|
required AuthenticationRepository authenticationRepository,
|
||||||
|
FormValidator validationStrategy = const EveryInputValidator(),
|
||||||
|
}) : _authenticationRepository = authenticationRepository,
|
||||||
|
_validationStrategy = validationStrategy,
|
||||||
|
super(const PasswordResetState());
|
||||||
|
|
||||||
void emailChanged(String value) {
|
void emailChanged(String value) {
|
||||||
final Email email = Email.dirty(value);
|
final Email email = Email.dirty(value);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
email: email,
|
email: email,
|
||||||
status: FormStatus.validate([email]),
|
status: _validationStrategy.rawValidate([email]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendPasswordResetEmail() async {
|
Future<void> sendPasswordResetEmail() async {
|
||||||
if (!state.status.isValidated) return;
|
if (!state.status.isValidated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
||||||
try {
|
try {
|
||||||
await _authenticationRepository.sendPasswordResetEmail(
|
await _authenticationRepository.sendPasswordResetEmail(
|
@ -31,13 +31,11 @@ class PasswordResetState extends Equatable {
|
|||||||
Email? email,
|
Email? email,
|
||||||
FormStatus? status,
|
FormStatus? status,
|
||||||
String? errorMessage,
|
String? errorMessage,
|
||||||
}) {
|
}) => PasswordResetState(
|
||||||
return PasswordResetState(
|
|
||||||
email: email ?? this.email,
|
email: email ?? this.email,
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
errorMessage: errorMessage ?? this.errorMessage,
|
errorMessage: errorMessage ?? this.errorMessage,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [email, status];
|
List<Object> get props => [email, status];
|
@ -14,24 +14,25 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/authentication/cubit/authentication_cubit.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/core/enum/auth_cubit_status.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/core/exceptions/exceptions.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/src/domain/repositories/authentication_repository.dart';
|
||||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||||
|
|
||||||
part 'sign_in_state.dart';
|
part 'sign_in_state.dart';
|
||||||
|
|
||||||
class SignInCubit extends Cubit<SignInState> {
|
class SignInCubit extends Cubit<SignInState> {
|
||||||
final AuthenticationRepositoryInterface _authenticationRepository;
|
final AuthenticationRepository _authenticationRepository;
|
||||||
final AuthenticationCubit _authenticationCubit;
|
|
||||||
|
final FormValidator _validationStrategy;
|
||||||
|
|
||||||
SignInCubit({
|
SignInCubit({
|
||||||
required AuthenticationRepositoryInterface authenticationRepository,
|
required AuthenticationRepository authenticationRepository,
|
||||||
required AuthenticationCubit authenticationCubit,
|
FormValidator validationStrategy = const EveryInputValidator(),
|
||||||
}) : _authenticationRepository = authenticationRepository,
|
}) : _authenticationRepository = authenticationRepository,
|
||||||
_authenticationCubit = authenticationCubit,
|
_validationStrategy = validationStrategy,
|
||||||
super(const SignInState());
|
super(const SignInState());
|
||||||
|
|
||||||
void emailChanged(String value) {
|
void emailChanged(String value) {
|
||||||
@ -39,7 +40,7 @@ class SignInCubit extends Cubit<SignInState> {
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
email: email,
|
email: email,
|
||||||
status: FormStatus.validate([email, state.password]),
|
status: _validationStrategy.rawValidate([email, state.password]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -49,7 +50,7 @@ class SignInCubit extends Cubit<SignInState> {
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
password: password,
|
password: password,
|
||||||
status: FormStatus.validate([state.email, password]),
|
status: _validationStrategy.rawValidate([state.email, password]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -62,7 +63,7 @@ class SignInCubit extends Cubit<SignInState> {
|
|||||||
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
||||||
try {
|
try {
|
||||||
await _authenticationRepository.signInAnonymously();
|
await _authenticationRepository.signInAnonymously();
|
||||||
_authenticationCubit.start();
|
_authenticationRepository.changeCubitStatus(AuthCubitStatus.started);
|
||||||
emit(state.copyWith(status: FormStatus.submissionSuccess));
|
emit(state.copyWith(status: FormStatus.submissionSuccess));
|
||||||
} on SignInAnonymouslyFailureInterface catch (e) {
|
} on SignInAnonymouslyFailureInterface catch (e) {
|
||||||
emit(
|
emit(
|
||||||
@ -84,7 +85,7 @@ class SignInCubit extends Cubit<SignInState> {
|
|||||||
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
||||||
try {
|
try {
|
||||||
await _authenticationRepository.signInWithGoogle();
|
await _authenticationRepository.signInWithGoogle();
|
||||||
_authenticationCubit.start();
|
_authenticationRepository.changeCubitStatus(AuthCubitStatus.started);
|
||||||
emit(state.copyWith(status: FormStatus.submissionSuccess));
|
emit(state.copyWith(status: FormStatus.submissionSuccess));
|
||||||
} on SignInWithGoogleFailureInterface catch (e) {
|
} on SignInWithGoogleFailureInterface catch (e) {
|
||||||
emit(
|
emit(
|
||||||
@ -99,14 +100,16 @@ class SignInCubit extends Cubit<SignInState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> signInWithEmailAndPassword() async {
|
Future<void> signInWithEmailAndPassword() async {
|
||||||
if (!state.status.isValidated) return;
|
if (!state.status.isValidated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
||||||
try {
|
try {
|
||||||
await _authenticationRepository.signInWithEmailAndPassword(
|
await _authenticationRepository.signInWithEmailAndPassword(
|
||||||
email: state.email.value,
|
email: state.email.value,
|
||||||
password: state.password.value,
|
password: state.password.value,
|
||||||
);
|
);
|
||||||
_authenticationCubit.start();
|
_authenticationRepository.changeCubitStatus(AuthCubitStatus.started);
|
||||||
emit(state.copyWith(status: FormStatus.submissionSuccess));
|
emit(state.copyWith(status: FormStatus.submissionSuccess));
|
||||||
} on SignInWithEmailAndPasswordFailureInterface catch (e) {
|
} on SignInWithEmailAndPasswordFailureInterface catch (e) {
|
||||||
emit(
|
emit(
|
@ -34,25 +34,22 @@ class SignInState extends Equatable {
|
|||||||
Password? password,
|
Password? password,
|
||||||
FormStatus? status,
|
FormStatus? status,
|
||||||
String? errorMessage,
|
String? errorMessage,
|
||||||
}) {
|
}) =>
|
||||||
return SignInState(
|
SignInState(
|
||||||
email: email ?? this.email,
|
email: email ?? this.email,
|
||||||
password: password ?? this.password,
|
password: password ?? this.password,
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
errorMessage: errorMessage ?? this.errorMessage,
|
errorMessage: errorMessage ?? this.errorMessage,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [email, password, status];
|
List<Object> get props => [email, password, status];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() => '''
|
||||||
return '''
|
|
||||||
email: $email,
|
email: $email,
|
||||||
password: $password,
|
password: $password,
|
||||||
status: $status,
|
status: $status,
|
||||||
errorMessage: $errorMessage,
|
errorMessage: $errorMessage,
|
||||||
''';
|
''';
|
||||||
}
|
|
||||||
}
|
}
|
@ -16,30 +16,31 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/authentication/cubit/authentication_cubit.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/core/enum/auth_cubit_status.dart';
|
||||||
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
|
import 'package:wyatt_authentication_bloc/src/core/exceptions/exceptions.dart';
|
||||||
|
import 'package:wyatt_authentication_bloc/src/domain/repositories/authentication_repository.dart';
|
||||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||||
|
|
||||||
part 'sign_up_state.dart';
|
part 'sign_up_state.dart';
|
||||||
|
|
||||||
class SignUpCubit extends Cubit<SignUpState> {
|
class SignUpCubit extends Cubit<SignUpState> {
|
||||||
final AuthenticationRepositoryInterface _authenticationRepository;
|
final AuthenticationRepository _authenticationRepository;
|
||||||
final AuthenticationCubit _authenticationCubit;
|
|
||||||
|
|
||||||
final Future Function(SignUpState state, String? uid)? _onSignUpSuccess;
|
final Future<void> Function(SignUpState state, String? uid)? _onSignUpSuccess;
|
||||||
|
final FormValidator _validationStrategy;
|
||||||
|
|
||||||
SignUpCubit({
|
SignUpCubit({
|
||||||
required AuthenticationRepositoryInterface authenticationRepository,
|
required AuthenticationRepository authenticationRepository,
|
||||||
required AuthenticationCubit authenticationCubit,
|
required FormData formData,
|
||||||
required FormData entries,
|
FormValidator validationStrategy = const EveryInputValidator(),
|
||||||
Future Function(SignUpState state, String? uid)? onSignUpSuccess,
|
Future<void> Function(SignUpState state, String? uid)? onSignUpSuccess,
|
||||||
}) : _authenticationRepository = authenticationRepository,
|
}) : _authenticationRepository = authenticationRepository,
|
||||||
_authenticationCubit = authenticationCubit,
|
|
||||||
_onSignUpSuccess = onSignUpSuccess,
|
_onSignUpSuccess = onSignUpSuccess,
|
||||||
super(SignUpState(data: entries));
|
_validationStrategy = validationStrategy,
|
||||||
|
super(SignUpState(data: formData));
|
||||||
|
|
||||||
void emailChanged(String value) {
|
void emailChanged(String value) {
|
||||||
final Email email = Email.dirty(value);
|
final Email email = Email.dirty(value);
|
||||||
@ -53,7 +54,7 @@ class SignUpCubit extends Cubit<SignUpState> {
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
email: email,
|
email: email,
|
||||||
status: FormStatus.validate(toValidate),
|
status: _validationStrategy.rawValidate(toValidate),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -70,25 +71,28 @@ class SignUpCubit extends Cubit<SignUpState> {
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
password: password,
|
password: password,
|
||||||
status: FormStatus.validate(toValidate),
|
status: _validationStrategy.rawValidate(toValidate),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take from wyatt_form_bloc/wyatt_form_bloc.dart
|
// Take from wyatt_form_bloc/wyatt_form_bloc.dart
|
||||||
void dataChanged<T>(String field, FormInputValidator dirtyValue) {
|
void dataChanged<T>(
|
||||||
final _form = state.data.clone();
|
String field,
|
||||||
|
FormInputValidator<T, ValidationError> dirtyValue,
|
||||||
|
) {
|
||||||
|
final form = state.data.clone();
|
||||||
|
|
||||||
if (_form.contains(field)) {
|
if (form.contains(field)) {
|
||||||
_form.updateValidator(field, dirtyValue);
|
form.updateValidator(field, dirtyValue);
|
||||||
} else {
|
} else {
|
||||||
throw Exception('Form field $field not found');
|
throw Exception('Form field $field not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
data: _form,
|
data: form,
|
||||||
status: FormStatus.validate(
|
status: _validationStrategy.rawValidate(
|
||||||
[
|
[
|
||||||
state.email,
|
state.email,
|
||||||
state.password,
|
state.password,
|
||||||
@ -104,27 +108,27 @@ class SignUpCubit extends Cubit<SignUpState> {
|
|||||||
FormData data, {
|
FormData data, {
|
||||||
SetOperation operation = SetOperation.replace,
|
SetOperation operation = SetOperation.replace,
|
||||||
}) {
|
}) {
|
||||||
FormData _form = data;
|
FormData form = data;
|
||||||
|
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case SetOperation.replace:
|
case SetOperation.replace:
|
||||||
_form = data;
|
form = data;
|
||||||
break;
|
break;
|
||||||
case SetOperation.difference:
|
case SetOperation.difference:
|
||||||
_form = state.data.difference(data);
|
form = state.data.difference(data);
|
||||||
break;
|
break;
|
||||||
case SetOperation.intersection:
|
case SetOperation.intersection:
|
||||||
_form = state.data.intersection(data);
|
form = state.data.intersection(data);
|
||||||
break;
|
break;
|
||||||
case SetOperation.union:
|
case SetOperation.union:
|
||||||
_form = state.data.union(data);
|
form = state.data.union(data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
data: _form,
|
data: form,
|
||||||
status: FormStatus.validate(
|
status: _validationStrategy.rawValidate(
|
||||||
[
|
[
|
||||||
state.email,
|
state.email,
|
||||||
state.password,
|
state.password,
|
||||||
@ -136,7 +140,9 @@ class SignUpCubit extends Cubit<SignUpState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> signUpFormSubmitted() async {
|
Future<void> signUpFormSubmitted() async {
|
||||||
if (!state.status.isValidated) return;
|
if (!state.status.isValidated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
||||||
try {
|
try {
|
||||||
final uid = await _authenticationRepository.signUp(
|
final uid = await _authenticationRepository.signUp(
|
||||||
@ -144,16 +150,8 @@ class SignUpCubit extends Cubit<SignUpState> {
|
|||||||
password: state.password.value,
|
password: state.password.value,
|
||||||
);
|
);
|
||||||
await _onSignUpSuccess?.call(state, uid);
|
await _onSignUpSuccess?.call(state, uid);
|
||||||
if (_authenticationCubit.start()) {
|
_authenticationRepository.changeCubitStatus(AuthCubitStatus.started);
|
||||||
emit(state.copyWith(status: FormStatus.submissionSuccess));
|
emit(state.copyWith(status: FormStatus.submissionSuccess));
|
||||||
} else {
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
errorMessage: 'Failed to start authentication cubit',
|
|
||||||
status: FormStatus.submissionFailure,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} on SignUpWithEmailAndPasswordFailureInterface catch (e) {
|
} on SignUpWithEmailAndPasswordFailureInterface catch (e) {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
@ -16,8 +16,7 @@
|
|||||||
|
|
||||||
part of 'sign_up_cubit.dart';
|
part of 'sign_up_cubit.dart';
|
||||||
|
|
||||||
@immutable
|
class SignUpState extends Equatable {
|
||||||
class SignUpState {
|
|
||||||
final Email email;
|
final Email email;
|
||||||
final Password password;
|
final Password password;
|
||||||
final FormStatus status;
|
final FormStatus status;
|
||||||
@ -25,10 +24,10 @@ class SignUpState {
|
|||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
|
|
||||||
const SignUpState({
|
const SignUpState({
|
||||||
|
required this.data,
|
||||||
this.email = const Email.pure(),
|
this.email = const Email.pure(),
|
||||||
this.password = const Password.pure(),
|
this.password = const Password.pure(),
|
||||||
this.status = FormStatus.pure,
|
this.status = FormStatus.pure,
|
||||||
required this.data,
|
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -38,40 +37,19 @@ class SignUpState {
|
|||||||
FormStatus? status,
|
FormStatus? status,
|
||||||
FormData? data,
|
FormData? data,
|
||||||
String? errorMessage,
|
String? errorMessage,
|
||||||
}) {
|
}) =>
|
||||||
return SignUpState(
|
SignUpState(
|
||||||
email: email ?? this.email,
|
email: email ?? this.email,
|
||||||
password: password ?? this.password,
|
password: password ?? this.password,
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
data: data ?? this.data,
|
data: data ?? this.data,
|
||||||
errorMessage: errorMessage ?? this.errorMessage,
|
errorMessage: errorMessage ?? this.errorMessage,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
String toString() => 'SignUpState(email: $email, password: $password, '
|
||||||
if (identical(this, other)) return true;
|
'status: $status, data: $data, errorMessage: $errorMessage)';
|
||||||
|
|
||||||
return other is SignUpState &&
|
|
||||||
other.email == email &&
|
|
||||||
other.password == password &&
|
|
||||||
other.status == status &&
|
|
||||||
other.data == data &&
|
|
||||||
other.errorMessage == errorMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
List<Object?> get props => [email, password, status, data, errorMessage];
|
||||||
return email.hashCode ^
|
|
||||||
password.hashCode ^
|
|
||||||
status.hashCode ^
|
|
||||||
data.hashCode ^
|
|
||||||
errorMessage.hashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
// ignore: lines_longer_than_80_chars
|
|
||||||
return 'SignUpState(email: $email, password: $password, status: $status, data: $data, errorMessage: $errorMessage)';
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,18 +0,0 @@
|
|||||||
// Copyright (C) 2022 WYATT GROUP
|
|
||||||
// Please see the AUTHORS file for details.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
export 'exceptions_firebase.dart';
|
|
||||||
export 'exceptions_interface.dart';
|
|
@ -1,18 +0,0 @@
|
|||||||
// Copyright (C) 2022 WYATT GROUP
|
|
||||||
// Please see the AUTHORS file for details.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
export 'exceptions/exceptions.dart';
|
|
||||||
export 'user/user.dart';
|
|
@ -14,10 +14,17 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
export 'authentication/authentication.dart';
|
export 'core/enum/auth_cubit_status.dart';
|
||||||
export 'email_verification/email_verification.dart';
|
export 'core/exceptions/exceptions.dart';
|
||||||
export 'models/models.dart';
|
export 'core/exceptions/exceptions_firebase.dart';
|
||||||
export 'password_reset/password_reset.dart';
|
export 'core/extensions/firebase_auth_user_x.dart';
|
||||||
export 'repositories/repositories.dart';
|
export 'core/utils/cryptography.dart';
|
||||||
export 'sign_in/sign_in.dart';
|
export 'data/models/user_firebase.dart';
|
||||||
export 'sign_up/sign_up.dart';
|
export 'data/repositories/authentication_repository_firebase.dart';
|
||||||
|
export 'domain/entities/user.dart';
|
||||||
|
export 'domain/repositories/authentication_repository.dart';
|
||||||
|
export 'features/authentication/authentication.dart';
|
||||||
|
export 'features/email_verification/email_verification.dart';
|
||||||
|
export 'features/password_reset/password_reset.dart';
|
||||||
|
export 'features/sign_in/sign_in.dart';
|
||||||
|
export 'features/sign_up/sign_up.dart';
|
||||||
|
@ -4,7 +4,7 @@ repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/mas
|
|||||||
version: 0.2.1+3
|
version: 0.2.1+3
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.15.1 <3.0.0"
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
flutter: ">=1.17.0"
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -31,6 +31,7 @@ dev_dependencies:
|
|||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
bloc_test: ^9.0.3
|
bloc_test: ^9.0.3
|
||||||
|
mocktail: ^0.3.0
|
||||||
|
|
||||||
wyatt_analysis:
|
wyatt_analysis:
|
||||||
git:
|
git:
|
||||||
|
@ -20,66 +20,87 @@ import 'package:mocktail/mocktail.dart';
|
|||||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||||
|
|
||||||
class MockAuthenticationRepository extends Mock
|
class MockAuthenticationRepository extends Mock
|
||||||
implements AuthenticationRepositoryInterface {}
|
implements AuthenticationRepository {}
|
||||||
|
|
||||||
class MockUser extends Mock implements UserInterface {}
|
class MockUser extends Mock implements User {}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('AuthenticationCubit', () {
|
group('AuthenticationCubit<T>', () {
|
||||||
final MockUser user = MockUser();
|
final MockUser user = MockUser();
|
||||||
late AuthenticationRepositoryInterface authenticationRepository;
|
late AuthenticationRepository authenticationRepository;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
authenticationRepository = MockAuthenticationRepository();
|
authenticationRepository = MockAuthenticationRepository();
|
||||||
when(() => authenticationRepository.user).thenAnswer(
|
when(() => authenticationRepository.user).thenAnswer(
|
||||||
(_) => const Stream.empty(),
|
(_) => const Stream.empty(),
|
||||||
);
|
);
|
||||||
|
when(() => authenticationRepository.cubitStatus).thenAnswer(
|
||||||
|
(_) => Stream.fromIterable([AuthCubitStatus.stoped]),
|
||||||
|
);
|
||||||
when(
|
when(
|
||||||
() => authenticationRepository.currentUser,
|
() => authenticationRepository.currentUser,
|
||||||
).thenReturn(const UserFirebase.empty());
|
).thenReturn(user);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('initial state is unknown', () {
|
test('initial auth state is `unknown`', () {
|
||||||
expect(
|
expect(
|
||||||
AuthenticationCubit(authenticationRepository: authenticationRepository)
|
AuthenticationCubit<void>(
|
||||||
.state,
|
authenticationRepository: authenticationRepository,
|
||||||
const AuthenticationState.unknown(),
|
).state,
|
||||||
|
const AuthenticationState<Never>.unknown(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('initial cubit status is `stoped`', () async {
|
||||||
|
expect(
|
||||||
|
await AuthenticationCubit<void>(
|
||||||
|
authenticationRepository: authenticationRepository,
|
||||||
|
).status,
|
||||||
|
AuthCubitStatus.stoped,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
group('UserChanged', () {
|
group('UserChanged', () {
|
||||||
blocTest<AuthenticationCubit, AuthenticationState>(
|
blocTest<AuthenticationCubit<void>, AuthenticationState<void>>(
|
||||||
'emits authenticated when user is not empty',
|
'emits authenticated when user is not empty',
|
||||||
setUp: () {
|
setUp: () {
|
||||||
when(() => user.isNotEmpty).thenReturn(true);
|
when(() => user.isNotEmpty).thenReturn(true);
|
||||||
when(() => authenticationRepository.user).thenAnswer(
|
when(() => authenticationRepository.user).thenAnswer(
|
||||||
(_) => Stream.value(user),
|
(_) => Stream.value(user),
|
||||||
);
|
);
|
||||||
},
|
when(() => authenticationRepository.cubitStatus).thenAnswer(
|
||||||
build: () => AuthenticationCubit(
|
(_) => Stream.value(AuthCubitStatus.started),
|
||||||
authenticationRepository: authenticationRepository,
|
|
||||||
)..init(),
|
|
||||||
seed: () => const AuthenticationState.unknown(),
|
|
||||||
expect: () => [AuthenticationState.authenticated(user, null)],
|
|
||||||
);
|
|
||||||
|
|
||||||
blocTest<AuthenticationCubit, AuthenticationState>(
|
|
||||||
'emits unauthenticated when user is empty',
|
|
||||||
setUp: () {
|
|
||||||
when(() => authenticationRepository.user).thenAnswer(
|
|
||||||
(_) => Stream.value(const UserFirebase.empty()),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
build: () => AuthenticationCubit(
|
build: () => AuthenticationCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
)..init(),
|
)..init(),
|
||||||
seed: () => const AuthenticationState.unknown(),
|
seed: () => const AuthenticationState.unknown(),
|
||||||
expect: () => [const AuthenticationState.unauthenticated()],
|
expect: () => [AuthenticationState<void>.authenticated(user, null)],
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<AuthenticationCubit<void>, AuthenticationState<void>>(
|
||||||
|
'emits unauthenticated when user is empty',
|
||||||
|
setUp: () {
|
||||||
|
when(() => user.isEmpty).thenReturn(true);
|
||||||
|
when(() => user.isNotEmpty).thenReturn(false);
|
||||||
|
when(() => authenticationRepository.user).thenAnswer(
|
||||||
|
(_) => Stream.value(user),
|
||||||
|
);
|
||||||
|
when(() => authenticationRepository.cubitStatus).thenAnswer(
|
||||||
|
(_) => Stream.value(AuthCubitStatus.started),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
build: () => AuthenticationCubit(
|
||||||
|
authenticationRepository: authenticationRepository,
|
||||||
|
)..init(),
|
||||||
|
seed: () => const AuthenticationState.unknown(),
|
||||||
|
expect: () => [const AuthenticationState<Never>.unauthenticated()],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
group('LogoutRequested', () {
|
group('LogoutRequested', () {
|
||||||
blocTest<AuthenticationCubit, AuthenticationState>(
|
blocTest<AuthenticationCubit<void>, AuthenticationState<void>>(
|
||||||
'invokes signOut',
|
'invokes signOut',
|
||||||
setUp: () {
|
setUp: () {
|
||||||
when(
|
when(
|
||||||
@ -89,7 +110,7 @@ void main() {
|
|||||||
build: () => AuthenticationCubit(
|
build: () => AuthenticationCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
),
|
),
|
||||||
act: (AuthenticationCubit cubit) => cubit.logOut(),
|
act: (cubit) => cubit.logOut(),
|
||||||
verify: (_) {
|
verify: (_) {
|
||||||
verify(() => authenticationRepository.signOut()).called(1);
|
verify(() => authenticationRepository.signOut()).called(1);
|
||||||
},
|
},
|
||||||
|
@ -18,13 +18,14 @@ import 'package:flutter_test/flutter_test.dart';
|
|||||||
import 'package:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||||
|
|
||||||
class MockUser extends Mock implements UserInterface {}
|
class MockUser extends Mock implements User {}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('AuthenticationState', () {
|
group('AuthenticationState', () {
|
||||||
group('unauthenticated', () {
|
group('unauthenticated', () {
|
||||||
test('has correct status', () {
|
test('has correct status', () {
|
||||||
const AuthenticationState state = AuthenticationState.unauthenticated();
|
const AuthenticationState<void> state =
|
||||||
|
AuthenticationState.unauthenticated();
|
||||||
expect(state.status, AuthenticationStatus.unauthenticated);
|
expect(state.status, AuthenticationStatus.unauthenticated);
|
||||||
expect(state.user, null);
|
expect(state.user, null);
|
||||||
});
|
});
|
||||||
@ -33,11 +34,23 @@ void main() {
|
|||||||
group('authenticated', () {
|
group('authenticated', () {
|
||||||
test('has correct status', () {
|
test('has correct status', () {
|
||||||
final MockUser user = MockUser();
|
final MockUser user = MockUser();
|
||||||
final AuthenticationState state =
|
final AuthenticationState<void> state =
|
||||||
AuthenticationState.authenticated(user, null);
|
AuthenticationState.authenticated(user, null);
|
||||||
expect(state.status, AuthenticationStatus.authenticated);
|
expect(state.status, AuthenticationStatus.authenticated);
|
||||||
expect(state.user, user);
|
expect(state.user, user);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('authenticated with extra data', () {
|
||||||
|
test('has correct status', () {
|
||||||
|
final MockUser user = MockUser();
|
||||||
|
const String extra = 'AwesomeExtraData';
|
||||||
|
final AuthenticationState<String> state =
|
||||||
|
AuthenticationState.authenticated(user, extra);
|
||||||
|
expect(state.status, AuthenticationStatus.authenticated);
|
||||||
|
expect(state.user, user);
|
||||||
|
expect(state.extra, extra);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,10 @@ import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
|||||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||||
|
|
||||||
class MockAuthenticationRepository extends Mock
|
class MockAuthenticationRepository extends Mock
|
||||||
implements AuthenticationRepositoryInterface {}
|
implements AuthenticationRepository {}
|
||||||
|
|
||||||
class MockAuthenticationCubit extends Mock implements AuthenticationCubit {}
|
class MockAuthenticationCubit extends Mock
|
||||||
|
implements AuthenticationCubit<void> {}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
const String invalidEmailString = 'invalid';
|
const String invalidEmailString = 'invalid';
|
||||||
@ -39,8 +40,8 @@ void main() {
|
|||||||
const Password validPassword = Password.dirty(validPasswordString);
|
const Password validPassword = Password.dirty(validPasswordString);
|
||||||
|
|
||||||
group('SignInCubit', () {
|
group('SignInCubit', () {
|
||||||
late AuthenticationRepositoryInterface authenticationRepository;
|
late AuthenticationRepository authenticationRepository;
|
||||||
late AuthenticationCubit authenticationCubit;
|
late AuthenticationCubit<void> authenticationCubit;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
authenticationRepository = MockAuthenticationRepository();
|
authenticationRepository = MockAuthenticationRepository();
|
||||||
@ -62,7 +63,6 @@ void main() {
|
|||||||
expect(
|
expect(
|
||||||
SignInCubit(
|
SignInCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
|
||||||
).state,
|
).state,
|
||||||
const SignInState(),
|
const SignInState(),
|
||||||
);
|
);
|
||||||
@ -73,9 +73,8 @@ void main() {
|
|||||||
'emits [invalid] when email/password are invalid',
|
'emits [invalid] when email/password are invalid',
|
||||||
build: () => SignInCubit(
|
build: () => SignInCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
|
||||||
),
|
),
|
||||||
act: (SignInCubit cubit) => cubit.emailChanged(invalidEmailString),
|
act: (cubit) => cubit.emailChanged(invalidEmailString),
|
||||||
expect: () => const <SignInState>[
|
expect: () => const <SignInState>[
|
||||||
SignInState(email: invalidEmail, status: FormStatus.invalid),
|
SignInState(email: invalidEmail, status: FormStatus.invalid),
|
||||||
],
|
],
|
||||||
@ -85,10 +84,9 @@ void main() {
|
|||||||
'emits [valid] when email/password are valid',
|
'emits [valid] when email/password are valid',
|
||||||
build: () => SignInCubit(
|
build: () => SignInCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
|
||||||
),
|
),
|
||||||
seed: () => const SignInState(password: validPassword),
|
seed: () => const SignInState(password: validPassword),
|
||||||
act: (SignInCubit cubit) => cubit.emailChanged(validEmailString),
|
act: (cubit) => cubit.emailChanged(validEmailString),
|
||||||
expect: () => const <SignInState>[
|
expect: () => const <SignInState>[
|
||||||
SignInState(
|
SignInState(
|
||||||
email: validEmail,
|
email: validEmail,
|
||||||
@ -104,10 +102,8 @@ void main() {
|
|||||||
'emits [invalid] when email/password are invalid',
|
'emits [invalid] when email/password are invalid',
|
||||||
build: () => SignInCubit(
|
build: () => SignInCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
|
||||||
),
|
),
|
||||||
act: (SignInCubit cubit) =>
|
act: (cubit) => cubit.passwordChanged(invalidPasswordString),
|
||||||
cubit.passwordChanged(invalidPasswordString),
|
|
||||||
expect: () => const <SignInState>[
|
expect: () => const <SignInState>[
|
||||||
SignInState(
|
SignInState(
|
||||||
password: invalidPassword,
|
password: invalidPassword,
|
||||||
@ -120,10 +116,9 @@ void main() {
|
|||||||
'emits [valid] when email/password are valid',
|
'emits [valid] when email/password are valid',
|
||||||
build: () => SignInCubit(
|
build: () => SignInCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
|
||||||
),
|
),
|
||||||
seed: () => const SignInState(email: validEmail),
|
seed: () => const SignInState(email: validEmail),
|
||||||
act: (SignInCubit cubit) => cubit.passwordChanged(validPasswordString),
|
act: (cubit) => cubit.passwordChanged(validPasswordString),
|
||||||
expect: () => const <SignInState>[
|
expect: () => const <SignInState>[
|
||||||
SignInState(
|
SignInState(
|
||||||
email: validEmail,
|
email: validEmail,
|
||||||
@ -139,9 +134,8 @@ void main() {
|
|||||||
'does nothing when status is not validated',
|
'does nothing when status is not validated',
|
||||||
build: () => SignInCubit(
|
build: () => SignInCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
|
||||||
),
|
),
|
||||||
act: (SignInCubit cubit) => cubit.signInWithEmailAndPassword(),
|
act: (cubit) => cubit.signInWithEmailAndPassword(),
|
||||||
expect: () => const <SignInState>[],
|
expect: () => const <SignInState>[],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -149,14 +143,13 @@ void main() {
|
|||||||
'calls signInWithEmailAndPassword with correct email/password',
|
'calls signInWithEmailAndPassword with correct email/password',
|
||||||
build: () => SignInCubit(
|
build: () => SignInCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
|
||||||
),
|
),
|
||||||
seed: () => const SignInState(
|
seed: () => const SignInState(
|
||||||
status: FormStatus.valid,
|
status: FormStatus.valid,
|
||||||
email: validEmail,
|
email: validEmail,
|
||||||
password: validPassword,
|
password: validPassword,
|
||||||
),
|
),
|
||||||
act: (SignInCubit cubit) => cubit.signInWithEmailAndPassword(),
|
act: (cubit) => cubit.signInWithEmailAndPassword(),
|
||||||
verify: (_) {
|
verify: (_) {
|
||||||
verify(
|
verify(
|
||||||
() => authenticationRepository.signInWithEmailAndPassword(
|
() => authenticationRepository.signInWithEmailAndPassword(
|
||||||
@ -172,14 +165,13 @@ void main() {
|
|||||||
'when signInWithEmailAndPassword succeeds',
|
'when signInWithEmailAndPassword succeeds',
|
||||||
build: () => SignInCubit(
|
build: () => SignInCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
|
||||||
),
|
),
|
||||||
seed: () => const SignInState(
|
seed: () => const SignInState(
|
||||||
status: FormStatus.valid,
|
status: FormStatus.valid,
|
||||||
email: validEmail,
|
email: validEmail,
|
||||||
password: validPassword,
|
password: validPassword,
|
||||||
),
|
),
|
||||||
act: (SignInCubit cubit) => cubit.signInWithEmailAndPassword(),
|
act: (cubit) => cubit.signInWithEmailAndPassword(),
|
||||||
expect: () => const <SignInState>[
|
expect: () => const <SignInState>[
|
||||||
SignInState(
|
SignInState(
|
||||||
status: FormStatus.submissionInProgress,
|
status: FormStatus.submissionInProgress,
|
||||||
@ -207,14 +199,13 @@ void main() {
|
|||||||
},
|
},
|
||||||
build: () => SignInCubit(
|
build: () => SignInCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
|
||||||
),
|
),
|
||||||
seed: () => const SignInState(
|
seed: () => const SignInState(
|
||||||
status: FormStatus.valid,
|
status: FormStatus.valid,
|
||||||
email: validEmail,
|
email: validEmail,
|
||||||
password: validPassword,
|
password: validPassword,
|
||||||
),
|
),
|
||||||
act: (SignInCubit cubit) => cubit.signInWithEmailAndPassword(),
|
act: (cubit) => cubit.signInWithEmailAndPassword(),
|
||||||
expect: () => const <SignInState>[
|
expect: () => const <SignInState>[
|
||||||
SignInState(
|
SignInState(
|
||||||
status: FormStatus.submissionInProgress,
|
status: FormStatus.submissionInProgress,
|
||||||
|
@ -21,9 +21,10 @@ import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
|||||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||||
|
|
||||||
class MockAuthenticationRepository extends Mock
|
class MockAuthenticationRepository extends Mock
|
||||||
implements AuthenticationRepositoryInterface {}
|
implements AuthenticationRepository {}
|
||||||
|
|
||||||
class MockAuthenticationCubit extends Mock implements AuthenticationCubit {}
|
class MockAuthenticationCubit extends Mock
|
||||||
|
implements AuthenticationCubit<void> {}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
const String invalidEmailString = 'invalid';
|
const String invalidEmailString = 'invalid';
|
||||||
@ -39,8 +40,8 @@ void main() {
|
|||||||
const Password validPassword = Password.dirty(validPasswordString);
|
const Password validPassword = Password.dirty(validPasswordString);
|
||||||
|
|
||||||
group('SignUpCubit', () {
|
group('SignUpCubit', () {
|
||||||
late AuthenticationRepositoryInterface authenticationRepository;
|
late AuthenticationRepository authenticationRepository;
|
||||||
late AuthenticationCubit authenticationCubit;
|
late AuthenticationCubit<void> authenticationCubit;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
authenticationRepository = MockAuthenticationRepository();
|
authenticationRepository = MockAuthenticationRepository();
|
||||||
@ -50,9 +51,7 @@ void main() {
|
|||||||
email: any(named: 'email'),
|
email: any(named: 'email'),
|
||||||
password: any(named: 'password'),
|
password: any(named: 'password'),
|
||||||
),
|
),
|
||||||
).thenAnswer((_) async {
|
).thenAnswer((_) async => 'uid');
|
||||||
return 'uid';
|
|
||||||
});
|
|
||||||
|
|
||||||
when(
|
when(
|
||||||
() => authenticationCubit.start(),
|
() => authenticationCubit.start(),
|
||||||
@ -67,8 +66,7 @@ void main() {
|
|||||||
expect(
|
expect(
|
||||||
SignUpCubit(
|
SignUpCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
formData: const FormData.empty(),
|
||||||
entries: const FormData.empty(),
|
|
||||||
).state,
|
).state,
|
||||||
const SignUpState(data: FormData.empty()),
|
const SignUpState(data: FormData.empty()),
|
||||||
);
|
);
|
||||||
@ -79,10 +77,9 @@ void main() {
|
|||||||
'emits [invalid] when email/password are invalid',
|
'emits [invalid] when email/password are invalid',
|
||||||
build: () => SignUpCubit(
|
build: () => SignUpCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
formData: const FormData.empty(),
|
||||||
entries: const FormData.empty(),
|
|
||||||
),
|
),
|
||||||
act: (SignUpCubit cubit) => cubit.emailChanged(invalidEmailString),
|
act: (cubit) => cubit.emailChanged(invalidEmailString),
|
||||||
expect: () => <SignUpState>[
|
expect: () => <SignUpState>[
|
||||||
const SignUpState(
|
const SignUpState(
|
||||||
email: invalidEmail,
|
email: invalidEmail,
|
||||||
@ -96,14 +93,13 @@ void main() {
|
|||||||
'emits [valid] when email/password are valid',
|
'emits [valid] when email/password are valid',
|
||||||
build: () => SignUpCubit(
|
build: () => SignUpCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
formData: const FormData.empty(),
|
||||||
entries: const FormData.empty(),
|
|
||||||
),
|
),
|
||||||
seed: () => const SignUpState(
|
seed: () => const SignUpState(
|
||||||
password: validPassword,
|
password: validPassword,
|
||||||
data: FormData.empty(),
|
data: FormData.empty(),
|
||||||
),
|
),
|
||||||
act: (SignUpCubit cubit) => cubit.emailChanged(validEmailString),
|
act: (cubit) => cubit.emailChanged(validEmailString),
|
||||||
expect: () => <SignUpState>[
|
expect: () => <SignUpState>[
|
||||||
const SignUpState(
|
const SignUpState(
|
||||||
email: validEmail,
|
email: validEmail,
|
||||||
@ -120,11 +116,9 @@ void main() {
|
|||||||
'emits [invalid] when email/password are invalid',
|
'emits [invalid] when email/password are invalid',
|
||||||
build: () => SignUpCubit(
|
build: () => SignUpCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
formData: const FormData.empty(),
|
||||||
entries: const FormData.empty(),
|
|
||||||
),
|
),
|
||||||
act: (SignUpCubit cubit) =>
|
act: (cubit) => cubit.passwordChanged(invalidPasswordString),
|
||||||
cubit.passwordChanged(invalidPasswordString),
|
|
||||||
expect: () => <SignUpState>[
|
expect: () => <SignUpState>[
|
||||||
const SignUpState(
|
const SignUpState(
|
||||||
password: invalidPassword,
|
password: invalidPassword,
|
||||||
@ -138,14 +132,13 @@ void main() {
|
|||||||
'emits [valid] when email/password are valid',
|
'emits [valid] when email/password are valid',
|
||||||
build: () => SignUpCubit(
|
build: () => SignUpCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
formData: const FormData.empty(),
|
||||||
entries: const FormData.empty(),
|
|
||||||
),
|
),
|
||||||
seed: () => const SignUpState(
|
seed: () => const SignUpState(
|
||||||
email: validEmail,
|
email: validEmail,
|
||||||
data: FormData.empty(),
|
data: FormData.empty(),
|
||||||
),
|
),
|
||||||
act: (SignUpCubit cubit) => cubit.passwordChanged(validPasswordString),
|
act: (cubit) => cubit.passwordChanged(validPasswordString),
|
||||||
expect: () => <SignUpState>[
|
expect: () => <SignUpState>[
|
||||||
const SignUpState(
|
const SignUpState(
|
||||||
email: validEmail,
|
email: validEmail,
|
||||||
@ -162,10 +155,9 @@ void main() {
|
|||||||
'does nothing when status is not validated',
|
'does nothing when status is not validated',
|
||||||
build: () => SignUpCubit(
|
build: () => SignUpCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
formData: const FormData.empty(),
|
||||||
entries: const FormData.empty(),
|
|
||||||
),
|
),
|
||||||
act: (SignUpCubit cubit) => cubit.signUpFormSubmitted(),
|
act: (cubit) => cubit.signUpFormSubmitted(),
|
||||||
expect: () => const <SignUpState>[],
|
expect: () => const <SignUpState>[],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -173,8 +165,7 @@ void main() {
|
|||||||
'calls signUp with correct email/password/confirmedPassword',
|
'calls signUp with correct email/password/confirmedPassword',
|
||||||
build: () => SignUpCubit(
|
build: () => SignUpCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
formData: const FormData.empty(),
|
||||||
entries: const FormData.empty(),
|
|
||||||
),
|
),
|
||||||
seed: () => const SignUpState(
|
seed: () => const SignUpState(
|
||||||
status: FormStatus.valid,
|
status: FormStatus.valid,
|
||||||
@ -182,7 +173,7 @@ void main() {
|
|||||||
password: validPassword,
|
password: validPassword,
|
||||||
data: FormData.empty(),
|
data: FormData.empty(),
|
||||||
),
|
),
|
||||||
act: (SignUpCubit cubit) => cubit.signUpFormSubmitted(),
|
act: (cubit) => cubit.signUpFormSubmitted(),
|
||||||
verify: (_) {
|
verify: (_) {
|
||||||
verify(
|
verify(
|
||||||
() => authenticationRepository.signUp(
|
() => authenticationRepository.signUp(
|
||||||
@ -198,8 +189,7 @@ void main() {
|
|||||||
'when signUp succeeds',
|
'when signUp succeeds',
|
||||||
build: () => SignUpCubit(
|
build: () => SignUpCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
formData: const FormData.empty(),
|
||||||
entries: const FormData.empty(),
|
|
||||||
),
|
),
|
||||||
seed: () => const SignUpState(
|
seed: () => const SignUpState(
|
||||||
status: FormStatus.valid,
|
status: FormStatus.valid,
|
||||||
@ -207,7 +197,7 @@ void main() {
|
|||||||
password: validPassword,
|
password: validPassword,
|
||||||
data: FormData.empty(),
|
data: FormData.empty(),
|
||||||
),
|
),
|
||||||
act: (SignUpCubit cubit) => cubit.signUpFormSubmitted(),
|
act: (cubit) => cubit.signUpFormSubmitted(),
|
||||||
expect: () => <SignUpState>[
|
expect: () => <SignUpState>[
|
||||||
const SignUpState(
|
const SignUpState(
|
||||||
status: FormStatus.submissionInProgress,
|
status: FormStatus.submissionInProgress,
|
||||||
@ -237,8 +227,7 @@ void main() {
|
|||||||
},
|
},
|
||||||
build: () => SignUpCubit(
|
build: () => SignUpCubit(
|
||||||
authenticationRepository: authenticationRepository,
|
authenticationRepository: authenticationRepository,
|
||||||
authenticationCubit: authenticationCubit,
|
formData: const FormData.empty(),
|
||||||
entries: const FormData.empty(),
|
|
||||||
),
|
),
|
||||||
seed: () => const SignUpState(
|
seed: () => const SignUpState(
|
||||||
status: FormStatus.valid,
|
status: FormStatus.valid,
|
||||||
@ -246,7 +235,7 @@ void main() {
|
|||||||
password: validPassword,
|
password: validPassword,
|
||||||
data: FormData.empty(),
|
data: FormData.empty(),
|
||||||
),
|
),
|
||||||
act: (SignUpCubit cubit) => cubit.signUpFormSubmitted(),
|
act: (cubit) => cubit.signUpFormSubmitted(),
|
||||||
expect: () => <SignUpState>[
|
expect: () => <SignUpState>[
|
||||||
const SignUpState(
|
const SignUpState(
|
||||||
status: FormStatus.submissionInProgress,
|
status: FormStatus.submissionInProgress,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user