diff --git a/lib/famedlysdk.dart b/lib/famedlysdk.dart index 39860ab..3a1c51d 100644 --- a/lib/famedlysdk.dart +++ b/lib/famedlysdk.dart @@ -28,6 +28,7 @@ export 'package:famedlysdk/src/utils/uri_extension.dart'; export 'package:famedlysdk/src/utils/matrix_localizations.dart'; export 'package:famedlysdk/src/utils/receipt.dart'; export 'package:famedlysdk/src/utils/states_map.dart'; +export 'package:famedlysdk/src/utils/sync_update_extension.dart'; export 'package:famedlysdk/src/utils/to_device_event.dart'; export 'package:famedlysdk/src/client.dart'; export 'package:famedlysdk/src/event.dart'; diff --git a/lib/src/utils/sync_update_extension.dart b/lib/src/utils/sync_update_extension.dart new file mode 100644 index 0000000..c4b9ecb --- /dev/null +++ b/lib/src/utils/sync_update_extension.dart @@ -0,0 +1,44 @@ +/* + * Famedly Matrix SDK + * Copyright (C) 2020 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 . + */ + +import 'package:famedlysdk/matrix_api.dart'; + +/// This extension adds easy-to-use filters for the sync update, meant to be used on the `client.onSync` stream, e.g. +/// `client.onSync.stream.where((s) => s.hasRoomUpdate)`. Multiple filters can easily be +/// combind with boolean logic: `client.onSync.stream.where((s) => s.hasRoomUpdate || s.hasPresenceUpdate)` +extension SyncUpdateFilters on SyncUpdate { + /// Returns true if this sync updat has a room update + /// That means there is account data, if there is a room in one of the `join`, `leave` or `invite` blocks of the sync or if there is a to_device event. + bool get hasRoomUpdate { + // if we have an account data change we need to re-render, as `m.direct` might have changed + if (accountData?.isNotEmpty ?? false) { + return true; + } + // check for a to_device event + if (toDevice?.isNotEmpty ?? false) { + return true; + } + // return if there are rooms to update + return (rooms?.join?.isNotEmpty ?? false) || + (rooms?.invite?.isNotEmpty ?? false) || + (rooms?.leave?.isNotEmpty ?? false); + } + + /// Returns if this sync update has presence updates + bool get hasPresenceUpdate => presence != null && presence.isNotEmpty; +} diff --git a/test/sync_filter_test.dart b/test/sync_filter_test.dart new file mode 100644 index 0000000..71ac01b --- /dev/null +++ b/test/sync_filter_test.dart @@ -0,0 +1,166 @@ +/* + * Ansible inventory script used at Famedly GmbH for managing many hosts + * Copyright (C) 2020 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 . + */ + +import 'package:famedlysdk/famedlysdk.dart'; +import 'package:test/test.dart'; + +const UPDATES = { + 'empty': { + 'next_batch': 'blah', + 'account_data': { + 'events': [], + }, + 'presences': { + 'events': [], + }, + 'rooms': { + 'join': {}, + 'leave': {}, + 'invite': {}, + }, + 'to_device': { + 'events': [], + }, + }, + 'presence': { + 'next_batch': 'blah', + 'presence': { + 'events': [ + { + 'content': { + 'avatar_url': 'mxc://localhost:wefuiwegh8742w', + 'last_active_ago': 2478593, + 'presence': 'online', + 'currently_active': false, + 'status_msg': 'Making cupcakes' + }, + 'type': 'm.presence', + 'sender': '@example:localhost', + }, + ], + }, + }, + 'account_data': { + 'next_batch': 'blah', + 'account_data': { + 'events': [ + { + 'type': 'blah', + 'content': { + 'beep': 'boop', + }, + }, + ], + }, + }, + 'invite': { + 'next_batch': 'blah', + 'rooms': { + 'invite': { + '!room': { + 'invite_state': { + 'events': [], + }, + }, + }, + }, + }, + 'leave': { + 'next_batch': 'blah', + 'rooms': { + 'leave': { + '!room': {}, + }, + }, + }, + 'join': { + 'next_batch': 'blah', + 'rooms': { + 'join': { + '!room': { + 'timeline': { + 'events': [], + }, + 'state': { + 'events': [], + }, + 'account_data': { + 'events': [], + }, + 'ephemeral': { + 'events': [], + }, + 'unread_notifications': {}, + 'summary': {}, + }, + }, + }, + }, + 'to_device': { + 'next_batch': 'blah', + 'to_device': { + 'events': [ + { + 'type': 'beep', + 'content': { + 'blah': 'blubb', + }, + }, + ], + }, + }, +}; + +void testUpdates(bool Function(SyncUpdate s) test, Map expected) { + for (final update in UPDATES.entries) { + var sync = SyncUpdate.fromJson(update.value); + expect(test(sync), expected[update.key]); + } +} + +void main() { + group('Sync Filters', () { + test('room update', () { + var testFn = (SyncUpdate s) => s.hasRoomUpdate; + final expected = { + 'empty': false, + 'presence': false, + 'account_data': true, + 'invite': true, + 'leave': true, + 'join': true, + 'to_device': true, + }; + testUpdates(testFn, expected); + }); + + test('presence update', () { + var testFn = (SyncUpdate s) => s.hasPresenceUpdate; + final expected = { + 'empty': false, + 'presence': true, + 'account_data': false, + 'invite': false, + 'leave': false, + 'join': false, + 'to_device': false, + }; + testUpdates(testFn, expected); + }); + }); +}