From 171f8b5dc9535dccab9386fe06d34da9b0f8dd7c Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Mon, 22 Mar 2021 13:03:44 +0300 Subject: [PATCH 1/5] Channel participants multiple selection fixed. --- lib/widgets/sheets/add/participants_list.dart | 67 +++++++++++-------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/lib/widgets/sheets/add/participants_list.dart b/lib/widgets/sheets/add/participants_list.dart index 778e3ace..e01bc87e 100644 --- a/lib/widgets/sheets/add/participants_list.dart +++ b/lib/widgets/sheets/add/participants_list.dart @@ -144,15 +144,17 @@ class _ParticipantsListState extends State { } } else if (state is Error) { // Show an error - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - state.message, - style: TextStyle( - color: Colors.red, + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + state.message, + style: TextStyle( + color: Colors.red, + ), ), + duration: Duration(seconds: 2), ), - duration: Duration(seconds: 2), - )); + ); } }, buildWhen: (_, current) { @@ -212,6 +214,7 @@ class _ParticipantsListState extends State { builder: (context, state) { var selectedIds = []; var selectedUsers = []; + var deselectedUsers = users; var name = ''; var description = ''; if (state is Updated) { @@ -219,27 +222,39 @@ class _ParticipantsListState extends State { description = state.repository?.description; selectedIds = state.repository?.members; if (!_isDirect) { + print('UsERS: ${users.map((e) => e.username)}'); + print('UsERS IDS: ${users.map((e) => e.id)}'); + print('selected ids: $selectedIds'); + selectedUsers = users .where((user) => selectedIds.contains(user.id)) .toList(); - users.excludeUsers(selectedUsers); + deselectedUsers = users + .where((user) => !selectedIds.contains(user.id)) + .toList(); + // users.excludeUsers(selectedUsers); } } - // print('Selected UsERS: ${selectedUsers.map((e) => e.username)}'); + print( + 'Selected UsERS: ${selectedUsers.map((e) => e.username)}'); + // print('Selected Ids: $selectedIds}'); + return ListView.builder( shrinkWrap: true, padding: EdgeInsets.only(top: 0), - itemCount: users.length + selectedUsers.length, + itemCount: _isDirect + ? users.length + : deselectedUsers.length + selectedUsers.length, itemBuilder: (context, index) { User user; - if (!_isDirect) { + if (_isDirect) { + user = users[index]; + } else { if (index < selectedUsers.length) { user = selectedUsers[index]; } else { - user = users[index - selectedUsers.length]; + user = deselectedUsers[index - selectedUsers.length]; } - } else { - user = users[index]; } return SearchItem( title: user.firstName.isNotEmpty || @@ -255,21 +270,19 @@ class _ParticipantsListState extends State { _createDirect(selectedIds); } else { if (selectedIds.contains(user.id)) { - setState(() { - selectedIds.remove(user.id); - // selectedUsers.removeWhere((selected) => selected.id == user.id); - }); + selectedIds.remove(user.id); + // selectedUsers.removeWhere((selected) => selected.id == user.id); } else { - setState(() { - selectedIds.add(user.id); - // selectedUsers.add(user); - }); + selectedIds.add(user.id); + // selectedUsers.add(user); } - context.read().add(Update( - name: name, - description: description, - participants: selectedIds, - )); + context.read().add( + Update( + name: name, + description: description, + participants: selectedIds, + ), + ); } }, ); From 8b1a6463b5e4fca69cc18d7476ef81a41b28ae62 Mon Sep 17 00:00:00 2001 From: Pavel Zarudnev Date: Mon, 22 Mar 2021 13:31:10 +0300 Subject: [PATCH 2/5] Channel participants state management fix. --- lib/widgets/sheets/add/participants_list.dart | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/lib/widgets/sheets/add/participants_list.dart b/lib/widgets/sheets/add/participants_list.dart index e01bc87e..f9e141c5 100644 --- a/lib/widgets/sheets/add/participants_list.dart +++ b/lib/widgets/sheets/add/participants_list.dart @@ -44,6 +44,9 @@ class _ParticipantsListState extends State { bool _shouldFocus = true; String _title; + var _selectedIds = []; + var _selectedUsers = []; + @override void initState() { super.initState(); @@ -212,31 +215,29 @@ class _ParticipantsListState extends State { return BlocBuilder( buildWhen: (previous, current) => current is Updated, builder: (context, state) { - var selectedIds = []; - var selectedUsers = []; var deselectedUsers = users; var name = ''; var description = ''; if (state is Updated) { name = state.repository?.name; description = state.repository?.description; - selectedIds = state.repository?.members; + _selectedIds = state.repository?.members; if (!_isDirect) { print('UsERS: ${users.map((e) => e.username)}'); print('UsERS IDS: ${users.map((e) => e.id)}'); - print('selected ids: $selectedIds'); + print('selected ids: $_selectedIds'); - selectedUsers = users - .where((user) => selectedIds.contains(user.id)) - .toList(); + // _selectedUsers = users + // .where((user) => _selectedIds.contains(user.id)) + // .toList(); deselectedUsers = users - .where((user) => !selectedIds.contains(user.id)) + .where((user) => !_selectedIds.contains(user.id)) .toList(); // users.excludeUsers(selectedUsers); } } print( - 'Selected UsERS: ${selectedUsers.map((e) => e.username)}'); + 'Selected UsERS: ${_selectedUsers.map((e) => e.username)}'); // print('Selected Ids: $selectedIds}'); return ListView.builder( @@ -244,16 +245,16 @@ class _ParticipantsListState extends State { padding: EdgeInsets.only(top: 0), itemCount: _isDirect ? users.length - : deselectedUsers.length + selectedUsers.length, + : deselectedUsers.length + _selectedUsers.length, itemBuilder: (context, index) { User user; if (_isDirect) { user = users[index]; } else { - if (index < selectedUsers.length) { - user = selectedUsers[index]; + if (index < _selectedUsers.length) { + user = _selectedUsers[index]; } else { - user = deselectedUsers[index - selectedUsers.length]; + user = deselectedUsers[index - _selectedUsers.length]; } } return SearchItem( @@ -261,26 +262,26 @@ class _ParticipantsListState extends State { user.lastName.isNotEmpty ? '${user.firstName} ${user.lastName}' : '${user.username}', - selected: selectedIds.contains(user.id), + selected: _selectedIds.contains(user.id), allowMultipleChoice: !_isDirect, onTap: () { FocusScope.of(context).requestFocus(FocusNode()); if (_isDirect) { - selectedIds = [user.id]; - _createDirect(selectedIds); + _selectedIds = [user.id]; + _createDirect(_selectedIds); } else { - if (selectedIds.contains(user.id)) { - selectedIds.remove(user.id); - // selectedUsers.removeWhere((selected) => selected.id == user.id); + if (_selectedIds.contains(user.id)) { + _selectedIds.remove(user.id); + _selectedUsers.removeWhere((selected) => selected.id == user.id); } else { - selectedIds.add(user.id); - // selectedUsers.add(user); + _selectedIds.add(user.id); + _selectedUsers.add(user); } context.read().add( Update( name: name, description: description, - participants: selectedIds, + participants: _selectedIds, ), ); } From d87cb08eaaf45e196cb87ad637615ba0c0cbd779 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Tue, 23 Mar 2021 15:19:28 +0300 Subject: [PATCH 3/5] Tried to fix emoji board flickering, but unsuccessfully --- lib/pages/messages_page.dart | 5 ++--- lib/pages/thread_page.dart | 1 - lib/widgets/message/message_edit_field.dart | 22 ++++++++++----------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/pages/messages_page.dart b/lib/pages/messages_page.dart index b0e7a088..15b1c4cc 100644 --- a/lib/pages/messages_page.dart +++ b/lib/pages/messages_page.dart @@ -70,8 +70,7 @@ class MessagesPage extends StatelessWidget { builder: (ctx, state) { BaseChannel parentChannel = T is Channel ? Channel() : Direct(); - if ((state is MessagesLoaded || - state is MessagesEmpty) && + if ((state is MessagesLoaded || state is MessagesEmpty) && state.parentChannel.id == ProfileBloc.selectedChannelId) { parentChannel = state.parentChannel; } @@ -229,9 +228,9 @@ class MessagesPage extends StatelessWidget { draftType = DraftType.direct; } + print("REBUILDING MESSAGE EDIT FIELD!"); return BlocBuilder( builder: (ctx, state) => MessageEditField( - key: UniqueKey(), autofocus: state is MessageEditing, initialText: state is MessageEditing ? state.originalStr : draft, diff --git a/lib/pages/thread_page.dart b/lib/pages/thread_page.dart index 916ffbd6..cea89f57 100644 --- a/lib/pages/thread_page.dart +++ b/lib/pages/thread_page.dart @@ -170,7 +170,6 @@ class _ThreadPageState extends State> { MessageEditState>( builder: (ctx, state) { return MessageEditField( - key: UniqueKey(), initialText: state is MessageEditing ? state.originalStr : draft ?? '', diff --git a/lib/widgets/message/message_edit_field.dart b/lib/widgets/message/message_edit_field.dart index 64f748f7..b7c67fed 100644 --- a/lib/widgets/message/message_edit_field.dart +++ b/lib/widgets/message/message_edit_field.dart @@ -10,7 +10,6 @@ class MessageEditField extends StatefulWidget { final String initialText; MessageEditField({ - Key key, @required this.onMessageSend, @required this.onTextUpdated, this.autofocus = false, @@ -113,18 +112,9 @@ class _MessageEditField extends State { return false; } - Widget buildEmojiBoard() { - return EmojiKeyboard( - onEmojiSelected: (emoji) { - _controller.text += emoji.text; - _scrollController.jumpTo(_scrollController.position.maxScrollExtent); - }, - height: MediaQuery.of(context).size.height * 0.35, - ); - } - @override Widget build(BuildContext context) { + print("REBUILDING EMOJI BOARD!"); return WillPopScope( onWillPop: onBackPress, child: Column( @@ -139,7 +129,15 @@ class _MessageEditField extends State { onMessageSend: widget.onMessageSend, canSend: _canSend, ), - _emojiVisible ? buildEmojiBoard() : Container(), + if (_emojiVisible) + EmojiKeyboard( + onEmojiSelected: (emoji) { + _controller.text += emoji.text; + _scrollController + .jumpTo(_scrollController.position.maxScrollExtent); + }, + height: MediaQuery.of(context).size.height * 0.35, + ), ], ), ); From 1ba542575966dc0e866ecfe53cfade545aed0d68 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Tue, 23 Mar 2021 15:36:38 +0300 Subject: [PATCH 4/5] Reworked channels sync when user is added to new channel --- lib/blocs/channels_bloc/channels_bloc.dart | 19 +++++++++++++++++-- lib/pages/messages_page.dart | 1 - lib/widgets/message/message_edit_field.dart | 1 - 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/blocs/channels_bloc/channels_bloc.dart b/lib/blocs/channels_bloc/channels_bloc.dart index d2e78887..3befb8b5 100644 --- a/lib/blocs/channels_bloc/channels_bloc.dart +++ b/lib/blocs/channels_bloc/channels_bloc.dart @@ -148,8 +148,23 @@ class ChannelsBloc extends BaseChannelBloc { item.visibility = event.data.visibility ?? item.visibility; item.lastMessage = event.data.lastMessage ?? item.lastMessage; } else { - this.add( - ReloadChannels(workspaceId: selectedParentId, forceFromApi: true)); + await repository.reload( + queryParams: { + 'workspace_id': selectedParentId, + 'company_id': workspacesBloc.selectedCompanyId, + }, + filters: [ + ['workspace_id', '=', selectedParentId] + ], + sortFields: {'name': true}, + forceFromApi: true, + ); + yield ChannelsLoaded( + selected: repository.selected, + channels: repository.items, + force: DateTime.now().toString(), + ); + return; } await repository.saveOne(item); diff --git a/lib/pages/messages_page.dart b/lib/pages/messages_page.dart index 15b1c4cc..9f9b2d95 100644 --- a/lib/pages/messages_page.dart +++ b/lib/pages/messages_page.dart @@ -228,7 +228,6 @@ class MessagesPage extends StatelessWidget { draftType = DraftType.direct; } - print("REBUILDING MESSAGE EDIT FIELD!"); return BlocBuilder( builder: (ctx, state) => MessageEditField( autofocus: state is MessageEditing, diff --git a/lib/widgets/message/message_edit_field.dart b/lib/widgets/message/message_edit_field.dart index b7c67fed..9bd56623 100644 --- a/lib/widgets/message/message_edit_field.dart +++ b/lib/widgets/message/message_edit_field.dart @@ -114,7 +114,6 @@ class _MessageEditField extends State { @override Widget build(BuildContext context) { - print("REBUILDING EMOJI BOARD!"); return WillPopScope( onWillPop: onBackPress, child: Column( From 469c5f6b534c12920896ceb31c9976199ee5c4b2 Mon Sep 17 00:00:00 2001 From: Makhmudov Babur Date: Wed, 24 Mar 2021 17:47:43 +0300 Subject: [PATCH 5/5] Added Best effort Workspaces resync when opening the drawer --- .../workspaces_bloc/workspace_event.dart | 18 ++++++++++++ .../workspaces_bloc/workspace_state.dart | 4 ++- .../workspaces_bloc/workspaces_bloc.dart | 17 +++++++++++ lib/repositories/collection_repository.dart | 28 +++++++++++++++++++ lib/sql/v1.dart | 9 ++---- lib/widgets/bars/main_app_bar.dart | 8 +++++- 6 files changed, 76 insertions(+), 8 deletions(-) diff --git a/lib/blocs/workspaces_bloc/workspace_event.dart b/lib/blocs/workspaces_bloc/workspace_event.dart index 716a20d7..cd874d31 100644 --- a/lib/blocs/workspaces_bloc/workspace_event.dart +++ b/lib/blocs/workspaces_bloc/workspace_event.dart @@ -15,6 +15,24 @@ class ReloadWorkspaces extends WorkspacesEvent { List get props => [companyId]; } +class ForceRefresh extends WorkspacesEvent { + // parent company id + const ForceRefresh(); + + @override + List get props => []; +} + +class CheckForChange extends WorkspacesEvent { + // parent company id + final String companyId; + + const CheckForChange(this.companyId); + + @override + List get props => [companyId]; +} + class ClearWorkspaces extends WorkspacesEvent { const ClearWorkspaces(); diff --git a/lib/blocs/workspaces_bloc/workspace_state.dart b/lib/blocs/workspaces_bloc/workspace_state.dart index 9d1bb35b..53c219dd 100644 --- a/lib/blocs/workspaces_bloc/workspace_state.dart +++ b/lib/blocs/workspaces_bloc/workspace_state.dart @@ -8,13 +8,15 @@ abstract class WorkspaceState extends Equatable { class WorkspacesLoaded extends WorkspaceState { final List workspaces; final Workspace selected; + final String force; const WorkspacesLoaded({ this.workspaces, + this.force, this.selected, }); @override - List get props => [workspaces, selected]; + List get props => [workspaces, selected, force]; } class WorkspaceSelected extends WorkspacesLoaded { diff --git a/lib/blocs/workspaces_bloc/workspaces_bloc.dart b/lib/blocs/workspaces_bloc/workspaces_bloc.dart index 2dadecbb..a7e5f133 100644 --- a/lib/blocs/workspaces_bloc/workspaces_bloc.dart +++ b/lib/blocs/workspaces_bloc/workspaces_bloc.dart @@ -74,6 +74,23 @@ class WorkspacesBloc extends Bloc { workspaces: repository.items, selected: repository.selected, ); + } else if (event is CheckForChange) { + // Sorry Pavel, but cannot block the stream here with await + repository.didChange( + filters: [ + ['company_id', '=', event.companyId] + ], + queryParams: {'company_id': event.companyId}, + ).then((changed) { + if (changed) this.add(ForceRefresh()); + }); + } else if (event is ForceRefresh) { + repository.items.sort((w1, w2) => w1.name.compareTo(w2.name)); + yield WorkspacesLoaded( + workspaces: repository.items, + selected: repository.selected, + force: DateTime.now().toString(), + ); } else if (event is ClearWorkspaces) { await repository.clean(); yield WorkspacesEmpty(); diff --git a/lib/repositories/collection_repository.dart b/lib/repositories/collection_repository.dart index c34cc007..f573079a 100644 --- a/lib/repositories/collection_repository.dart +++ b/lib/repositories/collection_repository.dart @@ -149,6 +149,34 @@ class CollectionRepository { return true; } + Future didChange({ + Map queryParams, + List filters, // fields to filter by in store + }) async { + final itemsList = await _storage.batchLoad( + type: _typeToStorageType[T], + filters: filters, + limit: 100000, + offset: 0, + ); + List remote; + if (itemsList.isEmpty) return false; + try { + remote = await _api.get(apiEndpoint, params: queryParams); + logger.w("GOT WORKSPACES: ${remote.map((w) => w['name']).toSet()}"); + } on ApiError catch (error) { + logger.d('ERROR while reloading $T items from api\n${error.message}'); + return false; + } + if (remote.length != itemsList.length) { + logger.w("LOCAL != REMOTE"); + await _storage.batchDelete(type: _typeToStorageType[T], filters: filters); + _updateItems(remote, saveToStore: true); + return true; + } + return false; + } + Future pullOne( Map queryParams, { bool addToItems = true, diff --git a/lib/sql/v1.dart b/lib/sql/v1.dart index 115be123..779c0757 100644 --- a/lib/sql/v1.dart +++ b/lib/sql/v1.dart @@ -24,8 +24,7 @@ CREATE TABLE workspace ( color TEXT, user_last_access INT, total_members INT DEFAULT 0, - is_selected INT DEFAULT 0, - FOREIGN KEY(company_id) REFERENCES company(id) + is_selected INT DEFAULT 0 ); CREATE INDEX workspace_company_idx ON workspace(company_id); '''; @@ -44,8 +43,7 @@ CREATE TABLE channel ( members_count INT DEFAULT 0, last_message TEXT, messages_unread INT DEFAULT 0, - is_selected INT DEFAULT 0, - FOREIGN KEY(workspace_id) REFERENCES workspace(id) + is_selected INT DEFAULT 0 ); CREATE INDEX channel_workspace_idx ON channel(workspace_id); '''; @@ -64,8 +62,7 @@ CREATE TABLE direct ( 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) + has_unread INT DEFAULT 0 ); CREATE INDEX direct_company_idx ON direct(company_id); '''; diff --git a/lib/widgets/bars/main_app_bar.dart b/lib/widgets/bars/main_app_bar.dart index 19b6a3b4..56cea61c 100644 --- a/lib/widgets/bars/main_app_bar.dart +++ b/lib/widgets/bars/main_app_bar.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:twake/blocs/workspaces_bloc/workspaces_bloc.dart'; +import 'package:twake/blocs/profile_bloc/profile_bloc.dart' show ProfileBloc; import 'package:twake/config/dimensions_config.dart'; import 'package:twake/widgets/common/image_avatar.dart'; @@ -14,7 +15,12 @@ class MainAppBar extends StatelessWidget implements PreferredSizeWidget { return AppBar( titleSpacing: 0.0, leading: InkWell( - onTap: () => scaffoldKey.currentState.openDrawer(), + onTap: () { + context + .read() + .add(CheckForChange(ProfileBloc.selectedCompany.id)); + scaffoldKey.currentState.openDrawer(); + }, child: Image.asset('assets/images/menu.png'), ), backgroundColor: Colors.white,