Merge branch 'main' of https://github.com/famedly/matrix-dart-sdk
This commit is contained in:
commit
eed8009808
28
CHANGELOG.md
28
CHANGELOG.md
|
|
@ -1,3 +1,31 @@
|
||||||
|
## [4.0.0] 13th November 2025
|
||||||
|
|
||||||
|
Matrix Dart SDK 4.0.0 comes with support for polls, adds first bits towards OIDC and improved
|
||||||
|
support for spaces and threads.
|
||||||
|
This release also fixes a major performance leak while updating user device keys in the sync loop.
|
||||||
|
Especially for larger accounts this should improve the performance a lot.
|
||||||
|
v4.0.0 It comes with some breaking changes:
|
||||||
|
|
||||||
|
#### Migration guide
|
||||||
|
|
||||||
|
- `Client.checkHomeserver()` now returns a fourth value. You can just ignore it if you don't need auth_metadata.
|
||||||
|
- `RelationshipType.reply` has been removed in favor of `Event.inReplyToEventId()` where you can set if you want to ignore fallbacks or not. This makes it easier to differenciate fallback replies and replies inside of a thread.
|
||||||
|
|
||||||
|
#### All changes
|
||||||
|
- feat: (BREAKING) Discover OIDC auth metadata on Client.checkHomeserver() (Christian Kußowski)
|
||||||
|
- feat: Allow init with access token (Christian Kußowski)
|
||||||
|
- feat: Implement msc 3381 polls (krille-chan)
|
||||||
|
- feat: Use small versions of bullet point characters (Kelrap)
|
||||||
|
- fix: Correctly remove space child (Christian Kußowski)
|
||||||
|
- fix: Set join rules with knowk_restricted and multiple allow condition room ids (Christian Kußowski)
|
||||||
|
- refactor: (BREAKING) Replace Event.relationshipType and Event.relationshipEventId with Event.inReplyToEventId() for replies. (Christian Kußowski)
|
||||||
|
- refactor: Add option to always call auth metadata (Christian Kußowski)
|
||||||
|
- refactor: Escape HTML tags before markdown rendering (Christian Kußowski)
|
||||||
|
- refactor: Make direct chat getter type safe (Christian Kußowski)
|
||||||
|
- refactor: Simpler update user device keys (Christian Kußowski)
|
||||||
|
- chore: Cache auth metadata response in client (Christian Kußowski)
|
||||||
|
- chore: Remove flutter from CI (Christian Kußowski)
|
||||||
|
|
||||||
## [3.0.2] 24th October 2025
|
## [3.0.2] 24th October 2025
|
||||||
|
|
||||||
- chore: bump vodozemac version to v0.4.0 (Karthikeyan S)
|
- chore: bump vodozemac version to v0.4.0 (Karthikeyan S)
|
||||||
|
|
|
||||||
|
|
@ -1188,6 +1188,20 @@ class FakeMatrixApi extends BaseClient {
|
||||||
'errcode': 'M_FORBIDDEN',
|
'errcode': 'M_FORBIDDEN',
|
||||||
'error': 'Blabla',
|
'error': 'Blabla',
|
||||||
},
|
},
|
||||||
|
'/client/v1/auth_metadata': (var req) => {
|
||||||
|
'authorization_endpoint':
|
||||||
|
'https://fakeserver.notexisting/oauth2/auth',
|
||||||
|
'code_challenge_methods_supported': ['S256'],
|
||||||
|
'grant_types_supported': ['authorization_code', 'refresh_token'],
|
||||||
|
'issuer': 'https://fakeserver.notexisting/',
|
||||||
|
'registration_endpoint':
|
||||||
|
'https://fakeserver.notexisting/oauth2/clients/register',
|
||||||
|
'response_modes_supported': ['query', 'fragment'],
|
||||||
|
'response_types_supported': ['code'],
|
||||||
|
'revocation_endpoint':
|
||||||
|
'https://fakeserver.notexisting/oauth2/revoke',
|
||||||
|
'token_endpoint': 'https://fakeserver.notexisting/oauth2/token',
|
||||||
|
},
|
||||||
'/media/v3/preview_url?url=https%3A%2F%2Fmatrix.org&ts=10': (var req) => {
|
'/media/v3/preview_url?url=https%3A%2F%2Fmatrix.org&ts=10': (var req) => {
|
||||||
'og:title': 'Matrix Blog Post',
|
'og:title': 'Matrix Blog Post',
|
||||||
'og:description': 'This is a really cool blog post from matrix.org',
|
'og:description': 'This is a really cool blog post from matrix.org',
|
||||||
|
|
@ -1338,7 +1352,7 @@ class FakeMatrixApi extends BaseClient {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'/client/v3/account/whoami': (var req) =>
|
'/client/v3/account/whoami': (var req) =>
|
||||||
{'user_id': 'alice@example.com'},
|
{'user_id': 'alice@example.com', 'device_id': 'ABCDEFGH'},
|
||||||
'/client/v3/capabilities': (var req) => {
|
'/client/v3/capabilities': (var req) => {
|
||||||
'capabilities': {
|
'capabilities': {
|
||||||
'm.change_password': {'enabled': false},
|
'm.change_password': {'enabled': false},
|
||||||
|
|
@ -2721,8 +2735,6 @@ class FakeMatrixApi extends BaseClient {
|
||||||
(var req) => {},
|
(var req) => {},
|
||||||
'/client/v3/user/%40test%3AfakeServer.notExisting/rooms/!localpart%3Aserver.abc/account_data/m.marked_unread':
|
'/client/v3/user/%40test%3AfakeServer.notExisting/rooms/!localpart%3Aserver.abc/account_data/m.marked_unread':
|
||||||
(var req) => {},
|
(var req) => {},
|
||||||
'/client/v3/user/%40test%3AfakeServer.notExisting/account_data/m.direct':
|
|
||||||
(var req) => {},
|
|
||||||
'/client/v3/user/%40othertest%3AfakeServer.notExisting/account_data/m.direct':
|
'/client/v3/user/%40othertest%3AfakeServer.notExisting/account_data/m.direct':
|
||||||
(var req) => {},
|
(var req) => {},
|
||||||
'/client/v3/profile/%40alice%3Aexample.com/displayname': (var reqI) => {},
|
'/client/v3/profile/%40alice%3Aexample.com/displayname': (var reqI) => {},
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ class Client extends MatrixApi {
|
||||||
@override
|
@override
|
||||||
set homeserver(Uri? homeserver) {
|
set homeserver(Uri? homeserver) {
|
||||||
if (this.homeserver != null && homeserver?.host != this.homeserver?.host) {
|
if (this.homeserver != null && homeserver?.host != this.homeserver?.host) {
|
||||||
_wellKnown = null;
|
_wellKnown = _getAuthMetadataResponseCache = null;
|
||||||
}
|
}
|
||||||
super.homeserver = homeserver;
|
super.homeserver = homeserver;
|
||||||
}
|
}
|
||||||
|
|
@ -440,8 +440,13 @@ class Client extends MatrixApi {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> get directChats =>
|
Map<String, List<String>> get directChats =>
|
||||||
_accountData['m.direct']?.content ?? {};
|
(_accountData['m.direct']?.content ?? {}).map(
|
||||||
|
(userId, list) => MapEntry(
|
||||||
|
userId,
|
||||||
|
(list is! List) ? [] : list.whereType<String>().toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
/// Returns the first room ID from the store (the room with the latest event)
|
/// Returns the first room ID from the store (the room with the latest event)
|
||||||
/// which is a private chat with the user [userId].
|
/// which is a private chat with the user [userId].
|
||||||
|
|
@ -516,9 +521,17 @@ class Client extends MatrixApi {
|
||||||
DiscoveryInformation?,
|
DiscoveryInformation?,
|
||||||
GetVersionsResponse versions,
|
GetVersionsResponse versions,
|
||||||
List<LoginFlow>,
|
List<LoginFlow>,
|
||||||
|
GetAuthMetadataResponse? authMetadata,
|
||||||
)> checkHomeserver(
|
)> checkHomeserver(
|
||||||
Uri homeserverUrl, {
|
Uri homeserverUrl, {
|
||||||
bool checkWellKnown = true,
|
bool checkWellKnown = true,
|
||||||
|
|
||||||
|
/// Weither this method should also call `/auth_metadata` to fetch
|
||||||
|
/// Matrix native OIDC information. Defaults to if the `/versions` endpoint
|
||||||
|
/// returns version v1.15 or higher. Set to `true` to always call the
|
||||||
|
/// endpoint if your homeserver supports the endpoint while not fully
|
||||||
|
/// supporting version v1.15 yet.
|
||||||
|
bool? fetchAuthMetadata,
|
||||||
Set<String>? overrideSupportedVersions,
|
Set<String>? overrideSupportedVersions,
|
||||||
}) async {
|
}) async {
|
||||||
final supportedVersions =
|
final supportedVersions =
|
||||||
|
|
@ -555,7 +568,21 @@ class Client extends MatrixApi {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (wellKnown, versions, loginTypes);
|
fetchAuthMetadata ??= versions.versions.any(
|
||||||
|
(v) => isVersionGreaterThanOrEqualTo(v, 'v1.15'),
|
||||||
|
);
|
||||||
|
GetAuthMetadataResponse? authMetadata;
|
||||||
|
if (fetchAuthMetadata) {
|
||||||
|
try {
|
||||||
|
authMetadata = await getAuthMetadata();
|
||||||
|
} on MatrixException catch (e, s) {
|
||||||
|
if (e.error != MatrixError.M_UNRECOGNIZED) {
|
||||||
|
Logs().w('Unable to discover OIDC auth metadata.', e, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (wellKnown, versions, loginTypes, authMetadata);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
homeserver = null;
|
homeserver = null;
|
||||||
rethrow;
|
rethrow;
|
||||||
|
|
@ -720,6 +747,12 @@ class Client extends MatrixApi {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetAuthMetadataResponse? _getAuthMetadataResponseCache;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GetAuthMetadataResponse> getAuthMetadata() async =>
|
||||||
|
_getAuthMetadataResponseCache ??= await super.getAuthMetadata();
|
||||||
|
|
||||||
/// Sends a logout command to the homeserver and clears all local data,
|
/// Sends a logout command to the homeserver and clears all local data,
|
||||||
/// including all persistent data from the store.
|
/// including all persistent data from the store.
|
||||||
@override
|
@override
|
||||||
|
|
@ -1961,9 +1994,9 @@ class Client extends MatrixApi {
|
||||||
///
|
///
|
||||||
/// Sends [LoginState.loggedIn] to [onLoginStateChanged].
|
/// Sends [LoginState.loggedIn] to [onLoginStateChanged].
|
||||||
///
|
///
|
||||||
/// If one of [newToken], [newUserID], [newDeviceID], [newDeviceName] is set then
|
/// If one of [newToken] is set, but one of [newUserID], [newDeviceID] is
|
||||||
/// all of them must be set! If you don't set them, this method will try to
|
/// null, then this method calls `/whoami` to fetch user ID and device ID
|
||||||
/// get them from the database.
|
/// and rethrows if this request fails.
|
||||||
///
|
///
|
||||||
/// Set [waitForFirstSync] and [waitUntilLoadCompletedLoaded] to false to speed this
|
/// Set [waitForFirstSync] and [waitUntilLoadCompletedLoaded] to false to speed this
|
||||||
/// up. You can then wait for `roomsLoading`, `_accountDataLoading` and
|
/// up. You can then wait for `roomsLoading`, `_accountDataLoading` and
|
||||||
|
|
@ -1987,16 +2020,9 @@ class Client extends MatrixApi {
|
||||||
/// To track what actually happens you can set a callback here.
|
/// To track what actually happens you can set a callback here.
|
||||||
void Function(InitState)? onInitStateChanged,
|
void Function(InitState)? onInitStateChanged,
|
||||||
}) async {
|
}) async {
|
||||||
if ((newToken != null ||
|
if (newToken != null && homeserver == null && newHomeserver == null) {
|
||||||
newUserID != null ||
|
|
||||||
newDeviceID != null ||
|
|
||||||
newDeviceName != null) &&
|
|
||||||
(newToken == null ||
|
|
||||||
newUserID == null ||
|
|
||||||
newDeviceID == null ||
|
|
||||||
newDeviceName == null)) {
|
|
||||||
throw ClientInitPreconditionError(
|
throw ClientInitPreconditionError(
|
||||||
'If one of [newToken, newUserID, newDeviceID, newDeviceName] is set then all of them must be set!',
|
'init() can not be performed with an access token when no homeserver was specified.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2088,6 +2114,12 @@ class Client extends MatrixApi {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (accessToken != null && (userID == null || deviceID == null)) {
|
||||||
|
final userInfo = await getTokenOwner();
|
||||||
|
_userID = userID = userInfo.userId;
|
||||||
|
_deviceID = userInfo.deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
if (accessToken == null || homeserver == null || userID == null) {
|
if (accessToken == null || homeserver == null || userID == null) {
|
||||||
if (legacyDatabaseBuilder != null) {
|
if (legacyDatabaseBuilder != null) {
|
||||||
await _migrateFromLegacyDatabase(
|
await _migrateFromLegacyDatabase(
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ import 'package:matrix/src/utils/markdown.dart';
|
||||||
import 'package:matrix/src/utils/multipart_request_progress.dart';
|
import 'package:matrix/src/utils/multipart_request_progress.dart';
|
||||||
|
|
||||||
abstract class RelationshipTypes {
|
abstract class RelationshipTypes {
|
||||||
static const String reply = 'm.in_reply_to';
|
|
||||||
static const String edit = 'm.replace';
|
static const String edit = 'm.replace';
|
||||||
static const String reaction = 'm.annotation';
|
static const String reaction = 'm.annotation';
|
||||||
static const String reference = 'm.reference';
|
static const String reference = 'm.reference';
|
||||||
|
|
@ -490,31 +489,13 @@ class Event extends MatrixEvent {
|
||||||
/// event fallback if the relationship type is `m.thread`.
|
/// event fallback if the relationship type is `m.thread`.
|
||||||
/// https://spec.matrix.org/v1.14/client-server-api/#fallback-for-unthreaded-clients
|
/// https://spec.matrix.org/v1.14/client-server-api/#fallback-for-unthreaded-clients
|
||||||
Future<Event?> getReplyEvent(Timeline timeline) async {
|
Future<Event?> getReplyEvent(Timeline timeline) async {
|
||||||
switch (relationshipType) {
|
final relationshipEventId = content
|
||||||
case RelationshipTypes.reply:
|
.tryGetMap<String, Object?>('m.relates_to')
|
||||||
final relationshipEventId = this.relationshipEventId;
|
?.tryGetMap<String, Object?>('m.in_reply_to')
|
||||||
return relationshipEventId == null
|
|
||||||
? null
|
|
||||||
: await timeline.getEventById(relationshipEventId);
|
|
||||||
|
|
||||||
case RelationshipTypes.thread:
|
|
||||||
final relationshipContent =
|
|
||||||
content.tryGetMap<String, Object?>('m.relates_to');
|
|
||||||
if (relationshipContent == null) return null;
|
|
||||||
final String? relationshipEventId;
|
|
||||||
if (relationshipContent.tryGet<bool>('is_falling_back') == true) {
|
|
||||||
relationshipEventId = relationshipContent
|
|
||||||
.tryGetMap<String, Object?>('m.in_reply_to')
|
|
||||||
?.tryGet<String>('event_id');
|
?.tryGet<String>('event_id');
|
||||||
} else {
|
|
||||||
relationshipEventId = this.relationshipEventId;
|
|
||||||
}
|
|
||||||
return relationshipEventId == null
|
return relationshipEventId == null
|
||||||
? null
|
? null
|
||||||
: await timeline.getEventById(relationshipEventId);
|
: await timeline.getEventById(relationshipEventId);
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If this event is encrypted and the decryption was not successful because
|
/// If this event is encrypted and the decryption was not successful because
|
||||||
|
|
@ -1021,28 +1002,28 @@ class Event extends MatrixEvent {
|
||||||
return transactionId == search;
|
return transactionId == search;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the relationship type of an event. `null` if there is none
|
/// Get the relationship type of an event. `null` if there is none.
|
||||||
String? get relationshipType {
|
String? get relationshipType => content
|
||||||
final mRelatesTo = content.tryGetMap<String, Object?>('m.relates_to');
|
.tryGetMap<String, Object?>('m.relates_to')
|
||||||
if (mRelatesTo == null) {
|
?.tryGet<String>('rel_type');
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final relType = mRelatesTo.tryGet<String>('rel_type');
|
|
||||||
if (relType == RelationshipTypes.thread) {
|
|
||||||
return RelationshipTypes.thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mRelatesTo.containsKey('m.in_reply_to')) {
|
/// Get the event ID that this relationship will reference and `null` if there
|
||||||
return RelationshipTypes.reply;
|
/// is none. This could for example be the thread root, the original event for
|
||||||
}
|
/// an edit or the event, this is an reaction for. For replies please use
|
||||||
return relType;
|
/// `Event.inReplyToEventId()` instead!
|
||||||
}
|
String? get relationshipEventId => content
|
||||||
|
.tryGetMap<String, Object?>('m.relates_to')
|
||||||
|
?.tryGet<String>('event_id');
|
||||||
|
|
||||||
/// Get the event ID that this relationship will reference. `null` if there is none
|
/// If this event is in reply to another event, this returns the event ID or
|
||||||
String? get relationshipEventId {
|
/// null if this event is not a reply.
|
||||||
final relatesToMap = content.tryGetMap<String, Object?>('m.relates_to');
|
String? inReplyToEventId({bool includingFallback = true}) {
|
||||||
return relatesToMap?.tryGet<String>('event_id') ??
|
final isFallback = content
|
||||||
relatesToMap
|
.tryGetMap<String, Object?>('m.relates_to')
|
||||||
|
?.tryGet<bool>('is_falling_back');
|
||||||
|
if (isFallback == true && !includingFallback) return null;
|
||||||
|
return content
|
||||||
|
.tryGetMap<String, Object?>('m.relates_to')
|
||||||
?.tryGetMap<String, Object?>('m.in_reply_to')
|
?.tryGetMap<String, Object?>('m.in_reply_to')
|
||||||
?.tryGet<String>('event_id');
|
?.tryGet<String>('event_id');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -436,7 +436,7 @@ class Room {
|
||||||
final cache = _cachedDirectChatMatrixId;
|
final cache = _cachedDirectChatMatrixId;
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
final roomIds = client.directChats[cache];
|
final roomIds = client.directChats[cache];
|
||||||
if (roomIds is List && roomIds.contains(id)) {
|
if (roomIds != null && roomIds.contains(id)) {
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1608,21 +1608,21 @@ class Room {
|
||||||
|
|
||||||
/// Sets this room as a direct chat for this user if not already.
|
/// Sets this room as a direct chat for this user if not already.
|
||||||
Future<void> addToDirectChat(String userID) async {
|
Future<void> addToDirectChat(String userID) async {
|
||||||
final directChats = client.directChats;
|
final dmRooms = List<String>.from(client.directChats[userID] ?? []);
|
||||||
if (directChats[userID] is List) {
|
if (dmRooms.contains(id)) {
|
||||||
if (!directChats[userID].contains(id)) {
|
Logs().d('Already a direct chat.');
|
||||||
directChats[userID].add(id);
|
|
||||||
} else {
|
|
||||||
return;
|
return;
|
||||||
} // Is already in direct chats
|
|
||||||
} else {
|
|
||||||
directChats[userID] = [id];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dmRooms.add(id);
|
||||||
|
|
||||||
await client.setAccountData(
|
await client.setAccountData(
|
||||||
client.userID!,
|
client.userID!,
|
||||||
'm.direct',
|
'm.direct',
|
||||||
directChats,
|
{
|
||||||
|
...client.directChats,
|
||||||
|
userID: dmRooms,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,13 @@ String markdown(
|
||||||
bool convertLinebreaks = true,
|
bool convertLinebreaks = true,
|
||||||
}) {
|
}) {
|
||||||
var ret = markdownToHtml(
|
var ret = markdownToHtml(
|
||||||
text.replaceNewlines(),
|
text
|
||||||
|
.replaceAllMapped(
|
||||||
|
// Replace HTML tags
|
||||||
|
RegExp(r'<([^>]*)>'),
|
||||||
|
(match) => '<${match.group(1)}>',
|
||||||
|
)
|
||||||
|
.replaceNewlines(),
|
||||||
extensionSet: ExtensionSet.gitHubFlavored,
|
extensionSet: ExtensionSet.gitHubFlavored,
|
||||||
blockSyntaxes: [
|
blockSyntaxes: [
|
||||||
BlockLatexSyntax(),
|
BlockLatexSyntax(),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: matrix
|
name: matrix
|
||||||
description: Matrix Dart SDK
|
description: Matrix Dart SDK
|
||||||
version: 3.0.2
|
version: 4.0.0
|
||||||
homepage: https://famedly.com
|
homepage: https://famedly.com
|
||||||
repository: https://github.com/famedly/matrix-dart-sdk.git
|
repository: https://github.com/famedly/matrix-dart-sdk.git
|
||||||
issue_tracker: https://github.com/famedly/matrix-dart-sdk/issues
|
issue_tracker: https://github.com/famedly/matrix-dart-sdk/issues
|
||||||
|
|
|
||||||
|
|
@ -524,6 +524,22 @@ void main() {
|
||||||
FakeMatrixApi.currentApi?.api = oldapi!;
|
FakeMatrixApi.currentApi?.api = oldapi!;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('init() with access token', () async {
|
||||||
|
final client = Client(
|
||||||
|
'testclient',
|
||||||
|
httpClient: FakeMatrixApi(),
|
||||||
|
database: await getDatabase(),
|
||||||
|
);
|
||||||
|
await client.init(
|
||||||
|
newToken: 'abcd1234',
|
||||||
|
newHomeserver: Uri.parse('https://fakeserver.notexisting'),
|
||||||
|
);
|
||||||
|
expect(client.isLogged(), true);
|
||||||
|
expect(client.userID, 'alice@example.com');
|
||||||
|
expect(client.deviceID, 'ABCDEFGH');
|
||||||
|
await client.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
test('Login', () async {
|
test('Login', () async {
|
||||||
matrix = Client(
|
matrix = Client(
|
||||||
'testclient',
|
'testclient',
|
||||||
|
|
@ -531,9 +547,14 @@ void main() {
|
||||||
database: await getDatabase(),
|
database: await getDatabase(),
|
||||||
);
|
);
|
||||||
|
|
||||||
await matrix.checkHomeserver(
|
final (_, _, _, authMetadata) = await matrix.checkHomeserver(
|
||||||
Uri.parse('https://fakeserver.notexisting'),
|
Uri.parse('https://fakeserver.notexisting'),
|
||||||
checkWellKnown: false,
|
checkWellKnown: false,
|
||||||
|
fetchAuthMetadata: true,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
authMetadata?.issuer.toString(),
|
||||||
|
'https://fakeserver.notexisting/',
|
||||||
);
|
);
|
||||||
|
|
||||||
final loginResp = await matrix.login(
|
final loginResp = await matrix.login(
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ void main() async {
|
||||||
expect(event.formattedText, formatted_body);
|
expect(event.formattedText, formatted_body);
|
||||||
expect(event.body, body);
|
expect(event.body, body);
|
||||||
expect(event.type, EventTypes.Message);
|
expect(event.type, EventTypes.Message);
|
||||||
expect(event.relationshipType, RelationshipTypes.reply);
|
expect(event.inReplyToEventId(), '\$1234:example.com');
|
||||||
jsonObj['state_key'] = '';
|
jsonObj['state_key'] = '';
|
||||||
final state = Event.fromJson(jsonObj, room);
|
final state = Event.fromJson(jsonObj, room);
|
||||||
expect(state.eventId, id);
|
expect(state.eventId, id);
|
||||||
|
|
@ -178,8 +178,8 @@ void main() async {
|
||||||
};
|
};
|
||||||
event = Event.fromJson(jsonObj, room);
|
event = Event.fromJson(jsonObj, room);
|
||||||
expect(event.messageType, MessageTypes.Text);
|
expect(event.messageType, MessageTypes.Text);
|
||||||
expect(event.relationshipType, RelationshipTypes.reply);
|
expect(event.inReplyToEventId(), '1234');
|
||||||
expect(event.relationshipEventId, '1234');
|
expect(event.relationshipEventId, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('relationship types', () async {
|
test('relationship types', () async {
|
||||||
|
|
@ -212,8 +212,22 @@ void main() async {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
event = Event.fromJson(jsonObj, room);
|
event = Event.fromJson(jsonObj, room);
|
||||||
expect(event.relationshipType, RelationshipTypes.reply);
|
expect(event.inReplyToEventId(), 'def');
|
||||||
expect(event.relationshipEventId, 'def');
|
expect(event.relationshipEventId, null);
|
||||||
|
|
||||||
|
jsonObj['content']['m.relates_to'] = {
|
||||||
|
'rel_type': 'm.thread',
|
||||||
|
'event_id': '\$root',
|
||||||
|
'm.in_reply_to': {
|
||||||
|
'event_id': '\$target',
|
||||||
|
},
|
||||||
|
'is_falling_back': true,
|
||||||
|
};
|
||||||
|
event = Event.fromJson(jsonObj, room);
|
||||||
|
expect(event.relationshipType, RelationshipTypes.thread);
|
||||||
|
expect(event.inReplyToEventId(), '\$target');
|
||||||
|
expect(event.inReplyToEventId(includingFallback: false), null);
|
||||||
|
expect(event.relationshipEventId, '\$root');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('redact', () async {
|
test('redact', () async {
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,10 @@ void main() {
|
||||||
),
|
),
|
||||||
'<p>The first<br/>codeblock</p><pre><code class="language-dart">void main(){\nprint(something);\n}\n</code></pre><p>And the second code block</p><pre><code class="language-js">meow\nmeow\n</code></pre>',
|
'<p>The first<br/>codeblock</p><pre><code class="language-dart">void main(){\nprint(something);\n}\n</code></pre><p>And the second code block</p><pre><code class="language-js">meow\nmeow\n</code></pre>',
|
||||||
);
|
);
|
||||||
|
expect(
|
||||||
|
markdown('Test <m> *unescaped*'),
|
||||||
|
'Test <m> <em>unescaped</em>',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
test('Checkboxes', () {
|
test('Checkboxes', () {
|
||||||
expect(
|
expect(
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,7 @@ void main() async {
|
||||||
await user1.setPower(50);
|
await user1.setPower(50);
|
||||||
});
|
});
|
||||||
test('startDirectChat', () async {
|
test('startDirectChat', () async {
|
||||||
|
FakeMatrixApi.client = user1.room.client;
|
||||||
await user1.startDirectChat(waitForSync: false);
|
await user1.startDirectChat(waitForSync: false);
|
||||||
});
|
});
|
||||||
test('getPresence', () async {
|
test('getPresence', () async {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue