From 68dce719b09f301602bf3a5cdb29928eece5062c Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Thu, 11 Mar 2021 01:02:48 +0300 Subject: [PATCH 01/26] Redirect to ServerConfiguration page on 404 implemented. --- lib/blocs/auth_bloc/auth_bloc.dart | 8 ++++++++ lib/blocs/auth_bloc/auth_state.dart | 5 +++++ .../notification_bloc/notification_bloc.dart | 2 +- lib/pages/auth_page.dart | 19 ++++++++++++++----- lib/pages/initial_page.dart | 2 +- lib/repositories/auth_repository.dart | 2 -- lib/services/api.dart | 15 ++++++++++++++- 7 files changed, 43 insertions(+), 10 deletions(-) diff --git a/lib/blocs/auth_bloc/auth_bloc.dart b/lib/blocs/auth_bloc/auth_bloc.dart index a633e4b2..2f1e1ec5 100644 --- a/lib/blocs/auth_bloc/auth_bloc.dart +++ b/lib/blocs/auth_bloc/auth_bloc.dart @@ -26,6 +26,7 @@ class AuthBloc extends Bloc { AuthBloc(this.repository, this.connectionBloc) : super(AuthInitializing()) { Api().resetAuthentication = resetAuthentication; + Api().invalidateConfiguration = resetHost; subscription = connectionBloc.listen((cb.ConnectionState state) async { if (state is cb.ConnectionLost) { connectionLost = true; @@ -196,6 +197,9 @@ class AuthBloc extends Bloc { } yield HostValidated(event.host); } + } else if (event is ResetHost) { + await repository.clean(); + yield HostReset(); } } @@ -215,6 +219,10 @@ class AuthBloc extends Bloc { this.add(ResetAuthentication(message: 'Session has expired')); } + void resetHost() { + this.add(ResetHost()); + } + @override Future close() { webView.dispose(); diff --git a/lib/blocs/auth_bloc/auth_state.dart b/lib/blocs/auth_bloc/auth_state.dart index 96e30707..fe782e0f 100644 --- a/lib/blocs/auth_bloc/auth_state.dart +++ b/lib/blocs/auth_bloc/auth_state.dart @@ -90,3 +90,8 @@ class HostInvalid extends AuthState { @override List get props => [host]; } + +class HostReset extends AuthState { + @override + List get props => []; +} \ No newline at end of file diff --git a/lib/blocs/notification_bloc/notification_bloc.dart b/lib/blocs/notification_bloc/notification_bloc.dart index 2b3f80d6..e37715e8 100644 --- a/lib/blocs/notification_bloc/notification_bloc.dart +++ b/lib/blocs/notification_bloc/notification_bloc.dart @@ -63,7 +63,7 @@ class NotificationBloc extends Bloc { .setTransports(['websocket']).build(), ); _authSubscription = authBloc.listen((state) { - if (state is Unauthenticated) { + if (state is Unauthenticated || state is HostReset) { for (String room in subscriptionRooms.keys) { unsubscribe(room); } diff --git a/lib/pages/auth_page.dart b/lib/pages/auth_page.dart index ce9c7d05..f573f336 100644 --- a/lib/pages/auth_page.dart +++ b/lib/pages/auth_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:twake/blocs/auth_bloc/auth_bloc.dart'; import 'package:twake/blocs/configuration_cubit/configuration_cubit.dart'; import 'package:twake/blocs/connection_bloc/connection_bloc.dart' as cb; import 'package:twake/config/dimensions_config.dart' show Dim; @@ -78,11 +79,19 @@ class _AuthPageState extends State { ), BlocListener( listener: connectionListener, - child: IndexedStack( - alignment: Alignment.bottomCenter, - sizing: StackFit.expand, - index: _index, - children: _widgets, + child: BlocBuilder( + buildWhen: (_, current) => current is HostReset, + builder: (context, state) { + if (state is HostReset) { + _index = 1; + } + return IndexedStack( + alignment: Alignment.bottomCenter, + sizing: StackFit.expand, + index: _index, + children: _widgets, + ); + } ), ), ], diff --git a/lib/pages/initial_page.dart b/lib/pages/initial_page.dart index 5e55edfd..776f8976 100644 --- a/lib/pages/initial_page.dart +++ b/lib/pages/initial_page.dart @@ -83,7 +83,7 @@ class _InitialPageState extends State with WidgetsBindingObserver { if (state is AuthInitializing) { return buildSplashScreen(); } - if (state is Unauthenticated) { + if (state is Unauthenticated || state is HostReset) { return AuthPage(); // return WebAuthPage(); } diff --git a/lib/repositories/auth_repository.dart b/lib/repositories/auth_repository.dart index c4ccbbbc..fc3bd5ed 100644 --- a/lib/repositories/auth_repository.dart +++ b/lib/repositories/auth_repository.dart @@ -267,8 +267,6 @@ class AuthRepository extends JsonSerializable { AuthResult _handleError(ApiError error) { if (error.type == ApiErrorType.Unauthorized) { return AuthResult.WrongCredentials; - } else if (error.type == ApiErrorType.BadRequest) { - } else { logger.e('Authentication error:\n${error.message}\n${error.type}'); return AuthResult.NetworkError; diff --git a/lib/services/api.dart b/lib/services/api.dart index d354f59f..e0287a92 100644 --- a/lib/services/api.dart +++ b/lib/services/api.dart @@ -14,16 +14,22 @@ const _PROXY_PREFIX = "/internal/mobile"; class Api { // singleton Api class instance static Api _api; + // Server address static String host; + // logging utility static final logger = Logger(); + // callback function to auto prolong token, if access token has expired Future Function() _prolongToken; + // callback function to validate token TokenStatus Function() _tokenIsValid; + // callback to reset authentication if for e.g. token has expired void Function() _resetAuthentication; + // callback to invalidate current backend configuration void Function() _invalidateConfiguration; @@ -63,9 +69,12 @@ class Api { // if refresh has changed, then we reset dio interceptor to account for this set prolongToken(value) => _prolongToken = value; + set tokenIsValid(value) => _tokenIsValid = value; + set resetAuthentication(value) => _resetAuthentication = value; - set invalidateDomain(value) => _invalidateConfiguration = value; + + set invalidateConfiguration(value) => _invalidateConfiguration = value; void checkConnection() { // logger.d('HAS CONNECTION: $hasConnection'); @@ -250,6 +259,9 @@ class Api { if (error.response.statusCode == 401 && _prolongToken != null) { logger.e('Token has expired prematuraly, prolonging...'); await _prolongToken(); + } else if (error.response.statusCode == 404 && + _invalidateConfiguration != null) { + _invalidateConfiguration(); } else { logger.e('status code: ${error.response.statusCode}'); return error; @@ -273,6 +285,7 @@ enum ApiErrorType { class ApiError implements Exception { final String message; final ApiErrorType type; + const ApiError({this.message: '', this.type: ApiErrorType.Unknown}); factory ApiError.fromDioError(DioError error) { From 8e37639df87722fde15aaf0bd707e9dbeebbb246 Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Thu, 11 Mar 2021 02:33:51 +0300 Subject: [PATCH 02/26] ServerConfiguration page fixes. --- lib/blocs/auth_bloc/auth_bloc.dart | 3 + lib/blocs/auth_bloc/auth_state.dart | 11 +- .../configuration_cubit.dart | 2 + .../configuration_state.dart | 11 +- lib/pages/initial_page.dart | 4 +- lib/pages/server_configuration.dart | 302 +++++++++--------- 6 files changed, 183 insertions(+), 150 deletions(-) diff --git a/lib/blocs/auth_bloc/auth_bloc.dart b/lib/blocs/auth_bloc/auth_bloc.dart index 2f1e1ec5..10b521dc 100644 --- a/lib/blocs/auth_bloc/auth_bloc.dart +++ b/lib/blocs/auth_bloc/auth_bloc.dart @@ -185,6 +185,7 @@ class AuthBloc extends Bloc { } else if (event is ResetPassword) { yield PasswordReset('https://console.twake.app/password-recovery'); } else if (event is ValidateHost) { + yield HostValidation(event.host); Api.host = event.host; final result = await repository.getAuthMode(); // final host = '${event.host}'; @@ -195,7 +196,9 @@ class AuthBloc extends Bloc { setUpWebView(); await runWebView(); } + print('Before yield'); yield HostValidated(event.host); + print('After yield'); } } else if (event is ResetHost) { await repository.clean(); diff --git a/lib/blocs/auth_bloc/auth_state.dart b/lib/blocs/auth_bloc/auth_state.dart index fe782e0f..463f394f 100644 --- a/lib/blocs/auth_bloc/auth_state.dart +++ b/lib/blocs/auth_bloc/auth_state.dart @@ -73,6 +73,15 @@ class AuthenticationError extends Unauthenticated { List get props => []; } +class HostValidation extends AuthState { + final String host; + + const HostValidation(this.host); + + @override + List get props => [host]; +} + class HostValidated extends AuthState { final String host; @@ -94,4 +103,4 @@ class HostInvalid extends AuthState { class HostReset extends AuthState { @override List get props => []; -} \ No newline at end of file +} diff --git a/lib/blocs/configuration_cubit/configuration_cubit.dart b/lib/blocs/configuration_cubit/configuration_cubit.dart index 3560b584..86c8fc76 100644 --- a/lib/blocs/configuration_cubit/configuration_cubit.dart +++ b/lib/blocs/configuration_cubit/configuration_cubit.dart @@ -13,9 +13,11 @@ class ConfigurationCubit extends Cubit { } Future save(String host) async { + emit(ConfigurationSaving(host: host)); try { repository.host = host; await repository.save(); + print('Before to emit'); emit(ConfigurationSaved(host: host)); } on Exception { emit(ConfigurationError(message: 'Error during the configuration saving')); diff --git a/lib/blocs/configuration_cubit/configuration_state.dart b/lib/blocs/configuration_cubit/configuration_state.dart index 2bb445cd..c968606a 100644 --- a/lib/blocs/configuration_cubit/configuration_state.dart +++ b/lib/blocs/configuration_cubit/configuration_state.dart @@ -2,7 +2,8 @@ import 'package:meta/meta.dart'; import 'package:equatable/equatable.dart'; abstract class ConfigurationState extends Equatable { - const ConfigurationState(); + final String host; + const ConfigurationState({@required this.host}); } class ConfigurationInitial extends ConfigurationState { @@ -19,6 +20,14 @@ class ConfigurationLoaded extends ConfigurationState { List get props => [host]; } +class ConfigurationSaving extends ConfigurationState { + final String host; + + ConfigurationSaving({@required this.host}); + @override + List get props => [host]; +} + class ConfigurationSaved extends ConfigurationState { final String host; diff --git a/lib/pages/initial_page.dart b/lib/pages/initial_page.dart index 776f8976..9dba06b2 100644 --- a/lib/pages/initial_page.dart +++ b/lib/pages/initial_page.dart @@ -78,7 +78,9 @@ class _InitialPageState extends State with WidgetsBindingObserver { resizeToAvoidBottomInset: false, body: BlocBuilder( buildWhen: (_, current) => - current is! HostValidated && current is! HostInvalid, + current is! HostValidation && + current is! HostValidated && + current is! HostInvalid, builder: (ctx, state) { if (state is AuthInitializing) { return buildSplashScreen(); diff --git a/lib/pages/server_configuration.dart b/lib/pages/server_configuration.dart index fc9661cf..f7c2eb64 100644 --- a/lib/pages/server_configuration.dart +++ b/lib/pages/server_configuration.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:twake/blocs/auth_bloc/auth_bloc.dart'; import 'package:twake/blocs/configuration_cubit/configuration_cubit.dart'; import 'package:twake/blocs/configuration_cubit/configuration_state.dart'; + // import 'package:twake/services/api.dart'; import 'package:twake/utils/extensions.dart'; @@ -56,169 +57,176 @@ class _ServerConfigurationState extends State { child: GestureDetector( onTap: () => FocusScope.of(context).requestFocus(FocusNode()), behavior: HitTestBehavior.opaque, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(height: 10.0), - Image.asset('assets/images/server.png'), - SizedBox(height: 15.0), - Text( - 'Server connection\npreference', - textAlign: TextAlign.center, - maxLines: 2, - style: TextStyle( - fontSize: 20.0, - fontWeight: FontWeight.w900, - color: Colors.black, - ), - ), - SizedBox(height: 36.0), - Padding( - padding: EdgeInsets.only(left: 16, right: 36.0), - child: Text( - 'Before you can proceed, please, choose a default server connection', - style: TextStyle( - fontSize: 15.0, - fontWeight: FontWeight.normal, - color: Colors.black.withOpacity(0.6), + child: BlocConsumer( + listenWhen: (_, current) => current is ConfigurationError, + listener: (context, state) { + if (state is ConfigurationError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + state.message, + style: TextStyle( + color: Colors.red, + ), + ), + duration: Duration(seconds: 2), ), - ), - ), - Padding( - padding: EdgeInsets.fromLTRB(14.0, 12.0, 14.0, 0), - child: BlocListener( - listener: (context, state) { - if (state is HostValidated) { - widget.onConfirm(); - } - if (state is HostInvalid) { - print('HOST INVALID'); - Scaffold.of(context).showSnackBar(SnackBar( - content: Text( - 'Invalid host', - style: TextStyle( - color: Colors.red, - ), - ), - duration: Duration(seconds: 2), - )); - } - }, - child: BlocConsumer( - listenWhen: (_, current) => - current is ConfigurationSaved || - current is ConfigurationError, + ); + } + }, + buildWhen: (_, current) => + current is ConfigurationLoaded || + current is ConfigurationSaving || + current is ConfigurationSaved, + builder: (context, state) { + + if (state is ConfigurationSaved) { + context.read().add(ValidateHost(_controller.text)); + } + + if (_controller.text.isReallyEmpty) { + _controller.text = state.host; + } + print('Server host: ${state.host}'); + + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 10.0), + Image.asset('assets/images/server.png'), + SizedBox(height: 15.0), + Text( + 'Server connection\npreference', + textAlign: TextAlign.center, + maxLines: 2, + style: TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.w900, + color: Colors.black, + ), + ), + SizedBox(height: 36.0), + Padding( + padding: EdgeInsets.only(left: 16, right: 36.0), + child: Text( + 'Before you can proceed, please, choose a default server connection', + style: TextStyle( + fontSize: 15.0, + fontWeight: FontWeight.normal, + color: Colors.black.withOpacity(0.6), + ), + ), + ), + Padding( + padding: EdgeInsets.fromLTRB(14.0, 12.0, 14.0, 0), + child: BlocListener( listener: (context, state) { - if (state is ConfigurationSaved) { - context - .read() - .add(ValidateHost(_controller.text)); - } else if (state is ConfigurationError) { - Scaffold.of(context).showSnackBar(SnackBar( - content: Text( - state.message, - style: TextStyle( - color: Colors.red, - ), - ), - duration: Duration(seconds: 2), - )); + if (state is HostValidated) { + widget.onConfirm(); } - }, - buildWhen: (_, current) => current is ConfigurationLoaded, - builder: (context, state) { - if (state is ConfigurationLoaded) { - if (_controller.text.isReallyEmpty) { - _controller.text = state.host; - } - print('Server host: ${state.host}'); - - return TextFormField( - key: _formKey, - validator: (value) => value.isEmpty - ? 'Address cannot be blank' - : null, - controller: _controller, - onFieldSubmitted: (_) => _connect(), - style: TextStyle( - fontSize: 17.0, - fontWeight: FontWeight.w400, - color: Colors.black, - ), - decoration: InputDecoration( - hintText: 'https://mobile.api.twake.app', - hintStyle: TextStyle( - fontSize: 17.0, - fontWeight: FontWeight.w400, - color: Color(0xffc8c8c8), - ), - alignLabelWithHint: true, - fillColor: Color(0xfff4f4f4), - filled: true, - suffix: Container( - width: 30, - height: 25, - padding: EdgeInsets.only(left: 10), - child: IconButton( - padding: EdgeInsets.all(0), - onPressed: () => _controller.clear(), - iconSize: 15, - icon: Icon(CupertinoIcons.clear), - ), - ), - border: UnderlineInputBorder( - borderRadius: BorderRadius.circular(10.0), - borderSide: BorderSide( - width: 0.0, - style: BorderStyle.none, + if (state is HostInvalid) { + print('HOST INVALID'); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Invalid host', + style: TextStyle( + color: Colors.red, ), ), + duration: Duration(seconds: 2), ), ); - } else { - return Center(child: CircularProgressIndicator()); } - }), - ), - ), - Spacer(), - Padding( - padding: EdgeInsets.symmetric(horizontal: 40.0), - child: Text( - 'Tap “Connect” if you don’t know exactly what is this all about', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 15.0, - fontWeight: FontWeight.normal, - color: Colors.black, + }, + child: TextFormField( + key: _formKey, + validator: (value) => + value.isEmpty ? 'Address cannot be blank' : null, + controller: _controller, + onFieldSubmitted: (_) => _connect(), + style: TextStyle( + fontSize: 17.0, + fontWeight: FontWeight.w400, + color: Colors.black, + ), + decoration: InputDecoration( + hintText: 'https://mobile.api.twake.app', + hintStyle: TextStyle( + fontSize: 17.0, + fontWeight: FontWeight.w400, + color: Color(0xffc8c8c8), + ), + alignLabelWithHint: true, + fillColor: Color(0xfff4f4f4), + filled: true, + suffix: Container( + width: 30, + height: 25, + padding: EdgeInsets.only(left: 10), + child: IconButton( + padding: EdgeInsets.all(0), + onPressed: () => _controller.clear(), + iconSize: 15, + icon: Icon(CupertinoIcons.clear), + ), + ), + border: UnderlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide( + width: 0.0, + style: BorderStyle.none, + ), + ), + ), + ), + ), ), - ), - ), - Padding( - padding: EdgeInsets.fromLTRB(24.0, 16.0, 24.0, 22.0), - child: TextButton( - onPressed: () => _connect(), - child: Container( - width: Size.infinite.width, - height: 50, - decoration: BoxDecoration( - color: Color(0xff3840f7), - borderRadius: BorderRadius.circular(14.0), + if (state is ConfigurationSaving) + Expanded( + child: Center(child: CircularProgressIndicator()), ), - alignment: Alignment.center, + if (state is! ConfigurationSaving) + Spacer(), + Padding( + padding: EdgeInsets.symmetric(horizontal: 40.0), child: Text( - 'Connect', + 'Tap “Connect” if you don’t know exactly what is this all about', textAlign: TextAlign.center, style: TextStyle( - fontSize: 17.0, - fontWeight: FontWeight.bold, - color: Colors.white, + fontSize: 15.0, + fontWeight: FontWeight.normal, + color: Colors.black, + ), + ), + ), + Padding( + padding: EdgeInsets.fromLTRB(24.0, 16.0, 24.0, 22.0), + child: TextButton( + onPressed: () => _connect(), + child: Container( + width: Size.infinite.width, + height: 50, + decoration: BoxDecoration( + color: Color(0xff3840f7), + borderRadius: BorderRadius.circular(14.0), + ), + alignment: Alignment.center, + child: Text( + 'Connect', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 17.0, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), ), ), ), - ), - ), - ], + ], + ); + }, ), ), ), From 58b703ccd9a9d455947a9ddbfed9d39938b5c0d6 Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Thu, 11 Mar 2021 02:55:22 +0300 Subject: [PATCH 03/26] ServerConfiguration flow bugfix. --- .../configuration_cubit.dart | 1 + lib/pages/server_configuration.dart | 17 ++++++++--------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/blocs/configuration_cubit/configuration_cubit.dart b/lib/blocs/configuration_cubit/configuration_cubit.dart index 86c8fc76..2dc6eefe 100644 --- a/lib/blocs/configuration_cubit/configuration_cubit.dart +++ b/lib/blocs/configuration_cubit/configuration_cubit.dart @@ -19,6 +19,7 @@ class ConfigurationCubit extends Cubit { await repository.save(); print('Before to emit'); emit(ConfigurationSaved(host: host)); + emit(ConfigurationLoaded(host: repository.host)); } on Exception { emit(ConfigurationError(message: 'Error during the configuration saving')); } diff --git a/lib/pages/server_configuration.dart b/lib/pages/server_configuration.dart index f7c2eb64..5c5244d2 100644 --- a/lib/pages/server_configuration.dart +++ b/lib/pages/server_configuration.dart @@ -58,8 +58,12 @@ class _ServerConfigurationState extends State { onTap: () => FocusScope.of(context).requestFocus(FocusNode()), behavior: HitTestBehavior.opaque, child: BlocConsumer( - listenWhen: (_, current) => current is ConfigurationError, + listenWhen: (_, current) => + current is ConfigurationError || current is ConfigurationSaved, listener: (context, state) { + if (state is ConfigurationSaved) { + context.read().add(ValidateHost(_controller.text)); + } if (state is ConfigurationError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -76,13 +80,9 @@ class _ServerConfigurationState extends State { }, buildWhen: (_, current) => current is ConfigurationLoaded || - current is ConfigurationSaving || - current is ConfigurationSaved, + current is ConfigurationSaving, builder: (context, state) { - - if (state is ConfigurationSaved) { - context.read().add(ValidateHost(_controller.text)); - } + print('Current state: $state'); if (_controller.text.isReallyEmpty) { _controller.text = state.host; @@ -186,8 +186,7 @@ class _ServerConfigurationState extends State { Expanded( child: Center(child: CircularProgressIndicator()), ), - if (state is! ConfigurationSaving) - Spacer(), + if (state is! ConfigurationSaving) Spacer(), Padding( padding: EdgeInsets.symmetric(horizontal: 40.0), child: Text( From d5673299053f1477c556373b5dcaf2477218b2aa Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Thu, 11 Mar 2021 03:24:49 +0300 Subject: [PATCH 04/26] ServerConfiguration flow bugfix. --- lib/blocs/auth_bloc/auth_bloc.dart | 1 + lib/pages/server_configuration.dart | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/blocs/auth_bloc/auth_bloc.dart b/lib/blocs/auth_bloc/auth_bloc.dart index 10b521dc..90e15a80 100644 --- a/lib/blocs/auth_bloc/auth_bloc.dart +++ b/lib/blocs/auth_bloc/auth_bloc.dart @@ -191,6 +191,7 @@ class AuthBloc extends Bloc { // final host = '${event.host}'; if (result == 'UNKNOWN') { yield HostInvalid(event.host); + yield HostValidation(event.host); } else { if (result == 'CONSOLE') { setUpWebView(); diff --git a/lib/pages/server_configuration.dart b/lib/pages/server_configuration.dart index 5c5244d2..8441affa 100644 --- a/lib/pages/server_configuration.dart +++ b/lib/pages/server_configuration.dart @@ -36,8 +36,7 @@ class _ServerConfigurationState extends State { FocusScope.of(context).requestFocus(FocusNode()); var host = _controller.text; if (host.isNotReallyEmpty) { - // Save host address locally. - context.read().save(host); + context.read().add(ValidateHost(_controller.text)); } // Api.host = host; // Apply changes to AuthBloc flow. @@ -62,7 +61,8 @@ class _ServerConfigurationState extends State { current is ConfigurationError || current is ConfigurationSaved, listener: (context, state) { if (state is ConfigurationSaved) { - context.read().add(ValidateHost(_controller.text)); + // context.read().add(ValidateHost(_controller.text)); + widget.onConfirm(); } if (state is ConfigurationError) { ScaffoldMessenger.of(context).showSnackBar( @@ -121,8 +121,10 @@ class _ServerConfigurationState extends State { padding: EdgeInsets.fromLTRB(14.0, 12.0, 14.0, 0), child: BlocListener( listener: (context, state) { + print('AuthBloc state: $state'); if (state is HostValidated) { - widget.onConfirm(); + // Save host address locally. + context.read().save(state.host); } if (state is HostInvalid) { print('HOST INVALID'); From f88be1a110b73fee5bdb511f5fd13eb6fc871071 Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Thu, 11 Mar 2021 03:31:52 +0300 Subject: [PATCH 05/26] ServerConfiguration flow another bugfix. --- lib/pages/auth_page.dart | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/pages/auth_page.dart b/lib/pages/auth_page.dart index f573f336..65931a4b 100644 --- a/lib/pages/auth_page.dart +++ b/lib/pages/auth_page.dart @@ -80,19 +80,19 @@ class _AuthPageState extends State { BlocListener( listener: connectionListener, child: BlocBuilder( - buildWhen: (_, current) => current is HostReset, - builder: (context, state) { - if (state is HostReset) { - _index = 1; - } - return IndexedStack( - alignment: Alignment.bottomCenter, - sizing: StackFit.expand, - index: _index, - children: _widgets, - ); - } - ), + buildWhen: (_, current) => + current is HostReset || current is HostValidated, + builder: (context, state) { + if (state is HostReset) { + _index = 1; + } + return IndexedStack( + alignment: Alignment.bottomCenter, + sizing: StackFit.expand, + index: _index, + children: _widgets, + ); + }), ), ], ), From 6d325cb05ac7016ab54f826162ac8054a8340346 Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Thu, 11 Mar 2021 03:50:19 +0300 Subject: [PATCH 06/26] ChannelInfoForm cleanup issue solved. --- lib/widgets/sheets/add/channel_info_form.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/widgets/sheets/add/channel_info_form.dart b/lib/widgets/sheets/add/channel_info_form.dart index 78bbebfc..2a36b2e1 100644 --- a/lib/widgets/sheets/add/channel_info_form.dart +++ b/lib/widgets/sheets/add/channel_info_form.dart @@ -143,7 +143,16 @@ class _ChannelInfoFormState extends State { _participants = []; _automaticallyAddNew = true; _channelType = ChannelType.public; - FocusScope.of(context).requestFocus(new FocusNode()); + + _batchUpdateState( + icon: _icon, + name: '', + description: '', + participants: _participants, + automaticallyAddNew: _automaticallyAddNew, + type: _channelType, + ); + FocusScope.of(context).requestFocus(FocusNode()); context.read().add(Clear()); } }, From 13f611b1e486216b009406b811f3f727dedffba0 Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Thu, 11 Mar 2021 03:56:56 +0300 Subject: [PATCH 07/26] WorkspaceInfoForm cleanup issue solved. --- lib/widgets/sheets/add/workspace_info_form.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/widgets/sheets/add/workspace_info_form.dart b/lib/widgets/sheets/add/workspace_info_form.dart index 66f788d6..5684d4a0 100644 --- a/lib/widgets/sheets/add/workspace_info_form.dart +++ b/lib/widgets/sheets/add/workspace_info_form.dart @@ -75,6 +75,10 @@ class _WorkspaceInfoFormState extends State { _workspaceNameController.clear(); _collaborators = []; _workspaceId = ''; + _batchUpdateState( + name: '', + collaborators: _collaborators, + ); FocusScope.of(context).requestFocus(FocusNode()); context.read().clear(); } From 0161fae3853b311e15909e2f9d81b727acc634e6 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Thu, 11 Mar 2021 12:01:24 +0300 Subject: [PATCH 08/26] Restored directs reordering after user posts a message --- lib/blocs/base_channel_bloc/base_channel_bloc.dart | 2 +- lib/blocs/channels_bloc/channel_event.dart | 4 ++-- lib/blocs/messages_bloc/messages_bloc.dart | 5 +++-- lib/blocs/threads_bloc/threads_bloc.dart | 5 +++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/blocs/base_channel_bloc/base_channel_bloc.dart b/lib/blocs/base_channel_bloc/base_channel_bloc.dart index 26399a53..76ab5268 100644 --- a/lib/blocs/base_channel_bloc/base_channel_bloc.dart +++ b/lib/blocs/base_channel_bloc/base_channel_bloc.dart @@ -28,7 +28,7 @@ abstract class BaseChannelBloc extends Bloc { final ch = await repository.getItemById(event.channelId); if (ch != null) { // ch.messagesTotal += event.totalModifier ?? 0; - ch.hasUnread = 1; + ch.hasUnread = event.hasUnread ?? 1; ch.messagesUnread += event.unreadModifier ?? 0; ch.lastActivity = event.timeStamp ?? DateTime.now().millisecondsSinceEpoch; diff --git a/lib/blocs/channels_bloc/channel_event.dart b/lib/blocs/channels_bloc/channel_event.dart index 7e761d1a..a28782d6 100644 --- a/lib/blocs/channels_bloc/channel_event.dart +++ b/lib/blocs/channels_bloc/channel_event.dart @@ -55,16 +55,16 @@ class ModifyMessageCount extends ChannelsEvent { final String channelId; final String workspaceId; final String companyId; - final int totalModifier; final int unreadModifier; + final int hasUnread; final int timeStamp; ModifyMessageCount({ this.channelId, this.workspaceId, this.companyId, - this.totalModifier, this.unreadModifier, + this.hasUnread, this.timeStamp, }); diff --git a/lib/blocs/messages_bloc/messages_bloc.dart b/lib/blocs/messages_bloc/messages_bloc.dart index e39944b6..bf95c5ad 100644 --- a/lib/blocs/messages_bloc/messages_bloc.dart +++ b/lib/blocs/messages_bloc/messages_bloc.dart @@ -287,6 +287,7 @@ class MessagesBloc this.repository.items.add(message); this.add(FinishLoadingMessages()); this.channelsBloc.add(ChangeSelectedChannel(selectedChannel.id)); + _updateParentChannel(selectedChannel.id, 0); }, ); this.repository.items.add(tempItem); @@ -341,12 +342,12 @@ class MessagesBloc return map; } - void _updateParentChannel([String channelId]) { + void _updateParentChannel(String channelId, [int hasUnread = 1]) { channelsBloc.add(ModifyMessageCount( workspaceId: ProfileBloc.selectedWorkspace, channelId: channelId ?? selectedChannel.id, companyId: ProfileBloc.selectedCompany, - totalModifier: 1, + hasUnread: hasUnread, )); } diff --git a/lib/blocs/threads_bloc/threads_bloc.dart b/lib/blocs/threads_bloc/threads_bloc.dart index 8718b87b..43453703 100644 --- a/lib/blocs/threads_bloc/threads_bloc.dart +++ b/lib/blocs/threads_bloc/threads_bloc.dart @@ -191,6 +191,7 @@ class ThreadsBloc channelId: event.channelId, threadId: message.threadId, )); + _updateParentChannel(event.channelId, 0); }, ); this.repository.items.add(tempItem); @@ -247,12 +248,12 @@ class ThreadsBloc parentChannel: parentChannel, ); - void _updateParentChannel(String channelId, {int totalModifier: 1}) { + void _updateParentChannel(String channelId, [int hasUnread = 1]) { messagesBloc.channelsBloc.add(ModifyMessageCount( channelId: channelId, workspaceId: T == DirectsBloc ? "direct" : ProfileBloc.selectedWorkspace, companyId: ProfileBloc.selectedCompany, - totalModifier: totalModifier, + hasUnread: hasUnread, )); } } From 3af5ccc41ff56a14b10dce8edb0ef25ea9fd2230 Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Fri, 12 Mar 2021 01:36:28 +0300 Subject: [PATCH 09/26] Scaffold access refactoring. --- lib/utils/navigation.dart | 4 +-- lib/widgets/message/message_tile.dart | 28 +++++++++++-------- lib/widgets/sheets/add/participants_list.dart | 2 +- .../sheets/add/workspace_info_form.dart | 5 ++-- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/lib/utils/navigation.dart b/lib/utils/navigation.dart index 230c8f2c..8c9572e5 100644 --- a/lib/utils/navigation.dart +++ b/lib/utils/navigation.dart @@ -50,8 +50,8 @@ void openChooseServer(BuildContext context) { void handleError(dynamic r, BuildContext context) { if (r is bool && r) { - Scaffold.of(context).hideCurrentSnackBar(); - Scaffold.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text('No connection to internet'), backgroundColor: Theme.of(context).errorColor, )); diff --git a/lib/widgets/message/message_tile.dart b/lib/widgets/message/message_tile.dart index 1c5741bc..4fcc9c47 100644 --- a/lib/widgets/message/message_tile.dart +++ b/lib/widgets/message/message_tile.dart @@ -68,7 +68,7 @@ class _MessageTileState onCopy({context, text}) { FlutterClipboard.copy(text); Navigator.of(context).pop(); - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( SnackBar( duration: Duration(milliseconds: 1300), content: Text('Message has been copied to clipboard'), @@ -115,17 +115,21 @@ class _MessageTileState final mebloc = ctx.read(); mebloc.add( EditMessage( - originalStr: _message.content.originalStr, - onMessageEditComplete: (text) { - // smbloc get's closed if - // listview disposes of message tile - smbloc.add(UpdateContent( - content: text, - workspaceId: - T == DirectsBloc ? 'direct' : null)); - mebloc.add(CancelMessageEdit()); - FocusManager.instance.primaryFocus.unfocus(); - }), + originalStr: _message.content.originalStr, + onMessageEditComplete: (text) { + // smbloc get's closed if + // listview disposes of message tile + smbloc.add( + UpdateContent( + content: text, + workspaceId: + T == DirectsBloc ? 'direct' : null, + ), + ); + mebloc.add(CancelMessageEdit()); + FocusManager.instance.primaryFocus.unfocus(); + }, + ), ); }, ctx: ctx, diff --git a/lib/widgets/sheets/add/participants_list.dart b/lib/widgets/sheets/add/participants_list.dart index 798cb72b..6ae71dcd 100644 --- a/lib/widgets/sheets/add/participants_list.dart +++ b/lib/widgets/sheets/add/participants_list.dart @@ -144,7 +144,7 @@ class _ParticipantsListState extends State { } } else if (state is Error) { // Show an error - Scaffold.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text( state.message, style: TextStyle( diff --git a/lib/widgets/sheets/add/workspace_info_form.dart b/lib/widgets/sheets/add/workspace_info_form.dart index 5684d4a0..602493a0 100644 --- a/lib/widgets/sheets/add/workspace_info_form.dart +++ b/lib/widgets/sheets/add/workspace_info_form.dart @@ -62,8 +62,7 @@ class _WorkspaceInfoFormState extends State { }) { context.read().update( name: name ?? _workspaceNameController.text, - members: - collaborators, //['31a4a6a4-54f2-11eb-a382-0242ac120004'];]//_collaborators,['senjertomat@yandex.ru'], + members: collaborators, //['31a4a6a4-54f2-11eb-a382-0242ac120004'];]//_collaborators,['senjertomat@yandex.ru'], ); } @@ -97,7 +96,7 @@ class _WorkspaceInfoFormState extends State { _shouldRedirect = true; } else if (state is Error) { // Show an error - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( state.message, From 5378800b2eb006cd40b3066cbe8009e6edd9adb7 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Fri, 12 Mar 2021 11:03:19 +0300 Subject: [PATCH 10/26] Added username fallback in case if user first name is not found --- lib/blocs/threads_bloc/threads_bloc.dart | 1 + lib/widgets/message/message_tile.dart | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/blocs/threads_bloc/threads_bloc.dart b/lib/blocs/threads_bloc/threads_bloc.dart index 43453703..d2fc703e 100644 --- a/lib/blocs/threads_bloc/threads_bloc.dart +++ b/lib/blocs/threads_bloc/threads_bloc.dart @@ -249,6 +249,7 @@ class ThreadsBloc ); void _updateParentChannel(String channelId, [int hasUnread = 1]) { + print("HAS UNREAD: $hasUnread"); messagesBloc.channelsBloc.add(ModifyMessageCount( channelId: channelId, workspaceId: T == DirectsBloc ? "direct" : ProfileBloc.selectedWorkspace, diff --git a/lib/widgets/message/message_tile.dart b/lib/widgets/message/message_tile.dart index 4fcc9c47..2847026f 100644 --- a/lib/widgets/message/message_tile.dart +++ b/lib/widgets/message/message_tile.dart @@ -182,7 +182,10 @@ class _MessageTileState mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '${messageState.firstName} ${messageState.lastName}', + messageState.firstName.isNotEmpty + ? '${messageState.firstName} ${messageState.lastName}' + : messageState.username[0].toUpperCase() + + messageState.username.substring(1), style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.w500, From fdd12af9c9bf540c3c49bc6e7ebb5b15cfe85fc5 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Fri, 12 Mar 2021 14:31:03 +0300 Subject: [PATCH 11/26] Changed default host to mobile.twake.app --- lib/pages/server_configuration.dart | 2 +- lib/repositories/configuration_repository.dart | 2 +- lib/services/api.dart | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/pages/server_configuration.dart b/lib/pages/server_configuration.dart index 8441affa..8911bb50 100644 --- a/lib/pages/server_configuration.dart +++ b/lib/pages/server_configuration.dart @@ -153,7 +153,7 @@ class _ServerConfigurationState extends State { color: Colors.black, ), decoration: InputDecoration( - hintText: 'https://mobile.api.twake.app', + hintText: 'https://mobile.twake.app', hintStyle: TextStyle( fontSize: 17.0, fontWeight: FontWeight.w400, diff --git a/lib/repositories/configuration_repository.dart b/lib/repositories/configuration_repository.dart index 171ce0be..217600e1 100644 --- a/lib/repositories/configuration_repository.dart +++ b/lib/repositories/configuration_repository.dart @@ -9,7 +9,7 @@ part 'configuration_repository.g.dart'; // because it's a global object, // it always has only one record in store const CONFIG_KEY = 'configuration'; -const DEFAULT_HOST = 'https://mobile.api.twake.app'; +const DEFAULT_HOST = 'https://mobile.twake.app'; @JsonSerializable() class ConfigurationRepository extends JsonSerializable { diff --git a/lib/services/api.dart b/lib/services/api.dart index e0287a92..c38b6378 100644 --- a/lib/services/api.dart +++ b/lib/services/api.dart @@ -3,9 +3,6 @@ import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:logger/logger.dart'; -// const String _HOST = 'mobile.api.twake.app'; -// const String _SHOST = 'https://mobile.api.twake.app'; -// const String _SCHEME = 'https'; const _RECEIVE_TIMEOUT = 7000; const _SEND_TIMEOUT = 5000; const _CONNECT_TIMEOUT = 50000; From e4491da102ad688e05979c4da8fe8de397cd6fd5 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Fri, 12 Mar 2021 14:42:28 +0300 Subject: [PATCH 12/26] Added keys on directs, so that avatars stay attached when reordering --- lib/widgets/channel/direct_messages_group.dart | 4 +++- lib/widgets/channel/direct_tile.dart | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/widgets/channel/direct_messages_group.dart b/lib/widgets/channel/direct_messages_group.dart index edf32bc3..404b84e3 100644 --- a/lib/widgets/channel/direct_messages_group.dart +++ b/lib/widgets/channel/direct_messages_group.dart @@ -19,7 +19,9 @@ class DirectMessagesGroup extends StatelessWidget { ), SizedBox(height: 11), if (state is ChannelsLoaded) - ...state.channels.map((d) => DirectTile(d)).toList(), + ...state.channels + .map((d) => DirectTile(d, ValueKey(d.id))) + .toList(), if (state is ChannelsEmpty) Padding( padding: EdgeInsets.all(7.0), diff --git a/lib/widgets/channel/direct_tile.dart b/lib/widgets/channel/direct_tile.dart index 7565a907..01cf84c6 100644 --- a/lib/widgets/channel/direct_tile.dart +++ b/lib/widgets/channel/direct_tile.dart @@ -14,7 +14,7 @@ import 'package:twake/widgets/common/stacked_image_avatars.dart'; class DirectTile extends StatelessWidget { final Direct direct; - DirectTile(this.direct); + DirectTile(this.direct, Key key) : super(key: key); @override Widget build(BuildContext context) { From c9d5f9130ae291be4a5314f34a68a483d48e3aab Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Fri, 12 Mar 2021 18:58:12 +0300 Subject: [PATCH 13/26] Got rid from automaticallyAddNew. --- .../add_channel_bloc/add_channel_bloc.dart | 3 --- lib/repositories/add_channel_repository.dart | 1 - lib/widgets/sheets/add/channel_info_form.dart | 20 ++++++++----------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/blocs/add_channel_bloc/add_channel_bloc.dart b/lib/blocs/add_channel_bloc/add_channel_bloc.dart index 7772c7e7..1820c43d 100644 --- a/lib/blocs/add_channel_bloc/add_channel_bloc.dart +++ b/lib/blocs/add_channel_bloc/add_channel_bloc.dart @@ -35,8 +35,6 @@ class AddChannelBloc extends Bloc { event.type ?? channelRepository.type ?? ChannelType.public; channelRepository.members = event.participants ?? channelRepository.members ?? []; - channelRepository.def = - event.automaticallyAddNew ?? channelRepository.def ?? true; // print('Updated data: ${repository.toJson()}'); final newRepo = AddChannelRepository( @@ -49,7 +47,6 @@ class AddChannelBloc extends Bloc { channelGroup: channelRepository.channelGroup, type: channelRepository.type, members: channelRepository.members, - def: channelRepository.def, ); yield Updated(newRepo); } else if (event is UpdateDirect) { diff --git a/lib/repositories/add_channel_repository.dart b/lib/repositories/add_channel_repository.dart index 63459bac..fa2bc00f 100644 --- a/lib/repositories/add_channel_repository.dart +++ b/lib/repositories/add_channel_repository.dart @@ -77,7 +77,6 @@ class AddChannelRepository { icon = ''; description = ''; channelGroup = ''; - def = true; members = []; type = ChannelType.public; } diff --git a/lib/widgets/sheets/add/channel_info_form.dart b/lib/widgets/sheets/add/channel_info_form.dart index 2a36b2e1..7349fb19 100644 --- a/lib/widgets/sheets/add/channel_info_form.dart +++ b/lib/widgets/sheets/add/channel_info_form.dart @@ -35,7 +35,7 @@ class _ChannelInfoFormState extends State { var _canGoNext = false; var _channelType = ChannelType.public; var _participants = []; - var _automaticallyAddNew = true; + // var _automaticallyAddNew = true; var _icon = '📄'; var _emojiVisible = false; @@ -97,7 +97,6 @@ class _ChannelInfoFormState extends State { description: description ?? _descriptionController.text, type: type ?? _channelType, participants: participants ?? _participants, - automaticallyAddNew: automaticallyAddNew ?? _automaticallyAddNew, ), ); } @@ -141,7 +140,6 @@ class _ChannelInfoFormState extends State { _channelNameController.clear(); _descriptionController.clear(); _participants = []; - _automaticallyAddNew = true; _channelType = ChannelType.public; _batchUpdateState( @@ -149,7 +147,6 @@ class _ChannelInfoFormState extends State { name: '', description: '', participants: _participants, - automaticallyAddNew: _automaticallyAddNew, type: _channelType, ); FocusScope.of(context).requestFocus(FocusNode()); @@ -200,7 +197,6 @@ class _ChannelInfoFormState extends State { if (state is Updated) { _channelType = state.repository?.type; _participants = state.repository?.members; - _automaticallyAddNew = state.repository.def; } return GestureDetector( @@ -279,13 +275,13 @@ class _ChannelInfoFormState extends State { if (_channelType == ChannelType.private) ParticipantsButton(count: _participants.length), SizedBox(height: 8), - if (_channelType == ChannelType.public) - SwitchField( - title: 'Automatically add new users', - value: _automaticallyAddNew, - onChanged: (value) => - _batchUpdateState(automaticallyAddNew: value), - ), + // if (_channelType == ChannelType.public) + // SwitchField( + // title: 'Automatically add new users', + // value: _automaticallyAddNew, + // onChanged: (value) => + // _batchUpdateState(automaticallyAddNew: value), + // ), if (_channelType == ChannelType.private) SizedBox(), HintLine( text: _channelType != ChannelType.private From 187ca9e74f4c8a8d35ff34475d2535dff61a26e0 Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Fri, 12 Mar 2021 19:14:58 +0300 Subject: [PATCH 14/26] SelectableAvatar icon font size update. --- lib/widgets/common/selectable_avatar.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/widgets/common/selectable_avatar.dart b/lib/widgets/common/selectable_avatar.dart index 0eb5ebb9..7a6ecdc6 100644 --- a/lib/widgets/common/selectable_avatar.dart +++ b/lib/widgets/common/selectable_avatar.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:twake/config/dimensions_config.dart'; class SelectableAvatar extends StatefulWidget { final double size; @@ -50,7 +51,12 @@ class _SelectableAvatarState extends State { width: widget.size, height: widget.size, child: (widget.icon != null && widget.icon.isNotEmpty) - ? Center(child: Text(widget.icon)) + ? Center( + child: Text( + widget.icon, + style: TextStyle(fontSize: Dim.tm3()), + ), + ) : Image.asset("assets/images/pic.png"), // child: _bytes != null // ? SizedBox() From 4bfe408485fe16435b101424caba2401fad902f1 Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Fri, 12 Mar 2021 19:33:44 +0300 Subject: [PATCH 15/26] Avatar behavior fix for EditChannel. --- lib/pages/edit_channel.dart | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/pages/edit_channel.dart b/lib/pages/edit_channel.dart index b59e6d0e..a7e0f3bd 100644 --- a/lib/pages/edit_channel.dart +++ b/lib/pages/edit_channel.dart @@ -310,12 +310,15 @@ class _EditChannelState extends State { onTap: () => _toggleEmojiBoard(), ), SizedBox(height: 4.0), - Text('Change avatar', - style: TextStyle( - color: Color(0xff3840f7), - fontSize: 13.0, - fontWeight: FontWeight.w400, - )), + GestureDetector( + onTap: () => _toggleEmojiBoard(), + child: Text('Change avatar', + style: TextStyle( + color: Color(0xff3840f7), + fontSize: 13.0, + fontWeight: FontWeight.w400, + )), + ), ], ), GestureDetector( From f9cc619c67d6e456c90b0eaae9ee6bb355adae7c Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Mon, 15 Mar 2021 11:08:47 +0300 Subject: [PATCH 16/26] Added appropriate error message when authenticating with incorrect server --- lib/widgets/auth/auth_form.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/widgets/auth/auth_form.dart b/lib/widgets/auth/auth_form.dart index 645118cb..faf82153 100644 --- a/lib/widgets/auth/auth_form.dart +++ b/lib/widgets/auth/auth_form.dart @@ -149,7 +149,7 @@ class _AuthFormState extends State { ), if (state is AuthenticationError) Text( - 'Network Error', + 'Server is unavailable', style: TextStyle(color: Colors.red), ), Expanded( @@ -157,7 +157,7 @@ class _AuthFormState extends State { alignment: Alignment.centerRight, child: BlocBuilder( - builder: (context, state) => FlatButton( + builder: (context, state) => TextButton( onPressed: state is cb.ConnectionLost ? null : () { From 27e5ef0293a6e1091760821f561f9315c2978065 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Mon, 15 Mar 2021 15:53:46 +0300 Subject: [PATCH 17/26] Restored splash screen instead of the black one --- .../res/drawable-v21/launch_background.xml | 6 +- lib/blocs/auth_bloc/auth_bloc.dart | 2 +- lib/main.dart | 109 ++++++++++-------- lib/pages/initial_page.dart | 1 + lib/pages/server_configuration.dart | 4 +- lib/repositories/auth_repository.dart | 2 +- .../configuration_repository.dart | 2 +- 7 files changed, 70 insertions(+), 56 deletions(-) diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml index f74085f3..204c5f48 100644 --- a/android/app/src/main/res/drawable-v21/launch_background.xml +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -4,9 +4,9 @@ - + android:src="@drawable/logo" /> + diff --git a/lib/blocs/auth_bloc/auth_bloc.dart b/lib/blocs/auth_bloc/auth_bloc.dart index 90e15a80..3ec8349b 100644 --- a/lib/blocs/auth_bloc/auth_bloc.dart +++ b/lib/blocs/auth_bloc/auth_bloc.dart @@ -80,7 +80,7 @@ class AuthBloc extends Bloc { print('WEBVIEW LOAD ERROR: $a, $b, $c'); }, onWebViewCreated: (ctrl) { - // print('CREATED WEBVIEW'); + print('CREATED WEBVIEW'); }, ); if (run) runWebView(); diff --git a/lib/main.dart b/lib/main.dart index 9aba5039..16f97058 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:connectivity/connectivity.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lottie/lottie.dart'; import 'package:twake/blocs/auth_bloc/auth_bloc.dart'; import 'package:twake/blocs/configuration_cubit/configuration_cubit.dart'; import 'package:twake/blocs/connection_bloc/connection_bloc.dart' as cb; @@ -18,18 +19,6 @@ void main() async { runZonedGuarded>(() async { WidgetsFlutterBinding.ensureInitialized(); - final AuthRepository authRepository = await initAuth(); - final ConfigurationRepository configurationRepository = - await ConfigurationRepository.load(); - cb.ConnectionState connectionState; - final res = await Connectivity().checkConnectivity(); - if (res == ConnectivityResult.none) { - connectionState = cb.ConnectionLost(''); - } else if (res == ConnectivityResult.wifi) { - connectionState = cb.ConnectionWifi(); - } else if (res == ConnectivityResult.mobile) { - connectionState = cb.ConnectionCellular(); - } await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); FlutterError.onError = (FlutterErrorDetails details) { @@ -42,11 +31,7 @@ void main() async { Zone.current.handleUncaughtError(details.exception, details.stack); } }; - runApp(TwakeMobileApp( - authRepository, - configurationRepository, - connectionState, - )); + runApp(TwakeMobileApp()); }, (Object error, StackTrace stackTrace) { // Whenever an error occurs, call the `reportError` function. This sends // Dart errors to the dev console or Sentry depending on the environment. @@ -55,15 +40,23 @@ void main() async { } class TwakeMobileApp extends StatelessWidget { - final AuthRepository authRepository; - final ConfigurationRepository configurationRepository; - final cb.ConnectionState connectionState; + const TwakeMobileApp(); - TwakeMobileApp( - this.authRepository, - this.configurationRepository, - this.connectionState, - ); + Widget buildSplashScreen() { + return Scaffold( + body: Center( + child: SizedBox( + width: Dim.heightPercent(13), + height: Dim.heightPercent(13), + child: Lottie.asset( + 'assets/animations/splash.json', + animate: true, + repeat: true, + ), + ), + ), + ); + } @override Widget build(BuildContext context) { @@ -72,29 +65,49 @@ class TwakeMobileApp extends StatelessWidget { builder: (context, orientation) { Dim.init(constraints, orientation); return MaterialApp( - debugShowCheckedModeBanner: false, - theme: StylesConfig.lightTheme, - title: 'Twake', - home: MultiBlocProvider( - providers: [ - BlocProvider( - create: (_) => cb.ConnectionBloc(connectionState), - lazy: false, - ), - BlocProvider( - create: (context) => AuthBloc( - authRepository, context.read()), - lazy: false, - ), - BlocProvider( - create: (context) => - ConfigurationCubit(configurationRepository), - lazy: false, - ) - ], - child: InitialPage(), - ), - ); + debugShowCheckedModeBanner: false, + theme: StylesConfig.lightTheme, + title: 'Twake', + home: FutureBuilder(future: () async { + final AuthRepository authRepository = await initAuth(); + final ConfigurationRepository configurationRepository = + await ConfigurationRepository.load(); + cb.ConnectionState connectionState; + final res = await Connectivity().checkConnectivity(); + if (res == ConnectivityResult.none) { + connectionState = cb.ConnectionLost(''); + } else if (res == ConnectivityResult.wifi) { + connectionState = cb.ConnectionWifi(); + } else if (res == ConnectivityResult.mobile) { + connectionState = cb.ConnectionCellular(); + } + final multiProvider = MultiBlocProvider( + providers: [ + BlocProvider( + create: (_) => cb.ConnectionBloc(connectionState), + lazy: false, + ), + BlocProvider( + create: (context) => AuthBloc( + authRepository, context.read()), + lazy: false, + ), + BlocProvider( + create: (context) => + ConfigurationCubit(configurationRepository), + lazy: false, + ) + ], + child: InitialPage(), + ); + return multiProvider; + }(), builder: (ctx, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return snapshot.data; + } else { + return buildSplashScreen(); + } + })); }, ), ); diff --git a/lib/pages/initial_page.dart b/lib/pages/initial_page.dart index 9dba06b2..3c0440c2 100644 --- a/lib/pages/initial_page.dart +++ b/lib/pages/initial_page.dart @@ -82,6 +82,7 @@ class _InitialPageState extends State with WidgetsBindingObserver { current is! HostValidated && current is! HostInvalid, builder: (ctx, state) { + print("BUILDING INITIAL PAGE"); if (state is AuthInitializing) { return buildSplashScreen(); } diff --git a/lib/pages/server_configuration.dart b/lib/pages/server_configuration.dart index 8911bb50..65fa2b33 100644 --- a/lib/pages/server_configuration.dart +++ b/lib/pages/server_configuration.dart @@ -82,12 +82,12 @@ class _ServerConfigurationState extends State { current is ConfigurationLoaded || current is ConfigurationSaving, builder: (context, state) { - print('Current state: $state'); + // print('Current state: $state'); if (_controller.text.isReallyEmpty) { _controller.text = state.host; } - print('Server host: ${state.host}'); + // print('Server host: ${state.host}'); return Column( crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/repositories/auth_repository.dart b/lib/repositories/auth_repository.dart index fc3bd5ed..f613d07f 100644 --- a/lib/repositories/auth_repository.dart +++ b/lib/repositories/auth_repository.dart @@ -65,7 +65,7 @@ class AuthRepository extends JsonSerializable { if (authMap != null) authMap = jsonDecode(authMap[_storage.settingsField]); final configurationRepository = await ConfigurationRepository.load(); - print('Actual host for auth: ${configurationRepository.host}'); + // print('Actual host for auth: ${configurationRepository.host}'); Api.host = configurationRepository.host; final fcmToken = (await FirebaseMessaging().getToken()); diff --git a/lib/repositories/configuration_repository.dart b/lib/repositories/configuration_repository.dart index 217600e1..0cb59e7e 100644 --- a/lib/repositories/configuration_repository.dart +++ b/lib/repositories/configuration_repository.dart @@ -34,7 +34,7 @@ class ConfigurationRepository extends JsonSerializable { key: CONFIG_KEY, ); - Logger().d('Configuration map: $configurationMap'); + // Logger().d('Configuration map: $configurationMap'); if (configurationMap != null) { final configurationRepository = ConfigurationRepository.fromJson( From a9652da362142048a753a197605c45e4f11f594a Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Mon, 15 Mar 2021 16:48:55 +0300 Subject: [PATCH 18/26] main dart rollback --- lib/main.dart | 109 ++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 61 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 16f97058..9aba5039 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,6 @@ import 'package:connectivity/connectivity.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:lottie/lottie.dart'; import 'package:twake/blocs/auth_bloc/auth_bloc.dart'; import 'package:twake/blocs/configuration_cubit/configuration_cubit.dart'; import 'package:twake/blocs/connection_bloc/connection_bloc.dart' as cb; @@ -19,6 +18,18 @@ void main() async { runZonedGuarded>(() async { WidgetsFlutterBinding.ensureInitialized(); + final AuthRepository authRepository = await initAuth(); + final ConfigurationRepository configurationRepository = + await ConfigurationRepository.load(); + cb.ConnectionState connectionState; + final res = await Connectivity().checkConnectivity(); + if (res == ConnectivityResult.none) { + connectionState = cb.ConnectionLost(''); + } else if (res == ConnectivityResult.wifi) { + connectionState = cb.ConnectionWifi(); + } else if (res == ConnectivityResult.mobile) { + connectionState = cb.ConnectionCellular(); + } await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); FlutterError.onError = (FlutterErrorDetails details) { @@ -31,7 +42,11 @@ void main() async { Zone.current.handleUncaughtError(details.exception, details.stack); } }; - runApp(TwakeMobileApp()); + runApp(TwakeMobileApp( + authRepository, + configurationRepository, + connectionState, + )); }, (Object error, StackTrace stackTrace) { // Whenever an error occurs, call the `reportError` function. This sends // Dart errors to the dev console or Sentry depending on the environment. @@ -40,23 +55,15 @@ void main() async { } class TwakeMobileApp extends StatelessWidget { - const TwakeMobileApp(); + final AuthRepository authRepository; + final ConfigurationRepository configurationRepository; + final cb.ConnectionState connectionState; - Widget buildSplashScreen() { - return Scaffold( - body: Center( - child: SizedBox( - width: Dim.heightPercent(13), - height: Dim.heightPercent(13), - child: Lottie.asset( - 'assets/animations/splash.json', - animate: true, - repeat: true, - ), - ), - ), - ); - } + TwakeMobileApp( + this.authRepository, + this.configurationRepository, + this.connectionState, + ); @override Widget build(BuildContext context) { @@ -65,49 +72,29 @@ class TwakeMobileApp extends StatelessWidget { builder: (context, orientation) { Dim.init(constraints, orientation); return MaterialApp( - debugShowCheckedModeBanner: false, - theme: StylesConfig.lightTheme, - title: 'Twake', - home: FutureBuilder(future: () async { - final AuthRepository authRepository = await initAuth(); - final ConfigurationRepository configurationRepository = - await ConfigurationRepository.load(); - cb.ConnectionState connectionState; - final res = await Connectivity().checkConnectivity(); - if (res == ConnectivityResult.none) { - connectionState = cb.ConnectionLost(''); - } else if (res == ConnectivityResult.wifi) { - connectionState = cb.ConnectionWifi(); - } else if (res == ConnectivityResult.mobile) { - connectionState = cb.ConnectionCellular(); - } - final multiProvider = MultiBlocProvider( - providers: [ - BlocProvider( - create: (_) => cb.ConnectionBloc(connectionState), - lazy: false, - ), - BlocProvider( - create: (context) => AuthBloc( - authRepository, context.read()), - lazy: false, - ), - BlocProvider( - create: (context) => - ConfigurationCubit(configurationRepository), - lazy: false, - ) - ], - child: InitialPage(), - ); - return multiProvider; - }(), builder: (ctx, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - return snapshot.data; - } else { - return buildSplashScreen(); - } - })); + debugShowCheckedModeBanner: false, + theme: StylesConfig.lightTheme, + title: 'Twake', + home: MultiBlocProvider( + providers: [ + BlocProvider( + create: (_) => cb.ConnectionBloc(connectionState), + lazy: false, + ), + BlocProvider( + create: (context) => AuthBloc( + authRepository, context.read()), + lazy: false, + ), + BlocProvider( + create: (context) => + ConfigurationCubit(configurationRepository), + lazy: false, + ) + ], + child: InitialPage(), + ), + ); }, ), ); From 37658c7dfaf15cbdb083adf8bee1bbdf814cd6cf Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Mon, 15 Mar 2021 19:49:31 +0300 Subject: [PATCH 19/26] Added unreaction fix + message update sync --- lib/blocs/messages_bloc/messages_bloc.dart | 3 ++- lib/models/message.dart | 2 +- lib/pages/initial_page.dart | 2 +- lib/repositories/messages_repository.dart | 7 +++++-- lib/widgets/message/messages_grouped_list.dart | 3 ++- lib/widgets/thread/thread_messages_list.dart | 7 +++++-- 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/blocs/messages_bloc/messages_bloc.dart b/lib/blocs/messages_bloc/messages_bloc.dart index bf95c5ad..f22431ba 100644 --- a/lib/blocs/messages_bloc/messages_bloc.dart +++ b/lib/blocs/messages_bloc/messages_bloc.dart @@ -197,16 +197,17 @@ class MessagesBloc _makeQueryParams(event), addToItems: event.channelId == selectedChannel.id, ); - _sortItems(); if (updateParent) { _updateParentChannel(event.channelId); } + _sortItems(); final newState = MessagesLoaded( messages: repository.items, messageCount: repository.itemsCount, force: DateTime.now().toString(), parentChannel: selectedChannel, ); + // repository.logger.w("OLD STATE == NEW STATE: ${newState == this.state}"); yield newState; } else if (event is ModifyResponsesCount) { var thread = await repository.updateResponsesCount(event.threadId); diff --git a/lib/models/message.dart b/lib/models/message.dart index a0e27e90..5d342eff 100644 --- a/lib/models/message.dart +++ b/lib/models/message.dart @@ -102,7 +102,7 @@ class Message extends CollectionItem { if (users.isEmpty) reactions.remove(r.key); if (emojiCode == r.key) { emojiCode = ''; - body['reactions'] = ''; + body['reaction'] = ''; } break; } diff --git a/lib/pages/initial_page.dart b/lib/pages/initial_page.dart index 3c0440c2..514d40fc 100644 --- a/lib/pages/initial_page.dart +++ b/lib/pages/initial_page.dart @@ -82,7 +82,7 @@ class _InitialPageState extends State with WidgetsBindingObserver { current is! HostValidated && current is! HostInvalid, builder: (ctx, state) { - print("BUILDING INITIAL PAGE"); + // print("BUILDING INITIAL PAGE"); if (state is AuthInitializing) { return buildSplashScreen(); } diff --git a/lib/repositories/messages_repository.dart b/lib/repositories/messages_repository.dart index f2502748..db29b247 100644 --- a/lib/repositories/messages_repository.dart +++ b/lib/repositories/messages_repository.dart @@ -198,7 +198,7 @@ class MessagesRepository { logger.e("MESSAGE EXISTS"); isNew = false; } - saveOne(item); + await saveOne(item); if (addToItems) { final query = 'SELECT message.*, ' 'user.username, ' @@ -218,7 +218,10 @@ class MessagesRepository { if (itemMapTemp.isNotEmpty) { itemMap = itemMapTemp[0]; } - if (itemMap == null) return false; + if (itemMap == null) { + logger.wtf("MESSAGE NOT FOUND"); + return false; + } final message = Message.fromJson(itemMap); final old = diff --git a/lib/widgets/message/messages_grouped_list.dart b/lib/widgets/message/messages_grouped_list.dart index 534f4ea9..145ab4fb 100644 --- a/lib/widgets/message/messages_grouped_list.dart +++ b/lib/widgets/message/messages_grouped_list.dart @@ -117,7 +117,8 @@ class MessagesGroupedList extends StatelessWidget { key: ValueKey( message.id + message.responsesCount.toString() + - message.reactions.keys.join(), + message.reactions.keys.join() + + message.content.originalStr, ), ); }), diff --git a/lib/widgets/thread/thread_messages_list.dart b/lib/widgets/thread/thread_messages_list.dart index 05297792..24b3e168 100644 --- a/lib/widgets/thread/thread_messages_list.dart +++ b/lib/widgets/thread/thread_messages_list.dart @@ -34,7 +34,8 @@ class _ThreadMessagesListState hideShowAnswers: true, key: ValueKey( state.threadMessage.id + - state.threadMessage.responsesCount.toString(), + state.threadMessage.responsesCount.toString() + + state.threadMessage.content.originalStr, ), ), ), @@ -142,7 +143,9 @@ class _ThreadMessagesListState return MessageTile( message: _messages[i], key: ValueKey( - _messages[i].id + _messages[i].reactions.keys.join(), + _messages[i].id + + _messages[i].reactions.keys.join() + + _messages[i].content.originalStr, ), ); } From 43fab0416474ce8c05d6c316f72c13ccb6b2aca4 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Tue, 16 Mar 2021 12:21:37 +0300 Subject: [PATCH 20/26] Compressed all the migrations + added last message field to channels --- lib/models/base_channel.dart | 5 ++- lib/models/channel.dart | 8 +++- lib/models/channel.g.dart | 2 + lib/models/direct.dart | 4 ++ lib/models/direct.g.dart | 2 + lib/services/storage/sqlite.dart | 48 +++++++--------------- lib/sql/client.js | 70 -------------------------------- lib/sql/migrations.dart | 6 --- lib/sql/v1.dart | 42 ++++++++++++++++++- lib/sql/v2.dart | 17 -------- lib/sql/v3.dart | 19 --------- lib/sql/v4.dart | 14 ------- lib/sql/v5.dart | 25 ------------ lib/sql/v6.dart | 24 ----------- lib/sql/v7.dart | 17 -------- 15 files changed, 73 insertions(+), 230 deletions(-) delete mode 100755 lib/sql/client.js delete mode 100644 lib/sql/v2.dart delete mode 100644 lib/sql/v3.dart delete mode 100644 lib/sql/v4.dart delete mode 100644 lib/sql/v5.dart delete mode 100644 lib/sql/v6.dart delete mode 100644 lib/sql/v7.dart diff --git a/lib/models/base_channel.dart b/lib/models/base_channel.dart index 2210cb08..f81e2582 100644 --- a/lib/models/base_channel.dart +++ b/lib/models/base_channel.dart @@ -24,11 +24,12 @@ abstract class BaseChannel extends CollectionItem { @JsonKey(name: 'last_activity', defaultValue: 0) int lastActivity; + @JsonKey(name: 'last_message') + Map lastMessage; + @JsonKey(name: 'user_last_access', defaultValue: 0) int lastAccess; - // @JsonKey(required: true, name: 'messages_total', defaultValue: 0) - // int messagesTotal; @JsonKey( name: 'has_unread', // defaultValue: 0, diff --git a/lib/models/channel.dart b/lib/models/channel.dart index 93532b25..14efecdc 100644 --- a/lib/models/channel.dart +++ b/lib/models/channel.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'package:json_annotation/json_annotation.dart'; import 'package:twake/models/base_channel.dart'; @@ -16,10 +17,15 @@ class Channel extends BaseChannel { }); factory Channel.fromJson(Map json) { + if (json['last_message'] is String) { + json['last_message'] = jsonDecode(json['last_message']); + } return _$ChannelFromJson(json); } Map toJson() { - return _$ChannelToJson(this); + var map = _$ChannelToJson(this); + map['last_message'] = jsonEncode(map['last_message']); + return map; } } diff --git a/lib/models/channel.g.dart b/lib/models/channel.g.dart index b1a0bf4c..a3aed72f 100644 --- a/lib/models/channel.g.dart +++ b/lib/models/channel.g.dart @@ -18,6 +18,7 @@ Channel _$ChannelFromJson(Map json) { ..description = json['description'] as String ..membersCount = json['members_count'] as int ?? 0 ..lastActivity = json['last_activity'] as int ?? 0 + ..lastMessage = json['last_message'] as Map ..lastAccess = json['user_last_access'] as int ?? 0 ..hasUnread = boolToInt(json['has_unread']) ..messagesUnread = json['messages_unread'] as int ?? 0 @@ -31,6 +32,7 @@ Map _$ChannelToJson(Channel instance) => { 'description': instance.description, 'members_count': instance.membersCount, 'last_activity': instance.lastActivity, + 'last_message': instance.lastMessage, 'user_last_access': instance.lastAccess, 'has_unread': boolToInt(instance.hasUnread), 'messages_unread': instance.messagesUnread, diff --git a/lib/models/direct.dart b/lib/models/direct.dart index 09d58c30..34b43530 100644 --- a/lib/models/direct.dart +++ b/lib/models/direct.dart @@ -19,12 +19,16 @@ class Direct extends BaseChannel { if (json['members'] is String) { json['members'] = jsonDecode(json['members']); } + if (json['last_message'] is String) { + json['last_message'] = jsonDecode(json['last_message']); + } return _$DirectFromJson(json); } Map toJson() { var map = _$DirectToJson(this); map['members'] = jsonEncode(map['members']); + map['last_message'] = jsonEncode(map['last_message']); return map; } } diff --git a/lib/models/direct.g.dart b/lib/models/direct.g.dart index 1fa85429..d23d83e3 100644 --- a/lib/models/direct.g.dart +++ b/lib/models/direct.g.dart @@ -18,6 +18,7 @@ Direct _$DirectFromJson(Map json) { ..description = json['description'] as String ..membersCount = json['members_count'] as int ?? 0 ..lastActivity = json['last_activity'] as int ?? 0 + ..lastMessage = json['last_message'] as Map ..lastAccess = json['user_last_access'] as int ?? 0 ..hasUnread = boolToInt(json['has_unread']) ..messagesUnread = json['messages_unread'] as int ?? 0 @@ -31,6 +32,7 @@ Map _$DirectToJson(Direct instance) => { 'description': instance.description, 'members_count': instance.membersCount, 'last_activity': instance.lastActivity, + 'last_message': instance.lastMessage, 'user_last_access': instance.lastAccess, 'has_unread': boolToInt(instance.hasUnread), 'messages_unread': instance.messagesUnread, diff --git a/lib/services/storage/sqlite.dart b/lib/services/storage/sqlite.dart index e2706c1f..477ba004 100644 --- a/lib/services/storage/sqlite.dart +++ b/lib/services/storage/sqlite.dart @@ -9,7 +9,7 @@ import 'package:twake/services/storage/storage.dart'; import 'package:twake/sql/migrations.dart'; const String _DATABASE_FILE = 'twakesql.db'; -const int _CURRENT_MIGRATION = 7; +const int _CURRENT_MIGRATION = 1; class SQLite with Storage { static Database _db; @@ -39,7 +39,7 @@ class SQLite with Storage { await db.execute("PRAGMA foreign_keys = ON"); }, onCreate: (Database db, int version) async { - for (var ddl in DDL_V7) { + for (var ddl in DDL_V1) { await db.execute(ddl); } }, @@ -48,38 +48,20 @@ class SQLite with Storage { logger.d('Opened twake db v.$v'); }, onUpgrade: (db, oldVersion, newVersion) async { - var ddl = List.from(DDL_V7); + // var ddl = List.from(DDL_V1); // logger.d('SQFlite onUpdate called with new version: $newVersion'); - logger.d('Migration to twake db v.$newVersion from v.$oldVersion'); - if (oldVersion == 1) { - ddl.removeWhere((el) => DDL_V1.contains(el)); - for (var migrationDdl in ddl) { - await db.execute(migrationDdl); - } - } else if (oldVersion == 2) { - ddl.removeWhere((el) => DDL_V2.contains(el)); - for (var migrationDdl in ddl) { - await db.execute(migrationDdl); - } - } else if (oldVersion == 3) { - ddl.removeWhere((el) => DDL_V3.contains(el)); - for (var migrationDdl in ddl) { - await db.execute(migrationDdl); - } - } else if (oldVersion == 4) { - ddl.removeWhere((el) => DDL_V4.contains(el)); - for (var migrationDdl in ddl) { - await db.execute(migrationDdl); - } - } else if (oldVersion == 5) { - for (var migrationDdl in MIGRATION_6) { - await db.execute(migrationDdl); - } - } else if (oldVersion == 6) { - for (var migrationDdl in MIGRATION_7) { - await db.execute(migrationDdl); - } - } + // logger.d('Migration to twake db v.$newVersion from v.$oldVersion'); + // if (oldVersion == 1) { + // ddl.removeWhere((el) => DDL_V1.contains(el)); + // for (var migrationDdl in ddl) { + // await db.execute(migrationDdl); + // } + // } else if (oldVersion == 2) { + // ddl.removeWhere((el) => DDL_V2.contains(el)); + // for (var migrationDdl in ddl) { + // await db.execute(migrationDdl); + // } + // } }, ); } diff --git a/lib/sql/client.js b/lib/sql/client.js deleted file mode 100755 index f21d28bd..00000000 --- a/lib/sql/client.js +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env node - -// var child_process = require('child_process'); -// child_process.execSync('npm i socket.io-client@2.3.1',{stdio:[0,1]}); - - -const WebsocketActions = { Join : 'realtime:join', Leave : 'realtime:leave' } - -const WebsocketEvents = { - Connected : 'connected', - Disconnected : 'disconnected', - JoinSuccess : 'realtime:join:success', - JoinError : 'realtime:join:error', - Resource : 'realtime:resource', - Event : 'realtime:event' - } - -const io = require("socket.io-client"); - -const socketEndpoint = 'https://web.qa.twake.app' - -const socket = io.connect(socketEndpoint, { path: '/socket',reconnectionDelayMax: 10000,}); - -socket.on('connect', function() { - console.log('socket.io conntected') - socket.emit('authenticate', {"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MTI5NjMyNDMsInR5cGUiOiJhY2Nlc3MiLCJpYXQiOjE2MTI5NTkwNDMsIm5iZiI6MTYxMjk1OTA0Mywic3ViIjoiNjBiYzFlYzgtNmFlNS0xMWViLWI5MzUtMDI0MmFjMTIwMDA2Iiwib3JnIjp7IjYwYzlkMGNjLTZhZTUtMTFlYi04YTAwLTAyNDJhYzEyMDAwNiI6eyJyb2xlIjoib3JnYW5pemF0aW9uX2FkbWluaXN0cmF0b3IiLCJ3a3MiOnsiNjBjZGYwM2EtNmFlNS0xMWViLWI4YjEtMDI0MmFjMTIwMDA2Ijp7ImFkbSI6dHJ1ZX19fX19.0YoIBYKdzDSCa-cpNdCaEByh90DRoOP6YyfOyA2YCno"}) -}); - -socket.on('disconnect', function(){ - console.log('socket.io is disconnected conntected') -}); - -const leave = function(path,tag = 'twake'){ - socket.emit(WebsocketActions.Leave, {"name": "previous:" + path,"token":tag}) -} - -const join = function(path,tag = 'twake'){ - socket.emit(WebsocketActions.Join, {"name": "previous:" + path,"token":tag}) -} - -socket.on('authenticated', () => { - console.log('Authenticated'); - - const websocketId = '60cdf03a-6ae5-11eb-b8b1-0242ac120006' - const userId = '60bc1ec8-6ae5-11eb-b935-0242ac120006' - const workspaceId = '' - const groupId = '' // это companyId - - join(`collections/${websocketId}`) - join(`users/${userId}`) // listen user - join(`group/${groupId}`) - join(`workspace/${workspaceId}`) - join(`workspaces_of_user/${workspaceId}`) // workspaces list - join(`workspace_apps/${workspaceId}`) - join(`workspace_users/${workspaceId}`) - - -}) - -socket.on(WebsocketEvents.JoinSuccess, (data)=>{ - console.log('joinSuccess', data) -}) - - -socket.on(WebsocketEvents.Event, (data)=>{ - console.log('realtime event', data) -}) - - - diff --git a/lib/sql/migrations.dart b/lib/sql/migrations.dart index 9902720b..d4c4a4cf 100644 --- a/lib/sql/migrations.dart +++ b/lib/sql/migrations.dart @@ -1,7 +1 @@ export 'v1.dart' show DDL_V1; -export 'v2.dart' show DDL_V2, MIGRATION_2; -export 'v3.dart' show DDL_V3, MIGRATION_3; -export 'v4.dart' show DDL_V4, MIGRATION_4; -export 'v5.dart' show DDL_V5, MIGRATION_5; -export 'v6.dart' show DDL_V6, MIGRATION_6; -export 'v7.dart' show DDL_V7, MIGRATION_7; \ No newline at end of file diff --git a/lib/sql/v1.dart b/lib/sql/v1.dart index 16fcc10b..115be123 100644 --- a/lib/sql/v1.dart +++ b/lib/sql/v1.dart @@ -38,8 +38,11 @@ CREATE TABLE channel ( icon TEXT NOT NULL, description TEXT, last_activity INT, + user_last_access INT DEFAULT 0, + has_unread INT DEFAULT 0, + visibility TEXT DEFAULT "public", members_count INT DEFAULT 0, - messages_total INT DEFAULT 0, + last_message TEXT, messages_unread INT DEFAULT 0, is_selected INT DEFAULT 0, FOREIGN KEY(workspace_id) REFERENCES workspace(id) @@ -56,10 +59,12 @@ CREATE TABLE direct ( icon TEXT, description TEXT, last_activity INT, + user_last_access INT DEFAULT 0, members_count INT DEFAULT 0, - messages_total INT DEFAULT 0, messages_unread INT DEFAULT 0, + last_message TEXT, is_selected INT DEFAULT 0, + has_unread INT DEFAULT 0, FOREIGN KEY(company_id) REFERENCES company(id) ); CREATE INDEX direct_company_idx ON direct(company_id); @@ -92,6 +97,36 @@ CREATE TABLE user ( ) '''; +const String CREATE_DRAFT_V1 = ''' +CREATE TABLE draft ( + id TEXT PRIMARY KEY, + value TEXT NOT NULL +) +'''; + +const String CREATE_MEMBER_V1 = ''' +CREATE TABLE member ( + id TEXT PRIMARY KEY, + type TEXT DEFAULT "member", + notification_level TEXT, + company_id TEXT NOT NULL, + workspace_id TEXT NOT NULL, + channel_id TEXT NOT NULL, + user_id TEXT NOT NULL, + favorite INT DEFAULT 0, + is_selected INT DEFAULT 0, + email TEXT +); +CREATE INDEX member_user_idx ON user(user_id); +'''; + +const String CREATE_CONFIGURATION_V1 = ''' +CREATE TABLE configuration ( + id TEXT PRIMARY KEY, + value TEXT NOT NULL +) +'''; + const DDL_V1 = [ CREATE_SETTINGS_V1, CREATE_COMPANY_V1, @@ -100,4 +135,7 @@ const DDL_V1 = [ CREATE_DIRECT_V1, CREATE_MESSAGE_V1, CREATE_USER_V1, + CREATE_DRAFT_V1, + CREATE_MEMBER_V1, + CREATE_CONFIGURATION_V1, ]; diff --git a/lib/sql/v2.dart b/lib/sql/v2.dart deleted file mode 100644 index 7e56d56a..00000000 --- a/lib/sql/v2.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:twake/sql/v1.dart'; - -const String CREATE_DRAFT_V2 = ''' -CREATE TABLE draft ( - id TEXT PRIMARY KEY, - value TEXT NOT NULL -) -'''; - -const DDL_V2 = [ - ...DDL_V1, - ...MIGRATION_2, -]; - -const MIGRATION_2 = [ - CREATE_DRAFT_V2, -]; diff --git a/lib/sql/v3.dart b/lib/sql/v3.dart deleted file mode 100644 index 36d1394a..00000000 --- a/lib/sql/v3.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'migrations.dart'; - -const String ALTER_CHANNEL_ADD_HAS_UNREAD = ''' -ALTER TABLE channel ADD COLUMN has_unread INT DEFAULT 0; -'''; - -const String ALTER_DIRECT_ADD_HAS_UNREAD = ''' -ALTER TABLE direct ADD COLUMN has_unread INT DEFAULT 0; -'''; - -const DDL_V3 = [ - ...DDL_V2, - ...MIGRATION_3, -]; - -const MIGRATION_3 = [ - ALTER_DIRECT_ADD_HAS_UNREAD, - ALTER_CHANNEL_ADD_HAS_UNREAD, -]; diff --git a/lib/sql/v4.dart b/lib/sql/v4.dart deleted file mode 100644 index 502887f5..00000000 --- a/lib/sql/v4.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'migrations.dart'; - -const String ALTER_CHANNEL_ADD_VISIBILITY = ''' -ALTER TABLE channel ADD COLUMN visibility TEXT DEFAULT public; -'''; - -const DDL_V4 = [ - ...DDL_V3, - ...MIGRATION_4, -]; - -const MIGRATION_4 = [ - ALTER_CHANNEL_ADD_VISIBILITY, -]; diff --git a/lib/sql/v5.dart b/lib/sql/v5.dart deleted file mode 100644 index ab51b510..00000000 --- a/lib/sql/v5.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'migrations.dart'; - -const String CREATE_MEMBER_V5 = ''' -CREATE TABLE member ( - id TEXT PRIMARY KEY, - type TEXT DEFAULT member, - notification_level TEXT, - company_id TEXT NOT NULL, - workspace_id TEXT NOT NULL, - channel_id TEXT NOT NULL, - user_id TEXT NOT NULL, - favorite INT DEFAULT 0, - is_selected INT DEFAULT 0 -); -CREATE INDEX member_user_idx ON user(user_id); -'''; - -const DDL_V5 = [ - ...DDL_V4, - ...MIGRATION_5, -]; - -const MIGRATION_5 = [ - CREATE_MEMBER_V5, -]; \ No newline at end of file diff --git a/lib/sql/v6.dart b/lib/sql/v6.dart deleted file mode 100644 index 66062971..00000000 --- a/lib/sql/v6.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'migrations.dart'; - -const String ALTER_MEMBER_ADD_EMAIL = ''' -ALTER TABLE member ADD COLUMN email TEXT; -'''; - -const String ALTER_CHANNEL_ADD_LAST_ACCESS = ''' -ALTER TABLE channel ADD COLUMN user_last_access INT DEFAULT 0; -'''; - -const String ALTER_DIRECT_ADD_LAST_ACCESS = ''' -ALTER TABLE direct ADD COLUMN user_last_access INT DEFAULT 0; -'''; - -const DDL_V6 = [ - ...DDL_V5, - ...MIGRATION_6, -]; - -const MIGRATION_6 = [ - ALTER_MEMBER_ADD_EMAIL, - ALTER_CHANNEL_ADD_LAST_ACCESS, - ALTER_DIRECT_ADD_LAST_ACCESS, -]; diff --git a/lib/sql/v7.dart b/lib/sql/v7.dart deleted file mode 100644 index dce1739b..00000000 --- a/lib/sql/v7.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'migrations.dart'; - -const String CREATE_CONFIGURATION_V7 = ''' -CREATE TABLE configuration ( - id TEXT PRIMARY KEY, - value TEXT NOT NULL -) -'''; - -const DDL_V7 = [ - ...DDL_V6, - ...MIGRATION_7, -]; - -const MIGRATION_7 = [ - CREATE_CONFIGURATION_V7, -]; \ No newline at end of file From a540d2e8cafe610d74d4546e38f2bb2b0a058d88 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Tue, 16 Mar 2021 12:53:02 +0300 Subject: [PATCH 21/26] Fixed read only channel records from local db --- lib/repositories/collection_repository.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/repositories/collection_repository.dart b/lib/repositories/collection_repository.dart index 2ad34788..245efe3b 100644 --- a/lib/repositories/collection_repository.dart +++ b/lib/repositories/collection_repository.dart @@ -16,7 +16,10 @@ class CollectionRepository { _typeToConstructor = { Company: (Map json) => Company.fromJson(json), Workspace: (Map json) => Workspace.fromJson(json), - Channel: (Map json) => Channel.fromJson(json), + Channel: (Map json) { + json = Map.from(json); + return Channel.fromJson(json); + }, Message: (Map json) { json = Map.from(json); return Message.fromJson(json); From deb9b9641bc1afb5d1b2a70f1df6452e8827de78 Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Tue, 16 Mar 2021 14:50:48 +0300 Subject: [PATCH 22/26] #337 fixed. --- lib/pages/edit_channel.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/edit_channel.dart b/lib/pages/edit_channel.dart index a7e0f3bd..2eef9a09 100644 --- a/lib/pages/edit_channel.dart +++ b/lib/pages/edit_channel.dart @@ -220,7 +220,7 @@ class _EditChannelState extends State { Widget build(BuildContext context) { return Scaffold( backgroundColor: Color(0xffefeef3), - resizeToAvoidBottomInset: true, + resizeToAvoidBottomInset: false, body: SlidingUpPanel( controller: _panelController, onPanelOpened: () => context.read().add(SetOpened()), From 851873c1b66f73178e02cb50cdbf18dc73640b24 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Tue, 16 Mar 2021 14:54:18 +0300 Subject: [PATCH 23/26] Added last message to channel tile --- lib/blocs/channels_bloc/channels_bloc.dart | 11 ++++----- lib/blocs/directs_bloc/directs_bloc.dart | 1 - .../notification_bloc/notification_bloc.dart | 7 +++--- lib/models/notification.dart | 3 +++ lib/models/notification.g.dart | 2 ++ lib/widgets/channel/channel_tile.dart | 24 +++++++++---------- 6 files changed, 25 insertions(+), 23 deletions(-) diff --git a/lib/blocs/channels_bloc/channels_bloc.dart b/lib/blocs/channels_bloc/channels_bloc.dart index 879d6723..2d1aa477 100644 --- a/lib/blocs/channels_bloc/channels_bloc.dart +++ b/lib/blocs/channels_bloc/channels_bloc.dart @@ -141,11 +141,11 @@ class ChannelsBloc extends BaseChannelBloc { repository.logger.d('UPDATING CHANNELS\n${event.data.toJson()}'); var item = await repository.getItemById(event.data.channelId) as Channel; if (item != null) { - item.icon = event.data.icon ?? '👽'; - item.name = event.data.name; - item.description = event.data.description; - item.visibility = event.data.visibility; - item.visibility = event.data.visibility; + item.icon = event.data.icon ?? item.icon ?? '👽'; + item.name = event.data.name ?? item.name; + item.description = event.data.description ?? item.description; + item.visibility = event.data.visibility ?? item.visibility; + item.lastMessage = event.data.lastMessage ?? item.lastMessage; } else { item = Channel.fromJson(event.data.toJson()); } @@ -160,7 +160,6 @@ class ChannelsBloc extends BaseChannelBloc { force: DateTime.now().toString(), ); } else if (event is LoadSingleChannel) { - // TODO implement single company loading throw 'Not implemented yet'; } else if (event is RemoveChannel) { repository.items.removeWhere((i) => i.id == event.channelId); diff --git a/lib/blocs/directs_bloc/directs_bloc.dart b/lib/blocs/directs_bloc/directs_bloc.dart index 13aa2e22..3ed085d6 100644 --- a/lib/blocs/directs_bloc/directs_bloc.dart +++ b/lib/blocs/directs_bloc/directs_bloc.dart @@ -119,7 +119,6 @@ class DirectsBloc extends BaseChannelBloc { selected: repository.selected, ); } else if (event is LoadSingleChannel) { - // TODO implement single channel loading throw 'Not implemented yet'; } else if (event is RemoveChannel) { throw 'Not implemented yet'; diff --git a/lib/blocs/notification_bloc/notification_bloc.dart b/lib/blocs/notification_bloc/notification_bloc.dart index e37715e8..a355bc0a 100644 --- a/lib/blocs/notification_bloc/notification_bloc.dart +++ b/lib/blocs/notification_bloc/notification_bloc.dart @@ -129,7 +129,7 @@ class NotificationBloc extends Bloc { handleSocketEvent(data); }); socket.on(SocketIOEvent.RESOURCE, (data) { - // logger.d('GOT RESOURCE: $data'); + logger.d('GOT RESOURCE: $data'); handleSocketResource(data); }); socket.on(SocketIOEvent.JOIN_ERROR, (data) { @@ -361,8 +361,9 @@ class NotificationBloc extends Bloc { return SocketResourceType.Unknown; final type = subscriptionRooms[resource['room']]['type']; if (type == 'CHANNELS_LIST') { - if (resource['type'] == 'channel') { - if (resource['action'] == 'saved') { + if (resource['type'] == 'channel' || + resource['type'] == 'channel_activity') { + if (resource['action'] == 'saved' || resource['action'] == 'updated') { return SocketResourceType.ChannelUpdate; } else if (resource['action'] == 'deleted') return SocketResourceType.ChannelDelete; diff --git a/lib/models/notification.dart b/lib/models/notification.dart index 2dbfa044..90d5affc 100644 --- a/lib/models/notification.dart +++ b/lib/models/notification.dart @@ -45,6 +45,8 @@ class SocketChannelUpdateNotification extends NotificationData { final String name; final String description; final String visibility; + @JsonKey(name: 'last_message') + final Map lastMessage; SocketChannelUpdateNotification({ this.channelId, @@ -54,6 +56,7 @@ class SocketChannelUpdateNotification extends NotificationData { this.description, this.icon, this.visibility, + this.lastMessage, }); factory SocketChannelUpdateNotification.fromJson(Map json) => diff --git a/lib/models/notification.g.dart b/lib/models/notification.g.dart index fcbe388e..072be27e 100644 --- a/lib/models/notification.g.dart +++ b/lib/models/notification.g.dart @@ -37,6 +37,7 @@ SocketChannelUpdateNotification _$SocketChannelUpdateNotificationFromJson( description: json['description'] as String, icon: json['icon'] as String, visibility: json['visibility'] as String, + lastMessage: json['last_message'] as Map, ); } @@ -50,6 +51,7 @@ Map _$SocketChannelUpdateNotificationToJson( 'name': instance.name, 'description': instance.description, 'visibility': instance.visibility, + 'last_message': instance.lastMessage, }; SocketMessageUpdateNotification _$SocketMessageUpdateNotificationFromJson( diff --git a/lib/widgets/channel/channel_tile.dart b/lib/widgets/channel/channel_tile.dart index 231ca337..56e2e75c 100644 --- a/lib/widgets/channel/channel_tile.dart +++ b/lib/widgets/channel/channel_tile.dart @@ -61,21 +61,19 @@ class ChannelTile extends StatelessWidget { size: 17.0, color: Color(0xff444444)), ], ), - if (channel.description != null && - channel.description.isNotEmpty) - Padding( - padding: EdgeInsets.only(top: 4.0), - child: Text( - channel.description, - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.start, - style: TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.w400, - color: Color(0xff444444), - ), + Padding( + padding: EdgeInsets.only(top: 4.0), + child: Text( + channel.lastMessage['text'] ?? 'No messages yet', + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.start, + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w400, + color: Color(0xff444444), ), ), + ), ], ), ), From 631c16a6879d8d0923d491a29ccf78be1862be39 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Tue, 16 Mar 2021 17:16:19 +0300 Subject: [PATCH 24/26] Added pending notifications clearing on direct channel opening --- lib/blocs/directs_bloc/directs_bloc.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/blocs/directs_bloc/directs_bloc.dart b/lib/blocs/directs_bloc/directs_bloc.dart index 3ed085d6..72f0320b 100644 --- a/lib/blocs/directs_bloc/directs_bloc.dart +++ b/lib/blocs/directs_bloc/directs_bloc.dart @@ -118,6 +118,7 @@ class DirectsBloc extends BaseChannelBloc { channels: repository.items, selected: repository.selected, ); + notificationBloc.add(CancelPendingSubscriptions(event.channelId)); } else if (event is LoadSingleChannel) { throw 'Not implemented yet'; } else if (event is RemoveChannel) { From 94af8a8e39c4ac674cdadb282215396d78a7aafb Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Tue, 16 Mar 2021 17:21:08 +0300 Subject: [PATCH 25/26] subtitle2 style fontweight = w400 --- lib/config/styles_config.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/config/styles_config.dart b/lib/config/styles_config.dart index 31f76076..1e95395e 100644 --- a/lib/config/styles_config.dart +++ b/lib/config/styles_config.dart @@ -29,7 +29,9 @@ class StylesConfig { color: Colors.transparent, ), // fontFamily: 'PT', - primaryColorBrightness: SchedulerBinding.instance?.window?.platformBrightness ?? Brightness.light, + primaryColorBrightness: + SchedulerBinding.instance?.window?.platformBrightness ?? + Brightness.light, ); static final TextTheme lightTextTheme = TextTheme( @@ -75,6 +77,7 @@ class StylesConfig { static final TextStyle _subtitle2 = TextStyle( color: subTitleTextColor, fontSize: Dim.tm2(decimal: -.5), + fontWeight: FontWeight.w400, ); static final TextStyle _button = TextStyle( From 2b5d382a93369c7e5107c4c123ec73bdbcbed760 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Tue, 16 Mar 2021 19:41:38 +0300 Subject: [PATCH 26/26] Build increment to 2.2.9+3 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index d3d96d21..0c59f92e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.2.9+2 +version: 2.2.9+3 environment: sdk: ">=2.7.0 <3.0.0"