[go: up one dir, main page]

Skip to content

Commit

Permalink
Implement drafts in Chat (team113#173, team113#102)
Browse files Browse the repository at this point in the history
Additionally:
- fix `GalleryPopup` incorrect behaviour with attached `Attachment`s in `Chat` 
- fix `router` late initialization error due to `NotificationService`
- fix `TextFieldState.text` not setting its previous value

Co-authored-by: SleepySquash <nordnikita@icloud.com>
  • Loading branch information
skazkiful and SleepySquash authored Nov 11, 2022
1 parent 6b4f222 commit e9bd9ed
Show file tree
Hide file tree
Showing 43 changed files with 533 additions and 29 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ All user visible changes to this project will be documented in this file. This p
- UI:
- Chat page:
- Swipe to reply gesture. ([#188], [#134])
- Drafts. ([#173], [#102])

### Changed

Expand All @@ -33,8 +34,10 @@ All user visible changes to this project will be documented in this file. This p
- Web:
- Context menu not opening over video previews. ([#198], [#196])

[#102]: /../../issues/102
[#134]: /../../issues/134
[#142]: /../../pull/142
[#173]: /../../pull/173
[#188]: /../../pull/188
[#190]: /../../issues/190
[#191]: /../../pull/191
Expand Down
9 changes: 8 additions & 1 deletion assets/l10n/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ label_direct_chat_link_in_chat_description =
- visit group profile,
- send messages to group chat,
- make calls
label_draft = Draft
label_drop_here =
Drop here
to upload
Expand Down Expand Up @@ -452,6 +453,7 @@ label_password = Password
label_password_not_set = Password not set
label_password_set_successfully = Password successfully set
label_password_was_changed = Password was changed
label_personalization = Personalization
label_phone_confirmation_code_was_send =
Confirmation code was send to your phone number
label_phones = Phones
Expand All @@ -465,7 +467,10 @@ label_recent = Recent
label_recover_account = Access recovery
label_recovery_code = Recovery code
label_repeat_password = Repeat password
label_personalization = Personalization
label_replies = [{$count} { $count ->
[1] reply
*[other] replies
}]
label_search = Search
label_search_hint = Search by Gapopa ID, login or name
label_search_not_found = Not found
Expand Down Expand Up @@ -513,4 +518,6 @@ label_was_removed = {$who} was removed
label_you = You
label_you_were_added_to_group = You were added to the group
plus = +
semicolon_space = :{" "}
space = {" "}
space_vertical_space = {" "}|{" "}
10 changes: 9 additions & 1 deletion assets/l10n/ru-RU.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ label_direct_chat_link_in_chat_description =
- просматривать профиль группы,
- отправлять сообщения в чат группы,
- совершать звонки
label_draft = Черновик
label_drop_here =
Перетащите сюда,
чтобы прикрепить
Expand Down Expand Up @@ -471,6 +472,7 @@ label_password = Пароль
label_password_not_set = Пароль не задан
label_password_set_successfully = Пароль успешно задан
label_password_was_changed = Пароль был изменен
label_personalization = Персонализация
label_phone_confirmation_code_was_send =
Код подтверждения был отправлен Вам на номер телефона
label_phones = Телефоны
Expand All @@ -484,7 +486,11 @@ label_recent = Недавние
label_recover_account = Восстановление доступа
label_recovery_code = Код восстановления
label_repeat_password = Повторите пароль
label_personalization = Персонализация
label_replies = [{$count} { $count ->
[1] ответ
[few] ответа
*[other] ответов
}]
label_search = Поиск
label_search_hint = Поиск по Gapopa ID, логину или имени
label_search_not_found = Ничего не найдено
Expand Down Expand Up @@ -532,4 +538,6 @@ label_was_removed = {$who} был(а) удален(а)
label_you = Вы
label_you_were_added_to_group = Вас добавили в группу
plus = +
semicolon_space = :{" "}
space = {" "}
space_vertical_space = {" "}|{" "}
13 changes: 13 additions & 0 deletions lib/domain/repository/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ abstract class RxChat {
/// Returns an actual [UserCallCover] of this [RxChat].
UserCallCover? get callCover;

/// [ChatMessage] being a draft in this [chat].
Rx<ChatMessage?> get draft;

/// Fetches the [messages] from the service.
Future<void> fetchMessages();

Expand All @@ -223,4 +226,14 @@ abstract class RxChat {

/// Removes a [ChatItem] identified by its [id].
Future<void> remove(ChatItemId itemId);

/// Updates the [draft] with the provided [text], [attachments] and
/// [repliesTo].
///
/// Resets it, if the specified fields are empty or `null`.
void setDraft({
ChatMessageText? text,
List<Attachment> attachments = const [],
List<ChatItem> repliesTo = const [],
});
}
11 changes: 5 additions & 6 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,18 @@ Future<void> main() async {

await _initHive();

Get.put(NotificationService())
.init(onNotificationResponse: onNotificationResponse);

var graphQlProvider = Get.put(GraphQlProvider());

Get.put<AbstractAuthRepository>(AuthRepository(graphQlProvider));
var authService =
Get.put(AuthService(AuthRepository(graphQlProvider), Get.find()));
await authService.init();
router = RouterState(authService);

await L10n.init();
Get.put(NotificationService())
.init(onNotificationResponse: onNotificationResponse);

router = RouterState(authService);
await authService.init();
await L10n.init();

Get.put(BackgroundWorker(Get.find()));

Expand Down
3 changes: 1 addition & 2 deletions lib/provider/hive/base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ import 'package:universal_io/io.dart';
import '/domain/model/user.dart';

/// Base class for data providers backed by [Hive].
abstract class HiveBaseProvider<T extends HiveObject>
extends DisposableInterface {
abstract class HiveBaseProvider<T> extends DisposableInterface {
/// [Box] that contains all of the data.
late Box<T> _box;

Expand Down
91 changes: 91 additions & 0 deletions lib/provider/hive/draft.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright © 2022 IT ENGINEERING MANAGEMENT INC, <https://github.com/team113>
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License v3.0 as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License v3.0 for
// more details.
//
// You should have received a copy of the GNU Affero General Public License v3.0
// along with this program. If not, see
// <https://www.gnu.org/licenses/agpl-3.0.html>.

import 'package:hive_flutter/hive_flutter.dart';

import '/domain/model/attachment.dart';
import '/domain/model/chat_call.dart';
import '/domain/model/chat_item.dart';
import '/domain/model/chat.dart';
import '/domain/model/crop_area.dart';
import '/domain/model/file.dart';
import '/domain/model/gallery_item.dart';
import '/domain/model/image_gallery_item.dart';
import '/domain/model/native_file.dart';
import '/domain/model/precise_date_time/precise_date_time.dart';
import '/domain/model/sending_status.dart';
import '/domain/model/user.dart';
import '/store/model/chat_item.dart';
import '/store/model/chat.dart';
import 'base.dart';
import 'chat_item.dart';

/// [Hive] storage for [ChatMessage]s being [RxChat.draft]s.
class DraftHiveProvider extends HiveBaseProvider<ChatMessage> {
@override
Stream<BoxEvent> get boxEvents => box.watch();

@override
String get boxName => 'draft';

@override
void registerAdapters() {
Hive.maybeRegisterAdapter(AttachmentIdAdapter());
Hive.maybeRegisterAdapter(ChatCallAdapter());
Hive.maybeRegisterAdapter(ChatCallMemberAdapter());
Hive.maybeRegisterAdapter(ChatCallRoomJoinLinkAdapter());
Hive.maybeRegisterAdapter(ChatDirectLinkAdapter());
Hive.maybeRegisterAdapter(ChatForwardAdapter());
Hive.maybeRegisterAdapter(ChatIdAdapter());
Hive.maybeRegisterAdapter(ChatItemsCursorAdapter());
Hive.maybeRegisterAdapter(ChatItemIdAdapter());
Hive.maybeRegisterAdapter(ChatItemVersionAdapter());
Hive.maybeRegisterAdapter(ChatMemberAdapter());
Hive.maybeRegisterAdapter(ChatMemberInfoAdapter());
Hive.maybeRegisterAdapter(ChatMessageAdapter());
Hive.maybeRegisterAdapter(ChatMessageTextAdapter());
Hive.maybeRegisterAdapter(ChatNameAdapter());
Hive.maybeRegisterAdapter(ChatVersionAdapter());
Hive.maybeRegisterAdapter(CropAreaAdapter());
Hive.maybeRegisterAdapter(FileAttachmentAdapter());
Hive.maybeRegisterAdapter(GalleryItemIdAdapter());
Hive.maybeRegisterAdapter(HiveChatCallAdapter());
Hive.maybeRegisterAdapter(HiveChatForwardAdapter());
Hive.maybeRegisterAdapter(HiveChatMemberInfoAdapter());
Hive.maybeRegisterAdapter(HiveChatMessageAdapter());
Hive.maybeRegisterAdapter(ImageAttachmentAdapter());
Hive.maybeRegisterAdapter(ImageGalleryItemAdapter());
Hive.maybeRegisterAdapter(LocalAttachmentAdapter());
Hive.maybeRegisterAdapter(MediaTypeAdapter());
Hive.maybeRegisterAdapter(NativeFileAdapter());
Hive.maybeRegisterAdapter(PreciseDateTimeAdapter());
Hive.maybeRegisterAdapter(SendingStatusAdapter());
Hive.maybeRegisterAdapter(StorageFileAdapter());
Hive.maybeRegisterAdapter(UserAdapter());
}

/// Returns a list of [ChatMessage]s from [Hive].
Iterable<ChatMessage> get drafts => valuesSafe;

/// Puts the provided [ChatMessage] to [Hive].
Future<void> put(ChatId id, ChatMessage draft) => putSafe(id.val, draft);

/// Returns a [ChatMessage] from [Hive] by the provided [id].
ChatMessage? get(ChatId id) => getSafe(id.val);

/// Removes a [ChatMessage] from [Hive] by the provided [id].
Future<void> remove(ChatId id) => deleteSafe(id.val);
}
5 changes: 5 additions & 0 deletions lib/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import 'provider/hive/background.dart';
import 'provider/hive/chat.dart';
import 'provider/hive/chat_call_credentials.dart';
import 'provider/hive/contact.dart';
import 'provider/hive/draft.dart';
import 'provider/hive/gallery_item.dart';
import 'provider/hive/media_settings.dart';
import 'provider/hive/my_user.dart';
Expand Down Expand Up @@ -387,6 +388,7 @@ class AppRouterDelegate extends RouterDelegate<RouteConfiguration>
deps.put(ApplicationSettingsHiveProvider()).init(userId: me),
deps.put(BackgroundHiveProvider()).init(userId: me),
deps.put(ChatCallCredentialsHiveProvider()).init(userId: me),
deps.put(DraftHiveProvider()).init(userId: me),
]);

AbstractSettingsRepository settingsRepository =
Expand Down Expand Up @@ -419,6 +421,7 @@ class AppRouterDelegate extends RouterDelegate<RouteConfiguration>
graphQlProvider,
Get.find(),
callRepository,
Get.find(),
userRepository,
me: me,
),
Expand Down Expand Up @@ -482,6 +485,7 @@ class AppRouterDelegate extends RouterDelegate<RouteConfiguration>
deps.put(ApplicationSettingsHiveProvider()).init(userId: me),
deps.put(BackgroundHiveProvider()).init(userId: me),
deps.put(ChatCallCredentialsHiveProvider()).init(userId: me),
deps.put(DraftHiveProvider()).init(userId: me),
]);

AbstractSettingsRepository settingsRepository =
Expand Down Expand Up @@ -514,6 +518,7 @@ class AppRouterDelegate extends RouterDelegate<RouteConfiguration>
graphQlProvider,
Get.find(),
callRepository,
Get.find(),
userRepository,
me: me,
),
Expand Down
34 changes: 31 additions & 3 deletions lib/store/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import '/provider/gql/graphql.dart';
import '/provider/hive/chat.dart';
import '/provider/hive/chat_call_credentials.dart';
import '/provider/hive/chat_item.dart';
import '/provider/hive/draft.dart';
import '/store/event/recent_chat.dart';
import '/store/user.dart';
import '/util/new_type.dart';
Expand All @@ -60,6 +61,7 @@ class ChatRepository implements AbstractChatRepository {
this._graphQlProvider,
this._chatLocal,
this._callRepo,
this._draftLocal,
this._userRepo, {
this.me,
});
Expand All @@ -80,6 +82,9 @@ class ChatRepository implements AbstractChatRepository {
/// [ChatCallCredentialsHiveProvider] persisting the [ChatCallCredentials].
final AbstractCallRepository _callRepo;

/// [RxChat.draft] local [Hive] storage.
final DraftHiveProvider _draftLocal;

/// [User]s repository, used to put the fetched [User]s into it.
final UserRepository _userRepo;

Expand All @@ -92,6 +97,9 @@ class ChatRepository implements AbstractChatRepository {
/// [ChatHiveProvider.boxEvents] subscription.
StreamIterator<BoxEvent>? _localSubscription;

/// [DraftHiveProvider.boxEvents] subscription.
StreamIterator<BoxEvent>? _draftSubscription;

/// [_recentChatsRemoteEvents] subscription.
///
/// May be uninitialized since connection establishment may fail.
Expand All @@ -111,14 +119,15 @@ class ChatRepository implements AbstractChatRepository {

if (!_chatLocal.isEmpty) {
for (HiveChat c in _chatLocal.chats) {
var entry = HiveRxChat(this, _chatLocal, c);
final HiveRxChat entry = HiveRxChat(this, _chatLocal, _draftLocal, c);
_chats[c.value.id] = entry;
entry.init();
}
_isReady.value = true;
}

_initLocalSubscription();
_initDraftSubscription();

HashMap<ChatId, ChatData> chats = await _recentChats();

Expand All @@ -145,6 +154,7 @@ class ChatRepository implements AbstractChatRepository {
}

_localSubscription?.cancel();
_draftSubscription?.cancel();
_remoteSubscription?.cancel();
}

Expand Down Expand Up @@ -892,7 +902,8 @@ class ChatRepository implements AbstractChatRepository {
} else {
HiveRxChat? chat = _chats[ChatId(event.key)];
if (chat == null) {
HiveRxChat entry = HiveRxChat(this, _chatLocal, event.value);
HiveRxChat entry =
HiveRxChat(this, _chatLocal, _draftLocal, event.value);
_chats[ChatId(event.key)] = entry;
entry.init();
entry.subscribe();
Expand All @@ -904,6 +915,23 @@ class ChatRepository implements AbstractChatRepository {
}
}

/// Initializes [DraftHiveProvider.boxEvents] subscription.
Future<void> _initDraftSubscription() async {
_draftSubscription = StreamIterator(_draftLocal.boxEvents);
while (await _draftSubscription!.moveNext()) {
BoxEvent event = _draftSubscription!.current;
if (event.deleted) {
_chats[ChatId(event.key)]?.draft.value = null;
} else {
HiveRxChat? chat = _chats[ChatId(event.key)];
if (chat != null) {
chat.draft.value = event.value;
chat.draft.refresh();
}
}
}
}

/// Initializes [_recentChatsRemoteEvents] subscription.
Future<void> _initRemoteSubscription() async {
_remoteSubscription?.cancel();
Expand Down Expand Up @@ -1000,7 +1028,7 @@ class ChatRepository implements AbstractChatRepository {
_putChat(data.chat);

if (entry == null) {
entry = HiveRxChat(this, _chatLocal, data.chat);
entry = HiveRxChat(this, _chatLocal, _draftLocal, data.chat);
_chats[data.chat.value.id] = entry;
entry.init();
entry.subscribe();
Expand Down
Loading

0 comments on commit e9bd9ed

Please sign in to comment.