Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEWS-32 Dodanie logowania i rejestracji firebasowej #28

Merged
merged 6 commits into from
Dec 30, 2020

Conversation

kightsonsanom
Copy link
Collaborator

allthenewsfirebaseios

@@ -53,20 +55,25 @@ void injectDependencies(Environment flavor) {
_locator.registerSingleton<AppConfig>(AppConfig());
_locator.registerSingleton<AppInfoRepository>(AppInfoLocalRepository());
_locator.registerSingleton<PersistenceRepository>(SharedPreferencesPersistenceRepository());
_locator.registerSingleton<SettingsRepository>(SettingsLocalRepository(_locator<PersistenceRepository>()));
_locator.registerSingleton<PresentationShowingRepository>(PresentationShowingLocalRepository(_locator<PersistenceRepository>()));
_locator.registerSingleton<SettingsRepository>(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

większość zmian w tym pliku wynika z ctrl + alt + L, niestety z racji, że w robo mamy inny threshhold zawijania linii niż tu to ciężko mi to uspójnić 😞

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nie boli :) możesz powiedzieć co tam masz poustawiane to ustawię tak samo, żeby z czasem każdy pull request nie zawierał więcej zmian formatowania niż tych tych faktycznych :D

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w takim razie możesz ustawić tak
image

if (selectedPage == 0) {
return Future.value(true);
} else {
setState(() {
Copy link
Collaborator Author

@kightsonsanom kightsonsanom Nov 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ten setState do wyjebania, bo notifier teraz zarządza stanem obecnej strony, żeby dało się cofać do tej samej strony po wejściu do settingsów

@override
_SettingsPageState createState() => _SettingsPageState();
}

class _SettingsPageState extends State<SettingsPage> {
final SettingsNotifier _settingsNotifier = inject<SettingsNotifier>();
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zauważyłem, że łatwiej jest zarządzać stanem, kiedy każdy widok ma swojego notifiera inicjalizowanego w swojej klasie. Dzięki temu kiedy jakiś widok jest odpalany z kilku miejsc to nie trzeba się martwić o to czy jest notifier providowany przy nawigacji do takiego widoku + scope notifiera jest zawsze optymalny

"register": "Register",
"registrationTitle": "Registration",
"name": "Name",
"email": "Email",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E-mail :)

import 'package:allthenews/src/domain/authorization/authorization.dart';
import 'package:firebase_auth/firebase_auth.dart';

class FirebaseAuthorization implements Authorization {
Copy link
Collaborator

@marekvogt marekvogt Nov 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

W zasadzie to Authentication :) - może FirebaseAuthenticationRepository i odpowiednio AuthenticationRepository interfejs? Trochę inaczej popakietować można, bo teraz w communication jest pakiet api i pakiet firebase a w zasadzie oba to api 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

proponuję to co w api obecnie dać do pakietu nytimes i wrzucić paczkę firebase do api

import 'package:firebase_auth/firebase_auth.dart';

class FirebaseAuthorization implements Authorization {
FirebaseAuth auth = FirebaseAuth.instance;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i final :D

FirebaseAuth auth = FirebaseAuth.instance;

@override
Future<UserCredential> createUser(String email, String password) async =>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zbędne async, pozostałe metody to samo


abstract class Authorization {

Stream<User> userStream();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w takim klasycznym podejściu pole reprezentuje stan, a metoda zachowanie, więc jak userStream to fajnie jakby było polem, jak metoda, to getUserStream albo observeUserChanges :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

observeUserChanges mi się bardziej podoba :)

injectDependencies(flavor);
FlutterError.onError = Crashlytics.instance.recordFlutterError;
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

await w mainie blokuje wątek więc trochę słabo - i widzę, że tak jest w example na pub.dev, ale w dokumentacji już dużo ładniej to ograli:
https://firebase.flutter.dev/docs/overview#initializing-flutterfire
Generalnie jest issue, że na flutterze 1.20 nie śmiga, ale fixem jest pozostawienie WidgetsFlutterBinding.ensureInitialized() w mainie :)
Tu wspomniane issue: firebase/flutterfire#3490

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

miałem to na liście rzeczy do poprawienia, ale cocoapods na iosie się zjebał i potem już zapomniałem :p

import 'package:flutter/material.dart';

mixin ErrorMessage {
Widget buildErrorMessage(FirebaseAuthException error) => error != null
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jako, że jest w pakiecie common to powinien być bardziej generyczny tj. przyjmować Exception, chyba, że ma być typowo pod FirebaseAuthException, wówczas chyba powinien być gdzieś w pakiecie authentication i ewentualnie mógłby nazywać się AuthenticationErrorMessage i odpowiednio buildAuthenticationErrorMessage

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to błąd, który będzie raczej używany tylko przy autentykacji, więc wybiorę tą drugą opcję

});

@override
Widget build(BuildContext context) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zastanawiam się czy podejście jest poprawne, bo skoro ChangeNotifier służy do zarządzania stanem to wydaje mi się, ze powinno być tak, że kiedy np. wpisuje się coś do pola password, to to triggeruje metodę notifier.updatePassword(password), co zmienia wewnatrz notifiera stan (state.password), który potem z powrotem propagowany jest na UI do TextFormField i analogicznie z walidacją, tj. po wciśnięciu przycisku wykonywana jest walidacja w notifierze, który w przypadku pustych pól wrzuca do viewState błąd Pole nie może być puste, który dodawany jest do TextFormFielda

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zauważ, że użyłem tu podejścia z widgetem Form - https://flutter.dev/docs/cookbook/forms/validation
to on przechowuje stan walidacji pod spodem i używa to tego klucza

final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

notifier posiada tylko metodę to walidacji pól, ale postanowiłem lokalnie walidować tylko niepustość i dlatego każde pole ma taką samą metodę walidującą. Podejście o którym mówisz sprawdziłoby się gdyby przechowywanie stanu walidacji było całkowicie po naszej stronie

Copy link
Collaborator

@marekvogt marekvogt Nov 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

znaczy ja rozumiem to podejście, tylko chodzi mi o to, że jeżeli mamy architekturę, w której notifier propaguje stan na ui to powinien propagować wszystko, bo teraz część stanu jest zarządzana przez notifiera, a część przez sam ui 🤔 Dokumentacja flutterowa nie uwzględnia żadnej architektury. W necie za bardzo nie ma użycia providera z formularzem i jego walidacją (znalazłem jeden ale gość pomieszał bloc z providerem :P), ale chodzi mi o mniej więcej coś takiego jak w samplach flutter_bloc:
https://github.com/felangel/bloc/blob/master/examples/flutter_form_validation/lib/main.dart

Copy link
Collaborator Author

@kightsonsanom kightsonsanom Nov 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w tym przykładzie co pokazałeś w ogóle nie jest używany widget Form, który ogarnia stany TextFormFieldów OOTB. Tak naprawdę jedyny powód, dla którego są tam TextFormFieldy to atrybut initialValue (który można by było też ograć na zwykłym TextFieldzie). Moim zdaniem jeżeli jest jakaś funkcjonalność dostępna przez framework to warto z niej skorzystać, jeżeli chcielibyśmy wszystko trzymać w stanie to równie dobrze pozycje scrolla moglibyśmy tam przechowywać, tylko po co?

Ja sugerowałem się przykładem z exampli do flutter_auth -> https://github.com/FirebaseExtended/flutterfire/blob/799231ac57a3005a4fbc8fd9c5951c3943e4a286/packages/firebase_auth/firebase_auth/example/lib/register_page.dart

W przypadku gdyby walidacja po stronie mobilnej byłaby bardziej skomplikowana i doszłoby do sytuacji gdzie trzeba customizować ten stan z Forma to wtedy opłacałoby się zachowywać ten stan ręcznie, ale w tym przypadku myślę, że skorzystanie z OOTB zachowania stanu ma sens

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ogólnie stan we flutterze można cały trzymać na ui i są wszystkie niezbędne narzędzia do tego, żeby zarządzać stanem tak jak to zostało zaprojektowane. Nie wymusza żadnej architektury. Jako, że nie wymusza architektury to powstało x różnych, które służą zarządzaniem stanu. Ten przykład z flutterfire nie ma żadnej architektury. Stan można ograć na UI, ale mimo wszystko stworzyłeś HomeNotifier (i słusznie), który wyciąga stan do notifiera, gdzie setState też to ogarnia, ale z notifierem jest zdecydowanie czyściej. Jeżeli przyjęta architektura wyciąga stan do notifierów i propaguje ten stan na ui to powinno być to konsekwetne. Jeżeli trzeba by było zarządzać samemu stanem scrolla to imo również powinien być propagowany z notifiera, ale na szczęście nie trzeba. Natomiast stan pól powinien być przechowywany w notifierze, ponieważ jest częścią stanu UI, który musi być zarządzany - tj. walidowany i wysyłany na backend. Sama walidacja też powinna odbywać się w walidatorze, który byłby gdzieś w domenie i notifier by sobie nim walidował pola przy kliknięciu buttona, a następnie wrzucił do viewState informację czy ma wyświetlić błedy walidacyjne i jak tak to które. W tym podejściu nie ma znaczenia czy walidacja jest skomplikowana czy nie, bo nawet jakby się skomplikowała to dla widgetów jest to niezauważalne, przeływ danych jest identyczny :) Kolejna kwestia to, że trzymanie wszystkiego w jednym miejscu (notifierze) pozwoli przetestować logikę klasę w izolacji od UI :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trochę nie widzę tej granicy kiedy korzystać z narzędzi ogarniających stan OOTB, a kiedy już samemu tym zarządzać. W tym przypadku widgety Form i TextFormField stają się bezużyteczne. Jeżeli chcemy mieć stan w notifierze + korzystać z tego co oferuje framework to może trzymać w stanie notifiera sam GlobalKey ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jeżeli jedyną wartością Form i TextFormField jest zarządzanie stanem na ui to przy obecnej architekturze rzeczywiście są bezużyteczne, ale nie powiedziałbym, że to coś złego. Wiesz, w androidzie jak robisz MVP to nie pchasz LiveDaty na UI tylko dlatego, że jest coś takiego, tylko trzymasz się założeń. Różnica między tym co mamy a blocem jest taka, że tam wszystko opiera się na strumieniach i powiedzmy, że jest większe wyróżnienie istoty eventów propagowanych z ui do bloca. I wg sampli też nie korzysta z walidacji, którą daje Form i to też nic złego. Takie podejście fajnie się sprawdza na pewno przy Simple app state management (na zakładce List of state management approaches na flutter.dev), ale założenia, które przyjęliśmy przy providerze są jednak inne. Imo nie ma co mieszać dwóch podejść i ładnie zrobić stan samemu, bo wpychanie GlobalState, który rozszerza State<StatefulWidget>> to trochę jednak wpychanie UI wyżej (do notifiera) niż powinien być (UI)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, przekonałeś mnie, zrobię zarządzanie stanem walidacji z poziomu notifiera 😄

FlatButton(
onPressed: () => _navigateTo(LoginPage()),
child: Text(
Strings.of(context).login,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

konsekwentnie moglibyśmy zawsze używać Strings.current :)

void _updateUserName(User user, String name) {
authorization
.updateUser(name)
.then((_) => returnToProfile.call())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returnToProfile?.call() i w pozostałych miejscach też z ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no właśnie zastanawiałem się czy to konieczne, dałem z ? przy logoucie bo to jest na ekranie profilu i z tamtego miejsca nie przekazujemy callbacku do cofania. Pozostałe dwa miejsca przy metodach 'signIni_updateUserName` będą wywołane tylko po wejściu do erkanu logowania/rejestracji, więc ten callback nie powinien być tam nullem 🤔

Copy link
Collaborator Author

@kightsonsanom kightsonsanom Nov 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a poza tym to myślałem czy warto na tym etapie przerzucać się na navigation 2.0, co myślisz?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

powiedzmy, że z poziomu samego notifiera widzisz tylko, że callback może być zainicjalizowany ale nie musi, bo nie jest to realizowane przez konstruktor, więc w przypadku kiedy zawsze będzie ? nie ma potrzeby zastanawiania się czy ma prawo wystąpić czy nie, bo w zależności od użycia może ;) Co do navigation 2.0 to jak najbardziej :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ma to sens, dodam ?. Dodałem też taska na trello do navigatora :)

import 'package:flutter/widgets.dart';

class AuthorizationNotifier extends ChangeNotifier {
final Authorization authorization = FirebaseAuthorization();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private + wstrzykiwanie do konstruktora :)


VoidCallback returnToProfile;

void getCurrentUser() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dziwnie wygląda metoda wyglądająca jak getter, która nic nie zwraca ;)

final viewState =
providerContext.select((AuthorizationNotifier notifier) => notifier.state);

if (viewState.user == null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

można by było zrobić jakoś bardziej generycznie bo jak domniemam, każdy ekran, który będziemy mieli w profilu będzie musiał sprawdzać czy użytkownik jest zalogowany? Więc może jakiś abstrakcyjny notifier i page dla tych ekranów? :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

co masz na myśli pisząc "każdy ekran w profilu"? zakładałem, że to będzie tylko jeden widok. Tak czy inaczej rzeczy związane z profilem zostawiłbym na taska z profilem

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chodzi mi o to, że z poziomu profilu będziemy przechodzić na inne zakładki i one też będą autoryzowane, bo w teorii może się zdarzyć, że ktoś zostanie na tym ekranie, apkę zrzuci na stos, potem wróci i już nie będzie zalogowany? Może być osobno przy pierwszym ekranie profilowym, jak najbardziej :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

może się tak zdarzyć jakby usunąć usera ręcznie z firebase i chyba jakby się ktoś w międzyczasie na innym device zalogował, ale nie sprawdzałem tego w sumie

children: [
Text(_authorizationNotifier.state.user?.email),
Text(_authorizationNotifier.state.user?.displayName),
Text("User uid: ${_authorizationNotifier.state.user?.uid}"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do stringsów :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docelowo myślę, że nie powinniśmy tej informacji wyświetlać na UI, dałem bardziej na potrzeby debugowania, proponuję, że dodam TODOsa na usunięcie tego przy okazji implementacji widoku profilu?

Copy link
Collaborator

@marekvogt marekvogt Nov 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zawsze można ograć ładnie, tak żeby wyświetlać tylko w devowej wersji, tj. notifier miałby informację czy wyświetlać czy nie na podstawie Environment :) Chyba, że faktycznie do wywalenia, to może być todos :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

raczej mało przydatne info, mail jest i tak unikatowy, w sumie to już teraz wywalę


Stream<User> userStream();

Future<UserCredential> createUser(String email, String password);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

domena powinna być czysto dartowa, więc mimo wszystko zbudowałbym swoje obiekty domenowe zamiast korzystania z tych firebasowych, żeby ich zasięg kończył się na api, a nie przechodził przez domenę aż do ui

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/widgets.dart';

class AuthorizationNotifier extends ChangeNotifier {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo osobny notifier dla profilu, rejestracji i logowania, profil korzysta tylko z userStream, rejestracja z createUser, a logowanie z signIn

@marekvogt
Copy link
Collaborator

Przeszedłem na login i z jego poziomu wszedłem na ustawienia i zmieniłem theme (druga sprawa, że ustawienia imo mogłyby zniknąć z ekranu logowania/rejestracji)
image

@marekvogt
Copy link
Collaborator

Brakuje paddingu
image
I ogólnie ten font taki nytimesowy zostawiłbym na tylko w appbarach :)

@marekvogt
Copy link
Collaborator

A no i wszystkie komunikaty niezależnie od języka przychodzą po angielsku, więc musiałoby być u nas mapowanie błędów firebasowych na nasze komunikaty

@marekvogt
Copy link
Collaborator

jak jestem zalogowany to przy przechodzeniu z dashboardu na profil ekran miga - na początku jest ekran logowania potem dopiero profil, może dałoby radę ograć to jakoś loadingiem, bo jednak niefajnie wygląda to mignięcie

border: OutlineInputBorder(
borderRadius: BorderRadius.circular(_Constants.borderRadius),
),
hintStyle:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hint mógłby być innego koloru niż tekst

controller: textController,
obscureText: obscureText,
decoration: InputDecoration(
labelText: hint,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jakoś dziwnie wygląda jak do labelText jest przypisywany hint, trudno mi to wytlumaczyć, ale jakby było odwrotnie to jakoś mniej dziwnie by mi to wyglądało, może dlatego, ze każdy label pasuje jako hint ale nie każdy hint pasuje jako label 🤔 - nie wiem czy słusznie, teraz przyszło mi do głowy :D

notifyListeners();
}

void _onUserAuthFailed(exception) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

brakuje typu dla exception

child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AuthTextFormField(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pole email mogłoby mieć TextInputType.emailAddress :)

@marekvogt
Copy link
Collaborator

osobno wyciągnąłbym pakiety authentication i registration poza profile, bo jednak są to osobne domeny

}

Future _navigateTo(Widget page) {
return Navigator.pushReplacement(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ogólnie to ten return potrzebny? 🤔

_updateUserName(name);
} on Exception catch (exception) {
_setNotifierState(_state.copyWith(
exception: _exceptionMapper.toDomainException(exception),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mapowanie powinno odbyć się w warstwie data, gdzieś bliżej komunikacji, w tym przypadku w firebaseAuthenticationRepository

Copy link
Collaborator Author

@kightsonsanom kightsonsanom Dec 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

właśnie moim zdaniem powinny być mapowane jak najbliżej warstwy prezentacji, żeby nie przepychać tych błędów przez wszystkie warstwy, ale tak jak napisałem we wcześniejszym komentarzu

Co do exceptionów to myślę, że dobrze byłoby ten temat przegadać i uspójnić, celowo dałem to domeny, bo w ten sposób przy wielu warstwach tylko w jednym miejscu trzeba łapać wyjątek, a nie w każdej warstwie po kolei go przepychać,

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generalnie exceptiony są domeną. W warstwie prezentacji powinien być jakiś mapper, który na podstawie błędów domenowych wyświetla odpowiedni komunikat. Tak jak zwrotka w postaci sukcesu jest mapowana na obiekt domenowy np. Article, tak też wszystkie błędy powinny być mapowane w warstwie danych tak, aby warstwa domeny nie miała żadnych zależności komunikacyjnych, ani żeby błędy komunikacyjne nie przebijały się do warstwy prezentacji. Data korzysta z domeny, ui korzysta z domeny i to tyle z zależności między warstwami. Wiesz, skoro jsona mapujemy na domenę to czemu nie error? W przypadku przepychania błędów z api wprost do ui, domena i ui przechowują te błędy. I przykładowo dostaniemy DioError, czemu w ogóle UI ma wiedzieć, że korzystamy z Dio jako klienta http, albo z firebase? Ucinanie tego poprzez mapowanie w data powoduje brak konieczności wprowadzania zmian w pozostałych warstwach.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

czyli wychodzi na to, że jedynym miejscem gdzie nie będzie mapowań jest właśnie domena. Tak to w data będzie mapowania błędów api/firebase czy tam cokolwiek innego na domenowe błędy, a w warstwie ui domenowe błędy na stringsy

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tak :) Analogicznie mapujemy teraz jeżeli chodzi o Article, w data z jsona na domenę, a w presentation z domeny na jakieś viewEntity w viewState :)

import 'package:flutter/cupertino.dart';

class RegistrationNotifier extends ChangeNotifier {
final AuthenticationRepository _authorizationRepository;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_authenticationRepository

VoidCallback returnToProfile;

Future<void> createUser(String email, String password, String name) async {
_validateFields();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Średnio mi się podoba bo ta walidacja częściowo jest w tej metodzie a częściowo w tym kolejnym ifie. Może jakby sygnatura wyglądała mniej więcej tak to zwięźlej by to wyglądało? 🤔 Taki luźny pomysł

_validateFields(
   onInvalid: (validationErrors) => ...,
   onValid: (state) => ...
)

_setNotifierState(_state.copyWith(email: email));
}

void setName(String name) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mega drobnostka ale może zamiast setName itd. to updateName (jakoś bardziej mi się kojarzy z aktualizacją stanu, ale może to moje jakieś takie spaczenie)


class LoginState {
final bool isLoading;
final AllTheNewsException exception;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthenticationException :)

class HomePage extends StatefulWidget {
final int initialPage;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w sumie HomePage nie musi trzymać initialPage, może to robić notifier co nie? 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to w ogóle można wyjebać

_setNotifierState(ProfileState(exception: exception as AuthenticationException));
}

String checkIfEmpty(String text) => text.isEmpty ? Strings.current.emptyFieldError : null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nieużywane

}

extension on ChangeNotifier {
void verifyCreateUser(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jak nie będziesz niepotrzebnie przesyłał parametrów do metod create i signIn to nie będą potrzebne te extensiony :)

@marekvogt
Copy link
Collaborator

Co do tych za dużych tasków to racja, tutaj powiedzmy spokojnie można było wydzielić na szybko osobno logowanie, rejestrację i update firebase, ale powiedzmy, że nawet w obrębie jednej funkcjonalności (np. logowanie) można na trello do taska porobić checkboxy jako subtaski i wrzucać je osobno, po kolei, jak już ten, który weźmie taska sobie rozplanuje co i w jakiej kolejności chce zrobić :)

@@ -0,0 +1,23 @@
import 'package:allthenews/src/domain/communication/all_the_news_exception.dart';

class AuthenticationApiException extends AllTheNewsException {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

powinno być w pakiecie lib/domain/authentication, bo domeną jest autentykacja

import 'package:allthenews/src/domain/model/user.dart' as domain;
import 'package:firebase_auth/firebase_auth.dart';

class FirebaseAuthenticationRepository implements AuthenticationRepository {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zmieniłbym pakiet na lib/src/data/authentication

@@ -0,0 +1,13 @@
import 'package:allthenews/src/domain/model/user.dart';

abstract class AuthenticationRepository {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pakiet z lib/src/domain/authorization na lib/src/domain/authentication

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do tej pory w authorization były rzeczy związane z kluczem api, jest to inny rodzaj autoryzacji niż logowanie od apki i dobrze by było to rozdzielić. Może obecne authorization zmienię na apiauthentication i zrobię osobny pakiet authentication na rzeczy związane z logowaniem/rejestracją?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Te rzeczy co były wcześniej w authorization niech zostaną w authorization, niech powstanie osobny pakiet authentication i do niego wrzuć te rzeczy z logowaniem, bo ten api key to autoryzacja, a logowanie to autentykacja

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git, też o tym myślałem, ale zbyt podobne wydawały mi się te nazwy

@@ -0,0 +1 @@
abstract class AllTheNewsException implements Exception {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dałbym w jakimś pakiecie lib/src/domain/common bo exceptiony w aplikacji niekoniecznie będą zawsze związane z jakąś komunikacją

@@ -0,0 +1,3 @@
abstract class MessageProvider {
String getMessage(Object error);
Copy link
Collaborator

@marekvogt marekvogt Dec 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Klasa mogłaby się nazywać ErrorMessageProvider skoro przyjmuje jako argument metody error

Copy link
Collaborator Author

@kightsonsanom kightsonsanom Dec 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nie jest powiedziane, że tylko errory będziemy mapować na stringi, z takiego samego powodu AllTheNewsException dajemy do common. Za to nazwa parametru nie powinna się nazywać error, tylko jakiś identifier

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w takim razie przyznam, że średnio mi się podoba przepychanie Object jako parametr, już lepiej jakby był generykiem:
MessageProvider<T> { String getMessage(T object) }

@@ -0,0 +1,23 @@
import 'package:allthenews/src/domain/communication/all_the_news_exception.dart';

class AuthenticationApiException extends AllTheNewsException {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lib/src/domain/authentication


void validateFieldsAndSignIn() {
_validateFields(
onInvalid: () {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onInvalid: () => notifyListeners()

import 'package:flutter/cupertino.dart';

class LoginNotifier extends ChangeNotifier {
final AuthenticationRepository _authorizationRepository;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_authenticationRepository

import 'package:allthenews/src/domain/authorization/authentication_field_error.dart';
import 'package:allthenews/src/ui/common/message_provider.dart';

class FieldErrorMessageProvider extends MessageProvider {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthFieldErrorMessageProvider

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nie jest powiedziane, że tylko błędy autentykacji tu będą, obecnie tak jest, ale jak dojdzie walidacja innych pól to tu też będzie można je określić. Myślę, że nie warto tego rozdzielać, bo te błędy mogą się powielać (np pustość). Taki był zamysł i zmieniłbym nazwę AuthenticationFieldError na FieldError

void initUserState() {
_setNotifierState(_state.copyWithLoading(isLoading: true));
_authorizationRepository.observeUserChanges().listen((user) {
_setNotifierState(_state.copyWithUserAndLoading(user: user, isLoading: false));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w sumie może tu wystąpić błąd, i zastanawiam się czy stream może nic nie wysłać, wtedy też będzie wieczny loading

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jeżeli nie będzie usera to zwróci nulla, każda inna zmiana będzie userem. Jeżeli chodzi o błędy to dokumentacja nic nie mówi na ten temat

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a choćby brak neta?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm sprawdzę

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jak nie ma neta to user leci jako null

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ciekawe :D w takim razie jest git :D

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w sumie słabo, bo jak rozpoznać czy jestem niezalogowany czy po prostu nie mam neta? dopiero przy logowaniu się dowiem

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm... racja, zostawiłbym to na osobne zadanie, bo chyba musi być na tym ekranie jakiś dodatkowy check robiony 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dodałem do trello

});
}

Widget _bindLoading() => const CircularProgressIndicator();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opakować w Center, żeby było na środku :)

@@ -0,0 +1,53 @@
import 'package:allthenews/src/domain/authorization/authentication_repository.dart';
import 'package:allthenews/src/ui/common/message_provider.dart';
import 'package:allthenews/src/ui/pages/authentication/authentication_message_provider.dart';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nieużywany import

@marekvogt
Copy link
Collaborator

wszystko spoko, tylko powód tego faila na bitrise, ale to jak już poprawisz to merguj :)

@kightsonsanom kightsonsanom merged commit e25f5d0 into master Dec 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants