Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Chat muting/unmuting (#63) #172

Merged
merged 40 commits into from
Nov 15, 2022
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a981fe0
some changes
skazkiful Oct 12, 2022
529c5df
Merge remote-tracking branch 'origin/main' into 63-chat-muting-and-un…
skazkiful Oct 17, 2022
6cda259
some changes
skazkiful Oct 17, 2022
65efb02
Some changes
skazkiful Oct 18, 2022
255bb90
Fix CI
skazkiful Oct 18, 2022
11dfd7d
Change parser
skazkiful Oct 19, 2022
2d37d8b
Some changes
skazkiful Oct 19, 2022
0b1ffd9
Some changes
skazkiful Oct 19, 2022
25b86c5
Corrections
krida2000 Oct 20, 2022
6ae74de
Some changes
skazkiful Oct 24, 2022
acb606b
Final fixes
skazkiful Oct 24, 2022
39715ec
Merge remote-tracking branch 'origin/main' into 63-chat-muting-and-un…
skazkiful Oct 24, 2022
9c47759
Final fixes
skazkiful Oct 24, 2022
d5bf226
Fix ci
skazkiful Oct 24, 2022
4e0806c
Fix ci
skazkiful Oct 25, 2022
4dcb11e
Corrections
krida2000 Oct 25, 2022
3120bd7
Merge remote-tracking branch 'origin/main' into 63-chat-muting-and-un…
krida2000 Oct 25, 2022
e974d7f
Merge remote-tracking branch 'origin/main' into 63-chat-muting-and-un…
skazkiful Oct 31, 2022
a6ef013
Some changes
skazkiful Oct 31, 2022
7c59446
Merge remote-tracking branch 'origin/main' into 63-chat-muting-and-un…
SleepySquash Nov 3, 2022
98a0c33
Corrections
SleepySquash Nov 3, 2022
34604b8
some changes
skazkiful Nov 3, 2022
eeac580
try ci
skazkiful Nov 4, 2022
a64eb3e
some changes
skazkiful Nov 7, 2022
2054329
Merge branch 'main' into 63-chat-muting-and-unmuting
skazkiful Nov 7, 2022
2fcb8a3
some changes
skazkiful Nov 7, 2022
ae232d2
Merge branch 'main' into 63-chat-muting-and-unmuting
skazkiful Nov 8, 2022
bf318e8
changes
skazkiful Nov 8, 2022
a9e1cb8
some changes
skazkiful Nov 9, 2022
e78cdfa
small changes
skazkiful Nov 11, 2022
9cd8a28
Merge branch 'main' into 63-chat-muting-and-unmuting
skazkiful Nov 11, 2022
fa30ec1
fix ci
skazkiful Nov 11, 2022
a2e926b
Merge remote-tracking branch 'origin/main' into 63-chat-muting-and-un…
SleepySquash Nov 14, 2022
fc1cc62
Corrections
SleepySquash Nov 14, 2022
c4252f9
Corrections
SleepySquash Nov 14, 2022
00c8539
some changes
skazkiful Nov 14, 2022
1f44b63
some changes
skazkiful Nov 14, 2022
f36588b
some changes
skazkiful Nov 14, 2022
c82ae4e
changes
skazkiful Nov 15, 2022
466c15c
Minor correction
SleepySquash Nov 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ All user visible changes to this project will be documented in this file. This p
- UI:
- Home page:
- Redesigned chats tab. ([#142])
- Chats tab:
- Chat muting/unmuting. ([#172], [#63])

[#63]: /../../issues/63
[#142]: /../../pull/142
[#172]: /../../pull/172



Expand Down
17 changes: 17 additions & 0 deletions assets/l10n/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ btn_leave_chat = Leave chat
btn_login = Log in
btn_logout = Logout
btn_media_settings = Media settings
btn_mute_chat = Mute chat
btn_next = Next
btn_ok = Ok
btn_participants = Participants
Expand All @@ -169,6 +170,7 @@ btn_settings = Settings
btn_share = Share
btn_start = Start
btn_submit = Submit
btn_unmute_chat = Unmute chat
btn_video_call = Video call
btn_write_message = Write a message
btn_your_profile = Your profile
Expand Down Expand Up @@ -230,6 +232,7 @@ err_size_too_big = File is too big. Maximum allowed size is 15 MiB
err_stale_version = Provided version is too stale
err_too_many_emails = Reached maximum allowed number of Email addresses
err_too_many_phones = Reached maximum allowed number of phone number
err_too_short = Mute duration cannot be shorter than one minute
err_unauthorized = Authentication required
err_uneditable_message = You can\'t edit this message
err_unknown = Unknown error
Expand Down Expand Up @@ -432,6 +435,20 @@ label_media_output = Output
label_media_settings = Media settings
label_menu = Menu
label_message_will_deleted_for_you = The message will be deleted only for you.
label_mute_for = { $days ->
[0] { $hours ->
[0] { $minutes ->
[0] Forever
[1] 1 minute
*[other] {$minutes} minutes
}
[1] 1 hour
*[other] {$hours} hours
}
[1] 1 day
*[other] {$days} days
}
label_mute_chat_for = Mute chat for
label_name = Name
label_name_hint = Your publicly visible name
label_new_password = New password
Expand Down
20 changes: 20 additions & 0 deletions assets/l10n/ru-RU.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ btn_leave_chat = Выйти из чата
btn_login = Войти
btn_logout = Выйти
btn_media_settings = Настройки медиа
btn_mute_chat = Заглушить чат
btn_next = Далее
btn_ok = Ок
btn_participants = Участники
Expand All @@ -169,6 +170,7 @@ btn_settings = Настройки
btn_share = Поделиться
btn_start = Начать
btn_submit = Применить
btn_unmute_chat = Включить звук
btn_video_call = Видеозвонок
btn_write_message = Написать сообщение
btn_your_profile = Ваш профиль
Expand Down Expand Up @@ -232,6 +234,7 @@ err_size_too_big = Файл превышает ограничение по ра
err_stale_version = Указанная версия слишком старая
err_too_many_emails = Был достигнут максимум Email адресов
err_too_many_phones = Был достигнут максимум номеров телефонов
err_too_short = Время заглушения чата не может быть меньше одной минуты
err_unauthorized = Требуется авторизация
err_uneditable_message = Сообщение нельзя редактировать
err_unknown = Неизвестная ошибка
Expand Down Expand Up @@ -451,6 +454,23 @@ label_media_output = Устройство выхода
label_media_settings = Настройки медиа
label_menu = Меню
label_message_will_deleted_for_you = Сообщение будет удалено только для Вас.
label_mute_for = { $days ->
[0] { $hours ->
[0] { $minutes ->
[0] Навсегда
[1] 1 минуту
[few] {$minutes} минуты
*[other] {$minutes} минут
}
[1] 1 час
[few] {$hours} часа
*[other] {$hours} часов
}
[1] 1 день
[few] {$days} дня
*[other] {$days} дней
}
label_mute_chat_for = Заглушить чат на
label_name = Имя
label_name_hint = Ваше видимое всем имя
label_new_password = Новый пароль
Expand Down
27 changes: 27 additions & 0 deletions lib/api/backend/graphql/mutation/chat/ToggleChatMute.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 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>.

mutation ToggleChatMute($id: ChatId!, $mute: Muting) {
toggleChatMute(id: $id, mute: $mute) {
__typename
... on ChatEventsVersioned {
...ChatEventsVersioned
}
... on ToggleChatMuteError {
code
}
}
}
20 changes: 10 additions & 10 deletions lib/api/backend/graphql/parsers/precise_date_time.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import '/domain/model/precise_date_time/precise_date_time.dart';

PreciseDateTime fromGraphQLDateTimeToDartPreciseDateTime(String v) =>
PreciseDateTime.parse(v);
DateTime fromDartPreciseDateTimeToGraphQLDateTime(PreciseDateTime v) => v.val;
String fromDartPreciseDateTimeToGraphQLDateTime(PreciseDateTime v) =>
v.val.toUtc().toIso8601String();
List<PreciseDateTime> fromGraphQLListDateTimeToDartListPreciseDateTime(
List<Object?> v) =>
v
.map((e) => fromGraphQLDateTimeToDartPreciseDateTime(e as String))
.toList();
List<DateTime> fromDartListPreciseDateTimeToGraphQLListDataTime(
List<String> fromDartListPreciseDateTimeToGraphQLListDataTime(
List<PreciseDateTime> v) =>
v.map((e) => fromDartPreciseDateTimeToGraphQLDateTime(e)).toList();
List<PreciseDateTime>?
Expand All @@ -35,17 +36,17 @@ List<PreciseDateTime>?
v
?.map((e) => fromGraphQLDateTimeToDartPreciseDateTime(e as String))
.toList();
List<DateTime>?
fromDartListNullablePreciseDateTimeToGraphQLListNullableDateTime(
List<PreciseDateTime>? v) =>
v?.map((e) => fromDartPreciseDateTimeToGraphQLDateTime(e)).toList();
List<String>? fromDartListNullablePreciseDateTimeToGraphQLListNullableDateTime(
List<PreciseDateTime>? v) =>
v?.map((e) => fromDartPreciseDateTimeToGraphQLDateTime(e)).toList();

PreciseDateTime? fromGraphQLDateTimeNullableToDartPreciseDateTimeNullable(
String? v) =>
v == null ? null : PreciseDateTime(DateTime.parse(v));
DateTime? fromDartPreciseDateTimeNullableToGraphQLDateTimeNullable(
String? fromDartPreciseDateTimeNullableToGraphQLDateTimeNullable(
skazkiful marked this conversation as resolved.
Show resolved Hide resolved
PreciseDateTime? v) =>
v?.val;
v?.val.toUtc().toIso8601String();

List<PreciseDateTime?>
fromGraphQLListDateTimeNullableToDartListPreciseDateTimeNullable(
List<Object?> v) =>
Expand All @@ -54,8 +55,7 @@ List<PreciseDateTime?>
fromGraphQLDateTimeNullableToDartPreciseDateTimeNullable(
e as String?))
.toList();
List<
DateTime?> fromDartListPreciseDateTimeNullableToGraphQLListDateTimeNullable(
List<String?> fromDartListPreciseDateTimeNullableToGraphQLListDateTimeNullable(
List<PreciseDateTime?> v) =>
v
.map((e) => fromDartPreciseDateTimeNullableToGraphQLDateTimeNullable(e))
Expand Down
3 changes: 2 additions & 1 deletion lib/domain/model/mute_duration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ class MuteDuration {
this.forever,
});

factory MuteDuration.forever() => MuteDuration();
factory MuteDuration.forever() => MuteDuration(forever: true);

factory MuteDuration.until(PreciseDateTime until) =>
MuteDuration(until: until);
}
5 changes: 5 additions & 0 deletions lib/domain/repository/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import '../model/native_file.dart';
import '../model/user.dart';
import '../model/user_call_cover.dart';
import '../repository/user.dart';
import '/api/backend/schema.dart' show Muting;
import '/util/obs/obs.dart';

/// [Chat]s repository interface.
Expand Down Expand Up @@ -168,6 +169,10 @@ abstract class AbstractChatRepository {
NativeFile? file,
void Function(int count, int total)? onSendProgress,
});

/// Mutes or unmutes the specified [Chat] for the authenticated [MyUser].
/// Overrides an existing mute even if it's longer.
Future<void> toggleChatMute(ChatId id, Muting? mute);
}

/// Unified reactive [Chat] entity with its [ChatItem]s.
Expand Down
5 changes: 5 additions & 0 deletions lib/domain/service/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,11 @@ class ChatService extends DisposableService {
onSendProgress: onSendProgress,
);

/// Mutes or unmutes the specified [Chat] for the authenticated [MyUser].
/// Overrides an existing mute even if it's longer.
Future<void> toggleChatMute(ChatId id, Muting? mute) =>
_chatRepository.toggleChatMute(id, mute);

/// Callback, called when a [User] identified by the provided [userId] gets
/// removed from the specified [Chat].
///
Expand Down
48 changes: 48 additions & 0 deletions lib/provider/gql/components/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,54 @@ abstract class ChatGraphQlMixin {
as ForwardChatItems$Mutation$ForwardChatItems$ChatEventsVersioned;
}

/// Mutes or unmutes the specified [Chat] for the authenticated [MyUser].
/// Overrides an existing mute even if it's longer.
///
/// Muted [Chat] implies that its events don't produce sounds and
/// notifications on a client side. This, however, has nothing to do with a
/// server and is the responsibility to be satisfied by a client side.
///
/// Note, that `Mutation.toggleChatMute` doesn't correlate with
/// `Mutation.toggleMyUserMute`. Muted [Chat] of unmuted [MyUser] should not
/// produce any sounds, and so, unmuted [Chat] of muted [MyUser] should not
/// produce any sounds too.
///
/// ### Authentication
///
/// Mandatory.
///
/// ### Result
///
/// Only the following [ChatEvent]s may be produced on success:
/// - [EventChatMuted] (if `until` argument is not `null`);
/// - [EventChatUnmuted] (if `until` argument is `null`).
///
/// ### Idempotent
///
/// Succeeds as no-op (and returns no [ChatEvent]) if the specified [Chat] is
/// already muted `until` the specified datetime (or unmuted) for the
/// authenticated [MyUser].
Future<ChatEventsVersionedMixin?> toggleChatMute(
ChatId id,
Muting? mute,
) async {
final variables = ToggleChatMuteArguments(id: id, mute: mute);

final QueryResult result = await client.mutate(
MutationOptions(
operationName: 'ToggleChatMute',
document: ToggleChatMuteMutation(variables: variables).document,
variables: variables.toJson(),
),
onException: (data) => ToggleChatMuteException(
(ToggleChatMute$Mutation.fromJson(data).toggleChatMute
as ToggleChatMute$Mutation$ToggleChatMute$ToggleChatMuteError)
.code),
);
return ToggleChatMute$Mutation.fromJson(result.data!).toggleChatMute
as ChatEventsVersionedMixin?;
}

/// Returns the [Attachment]s of a [ChatItem] identified by the provided [id].
///
/// The authenticated [MyUser] should be a member of the [Chat] the provided
Expand Down
25 changes: 25 additions & 0 deletions lib/provider/gql/exceptions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1292,3 +1292,28 @@ class UpdateChatAvatarException
}
}
}

/// Exception of `Mutation.toggleChatMute` described in the [code].
class ToggleChatMuteException
with LocalizedExceptionMixin
implements Exception {
const ToggleChatMuteException(this.code);

/// Reason of why the mutation has failed.
final ToggleChatMuteErrorCode code;

@override
String toString() => 'ToggleChatMuteException($code)';

@override
String toMessage() {
switch (code) {
case ToggleChatMuteErrorCode.tooShort:
return 'err_too_short'.l10n;
case ToggleChatMuteErrorCode.unknownChat:
return 'err_unknown_chat'.l10n;
case ToggleChatMuteErrorCode.artemisUnknown:
return 'err_unknown'.l10n;
}
}
}
28 changes: 27 additions & 1 deletion lib/store/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,26 @@ class ChatRepository implements AbstractChatRepository {
);
}

@override
Future<void> toggleChatMute(ChatId id, Muting? mute) async {
HiveRxChat? chat = _chats[id];
MuteDuration? muteDuration = chat?.chat.value.muted;

if (mute == null) {
chat?.chat.update((c) => c?.muted = null);
} else {
chat?.chat.update((c) => c?.muted = mute.duration == null
? MuteDuration.forever()
: MuteDuration.until(mute.duration!));
}
try {
await _graphQlProvider.toggleChatMute(id, mute);
} catch (e) {
chat?.chat.update((c) => c?.muted = muteDuration);
rethrow;
}
}

// TODO: Messages list can be huge, so we should implement pagination and
// loading on demand.
/// Fetches __all__ [ChatItem]s of the [chat] ordered by their posting time.
Expand Down Expand Up @@ -708,7 +728,13 @@ class ChatRepository implements AbstractChatRepository {
var node = e as ChatEventsVersionedMixin$Events$EventChatMuted;
return EventChatMuted(
e.chatId,
node.duration as MuteDuration,
node.duration.$$typename == 'MuteForeverDuration'
? MuteDuration.forever()
: MuteDuration.until(
(node.duration
as ChatEventsVersionedMixin$Events$EventChatMuted$Duration$MuteUntilDuration)
.until,
),
);
} else if (e.$$typename == 'EventChatAvatarDeleted') {
var node = e as ChatEventsVersionedMixin$Events$EventChatAvatarDeleted;
Expand Down
12 changes: 12 additions & 0 deletions lib/store/chat_rx.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ class HiveRxChat extends RxChat {
/// [Worker] reacting on the [User] changes updating the [avatar].
Worker? _userWorker;

/// [Timer], used to unmmute [Chat].
Timer? _muteTimer;

/// [ChatItemHiveProvider.boxEvents] subscription.
StreamIterator<BoxEvent>? _localSubscription;

Expand Down Expand Up @@ -197,6 +200,7 @@ class HiveRxChat extends RxChat {
return _guard.protect(() async {
status.value = RxStatus.loading();
messages.clear();
_muteTimer?.cancel();
_localSubscription?.cancel();
_remoteSubscription?.cancel();
_remoteSubscriptionInitialized = false;
Expand Down Expand Up @@ -520,6 +524,14 @@ class HiveRxChat extends RxChat {
avatar.value = chat.value.avatar;
}

_muteTimer?.cancel();
if (chat.value.muted?.until != null) {
_muteTimer = Timer(
chat.value.muted!.until!.val.difference(DateTime.now()),
() => chat.value.muted = null,
);
}
skazkiful marked this conversation as resolved.
Show resolved Hide resolved
skazkiful marked this conversation as resolved.
Show resolved Hide resolved

// TODO: Users list can be huge, so we should implement pagination and
// loading on demand.
for (var m in chat.value.members) {
Expand Down
Loading