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

Replace localStorage usage with drift tables (#1045) #1049

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 38 additions & 0 deletions lib/domain/model/chat_call.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import '/api/backend/schema.dart';
import '/util/new_type.dart';
import 'chat.dart';
import 'chat_item.dart';
import 'ongoing_call.dart';
import 'precise_date_time/precise_date_time.dart';
import 'user.dart';

Expand Down Expand Up @@ -291,3 +292,40 @@ class ChatMembersDialedConcrete implements ChatMembersDialed {
@override
int get hashCode => members.hashCode;
}

/// Credentials information of an [OngoingCall].
@JsonSerializable()
class ActiveCall {
const ActiveCall({
required this.chatId,
this.call,
this.creds,
this.deviceId,
this.state = OngoingCallState.local,
});

/// Constructs an [ActiveCall] from the provided [json].
factory ActiveCall.fromJson(Map<String, dynamic> json) =>
_$ActiveCallFromJson(json);

/// [ChatCall] of this [ActiveCall].
final ChatCall? call;

/// [ChatId] of this [ActiveCall].
final ChatId chatId;

/// Stored [OngoingCall.creds].
final ChatCallCredentials? creds;

/// Stored [OngoingCall.deviceId].
final ChatCallDeviceId? deviceId;

/// Stored [OngoingCall.state].
final OngoingCallState state;

/// Returns a [Map] representing this [ActiveCall].
Map<String, dynamic> toJson() => _$ActiveCallToJson(this);

@override
String toString() => 'ActiveCall(${call?.id})';
}
12 changes: 12 additions & 0 deletions lib/domain/model/ongoing_call.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,18 @@ class OngoingCall {
_notifications.add(ErrorNotification(message: message));
}

/// Constructs a [ActiveCall] containing all necessary information of this
/// [OngoingCall].
ActiveCall toStored() {
return ActiveCall(
chatId: chatId.value,
call: call.value,
creds: creds,
state: state.value,
deviceId: deviceId,
);
}

/// Returns [MediaStreamSettings] with [audio], [video], [screen] enabled or
/// not.
///
Expand Down
14 changes: 7 additions & 7 deletions lib/domain/repository/call.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import '/l10n/l10n.dart';
import '/store/event/chat_call.dart';
import '/util/localized_exception.dart';
import '/util/obs/obs.dart';
import '/util/web/web_utils.dart';

/// [OngoingCall]s repository interface.
abstract class AbstractCallRepository {
Expand All @@ -45,10 +44,10 @@ abstract class AbstractCallRepository {
/// Adds the provided [ChatCall] to the [calls], if not already.
Future<Rx<OngoingCall>?> add(ChatCall call);

/// Transforms the provided [WebStoredCall] into an [OngoingCall] and adds it,
/// if not already.
/// Transforms the provided [ActiveCall] into an [OngoingCall] and adds it, if
/// not already.
Rx<OngoingCall> addStored(
WebStoredCall stored, {
ActiveCall stored, {
bool withAudio = true,
bool withVideo = true,
bool withScreen = false,
Expand All @@ -62,9 +61,10 @@ abstract class AbstractCallRepository {
/// [chatId].
Rx<OngoingCall>? remove(ChatId chatId);

/// Returns `true` if an [OngoingCall] identified by [chatId] exists in the
/// [calls] map.
bool contains(ChatId chatId);
/// Returns `true` if an [OngoingCall] identified by [chatId] exists.
Future<bool> contains(ChatId chatId);

Future<ActiveCall?> get(ChatId chatId);

/// Starts a new [OngoingCall] in the specified [chatId] by the authenticated
/// [MyUser].
Expand Down
62 changes: 0 additions & 62 deletions lib/domain/service/auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
// <https://www.gnu.org/licenses/agpl-3.0.html>.

import 'dart:async';
import 'dart:convert';

import 'package:collection/collection.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart' show visibleForTesting;
import 'package:get/get.dart';
Expand Down Expand Up @@ -90,10 +88,6 @@ class AuthService extends DisposableService {
/// Minimal allowed [credentials] TTL.
final Duration _accessTokenMinTtl = const Duration(minutes: 2);

/// [StreamSubscription] to [WebUtils.onStorageChange] fetching new
/// [Credentials].
StreamSubscription? _storageSubscription;

/// Returns the currently authorized [Credentials.userId].
UserId? get userId => credentials.value?.userId;

Expand All @@ -111,7 +105,6 @@ class AuthService extends DisposableService {
void onClose() {
Log.debug('onClose()', '$runtimeType');

_storageSubscription?.cancel();
_refreshTimers.forEach((_, t) => t.cancel());
_refreshTimers.clear();
}
Expand All @@ -137,62 +130,9 @@ class AuthService extends DisposableService {
}
};

// Listen to the [Credentials] changes to stay synchronized with another
// tabs.
_storageSubscription = WebUtils.onStorageChange.listen((e) {
if (e.key?.startsWith('credentials_') ?? false) {
Log.debug(
'_storageSubscription(${e.key}): received a credentials update',
'$runtimeType',
);
if (e.newValue != null) {
final Credentials received =
Credentials.fromJson(json.decode(e.newValue!));
Credentials? current = credentials.value;
final bool authorized = _hasAuthorization;

if (!authorized ||
received.userId == current?.userId &&
received.access.secret != current?.access.secret) {
// These [Credentials] should be treated as current ones, so just
// apply them as saving to local storage has already been performed
// by another tab.
_authRepository.token = received.access.secret;
_authRepository.applyToken();
credentials.value = received;
_putCredentials(received);
status.value = RxStatus.success();

if (!authorized) {
router.home();
}
} else {
current = accounts[received.userId]?.value;
if (received.access.secret != current?.access.secret) {
// These [Credentials] are of another account, so just save them.
_putCredentials(received);
}
}
} else {
final UserId? deletedId = accounts.keys
.firstWhereOrNull((k) => e.key?.endsWith(k.val) ?? false);

accounts.remove(deletedId);

final bool currentAreNull = credentials.value == null;
final bool currentDeleted = deletedId != null && deletedId == userId;

if ((currentAreNull || currentDeleted) && !WebUtils.isPopup) {
router.go(_unauthorized());
}
}
}
});

return await WebUtils.protect(() async {
final List<Credentials> allCredentials = await _credentialsProvider.all();
for (final Credentials e in allCredentials) {
WebUtils.putCredentials(e);
_putCredentials(e);
}

Expand Down Expand Up @@ -842,7 +782,6 @@ class AuthService extends DisposableService {
credentials.value = creds;
_putCredentials(creds);

WebUtils.putCredentials(creds);
await Future.wait([
_credentialsProvider.upsert(creds),
_accountProvider.upsert(creds.userId),
Expand All @@ -862,7 +801,6 @@ class AuthService extends DisposableService {
_credentialsProvider.delete(id);
_refreshTimers.remove(id)?.cancel();
accounts.remove(id);
WebUtils.removeCredentials(id);
}

if (id == _accountProvider.userId) {
Expand Down
31 changes: 17 additions & 14 deletions lib/domain/service/call.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class CallService extends DisposableService {

final Rx<OngoingCall>? stored = _callsRepo[chatId];

if (WebUtils.containsCall(chatId)) {
if (await _callsRepo.contains(chatId) && !WebUtils.isPopup) {
throw CallIsInPopupException();
} else if (stored != null &&
stored.value.state.value != OngoingCallState.ended) {
Expand Down Expand Up @@ -121,7 +121,8 @@ class CallService extends DisposableService {
'$runtimeType',
);

if (WebUtils.containsCall(chatId) && !WebUtils.isPopup) {
// TODO: Try pinging the popup to ensure it still exists.
if (await _callsRepo.contains(chatId) && !WebUtils.isPopup) {
throw CallIsInPopupException();
}

Expand Down Expand Up @@ -184,7 +185,7 @@ class CallService extends DisposableService {
call.value.dispose();
}

deviceId ??= WebUtils.getCall(chatId)?.deviceId;
deviceId ??= (await _callsRepo.get(chatId))?.deviceId;

if (deviceId != null) {
await _callsRepo.leave(chatId, deviceId);
Expand All @@ -193,7 +194,6 @@ class CallService extends DisposableService {
}

_callsRepo.remove(chatId);
WebUtils.removeCall(chatId);
}

/// Declines an [OngoingCall] identified by the given [chatId].
Expand All @@ -218,7 +218,7 @@ class CallService extends DisposableService {

/// Constructs an [OngoingCall] from the provided [stored] call.
Rx<OngoingCall> addStored(
WebStoredCall stored, {
ActiveCall stored, {
bool withAudio = true,
bool withVideo = true,
bool withScreen = false,
Expand Down Expand Up @@ -258,7 +258,7 @@ class CallService extends DisposableService {
Future<void> redialChatCallMember(ChatId chatId, UserId memberId) async {
Log.debug('redialChatCallMember($chatId, $memberId)', '$runtimeType');

if (_callsRepo.contains(chatId)) {
if (await _callsRepo.contains(chatId)) {
await _callsRepo.redialChatCallMember(chatId, memberId);
}
}
Expand All @@ -271,7 +271,7 @@ class CallService extends DisposableService {
Future<void> removeChatCallMember(ChatId chatId, UserId userId) async {
Log.debug('removeChatCallMember($chatId, $userId)', '$runtimeType');

if (_callsRepo.contains(chatId)) {
if (await _callsRepo.contains(chatId)) {
await _callsRepo.removeChatCallMember(chatId, userId);
}
}
Expand Down Expand Up @@ -321,13 +321,10 @@ class CallService extends DisposableService {
'$runtimeType',
);

final Rx<OngoingCall>? call = _callsRepo[chatId];
if (call != null) {
_callsRepo.move(chatId, newChatId);
_callsRepo.moveCredentials(callId, newCallId, chatId, newChatId);
if (WebUtils.isPopup) {
WebUtils.moveCall(chatId, newChatId, newState: call.value.toStored());
}
_callsRepo.move(chatId, newChatId);
_callsRepo.moveCredentials(callId, newCallId, chatId, newChatId);
if (WebUtils.isPopup) {
WebUtils.moveCall(chatId, newChatId);
}
}

Expand All @@ -350,4 +347,10 @@ class CallService extends DisposableService {
Log.debug('getChat($id)', '$runtimeType');
return _chatService.get(id);
}

/// Returns a [RxChat] by the provided [id].
Future<ActiveCall?> getCall(ChatId id) async {
Log.debug('getCall($id)', '$runtimeType');
return _callsRepo.get(id);
}
}
Loading