Merge branch 'recent-emoji' into 'main'
feat: implement io.element.recent_emoji See merge request famedly/company/frontend/famedlysdk!1022
This commit is contained in:
commit
97b32e9872
|
|
@ -49,3 +49,6 @@ export 'src/utils/to_device_event.dart';
|
|||
export 'src/utils/uia_request.dart';
|
||||
export 'src/utils/uri_extension.dart';
|
||||
export 'src/voip_content.dart';
|
||||
|
||||
export 'msc_extensions/extension_recent_emoji/recent_emoji.dart';
|
||||
export 'msc_extensions/msc_1236_widgets/msc_1236_widgets.dart';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# MSC extensions
|
||||
|
||||
This folder contains non-spec feature implementations, usually proposed in Matrix Specification Changes (MSCs).
|
||||
|
||||
Please try to cover the following conventions:
|
||||
|
||||
- name your implementation `/lib/msc_extensions/msc_NUMER_short_name/whatsoever.dart`,
|
||||
e.g. `/lib/msc_extensions/msc_3588_stories/stories.dart`
|
||||
- please link the MSC in a comment in the first line:
|
||||
```dart
|
||||
/// MSC3588: Stories As Rooms (https://github.com/matrix-org/matrix-spec-proposals/blob/d818877504cfda00ac52430ba5b9e8423c878b77/proposals/3588-stories-as-rooms.md)
|
||||
```
|
||||
- the implementation should provide an `extension NAME on ...` (usually `Client`)
|
||||
- proprietary implementations without MSC should be given a useful name and
|
||||
corresponding, useful documentation comments, e.g. `/lib/msc_extensions/extension_recent_emoji/recent_emoji.dart`
|
||||
- Moreover, all implemented non-spec features should be listed below:
|
||||
|
||||
## Implemented non-spec features
|
||||
|
||||
- MSC 1236 - Widget API V2
|
||||
- `io.element.recent_emoji` - recent emoji sync in account data
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
library extension_recent_emoji;
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
/// Syncs recent emojis in account data
|
||||
///
|
||||
/// Keeps recently used emojis stored in account data by
|
||||
///
|
||||
/// ```js
|
||||
/// { // the account data
|
||||
/// "io.element.recent_emoji": {
|
||||
/// "recent_emoji" : {
|
||||
/// "emoji character": n, // number used
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Proprietary extension by New Vector Ltd.
|
||||
extension RecentEmojiExtension on Client {
|
||||
/// returns the recently used emojis from the account data
|
||||
///
|
||||
/// There's no corresponding standard or MSC, it's just the reverse-engineered
|
||||
/// API from New Vector Ltd.
|
||||
Map<String, int> get recentEmojis => Map.fromEntries(
|
||||
(accountData['io.element.recent_emoji']?.content['recent_emoji']
|
||||
as List<dynamic>? ??
|
||||
[])
|
||||
.map(
|
||||
(e) => MapEntry(e[0] as String, e[1] as int),
|
||||
),
|
||||
);
|
||||
|
||||
/// +1 the stated emoji in the account data
|
||||
Future<void> addRecentEmoji(String emoji) async {
|
||||
final data = recentEmojis;
|
||||
if (data.containsKey(emoji)) {
|
||||
data[emoji] = data[emoji]! + 1;
|
||||
} else {
|
||||
data[emoji] = 1;
|
||||
}
|
||||
return setRecentEmojiData(data);
|
||||
}
|
||||
|
||||
/// sets the raw recent emoji account data. Use [addRecentEmoji] instead
|
||||
Future<void> setRecentEmojiData(Map<String, int> data) async {
|
||||
final content = List.from(data.entries.map((e) => [e.key, e.value]));
|
||||
return setAccountData(
|
||||
userID!, 'io.element.recent_emoji', {'recent_emoji': content});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
library msc_1236_widgets;
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
export 'src/widget.dart';
|
||||
|
||||
extension MatrixWidgets on Room {
|
||||
/// Returns all present Widgets in the room.
|
||||
List<MatrixWidget> get widgets => {
|
||||
...states['m.widget'] ?? states['im.vector.modular.widgets'] ?? {},
|
||||
}.values.expand((e) {
|
||||
try {
|
||||
return [MatrixWidget.fromJson(e.content, this)];
|
||||
} catch (_) {
|
||||
return <MatrixWidget>[];
|
||||
}
|
||||
}).toList();
|
||||
|
||||
Future<String> addWidget(MatrixWidget widget) {
|
||||
final user = client.userID;
|
||||
final widgetId =
|
||||
widget.name!.toLowerCase().replaceAll(RegExp(r'\W'), '_') + '_' + user!;
|
||||
|
||||
final json = widget.toJson();
|
||||
json['creatorUserId'] = user;
|
||||
json['id'] = widgetId;
|
||||
return client.setRoomStateWithKey(
|
||||
id,
|
||||
'im.vector.modular.widgets',
|
||||
widgetId,
|
||||
json,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> deleteWidget(String widgetId) {
|
||||
return client.setRoomStateWithKey(
|
||||
id,
|
||||
'im.vector.modular.widgets',
|
||||
widgetId,
|
||||
{},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -224,6 +224,7 @@ class Client extends MatrixApi {
|
|||
/// Returns the current login state.
|
||||
LoginState get loginState => __loginState;
|
||||
LoginState __loginState;
|
||||
|
||||
set _loginState(LoginState state) {
|
||||
__loginState = state;
|
||||
onLoginStateChanged.add(state);
|
||||
|
|
@ -256,7 +257,9 @@ class Client extends MatrixApi {
|
|||
}
|
||||
|
||||
/// Key/Value store of account data.
|
||||
Map<String, BasicEvent> accountData = {};
|
||||
Map<String, BasicEvent> _accountData = {};
|
||||
|
||||
Map<String, BasicEvent> get accountData => _accountData;
|
||||
|
||||
/// Presences of users by a given matrix ID
|
||||
Map<String, Presence> presences = {};
|
||||
|
|
@ -287,12 +290,12 @@ class Client extends MatrixApi {
|
|||
}
|
||||
|
||||
Map<String, dynamic> get directChats =>
|
||||
accountData['m.direct']?.content ?? {};
|
||||
_accountData['m.direct']?.content ?? {};
|
||||
|
||||
/// Returns the (first) room ID from the store which is a private chat with the user [userId].
|
||||
/// Returns null if there is none.
|
||||
String? getDirectChatFromUserId(String userId) {
|
||||
final directChats = accountData['m.direct']?.content[userId];
|
||||
final directChats = _accountData['m.direct']?.content[userId];
|
||||
if (directChats is List<dynamic> && directChats.isNotEmpty) {
|
||||
final potentialRooms = directChats
|
||||
.cast<String>()
|
||||
|
|
@ -834,13 +837,13 @@ class Client extends MatrixApi {
|
|||
|
||||
/// Returns the global push rules for the logged in user.
|
||||
PushRuleSet? get globalPushRules {
|
||||
final pushrules = accountData['m.push_rules']?.content['global'];
|
||||
final pushrules = _accountData['m.push_rules']?.content['global'];
|
||||
return pushrules != null ? PushRuleSet.fromJson(pushrules) : null;
|
||||
}
|
||||
|
||||
/// Returns the device push rules for the logged in user.
|
||||
PushRuleSet? get devicePushRules {
|
||||
final pushrules = accountData['m.push_rules']?.content['device'];
|
||||
final pushrules = _accountData['m.push_rules']?.content['device'];
|
||||
return pushrules != null ? PushRuleSet.fromJson(pushrules) : null;
|
||||
}
|
||||
|
||||
|
|
@ -1104,7 +1107,7 @@ class Client extends MatrixApi {
|
|||
/// get them from the database.
|
||||
///
|
||||
/// 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
|
||||
/// `userDeviceKeysLoading` where it is necessary.
|
||||
Future<void> init({
|
||||
String? newToken,
|
||||
|
|
@ -1232,13 +1235,13 @@ class Client extends MatrixApi {
|
|||
_rooms = rooms;
|
||||
_sortRooms();
|
||||
});
|
||||
accountDataLoading =
|
||||
database.getAccountData().then((data) => accountData = data);
|
||||
_accountDataLoading =
|
||||
database.getAccountData().then((data) => _accountData = data);
|
||||
presences.clear();
|
||||
if (waitUntilLoadCompletedLoaded) {
|
||||
await userDeviceKeysLoading;
|
||||
await roomsLoading;
|
||||
await accountDataLoading;
|
||||
await _accountDataLoading;
|
||||
}
|
||||
}
|
||||
_initLock = false;
|
||||
|
|
@ -1379,7 +1382,7 @@ class Client extends MatrixApi {
|
|||
if (database != null) {
|
||||
await userDeviceKeysLoading;
|
||||
await roomsLoading;
|
||||
await accountDataLoading;
|
||||
await _accountDataLoading;
|
||||
_currentTransaction = database.transaction(() async {
|
||||
await _handleSync(syncResp);
|
||||
if (prevBatch != syncResp.nextBatch) {
|
||||
|
|
@ -1888,7 +1891,9 @@ class Client extends MatrixApi {
|
|||
|
||||
Future? userDeviceKeysLoading;
|
||||
Future? roomsLoading;
|
||||
Future? accountDataLoading;
|
||||
Future? _accountDataLoading;
|
||||
|
||||
Future? get accountDataLoading => _accountDataLoading;
|
||||
|
||||
/// A map of known device keys per user.
|
||||
Map<String, DeviceKeysList> get userDeviceKeys => _userDeviceKeys;
|
||||
|
|
@ -2338,7 +2343,7 @@ class Client extends MatrixApi {
|
|||
/// rule of the push rules: https://matrix.org/docs/spec/client_server/r0.6.0#m-rule-master
|
||||
bool get allPushNotificationsMuted {
|
||||
final Map<String, dynamic>? globalPushRules =
|
||||
accountData['m.push_rules']?.content['global'];
|
||||
_accountData['m.push_rules']?.content['global'];
|
||||
if (globalPushRules == null) return false;
|
||||
|
||||
if (globalPushRules['override'] is List) {
|
||||
|
|
@ -2418,11 +2423,11 @@ class Client extends MatrixApi {
|
|||
}
|
||||
|
||||
/// A list of mxids of users who are ignored.
|
||||
List<String> get ignoredUsers => (accountData
|
||||
List<String> get ignoredUsers => (_accountData
|
||||
.containsKey('m.ignored_user_list') &&
|
||||
accountData['m.ignored_user_list']?.content['ignored_users'] is Map)
|
||||
_accountData['m.ignored_user_list']?.content['ignored_users'] is Map)
|
||||
? List<String>.from(
|
||||
accountData['m.ignored_user_list']?.content['ignored_users'].keys)
|
||||
_accountData['m.ignored_user_list']?.content['ignored_users'].keys)
|
||||
: [];
|
||||
|
||||
/// Ignore another user. This will clear the local cached messages to
|
||||
|
|
@ -2630,6 +2635,7 @@ class SyncStatusUpdate {
|
|||
final SyncStatus status;
|
||||
final SdkError? error;
|
||||
final double? progress;
|
||||
|
||||
const SyncStatusUpdate(this.status, {this.error, this.progress});
|
||||
}
|
||||
|
||||
|
|
@ -2643,6 +2649,7 @@ enum SyncStatus {
|
|||
|
||||
class BadServerVersionsException implements Exception {
|
||||
final Set<String> serverVersions, supportedVersions;
|
||||
|
||||
BadServerVersionsException(this.serverVersions, this.supportedVersions);
|
||||
|
||||
@override
|
||||
|
|
@ -2652,6 +2659,7 @@ class BadServerVersionsException implements Exception {
|
|||
|
||||
class BadServerLoginTypesException implements Exception {
|
||||
final Set<String> serverLoginTypes, supportedLoginTypes;
|
||||
|
||||
BadServerLoginTypesException(this.serverLoginTypes, this.supportedLoginTypes);
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import 'package:html_unescape/html_unescape.dart';
|
|||
import 'package:matrix/src/utils/crypto/crypto.dart';
|
||||
import 'package:matrix/src/utils/file_send_request_credentials.dart';
|
||||
import 'package:matrix/src/utils/space_child.dart';
|
||||
import 'package:matrix/widget.dart';
|
||||
|
||||
import '../matrix.dart';
|
||||
import 'utils/markdown.dart';
|
||||
|
|
@ -357,42 +356,6 @@ class Room {
|
|||
: [];
|
||||
}
|
||||
|
||||
/// Returns all present Widgets in the room.
|
||||
List<MatrixWidget> get widgets => {
|
||||
...states['m.widget'] ?? states['im.vector.modular.widgets'] ?? {},
|
||||
}.values.expand((e) {
|
||||
try {
|
||||
return [MatrixWidget.fromJson(e.content, this)];
|
||||
} catch (_) {
|
||||
return <MatrixWidget>[];
|
||||
}
|
||||
}).toList();
|
||||
|
||||
Future<String> addWidget(MatrixWidget widget) {
|
||||
final user = client.userID;
|
||||
final widgetId =
|
||||
widget.name!.toLowerCase().replaceAll(RegExp(r'\W'), '_') + '_' + user!;
|
||||
|
||||
final json = widget.toJson();
|
||||
json['creatorUserId'] = user;
|
||||
json['id'] = widgetId;
|
||||
return client.setRoomStateWithKey(
|
||||
id,
|
||||
'im.vector.modular.widgets',
|
||||
widgetId,
|
||||
json,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> deleteWidget(String widgetId) {
|
||||
return client.setRoomStateWithKey(
|
||||
id,
|
||||
'im.vector.modular.widgets',
|
||||
widgetId,
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
/// Your current client instance.
|
||||
final Client client;
|
||||
|
||||
|
|
|
|||
|
|
@ -322,6 +322,15 @@ void main() {
|
|||
await matrix.setAvatar(testFile);
|
||||
});
|
||||
|
||||
test('recentEmoji', () async {
|
||||
final client = await getClient();
|
||||
final emojis = client.recentEmojis;
|
||||
expect(emojis.isEmpty, isTrue);
|
||||
|
||||
await client.addRecentEmoji('🦙');
|
||||
expect(client.recentEmojis['🦙'], 1);
|
||||
});
|
||||
|
||||
test('setMuteAllPushNotifications', () async {
|
||||
await matrix.setMuteAllPushNotifications(false);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2121,6 +2121,8 @@ class FakeMatrixApi extends MockClient {
|
|||
'/client/unstable/room_keys/version': (var reqI) => {'version': '5'},
|
||||
},
|
||||
'PUT': {
|
||||
'/client/r0/user/${Uri.encodeComponent('@alice:example.com')}/account_data/io.element.recent_emoji}':
|
||||
(var req) => {},
|
||||
'/client/r0/user/%40test%3AfakeServer.notExisting/account_data/m.ignored_user_list':
|
||||
(var req) => {},
|
||||
'/client/r0/presence/${Uri.encodeComponent('@alice:example.com')}/status':
|
||||
|
|
|
|||
Loading…
Reference in New Issue