Merge branch 'main' of gitlab.com:famedly/company/frontend/famedlysdk into td/enable_group
This commit is contained in:
commit
9eb74f08c2
|
|
@ -1,8 +1,7 @@
|
|||
stages:
|
||||
- coverage
|
||||
- builddocs
|
||||
- deploy
|
||||
- publish
|
||||
include:
|
||||
- project: "famedly/company/frontend/ci-templates"
|
||||
ref: main
|
||||
file: "/all.yml"
|
||||
|
||||
workflow:
|
||||
rules:
|
||||
|
|
@ -16,7 +15,7 @@ variables:
|
|||
coverage:
|
||||
tags:
|
||||
- linux
|
||||
stage: coverage
|
||||
stage: test
|
||||
image: registry.gitlab.com/famedly/company/frontend/flutter-dockerimages/flutter-linux/stable:${FLUTTER_IMAGE_TAG}
|
||||
dependencies: []
|
||||
script:
|
||||
|
|
@ -31,7 +30,7 @@ coverage:
|
|||
coverage_without_olm:
|
||||
tags:
|
||||
- linux
|
||||
stage: coverage
|
||||
stage: test
|
||||
image: dart
|
||||
dependencies: []
|
||||
script:
|
||||
|
|
@ -41,7 +40,7 @@ coverage_without_olm:
|
|||
e2ee_test:
|
||||
tags:
|
||||
- linux
|
||||
stage: coverage
|
||||
stage: test
|
||||
image: registry.gitlab.com/famedly/company/frontend/flutter-dockerimages/flutter-linux/stable:${FLUTTER_IMAGE_TAG}
|
||||
dependencies: []
|
||||
script:
|
||||
|
|
@ -51,35 +50,8 @@ e2ee_test:
|
|||
timeout: 16m
|
||||
resource_group: e2ee_test
|
||||
|
||||
code_analyze:
|
||||
tags:
|
||||
- docker
|
||||
stage: coverage
|
||||
image: dart
|
||||
dependencies: []
|
||||
script:
|
||||
- dart pub get
|
||||
- dart format lib/ test/ test_driver/ --set-exit-if-changed
|
||||
- dart analyze
|
||||
|
||||
code_quality:
|
||||
tags:
|
||||
- docker
|
||||
stage: coverage
|
||||
image: dart
|
||||
before_script:
|
||||
- dart pub global activate dart_code_metrics
|
||||
script:
|
||||
- dart pub global run dart_code_metrics:metrics analyze lib -r gitlab > code-quality-report.json
|
||||
artifacts:
|
||||
reports:
|
||||
codequality: code-quality-report.json
|
||||
# also create an actual artifact for inspection purposes
|
||||
paths:
|
||||
- code-quality-report.json
|
||||
|
||||
dry-run:
|
||||
stage: publish
|
||||
stage: deploy
|
||||
image: dart
|
||||
script:
|
||||
- rm -rf ./docs
|
||||
|
|
@ -88,7 +60,7 @@ dry-run:
|
|||
|
||||
|
||||
pub-dev:
|
||||
stage: publish
|
||||
stage: deploy
|
||||
image: dart
|
||||
dependencies: [
|
||||
dry-run
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@
|
|||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
|
||||
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||
import '../matrix.dart';
|
||||
import 'encryption.dart';
|
||||
import 'ssss.dart';
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,16 +19,16 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
|
||||
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||
import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import '../matrix.dart';
|
||||
import '../src/utils/run_in_root.dart';
|
||||
import './encryption.dart';
|
||||
import './utils/outbound_group_session.dart';
|
||||
import './utils/session_key.dart';
|
||||
import '../matrix.dart';
|
||||
import '../src/utils/run_in_root.dart';
|
||||
|
||||
const megolmKey = EventTypes.MegolmBackup;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import 'dart:convert';
|
|||
import 'package:async/async.dart';
|
||||
import 'package:canonical_json/canonical_json.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../encryption/utils/json_signature_check_extension.dart';
|
||||
import '../src/utils/run_in_root.dart';
|
||||
import 'encryption.dart';
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ import 'dart:core';
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:base58check/base58.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||
import '../matrix.dart';
|
||||
import '../src/utils/crypto/crypto.dart' as uc;
|
||||
import '../src/utils/run_in_root.dart';
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ import 'dart:typed_data';
|
|||
import 'package:canonical_json/canonical_json.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
|
||||
import '../encryption.dart';
|
||||
import '../ssss.dart';
|
||||
import '../key_manager.dart';
|
||||
import '../../matrix.dart';
|
||||
import '../encryption.dart';
|
||||
import '../key_manager.dart';
|
||||
import '../ssss.dart';
|
||||
import 'base64_unpadded.dart';
|
||||
|
||||
enum BootstrapState {
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
|
||||
import 'package:matrix_api_lite/src/utils/filter_map_extension.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
|
||||
import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
|
||||
import '../../matrix.dart';
|
||||
|
||||
class SessionKey {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ library msc_2835_uia_login;
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' hide Client;
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
extension UiaLogin on Client {
|
||||
|
|
|
|||
|
|
@ -23,15 +23,16 @@ import 'dart:typed_data';
|
|||
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||
import 'package:matrix/src/utils/run_in_root.dart';
|
||||
import 'package:matrix/src/utils/sync_update_item_count.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:random_string/random_string.dart';
|
||||
|
||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||
import 'package:matrix/src/utils/run_in_root.dart';
|
||||
import 'package:matrix/src/utils/sync_update_item_count.dart';
|
||||
import '../encryption.dart';
|
||||
import '../matrix.dart';
|
||||
import 'models/timeline_chunk.dart';
|
||||
import 'utils/multilock.dart';
|
||||
import 'utils/run_benchmarked.dart';
|
||||
|
||||
|
|
@ -287,7 +288,7 @@ class Client extends MatrixApi {
|
|||
/// found. If you have loaded the [loadArchive()] before, it can also return
|
||||
/// archived rooms.
|
||||
Room? getRoomById(String id) {
|
||||
for (final room in <Room>[...rooms, ..._archivedRooms]) {
|
||||
for (final room in <Room>[...rooms, ..._archivedRooms.map((e) => e.room)]) {
|
||||
if (room.id == id) return room;
|
||||
}
|
||||
|
||||
|
|
@ -781,9 +782,35 @@ class Client extends MatrixApi {
|
|||
avatarUrl: profile.avatarUrl);
|
||||
}
|
||||
|
||||
final List<Room> _archivedRooms = [];
|
||||
final List<ArchivedRoom> _archivedRooms = [];
|
||||
|
||||
/// Return an archive room containing the room and the timeline for a specific archived room.
|
||||
ArchivedRoom? getArchiveRoomFromCache(String roomId) {
|
||||
for (var i = 0; i < _archivedRooms.length; i++) {
|
||||
final archive = _archivedRooms[i];
|
||||
if (archive.room.id == roomId) return archive;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Remove all the archives stored in cache.
|
||||
void clearArchivesFromCache() {
|
||||
_archivedRooms.clear();
|
||||
}
|
||||
|
||||
@Deprecated('Use [loadArchive()] instead.')
|
||||
Future<List<Room>> get archive => loadArchive();
|
||||
|
||||
/// Fetch all the archived rooms from the server and return the list of the
|
||||
/// room. If you want to have the Timelines bundled with it, use
|
||||
/// loadArchiveWithTimeline instead.
|
||||
Future<List<Room>> loadArchive() async {
|
||||
return (await loadArchiveWithTimeline()).map((e) => e.room).toList();
|
||||
}
|
||||
|
||||
/// Fetch the archived rooms from the server and return them as a list of
|
||||
/// [ArchivedRoom] objects containing the [Room] and the associated [Timeline].
|
||||
Future<List<ArchivedRoom>> loadArchiveWithTimeline() async {
|
||||
_archivedRooms.clear();
|
||||
final syncResp = await sync(
|
||||
filter: '{"room":{"include_leave":true,"timeline":{"limit":10}}}',
|
||||
|
|
@ -804,12 +831,32 @@ class Client extends MatrixApi {
|
|||
<String, BasicRoomEvent>{},
|
||||
);
|
||||
|
||||
final timeline = Timeline(
|
||||
room: leftRoom,
|
||||
chunk: TimelineChunk(
|
||||
events: room.timeline?.events?.reversed
|
||||
.toList() // we display the event in the other sence
|
||||
.map((e) => Event.fromMatrixEvent(e, leftRoom))
|
||||
.toList() ??
|
||||
[]));
|
||||
|
||||
for (var i = 0; i < timeline.events.length; i++) {
|
||||
// Try to decrypt encrypted events but don't update the database.
|
||||
if (leftRoom.encrypted && leftRoom.client.encryptionEnabled) {
|
||||
if (timeline.events[i].type == EventTypes.Encrypted) {
|
||||
timeline.events[i] = await leftRoom.client.encryption!
|
||||
.decryptRoomEvent(leftRoom.id, timeline.events[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
room.timeline?.events?.forEach((event) {
|
||||
leftRoom.setState(Event.fromMatrixEvent(
|
||||
event,
|
||||
leftRoom,
|
||||
));
|
||||
});
|
||||
|
||||
leftRoom.prev_batch = room.timeline?.prevBatch;
|
||||
room.state?.forEach((event) {
|
||||
leftRoom.setState(Event.fromMatrixEvent(
|
||||
|
|
@ -817,7 +864,8 @@ class Client extends MatrixApi {
|
|||
leftRoom,
|
||||
));
|
||||
});
|
||||
_archivedRooms.add(leftRoom);
|
||||
|
||||
_archivedRooms.add(ArchivedRoom(room: leftRoom, timeline: timeline));
|
||||
}
|
||||
}
|
||||
return _archivedRooms;
|
||||
|
|
@ -1658,6 +1706,12 @@ class Client extends MatrixApi {
|
|||
await database?.storeRoomUpdate(id, syncRoomUpdate, this);
|
||||
final room = _updateRoomsByRoomUpdate(id, syncRoomUpdate);
|
||||
|
||||
final timelineUpdateType = direction != null
|
||||
? (direction == Direction.b
|
||||
? EventUpdateType.history
|
||||
: EventUpdateType.timeline)
|
||||
: EventUpdateType.timeline;
|
||||
|
||||
/// Handle now all room events and save them in the database
|
||||
if (syncRoomUpdate is JoinedRoomUpdate) {
|
||||
final state = syncRoomUpdate.state;
|
||||
|
|
@ -1673,14 +1727,7 @@ class Client extends MatrixApi {
|
|||
|
||||
final timelineEvents = syncRoomUpdate.timeline?.events;
|
||||
if (timelineEvents != null && timelineEvents.isNotEmpty) {
|
||||
await _handleRoomEvents(
|
||||
room,
|
||||
timelineEvents,
|
||||
direction != null
|
||||
? (direction == Direction.b
|
||||
? EventUpdateType.history
|
||||
: EventUpdateType.timeline)
|
||||
: EventUpdateType.timeline);
|
||||
await _handleRoomEvents(room, timelineEvents, timelineUpdateType);
|
||||
}
|
||||
|
||||
final ephemeral = syncRoomUpdate.ephemeral;
|
||||
|
|
@ -1705,23 +1752,19 @@ class Client extends MatrixApi {
|
|||
if (syncRoomUpdate is LeftRoomUpdate) {
|
||||
final timelineEvents = syncRoomUpdate.timeline?.events;
|
||||
if (timelineEvents != null && timelineEvents.isNotEmpty) {
|
||||
await _handleRoomEvents(
|
||||
room,
|
||||
timelineEvents,
|
||||
EventUpdateType.timeline,
|
||||
);
|
||||
await _handleRoomEvents(room, timelineEvents, timelineUpdateType,
|
||||
store: false);
|
||||
}
|
||||
final accountData = syncRoomUpdate.accountData;
|
||||
if (accountData != null && accountData.isNotEmpty) {
|
||||
await _handleRoomEvents(
|
||||
room,
|
||||
accountData,
|
||||
EventUpdateType.accountData,
|
||||
);
|
||||
room, accountData, EventUpdateType.accountData,
|
||||
store: false);
|
||||
}
|
||||
final state = syncRoomUpdate.state;
|
||||
if (state != null && state.isNotEmpty) {
|
||||
await _handleRoomEvents(room, state, EventUpdateType.state);
|
||||
await _handleRoomEvents(room, state, EventUpdateType.state,
|
||||
store: false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1795,10 +1838,8 @@ class Client extends MatrixApi {
|
|||
}
|
||||
|
||||
Future<void> _handleRoomEvents(
|
||||
Room room,
|
||||
List<BasicEvent> events,
|
||||
EventUpdateType type,
|
||||
) async {
|
||||
Room room, List<BasicEvent> events, EventUpdateType type,
|
||||
{bool store = true}) async {
|
||||
// Calling events can be omitted if they are outdated from the same sync. So
|
||||
// we collect them first before we handle them.
|
||||
final callEvents = <Event>{};
|
||||
|
|
@ -1833,7 +1874,7 @@ class Client extends MatrixApi {
|
|||
}
|
||||
}
|
||||
_updateRoomsByEventUpdate(room, update);
|
||||
if (type != EventUpdateType.ephemeral) {
|
||||
if (type != EventUpdateType.ephemeral && store) {
|
||||
await database?.storeEventUpdate(update, this);
|
||||
}
|
||||
if (encryptionEnabled) {
|
||||
|
|
@ -1941,6 +1982,10 @@ class Client extends MatrixApi {
|
|||
|
||||
// Does the chat already exist in the list rooms?
|
||||
if (!found && membership != Membership.leave) {
|
||||
// Check if the room is not in the rooms in the invited list
|
||||
if (_archivedRooms.isNotEmpty) {
|
||||
_archivedRooms.removeWhere((archive) => archive.room.id == roomId);
|
||||
}
|
||||
final position = membership == Membership.invite ? 0 : rooms.length;
|
||||
// Add the new chat to the list
|
||||
rooms.insert(position, room);
|
||||
|
|
@ -2857,3 +2902,9 @@ class HomeserverSummary {
|
|||
required this.loginFlows,
|
||||
});
|
||||
}
|
||||
|
||||
class ArchivedRoom {
|
||||
final Room room;
|
||||
final Timeline timeline;
|
||||
ArchivedRoom({required this.room, required this.timeline});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import 'package:matrix/encryption/utils/outbound_group_session.dart';
|
|||
import 'package:matrix/encryption/utils/ssss_cache.dart';
|
||||
import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
|
||||
import 'package:matrix/src/utils/queued_to_device_event.dart';
|
||||
|
||||
import '../../matrix.dart';
|
||||
|
||||
abstract class DatabaseApi {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import 'dart:typed_data';
|
|||
|
||||
import 'package:fluffybox/fluffybox.dart';
|
||||
import 'package:fluffybox/hive.dart' show HiveCipher;
|
||||
|
||||
import 'package:matrix/encryption/utils/olm_session.dart';
|
||||
import 'package:matrix/encryption/utils/outbound_group_session.dart';
|
||||
import 'package:matrix/encryption/utils/ssss_cache.dart';
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import 'dart:math';
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
import 'package:matrix/encryption/utils/olm_session.dart';
|
||||
import 'package:matrix/encryption/utils/outbound_group_session.dart';
|
||||
import 'package:matrix/encryption/utils/ssss_cache.dart';
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import 'dart:math';
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
import 'package:matrix/encryption/utils/olm_session.dart';
|
||||
import 'package:matrix/encryption/utils/outbound_group_session.dart';
|
||||
import 'package:matrix/encryption/utils/ssss_cache.dart';
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import 'dart:typed_data';
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:matrix/src/utils/file_send_request_credentials.dart';
|
||||
|
||||
import 'package:matrix/src/utils/file_send_request_credentials.dart';
|
||||
import '../matrix.dart';
|
||||
import 'utils/event_localizations.dart';
|
||||
import 'utils/html_to_text.dart';
|
||||
|
|
|
|||
|
|
@ -22,12 +22,12 @@ import 'dart:typed_data';
|
|||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:html_unescape/html_unescape.dart';
|
||||
|
||||
import 'package:matrix/src/models/timeline_chunk.dart';
|
||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||
import 'package:matrix/src/utils/crypto/crypto.dart';
|
||||
import 'package:matrix/src/utils/file_send_request_credentials.dart';
|
||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||
import 'package:matrix/src/utils/space_child.dart';
|
||||
|
||||
import '../matrix.dart';
|
||||
import 'utils/markdown.dart';
|
||||
import 'utils/marked_unread.dart';
|
||||
|
|
@ -1136,6 +1136,9 @@ class Room {
|
|||
void Function()? onHistoryReceived,
|
||||
direction = Direction.b}) async {
|
||||
final prev_batch = this.prev_batch;
|
||||
|
||||
final storeInDatabase = !isArchived;
|
||||
|
||||
if (prev_batch == null) {
|
||||
throw 'Tried to request history without a prev_batch token';
|
||||
}
|
||||
|
|
@ -1179,7 +1182,9 @@ class Room {
|
|||
state: resp.state,
|
||||
timeline: TimelineUpdate(
|
||||
limited: false,
|
||||
events: resp.chunk,
|
||||
events: direction == Direction.b
|
||||
? resp.chunk
|
||||
: resp.chunk.reversed.toList(),
|
||||
prevBatch: direction == Direction.b
|
||||
? resp.end
|
||||
: resp.start,
|
||||
|
|
@ -1193,7 +1198,9 @@ class Room {
|
|||
|
||||
if (client.database != null) {
|
||||
await client.database?.transaction(() async {
|
||||
await client.database?.setRoomPrevBatch(resp.end!, id, client);
|
||||
if (storeInDatabase) {
|
||||
await client.database?.setRoomPrevBatch(resp.end!, id, client);
|
||||
}
|
||||
await loadFn();
|
||||
});
|
||||
} else {
|
||||
|
|
@ -1304,6 +1311,9 @@ class Room {
|
|||
return;
|
||||
}
|
||||
|
||||
/// Is the room archived
|
||||
bool get isArchived => membership == Membership.leave;
|
||||
|
||||
/// Creates a timeline from the store. Returns a [Timeline] object. If you
|
||||
/// just want to update the whole timeline on every change, use the [onUpdate]
|
||||
/// callback. For updating only the parts that have changed, use the
|
||||
|
|
@ -1319,35 +1329,41 @@ class Room {
|
|||
String? eventContextId}) async {
|
||||
await postLoad();
|
||||
|
||||
final _events = await client.database?.getEventList(
|
||||
this,
|
||||
limit: defaultHistoryCount,
|
||||
);
|
||||
var events;
|
||||
|
||||
var chunk = TimelineChunk(events: _events ?? []);
|
||||
if (!isArchived) {
|
||||
events = await client.database?.getEventList(
|
||||
this,
|
||||
limit: defaultHistoryCount,
|
||||
) ??
|
||||
<Event>[];
|
||||
} else {
|
||||
final archive = client.getArchiveRoomFromCache(id);
|
||||
events = archive?.timeline.events.toList() ?? [];
|
||||
}
|
||||
|
||||
if (_events != null) {
|
||||
if (eventContextId != null) {
|
||||
if (_events
|
||||
.firstWhereOrNull((event) => event.eventId == eventContextId) !=
|
||||
null) {
|
||||
chunk = TimelineChunk(events: _events);
|
||||
} else {
|
||||
chunk = await getEventContext(eventContextId) ??
|
||||
TimelineChunk(events: []);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all users from database we have got here.
|
||||
if (eventContextId == null) {
|
||||
for (final event in _events) {
|
||||
if (getState(EventTypes.RoomMember, event.senderId) != null) continue;
|
||||
final dbUser = await client.database?.getUser(event.senderId, this);
|
||||
if (dbUser != null) setState(dbUser);
|
||||
}
|
||||
var chunk = TimelineChunk(events: events);
|
||||
// Load the timeline arround eventContextId if set
|
||||
if (eventContextId != null) {
|
||||
if (events.firstWhereOrNull((event) => event.eventId == eventContextId) !=
|
||||
null) {
|
||||
chunk = TimelineChunk(events: events);
|
||||
} else {
|
||||
chunk =
|
||||
await getEventContext(eventContextId) ?? TimelineChunk(events: []);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all users from database we have got here.
|
||||
if (eventContextId == null) {
|
||||
for (final event in events) {
|
||||
if (getState(EventTypes.RoomMember, event.senderId) != null) continue;
|
||||
final dbUser = await client.database?.getUser(event.senderId, this);
|
||||
if (dbUser != null) setState(dbUser);
|
||||
}
|
||||
}
|
||||
|
||||
// Try again to decrypt encrypted events and update the database.
|
||||
if (encrypted && client.encryptionEnabled) {
|
||||
// decrypt messages
|
||||
for (var i = 0; i < chunk.events.length; i++) {
|
||||
|
|
@ -1364,8 +1380,9 @@ class Room {
|
|||
await client.database?.transaction(() async {
|
||||
for (var i = 0; i < chunk.events.length; i++) {
|
||||
if (chunk.events[i].content['can_request_session'] == true) {
|
||||
chunk.events[i] = await client.encryption!
|
||||
.decryptRoomEvent(id, chunk.events[i], store: true);
|
||||
chunk.events[i] = await client.encryption!.decryptRoomEvent(
|
||||
id, chunk.events[i],
|
||||
store: !isArchived);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -224,9 +224,7 @@ class Timeline {
|
|||
}
|
||||
|
||||
// Try to decrypt encrypted events but don't update the database.
|
||||
if (room.encrypted &&
|
||||
room.client.database != null &&
|
||||
room.client.encryptionEnabled) {
|
||||
if (room.encrypted && room.client.encryptionEnabled) {
|
||||
for (var i = 0; i < newEvents.length; i++) {
|
||||
if (newEvents[i].type == EventTypes.Encrypted) {
|
||||
newEvents[i] = await room.client.encryption!
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
|
||||
export 'native.dart' if (dart.library.js) 'js.dart';
|
||||
|
||||
import 'dart:typed_data';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
Uint8List secureRandomBytes(int len) {
|
||||
final rng = Random.secure();
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||
|
||||
import 'crypto.dart';
|
||||
|
||||
class EncryptedFile {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'subtle.dart';
|
||||
import 'subtle.dart' as subtle;
|
||||
import 'subtle.dart';
|
||||
|
||||
abstract class Hash {
|
||||
Hash._(this.name);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ffi';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'ffi.dart';
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@
|
|||
@JS()
|
||||
library subtle;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:js_util';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:js/js.dart';
|
||||
|
||||
@JS()
|
||||
@anonymous
|
||||
class Pbkdf2Params {
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ import 'dart:convert';
|
|||
|
||||
import 'package:canonical_json/canonical_json.dart';
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../../encryption.dart';
|
||||
|
||||
enum UserVerifiedStatus { verified, unknown, unknownDevice }
|
||||
|
|
|
|||
|
|
@ -17,9 +17,8 @@
|
|||
*/
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:html_unescape/html_unescape.dart';
|
||||
|
||||
class HtmlToText {
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:slugify/slugify.dart';
|
||||
import 'package:matrix_api_lite/matrix_api_lite.dart';
|
||||
import 'package:slugify/slugify.dart';
|
||||
|
||||
import '../room.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:markdown/markdown.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:markdown/markdown.dart';
|
||||
|
||||
const htmlAttrEscape = HtmlEscape(HtmlEscapeMode.attribute);
|
||||
|
||||
class LinebreakSyntax extends InlineSyntax {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
class MatrixDefaultLocalizations extends MatrixLocalizations {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
/// Lock management class. It allows to lock and unlock multiple keys at once. The keys have
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
class QueuedToDeviceEvent {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@
|
|||
import 'dart:async';
|
||||
import 'dart:core';
|
||||
|
||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||
import 'package:webrtc_interface/webrtc_interface.dart';
|
||||
|
||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||
import '../../matrix.dart';
|
||||
|
||||
/// https://github.com/matrix-org/matrix-doc/pull/2746
|
||||
|
|
@ -84,9 +84,9 @@ class WrappedMediaStream {
|
|||
renderer.srcObject = null;
|
||||
if (isLocal() && !isGroupCall && stream != null) {
|
||||
if (isWeb) {
|
||||
stream!.getTracks().forEach((element) {
|
||||
element.stop();
|
||||
});
|
||||
for (final element in stream!.getTracks()) {
|
||||
await element.stop();
|
||||
}
|
||||
}
|
||||
await stream?.dispose();
|
||||
stream = null;
|
||||
|
|
@ -395,7 +395,7 @@ class CallSession {
|
|||
setCallState(CallState.kCreateOffer);
|
||||
final stream = await _getUserMedia(type);
|
||||
if (stream != null) {
|
||||
addLocalStream(stream, SDPStreamMetadataPurpose.Usermedia);
|
||||
await addLocalStream(stream, SDPStreamMetadataPurpose.Usermedia);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -407,7 +407,7 @@ class CallSession {
|
|||
if (!isGroupCall) {
|
||||
final stream = await _getUserMedia(type);
|
||||
if (stream != null) {
|
||||
addLocalStream(stream, SDPStreamMetadataPurpose.Usermedia);
|
||||
await addLocalStream(stream, SDPStreamMetadataPurpose.Usermedia);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -473,12 +473,12 @@ class CallSession {
|
|||
|
||||
waitForLocalAVStream = false;
|
||||
|
||||
callFeeds.forEach((element) async {
|
||||
addLocalStream(
|
||||
for (final element in callFeeds) {
|
||||
await addLocalStream(
|
||||
await voip.delegate.cloneStream(element.stream!), element.purpose);
|
||||
});
|
||||
}
|
||||
|
||||
answer();
|
||||
await answer();
|
||||
}
|
||||
|
||||
Future<void> placeCallWithStreams(List<WrappedMediaStream> callFeeds,
|
||||
|
|
@ -490,27 +490,27 @@ class CallSession {
|
|||
// create the peer connection now so it can be gathering candidates while we get user
|
||||
// media (assuming a candidate pool size is configured)
|
||||
await _preparePeerConnection();
|
||||
gotCallFeedsForInvite(callFeeds, requestScreenshareFeed);
|
||||
await gotCallFeedsForInvite(callFeeds, requestScreenshareFeed);
|
||||
}
|
||||
|
||||
void gotCallFeedsForInvite(List<WrappedMediaStream> callFeeds,
|
||||
[bool requestScreenshareFeed = false]) {
|
||||
Future<void> gotCallFeedsForInvite(List<WrappedMediaStream> callFeeds,
|
||||
[bool requestScreenshareFeed = false]) async {
|
||||
if (successor != null) {
|
||||
successor!.gotCallFeedsForAnswer(callFeeds);
|
||||
await successor!.gotCallFeedsForAnswer(callFeeds);
|
||||
return;
|
||||
}
|
||||
if (state == CallState.kEnded) {
|
||||
cleanUp();
|
||||
await cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
callFeeds.forEach((element) async {
|
||||
addLocalStream(
|
||||
for (final element in callFeeds) {
|
||||
await addLocalStream(
|
||||
await voip.delegate.cloneStream(element.stream!), element.purpose);
|
||||
});
|
||||
}
|
||||
|
||||
if (requestScreenshareFeed) {
|
||||
pc!.addTransceiver(
|
||||
await pc!.addTransceiver(
|
||||
kind: RTCRtpMediaType.RTCRtpMediaTypeVideo,
|
||||
init:
|
||||
RTCRtpTransceiverInit(direction: TransceiverDirection.RecvOnly));
|
||||
|
|
@ -526,7 +526,7 @@ class CallSession {
|
|||
setCallState(CallState.kEnded);
|
||||
}
|
||||
|
||||
void onAnswerReceived(
|
||||
Future<void> onAnswerReceived(
|
||||
RTCSessionDescription answer, SDPStreamMetadata? metadata) async {
|
||||
if (metadata != null) {
|
||||
_updateRemoteSDPStreamMetadata(metadata);
|
||||
|
|
@ -535,7 +535,9 @@ class CallSession {
|
|||
if (direction == CallDirection.kOutgoing) {
|
||||
setCallState(CallState.kConnecting);
|
||||
await pc!.setRemoteDescription(answer);
|
||||
remoteCandidates.forEach((candidate) => pc!.addCandidate(candidate));
|
||||
for (final candidate in remoteCandidates) {
|
||||
await pc!.addCandidate(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
/// Send select_answer event.
|
||||
|
|
@ -543,7 +545,7 @@ class CallSession {
|
|||
opts.room, callId, lifetimeMs, localPartyId, remotePartyId!);
|
||||
}
|
||||
|
||||
void onNegotiateReceived(
|
||||
Future<void> onNegotiateReceived(
|
||||
SDPStreamMetadata? metadata, RTCSessionDescription description) async {
|
||||
final polite = direction == CallDirection.kIncoming;
|
||||
|
||||
|
|
@ -611,13 +613,13 @@ class CallSession {
|
|||
});
|
||||
}
|
||||
|
||||
void onSDPStreamMetadataReceived(SDPStreamMetadata metadata) async {
|
||||
Future<void> onSDPStreamMetadataReceived(SDPStreamMetadata metadata) async {
|
||||
_updateRemoteSDPStreamMetadata(metadata);
|
||||
fireCallEvent(CallEvent.kFeedsChanged);
|
||||
}
|
||||
|
||||
void onCandidatesReceived(List<dynamic> candidates) {
|
||||
candidates.forEach((json) async {
|
||||
Future<void> onCandidatesReceived(List<dynamic> candidates) async {
|
||||
for (final json in candidates) {
|
||||
final candidate = RTCIceCandidate(
|
||||
json['candidate'],
|
||||
json['sdpMid'] ?? '',
|
||||
|
|
@ -633,16 +635,16 @@ class CallSession {
|
|||
} else {
|
||||
remoteCandidates.add(candidate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (pc != null &&
|
||||
pc!.iceConnectionState ==
|
||||
RTCIceConnectionState.RTCIceConnectionStateDisconnected) {
|
||||
restartIce();
|
||||
await restartIce();
|
||||
}
|
||||
}
|
||||
|
||||
void onAssertedIdentityReceived(AssertedIdentity identity) async {
|
||||
void onAssertedIdentityReceived(AssertedIdentity identity) {
|
||||
remoteAssertedIdentity = identity;
|
||||
fireCallEvent(CallEvent.kAssertedIdentityChanged);
|
||||
}
|
||||
|
|
@ -674,7 +676,7 @@ class CallSession {
|
|||
setScreensharingEnabled(false);
|
||||
};
|
||||
});
|
||||
addLocalStream(stream, SDPStreamMetadataPurpose.Screenshare);
|
||||
await addLocalStream(stream, SDPStreamMetadataPurpose.Screenshare);
|
||||
return true;
|
||||
} catch (err) {
|
||||
fireCallEvent(CallEvent.kError);
|
||||
|
|
@ -696,7 +698,7 @@ class CallSession {
|
|||
}
|
||||
}
|
||||
|
||||
void addLocalStream(MediaStream stream, String purpose,
|
||||
Future<void> addLocalStream(MediaStream stream, String purpose,
|
||||
{bool addToPeerConnection = true}) async {
|
||||
final existingStream =
|
||||
getLocalStreams.where((element) => element.purpose == purpose);
|
||||
|
|
@ -722,14 +724,14 @@ class CallSession {
|
|||
if (addToPeerConnection) {
|
||||
if (purpose == SDPStreamMetadataPurpose.Screenshare) {
|
||||
screensharingSenders.clear();
|
||||
stream.getTracks().forEach((track) async {
|
||||
for (final track in stream.getTracks()) {
|
||||
screensharingSenders.add(await pc!.addTrack(track, stream));
|
||||
});
|
||||
}
|
||||
} else if (purpose == SDPStreamMetadataPurpose.Usermedia) {
|
||||
usermediaSenders.clear();
|
||||
stream.getTracks().forEach((track) async {
|
||||
for (final track in stream.getTracks()) {
|
||||
usermediaSenders.add(await pc!.addTrack(track, stream));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -744,7 +746,7 @@ class CallSession {
|
|||
fireCallEvent(CallEvent.kFeedsChanged);
|
||||
}
|
||||
|
||||
void _addRemoteStream(MediaStream stream) async {
|
||||
Future<void> _addRemoteStream(MediaStream stream) async {
|
||||
//final userId = remoteUser.id;
|
||||
final metadata = remoteSDPStreamMetadata!.sdpStreamMetadatas[stream.id];
|
||||
if (metadata == null) {
|
||||
|
|
@ -783,17 +785,17 @@ class CallSession {
|
|||
Logs().i('Pushed remote stream (id="${stream.id}", purpose=$purpose)');
|
||||
}
|
||||
|
||||
void deleteAllStreams() {
|
||||
streams.forEach((stream) async {
|
||||
Future<void> deleteAllStreams() async {
|
||||
for (final stream in streams) {
|
||||
if (stream.isLocal() || groupCallId == null) {
|
||||
await stream.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
streams.clear();
|
||||
fireCallEvent(CallEvent.kFeedsChanged);
|
||||
}
|
||||
|
||||
void deleteFeedByStream(MediaStream stream) {
|
||||
Future<void> deleteFeedByStream(MediaStream stream) async {
|
||||
final index =
|
||||
streams.indexWhere((element) => element.stream!.id == stream.id);
|
||||
if (index == -1) {
|
||||
|
|
@ -802,23 +804,23 @@ class CallSession {
|
|||
}
|
||||
final wstream = streams.elementAt(index);
|
||||
onStreamRemoved.add(wstream);
|
||||
deleteStream(wstream);
|
||||
await deleteStream(wstream);
|
||||
}
|
||||
|
||||
void deleteStream(WrappedMediaStream stream) {
|
||||
stream.dispose();
|
||||
Future<void> deleteStream(WrappedMediaStream stream) async {
|
||||
await stream.dispose();
|
||||
streams.removeAt(streams.indexOf(stream));
|
||||
fireCallEvent(CallEvent.kFeedsChanged);
|
||||
}
|
||||
|
||||
void removeLocalStream(WrappedMediaStream callFeed) {
|
||||
Future<void> removeLocalStream(WrappedMediaStream callFeed) async {
|
||||
final senderArray = callFeed.purpose == SDPStreamMetadataPurpose.Usermedia
|
||||
? usermediaSenders
|
||||
: screensharingSenders;
|
||||
|
||||
senderArray.forEach((element) async {
|
||||
for (final element in senderArray) {
|
||||
await pc!.removeTrack(element);
|
||||
});
|
||||
}
|
||||
|
||||
if (callFeed.purpose == SDPStreamMetadataPurpose.Screenshare) {
|
||||
stopMediaStream(callFeed.stream);
|
||||
|
|
@ -827,7 +829,7 @@ class CallSession {
|
|||
// Empty the array
|
||||
senderArray.removeRange(0, senderArray.length);
|
||||
onStreamRemoved.add(callFeed);
|
||||
deleteStream(callFeed);
|
||||
await deleteStream(callFeed);
|
||||
}
|
||||
|
||||
void setCallState(CallState newState) {
|
||||
|
|
@ -850,7 +852,7 @@ class CallSession {
|
|||
|
||||
bool get isMicrophoneMuted => localUserMediaStream?.isAudioMuted() ?? false;
|
||||
|
||||
void setRemoteOnHold(bool onHold) async {
|
||||
Future<void> setRemoteOnHold(bool onHold) async {
|
||||
if (isRemoteOnHold == onHold) return;
|
||||
remoteOnHold = onHold;
|
||||
final transceivers = await pc!.getTransceivers();
|
||||
|
|
@ -859,7 +861,7 @@ class CallSession {
|
|||
? TransceiverDirection.SendOnly
|
||||
: TransceiverDirection.SendRecv);
|
||||
}
|
||||
_updateMuteStatus();
|
||||
await _updateMuteStatus();
|
||||
fireCallEvent(CallEvent.kRemoteHoldUnhold);
|
||||
}
|
||||
|
||||
|
|
@ -884,7 +886,7 @@ class CallSession {
|
|||
return callOnHold;
|
||||
}
|
||||
|
||||
void answer() async {
|
||||
Future<void> answer() async {
|
||||
if (inviteOrAnswerSent) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -895,7 +897,9 @@ class CallSession {
|
|||
setCallState(CallState.kCreateAnswer);
|
||||
|
||||
final answer = await pc!.createAnswer({});
|
||||
remoteCandidates.forEach((candidate) => pc!.addCandidate(candidate));
|
||||
for (final candidate in remoteCandidates) {
|
||||
await pc!.addCandidate(candidate);
|
||||
}
|
||||
|
||||
final callCapabilities = CallCapabilities()
|
||||
..dtmf = false
|
||||
|
|
@ -924,7 +928,7 @@ class CallSession {
|
|||
/// This used to be done by calling hangup, but is a separate method and protocol
|
||||
/// event as of MSC2746.
|
||||
///
|
||||
void reject() {
|
||||
Future<void> reject() async {
|
||||
// stop play ringtone
|
||||
voip.delegate.stopRingtone();
|
||||
|
||||
|
|
@ -933,15 +937,15 @@ class CallSession {
|
|||
return;
|
||||
}
|
||||
Logs().d('[VOIP] Rejecting call: $callId');
|
||||
terminate(CallParty.kLocal, CallErrorCode.UserHangup, true);
|
||||
sendCallReject(room, callId, lifetimeMs, localPartyId);
|
||||
await terminate(CallParty.kLocal, CallErrorCode.UserHangup, true);
|
||||
await sendCallReject(room, callId, lifetimeMs, localPartyId);
|
||||
}
|
||||
|
||||
void hangup([String? reason, bool suppressEvent = true]) async {
|
||||
Future<void> hangup([String? reason, bool suppressEvent = true]) async {
|
||||
// stop play ringtone
|
||||
voip.delegate.stopRingtone();
|
||||
|
||||
terminate(
|
||||
await terminate(
|
||||
CallParty.kLocal, reason ?? CallErrorCode.UserHangup, !suppressEvent);
|
||||
|
||||
try {
|
||||
|
|
@ -953,7 +957,7 @@ class CallSession {
|
|||
}
|
||||
}
|
||||
|
||||
void sendDTMF(String tones) async {
|
||||
Future<void> sendDTMF(String tones) async {
|
||||
final senders = await pc!.getSenders();
|
||||
for (final sender in senders) {
|
||||
if (sender.track != null && sender.track!.kind == 'audio') {
|
||||
|
|
@ -964,7 +968,8 @@ class CallSession {
|
|||
Logs().e('Unable to find a track to send DTMF on');
|
||||
}
|
||||
|
||||
void terminate(CallParty party, String reason, bool shouldEmit) async {
|
||||
Future<void> terminate(
|
||||
CallParty party, String reason, bool shouldEmit) async {
|
||||
if (state == CallState.kEnded) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -981,7 +986,7 @@ class CallSession {
|
|||
setCallState(CallState.kEnded);
|
||||
voip.currentCID = null;
|
||||
voip.calls.remove(callId);
|
||||
cleanUp();
|
||||
await cleanUp();
|
||||
|
||||
onCallHangup.add(this);
|
||||
|
||||
|
|
@ -991,7 +996,7 @@ class CallSession {
|
|||
}
|
||||
}
|
||||
|
||||
void onRejectReceived(String? reason) {
|
||||
Future<void> onRejectReceived(String? reason) async {
|
||||
Logs().v('[VOIP] Reject received for call ID ' + callId);
|
||||
// No need to check party_id for reject because if we'd received either
|
||||
// an answer or reject, we wouldn't be in state InviteSent
|
||||
|
|
@ -1001,7 +1006,8 @@ class CallSession {
|
|||
CallState.kRinging == state;
|
||||
|
||||
if (shouldTerminate) {
|
||||
terminate(CallParty.kRemote, reason ?? CallErrorCode.UserHangup, true);
|
||||
await terminate(
|
||||
CallParty.kRemote, reason ?? CallErrorCode.UserHangup, true);
|
||||
} else {
|
||||
Logs().e('Call is in state: ${state.toString()}: ignoring reject');
|
||||
}
|
||||
|
|
@ -1018,7 +1024,8 @@ class CallSession {
|
|||
await pc!.setLocalDescription(offer);
|
||||
} catch (err) {
|
||||
Logs().d('Error setting local description! ${err.toString()}');
|
||||
terminate(CallParty.kLocal, CallErrorCode.SetLocalDescription, true);
|
||||
await terminate(
|
||||
CallParty.kLocal, CallErrorCode.SetLocalDescription, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1051,7 +1058,7 @@ class CallSession {
|
|||
}
|
||||
}
|
||||
|
||||
void onNegotiationNeeded() async {
|
||||
Future<void> onNegotiationNeeded() async {
|
||||
Logs().i('Negotiation is needed!');
|
||||
makingOffer = true;
|
||||
try {
|
||||
|
|
@ -1112,10 +1119,10 @@ class CallSession {
|
|||
terminate(CallParty.kRemote, CallErrorCode.AnsweredElsewhere, true);
|
||||
}
|
||||
|
||||
void cleanUp() async {
|
||||
streams.forEach((stream) {
|
||||
stream.dispose();
|
||||
});
|
||||
Future<void> cleanUp() async {
|
||||
for (final stream in streams) {
|
||||
await stream.dispose();
|
||||
}
|
||||
streams.clear();
|
||||
if (pc != null) {
|
||||
await pc!.close();
|
||||
|
|
@ -1123,7 +1130,7 @@ class CallSession {
|
|||
}
|
||||
}
|
||||
|
||||
void _updateMuteStatus() async {
|
||||
Future<void> _updateMuteStatus() async {
|
||||
final micShouldBeMuted = (localUserMediaStream != null &&
|
||||
localUserMediaStream!.isAudioMuted()) ||
|
||||
remoteOnHold;
|
||||
|
|
@ -1141,9 +1148,9 @@ class CallSession {
|
|||
}
|
||||
|
||||
void _setTracksEnabled(List<MediaStreamTrack> tracks, bool enabled) {
|
||||
tracks.forEach((track) async {
|
||||
for (final track in tracks) {
|
||||
track.enabled = enabled;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SDPStreamMetadata _getLocalSDPStreamMetadata() {
|
||||
|
|
@ -1159,7 +1166,7 @@ class CallSession {
|
|||
return metadata;
|
||||
}
|
||||
|
||||
void restartIce() async {
|
||||
Future<void> restartIce() async {
|
||||
Logs().v('[VOIP] iceRestart.');
|
||||
// Needs restart ice on session.pc and renegotiation.
|
||||
iceGatheringFinished = false;
|
||||
|
|
@ -1229,7 +1236,7 @@ class CallSession {
|
|||
pc?.createDataChannel(label, dataChannelDict);
|
||||
}
|
||||
|
||||
void tryRemoveStopedStreams() {
|
||||
Future<void> tryRemoveStopedStreams() async {
|
||||
final removedStreams = <String, WrappedMediaStream>{};
|
||||
streams.forEach((stream) {
|
||||
if (stream.stopped) {
|
||||
|
|
@ -1238,9 +1245,9 @@ class CallSession {
|
|||
});
|
||||
streams
|
||||
.removeWhere((stream) => removedStreams.containsKey(stream.stream!.id));
|
||||
removedStreams.forEach((id, element) {
|
||||
_removeStream(element.stream!);
|
||||
});
|
||||
for (final element in removedStreams.entries) {
|
||||
await _removeStream(element.value.stream!);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _removeStream(MediaStream stream) async {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@
|
|||
import 'dart:async';
|
||||
import 'dart:core';
|
||||
|
||||
import 'package:webrtc_interface/webrtc_interface.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||
import 'package:webrtc_interface/webrtc_interface.dart';
|
||||
|
||||
/// TODO(@duan): Need to add voice activity detection mechanism
|
||||
/// const int SPEAKING_THRESHOLD = -60; // dB
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import 'dart:core';
|
||||
|
||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||
import 'package:webrtc_interface/webrtc_interface.dart';
|
||||
import 'package:sdp_transform/sdp_transform.dart' as sdp_transform;
|
||||
import 'package:webrtc_interface/webrtc_interface.dart';
|
||||
|
||||
import 'package:matrix/src/utils/cached_stream_controller.dart';
|
||||
import '../../matrix.dart';
|
||||
|
||||
/// Delegate WebRTC basic functionality.
|
||||
|
|
@ -219,7 +219,7 @@ class VoIP {
|
|||
delegate.playRingtone();
|
||||
}
|
||||
|
||||
void onCallAnswer(
|
||||
Future<void> onCallAnswer(
|
||||
String roomId, String senderId, Map<String, dynamic> content) async {
|
||||
Logs().v('[VOIP] onCallAnswer => ${content.toString()}');
|
||||
final String callId = content['call_id'];
|
||||
|
|
@ -252,13 +252,13 @@ class VoIP {
|
|||
if (content[sdpStreamMetadataKey] != null) {
|
||||
metadata = SDPStreamMetadata.fromJson(content[sdpStreamMetadataKey]);
|
||||
}
|
||||
call.onAnswerReceived(answer, metadata);
|
||||
await call.onAnswerReceived(answer, metadata);
|
||||
} else {
|
||||
Logs().v('[VOIP] onCallAnswer: Session [$callId] not found!');
|
||||
}
|
||||
}
|
||||
|
||||
void onCallCandidates(
|
||||
Future<void> onCallCandidates(
|
||||
String roomId, String senderId, Map<String, dynamic> content) async {
|
||||
if (senderId == client.userID) {
|
||||
// Ignore messages to yourself.
|
||||
|
|
@ -273,13 +273,13 @@ class VoIP {
|
|||
'Ignoring call candidates for room $roomId claiming to be for call in room ${call.room.id}');
|
||||
return;
|
||||
}
|
||||
call.onCandidatesReceived(content['candidates']);
|
||||
await call.onCandidatesReceived(content['candidates']);
|
||||
} else {
|
||||
Logs().v('[VOIP] onCallCandidates: Session [$callId] not found!');
|
||||
}
|
||||
}
|
||||
|
||||
void onCallHangup(String roomId, String _ /*senderId unused*/,
|
||||
Future<void> onCallHangup(String roomId, String _ /*senderId unused*/,
|
||||
Map<String, dynamic> content) async {
|
||||
// stop play ringtone, if this is an incoming call
|
||||
if (!delegate.isBackgroud) {
|
||||
|
|
@ -295,7 +295,7 @@ class VoIP {
|
|||
return;
|
||||
}
|
||||
// hangup in any case, either if the other party hung up or we did on another device
|
||||
call.terminate(CallParty.kRemote,
|
||||
await call.terminate(CallParty.kRemote,
|
||||
content['reason'] ?? CallErrorCode.UserHangup, true);
|
||||
} else {
|
||||
Logs().v('[VOIP] onCallHangup: Session [$callId] not found!');
|
||||
|
|
@ -303,7 +303,7 @@ class VoIP {
|
|||
currentCID = null;
|
||||
}
|
||||
|
||||
void onCallReject(
|
||||
Future<void> onCallReject(
|
||||
String roomId, String senderId, Map<String, dynamic> content) async {
|
||||
if (senderId == client.userID) {
|
||||
// Ignore messages to yourself.
|
||||
|
|
@ -319,13 +319,13 @@ class VoIP {
|
|||
'Ignoring call reject for room $roomId claiming to be for call in room ${call.room.id}');
|
||||
return;
|
||||
}
|
||||
call.onRejectReceived(content['reason']);
|
||||
await call.onRejectReceived(content['reason']);
|
||||
} else {
|
||||
Logs().v('[VOIP] onCallHangup: Session [$callId] not found!');
|
||||
}
|
||||
}
|
||||
|
||||
void onCallReplaces(
|
||||
Future<void> onCallReplaces(
|
||||
String roomId, String senderId, Map<String, dynamic> content) async {
|
||||
if (senderId == client.userID) {
|
||||
// Ignore messages to yourself.
|
||||
|
|
@ -344,7 +344,7 @@ class VoIP {
|
|||
}
|
||||
}
|
||||
|
||||
void onCallSelectAnswer(
|
||||
Future<void> onCallSelectAnswer(
|
||||
String roomId, String senderId, Map<String, dynamic> content) async {
|
||||
if (senderId == client.userID) {
|
||||
// Ignore messages to yourself.
|
||||
|
|
@ -365,7 +365,7 @@ class VoIP {
|
|||
}
|
||||
}
|
||||
|
||||
void onSDPStreamMetadataChangedReceived(
|
||||
Future<void> onSDPStreamMetadataChangedReceived(
|
||||
String roomId, String senderId, Map<String, dynamic> content) async {
|
||||
if (senderId == client.userID) {
|
||||
// Ignore messages to yourself.
|
||||
|
|
@ -385,12 +385,12 @@ class VoIP {
|
|||
Logs().d('SDP Stream metadata is null');
|
||||
return;
|
||||
}
|
||||
call.onSDPStreamMetadataReceived(
|
||||
await call.onSDPStreamMetadataReceived(
|
||||
SDPStreamMetadata.fromJson(content[sdpStreamMetadataKey]));
|
||||
}
|
||||
}
|
||||
|
||||
void onAssertedIdentityReceived(
|
||||
Future<void> onAssertedIdentityReceived(
|
||||
String roomId, String senderId, Map<String, dynamic> content) async {
|
||||
if (senderId == client.userID) {
|
||||
// Ignore messages to yourself.
|
||||
|
|
@ -415,7 +415,7 @@ class VoIP {
|
|||
}
|
||||
}
|
||||
|
||||
void onCallNegotiate(
|
||||
Future<void> onCallNegotiate(
|
||||
String roomId, String senderId, Map<String, dynamic> content) async {
|
||||
if (senderId == client.userID) {
|
||||
// Ignore messages to yourself.
|
||||
|
|
@ -437,7 +437,7 @@ class VoIP {
|
|||
if (content[sdpStreamMetadataKey] != null) {
|
||||
metadata = SDPStreamMetadata.fromJson(content[sdpStreamMetadataKey]);
|
||||
}
|
||||
call.onNegotiateReceived(metadata,
|
||||
await call.onNegotiateReceived(metadata,
|
||||
RTCSessionDescription(description['sdp'], description['type']));
|
||||
} catch (err) {
|
||||
Logs().e('Failed to complete negotiation ${err.toString()}');
|
||||
|
|
@ -564,7 +564,7 @@ class VoIP {
|
|||
return groupCalls[groupCallId];
|
||||
}
|
||||
|
||||
void startGroupCalls() async {
|
||||
Future<void> startGroupCalls() async {
|
||||
final rooms = client.rooms;
|
||||
rooms.forEach((element) {
|
||||
createGroupCallForRoom(element);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ dependencies:
|
|||
fluffybox: ^0.4.3
|
||||
|
||||
dev_dependencies:
|
||||
import_sorter: ^4.6.0
|
||||
dart_code_metrics: ^4.10.1
|
||||
pedantic: ^1.11.0
|
||||
test: ^1.15.7
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@
|
|||
*/
|
||||
|
||||
import 'package:canonical_json/canonical_json.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
void main() {
|
||||
/// All Tests related to the ChatTime
|
||||
group('Canonical Json', () {
|
||||
|
|
|
|||
|
|
@ -20,12 +20,11 @@ import 'dart:async';
|
|||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:canonical_json/canonical_json.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
import 'package:canonical_json/canonical_json.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'fake_client.dart';
|
||||
import 'fake_database.dart';
|
||||
import 'fake_matrix_api.dart';
|
||||
|
|
@ -339,21 +338,6 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
test('get archive', () async {
|
||||
final archive = await matrix.loadArchive();
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 50));
|
||||
expect(archive.length, 2);
|
||||
expect(archive[0].id, '!5345234234:example.com');
|
||||
expect(archive[0].membership, Membership.leave);
|
||||
expect(archive[0].name, 'The room name');
|
||||
expect(archive[0].lastEvent?.body, 'This is an example text message');
|
||||
expect(archive[0].roomAccountData.length, 1);
|
||||
expect(archive[1].id, '!5345234235:example.com');
|
||||
expect(archive[1].membership, Membership.leave);
|
||||
expect(archive[1].name, 'The room name 2');
|
||||
});
|
||||
|
||||
test('sync state event in-memory handling', () async {
|
||||
final roomId = '!726s6s6q:example.com';
|
||||
final room = matrix.getRoomById(roomId)!;
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@
|
|||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'fake_client.dart';
|
||||
import 'fake_matrix_api.dart';
|
||||
|
|
|
|||
|
|
@ -15,14 +15,15 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
|
||||
import 'fake_database.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
|||
|
|
@ -18,11 +18,10 @@
|
|||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import './fake_client.dart';
|
||||
import './fake_matrix_api.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -19,12 +19,11 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix/encryption.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/encryption.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../fake_client.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
|||
|
|
@ -18,11 +18,10 @@
|
|||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../fake_client.dart';
|
||||
import '../fake_matrix_api.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../fake_client.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
|||
|
|
@ -16,11 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../fake_client.dart';
|
||||
import '../fake_database.dart';
|
||||
import '../fake_matrix_api.dart';
|
||||
|
|
|
|||
|
|
@ -18,11 +18,10 @@
|
|||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../fake_client.dart';
|
||||
import '../fake_matrix_api.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@
|
|||
*/
|
||||
|
||||
import 'dart:convert';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../fake_client.dart';
|
||||
import '../fake_matrix_api.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -19,12 +19,11 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix/encryption.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/encryption.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../fake_client.dart';
|
||||
import '../fake_database.dart';
|
||||
import '../fake_matrix_api.dart';
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@
|
|||
*/
|
||||
|
||||
import 'dart:convert';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:matrix/encryption/utils/json_signature_check_extension.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/encryption/utils/json_signature_check_extension.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../fake_client.dart';
|
||||
import '../fake_matrix_api.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,10 @@
|
|||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../fake_client.dart';
|
||||
import '../fake_matrix_api.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -16,16 +16,15 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:typed_data';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix/encryption.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/encryption.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../fake_client.dart';
|
||||
import '../fake_matrix_api.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,10 @@
|
|||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/encryption/utils/base64_unpadded.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('Utils', () {
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:matrix/encryption.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix/src/models/timeline_chunk.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/encryption.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix/src/models/timeline_chunk.dart';
|
||||
import 'fake_client.dart';
|
||||
import 'fake_matrix_api.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,8 @@
|
|||
*/
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'fake_matrix_api.dart';
|
||||
import 'fake_database.dart';
|
||||
import 'fake_matrix_api.dart';
|
||||
|
||||
const ssssPassphrase = 'nae7ahDiequ7ohniufah3ieS2je1thohX4xeeka7aixohsho9O';
|
||||
const ssssKey = 'EsT9 RzbW VhPW yqNp cC7j ViiW 5TZB LuY4 ryyv 9guN Ysmr WDPH';
|
||||
|
|
|
|||
|
|
@ -19,10 +19,11 @@
|
|||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
Future<DatabaseApi> getDatabase(Client? _) => getHiveCollectionsDatabase(_);
|
||||
|
||||
bool hiveInitialized = false;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import 'dart:core';
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart' as sdk;
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
|
|
@ -360,6 +361,65 @@ class FakeMatrixApi extends BaseClient {
|
|||
'state': [],
|
||||
};
|
||||
|
||||
static Map<String, dynamic> archivesMessageResponse = {
|
||||
'start': 't47429-4392820_219380_26003_2265',
|
||||
'end': 't47409-4357353_219380_26003_2265',
|
||||
'chunk': [
|
||||
{
|
||||
'content': {
|
||||
'body': 'This is an example text message',
|
||||
'msgtype': 'm.text',
|
||||
'format': 'org.matrix.custom.html',
|
||||
'formatted_body': '<b>This is an example text message</b>'
|
||||
},
|
||||
'type': 'm.room.message',
|
||||
'event_id': '3143273582443PhrSn:example.org',
|
||||
'room_id': '!5345234234:example.com',
|
||||
'sender': '@example:example.org',
|
||||
'origin_server_ts': 1432735824653,
|
||||
'unsigned': {'age': 1234}
|
||||
},
|
||||
{
|
||||
'content': {'name': 'The room name'},
|
||||
'type': 'm.room.name',
|
||||
'event_id': '2143273582443PhrSn:example.org',
|
||||
'room_id': '!5345234234:example.com',
|
||||
'sender': '@example:example.org',
|
||||
'origin_server_ts': 1432735824653,
|
||||
'unsigned': {'age': 1234},
|
||||
'state_key': ''
|
||||
},
|
||||
{
|
||||
'content': {
|
||||
'body': 'Gangnam Style',
|
||||
'url': 'mxc://example.org/a526eYUSFFxlgbQYZmo442',
|
||||
'info': {
|
||||
'thumbnail_url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe',
|
||||
'thumbnail_info': {
|
||||
'mimetype': 'image/jpeg',
|
||||
'size': 46144,
|
||||
'w': 300,
|
||||
'h': 300
|
||||
},
|
||||
'w': 480,
|
||||
'h': 320,
|
||||
'duration': 2140786,
|
||||
'size': 1563685,
|
||||
'mimetype': 'video/mp4'
|
||||
},
|
||||
'msgtype': 'm.video'
|
||||
},
|
||||
'type': 'm.room.message',
|
||||
'event_id': '1143273582466PhrSn:example.org',
|
||||
'room_id': '!5345234234:example.com',
|
||||
'sender': '@example:example.org',
|
||||
'origin_server_ts': 1432735824654,
|
||||
'unsigned': {'age': 1234}
|
||||
}
|
||||
],
|
||||
'state': [],
|
||||
};
|
||||
|
||||
static Map<String, dynamic> syncResponse = {
|
||||
'next_batch': Random().nextDouble().toString(),
|
||||
'rooms': {
|
||||
|
|
@ -887,19 +947,36 @@ class FakeMatrixApi extends BaseClient {
|
|||
'events': [
|
||||
{
|
||||
'content': {
|
||||
'body': 'This is an example text message',
|
||||
'body': 'This is a second text example message',
|
||||
'msgtype': 'm.text',
|
||||
'format': 'org.matrix.custom.html',
|
||||
'formatted_body': '<b>This is an example text message</b>'
|
||||
'formatted_body':
|
||||
'<b>This is a second text example message</b>'
|
||||
},
|
||||
'type': 'm.room.message',
|
||||
'event_id': '143273582443PhrSn:example.org',
|
||||
'event_id': '143274597446PhrSn:example.org',
|
||||
'room_id': '!5345234234:example.com',
|
||||
'sender': '@example:example.org',
|
||||
'origin_server_ts': 1432735824654,
|
||||
'unsigned': {'age': 1234}
|
||||
},
|
||||
{
|
||||
'content': {
|
||||
'body': 'This is a first text example message',
|
||||
'msgtype': 'm.text',
|
||||
'format': 'org.matrix.custom.html',
|
||||
'formatted_body':
|
||||
'<b>This is a first text example message</b>'
|
||||
},
|
||||
'type': 'm.room.message',
|
||||
'event_id': '143274597443PhrSn:example.org',
|
||||
'room_id': '!5345234234:example.com',
|
||||
'sender': '@example:example.org',
|
||||
'origin_server_ts': 1432735824653,
|
||||
'unsigned': {'age': 1234}
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
'prev_batch': 't_1234a'
|
||||
},
|
||||
'state': {
|
||||
'events': [
|
||||
|
|
@ -939,7 +1016,8 @@ class FakeMatrixApi extends BaseClient {
|
|||
'state_key': ''
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
'prev_batch': 't_1234b'
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1517,6 +1595,8 @@ class FakeMatrixApi extends BaseClient {
|
|||
(var req) => messagesResponseFutureEnd,
|
||||
'/client/v3/rooms/!1234%3Aexample.com/messages?from=t789&dir=f&limit=30&filter=%7B%22lazy_load_members%22%3Atrue%7D':
|
||||
(var req) => messagesResponseFutureEnd,
|
||||
'/client/v3/rooms/!5345234234%3Aexample.com/messages?from=t_1234a&dir=b&limit=30&filter=%7B%22lazy_load_members%22%3Atrue%7D':
|
||||
(var req) => archivesMessageResponse,
|
||||
'/client/versions': (var req) => {
|
||||
'versions': [
|
||||
'v1.1',
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:matrix/src/utils/html_to_text.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/src/utils/html_to_text.dart';
|
||||
|
||||
void main() {
|
||||
group('htmlToText', () {
|
||||
final testMap = <String, String>{
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'fake_client.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:matrix/src/utils/markdown.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/src/utils/markdown.dart';
|
||||
|
||||
void main() {
|
||||
group('markdown', () {
|
||||
final emotePacks = {
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:matrix/src/utils/map_copy_extension.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/src/utils/map_copy_extension.dart';
|
||||
|
||||
void main() {
|
||||
group('Map-copy-extension', () {
|
||||
test('it should work', () {
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'fake_database.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,12 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
void main() {
|
||||
/// All Tests related to device keys
|
||||
group('Matrix Exception', () {
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@
|
|||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
void main() {
|
||||
/// All Tests related to device keys
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/src/utils/matrix_id_string_extension.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
void main() {
|
||||
/// All Tests related to device keys
|
||||
group('Matrix Localizations', () {
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:matrix/src/utils/multilock.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/src/utils/multilock.dart';
|
||||
|
||||
void main() {
|
||||
group('lock', () {
|
||||
final lock = MultiLock<String>();
|
||||
|
|
|
|||
|
|
@ -16,10 +16,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'fake_matrix_api.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
void main() {
|
||||
group('Push Notification', () {
|
||||
Logs().level = Level.error;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Famedly Matrix SDK
|
||||
* Copyright (C) 2022 Famedly GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'fake_client.dart';
|
||||
|
||||
void main() {
|
||||
group('Timeline', () {
|
||||
Logs().level = Level.error;
|
||||
var olmEnabled = true;
|
||||
|
||||
final insertList = <int>[];
|
||||
|
||||
late Client client;
|
||||
|
||||
setUp(() async {
|
||||
try {
|
||||
await olm.init();
|
||||
olm.get_library_version();
|
||||
} catch (e) {
|
||||
olmEnabled = false;
|
||||
Logs().w('[LibOlm] Failed to load LibOlm', e);
|
||||
}
|
||||
Logs().i('[LibOlm] Enabled: $olmEnabled');
|
||||
|
||||
client = await getClient();
|
||||
client.sendMessageTimeoutSeconds = 5;
|
||||
|
||||
await client.abortSync();
|
||||
insertList.clear();
|
||||
});
|
||||
|
||||
tearDown(() => client.dispose().onError((e, s) {}));
|
||||
|
||||
test('archive room not loaded', () async {
|
||||
final archiveRoom =
|
||||
client.getArchiveRoomFromCache('!5345234234:example.com');
|
||||
expect(archiveRoom, null);
|
||||
});
|
||||
|
||||
test('get archive', () async {
|
||||
final archive = await client.loadArchiveWithTimeline();
|
||||
|
||||
expect(archive.length, 2);
|
||||
expect(client.rooms.length, 2);
|
||||
expect(archive[0].room.id, '!5345234234:example.com');
|
||||
expect(archive[0].room.membership, Membership.leave);
|
||||
expect(archive[0].room.name, 'The room name');
|
||||
expect(archive[0].room.lastEvent?.body,
|
||||
'This is a second text example message');
|
||||
expect(archive[0].room.roomAccountData.length, 1);
|
||||
expect(archive[1].room.id, '!5345234235:example.com');
|
||||
expect(archive[1].room.membership, Membership.leave);
|
||||
expect(archive[1].room.name, 'The room name 2');
|
||||
|
||||
final archiveRoom =
|
||||
client.getArchiveRoomFromCache('!5345234234:example.com');
|
||||
expect(archiveRoom != null, true);
|
||||
expect(archiveRoom!.timeline.events.length, 2);
|
||||
});
|
||||
|
||||
test('request history', () async {
|
||||
await client.loadArchiveWithTimeline();
|
||||
final archiveRoom = client.getRoomById('!5345234234:example.com');
|
||||
expect(archiveRoom != null, true);
|
||||
|
||||
final timeline = await archiveRoom!.getTimeline(onInsert: insertList.add);
|
||||
|
||||
expect(timeline.events.length, 2);
|
||||
expect(timeline.events[0].eventId, '143274597443PhrSn:example.org');
|
||||
expect(timeline.events[1].eventId, '143274597446PhrSn:example.org');
|
||||
|
||||
await timeline.requestHistory();
|
||||
|
||||
expect(timeline.events.length, 5);
|
||||
expect(timeline.events[0].eventId, '143274597443PhrSn:example.org');
|
||||
expect(timeline.events[1].eventId, '143274597446PhrSn:example.org');
|
||||
expect(timeline.events[2].eventId, '3143273582443PhrSn:example.org');
|
||||
expect(timeline.events[3].eventId, '2143273582443PhrSn:example.org');
|
||||
expect(timeline.events[4].eventId, '1143273582466PhrSn:example.org');
|
||||
expect(insertList.length, 3);
|
||||
});
|
||||
|
||||
test('expect database to be empty', () async {
|
||||
await client.loadArchiveWithTimeline();
|
||||
final archiveRoom = client.getRoomById('!5345234234:example.com');
|
||||
expect(archiveRoom != null, true);
|
||||
|
||||
final eventsFromStore = await client.database?.getEventList(
|
||||
archiveRoom!,
|
||||
start: 0,
|
||||
limit: Room.defaultHistoryCount,
|
||||
);
|
||||
expect(eventsFromStore?.isEmpty, true);
|
||||
});
|
||||
|
||||
test('discard room from archives when membership change', () async {
|
||||
await client.loadArchiveWithTimeline();
|
||||
expect(client.getArchiveRoomFromCache('!5345234235:example.com') != null,
|
||||
true);
|
||||
await client.handleSync(SyncUpdate(
|
||||
nextBatch: 't_456',
|
||||
rooms: RoomsUpdate(
|
||||
invite: {'!5345234235:example.com': InvitedRoomUpdate()})));
|
||||
expect(client.getArchiveRoomFromCache('!5345234235:example.com'), null);
|
||||
});
|
||||
|
||||
test('clear archive', () async {
|
||||
await client.loadArchiveWithTimeline();
|
||||
client.clearArchivesFromCache();
|
||||
expect(client.getArchiveRoomFromCache('!5345234234:example.com'), null);
|
||||
});
|
||||
|
||||
test('logout', () async {
|
||||
await client.logout();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -19,10 +19,9 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'fake_client.dart';
|
||||
import 'fake_matrix_api.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
const updates = {
|
||||
'empty': {
|
||||
'next_batch': 'blah',
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix/src/models/timeline_chunk.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'fake_client.dart';
|
||||
import 'fake_matrix_api.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix/src/models/timeline_chunk.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'fake_client.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
void main() {
|
||||
group('UIA', () {
|
||||
Logs().level = Level.error;
|
||||
|
|
|
|||
|
|
@ -16,10 +16,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'fake_matrix_api.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import '../test/fake_database.dart';
|
||||
import 'test_config.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
|
||||
void main() => test();
|
||||
const String testMessage = 'Hello world';
|
||||
|
|
|
|||
Loading…
Reference in New Issue