feat: Add image pack event content models
This commit is contained in:
parent
bad334d441
commit
cfa633b489
|
|
@ -41,6 +41,7 @@ export 'src/model/basic_event_with_sender.dart';
|
||||||
export 'src/model/basic_room_event.dart';
|
export 'src/model/basic_room_event.dart';
|
||||||
export 'src/model/event_types.dart';
|
export 'src/model/event_types.dart';
|
||||||
export 'src/model/events/forwarded_room_key_content.dart';
|
export 'src/model/events/forwarded_room_key_content.dart';
|
||||||
|
export 'src/model/events/image_pack_content.dart';
|
||||||
export 'src/model/events/olm_plaintext_payload.dart';
|
export 'src/model/events/olm_plaintext_payload.dart';
|
||||||
export 'src/model/events/room_encrypted_content.dart';
|
export 'src/model/events/room_encrypted_content.dart';
|
||||||
export 'src/model/events/room_encryption_content.dart';
|
export 'src/model/events/room_encryption_content.dart';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
/* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019, 2020, 2021 Famedly GmbH
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import '../basic_event.dart';
|
||||||
|
import '../../utils/filter_map_extension.dart';
|
||||||
|
import '../../utils/try_get_map_extension.dart';
|
||||||
|
|
||||||
|
extension ImagePackContentBasicEventExtension on BasicEvent {
|
||||||
|
ImagePackContent get parsedImagePackContent =>
|
||||||
|
ImagePackContent.fromJson(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ImagePackUsage {
|
||||||
|
sticker,
|
||||||
|
emoticon,
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ImagePackUsage>? imagePackUsageFromJson(List<String>? json) => json
|
||||||
|
?.map((v) => {
|
||||||
|
'sticker': ImagePackUsage.sticker,
|
||||||
|
'emoticon': ImagePackUsage.emoticon,
|
||||||
|
}[v])
|
||||||
|
.whereType<ImagePackUsage>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
List<String> imagePackUsageToJson(
|
||||||
|
List<ImagePackUsage>? usage, List<String>? prevUsage) {
|
||||||
|
final knownUsages = <String>{'sticker', 'emoticon'};
|
||||||
|
final usagesStr = usage
|
||||||
|
?.map((v) => {
|
||||||
|
ImagePackUsage.sticker: 'sticker',
|
||||||
|
ImagePackUsage.emoticon: 'emoticon',
|
||||||
|
}[v])
|
||||||
|
.whereType<String>()
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
|
// first we add all the unknown usages and the previous known usages which are new again
|
||||||
|
final newUsages = prevUsage
|
||||||
|
?.where((v) => !knownUsages.contains(v) || usagesStr.contains(v))
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
|
// now we need to add the new usages that we didn't add yet
|
||||||
|
newUsages.addAll(usagesStr.where((v) => !newUsages.contains(v)));
|
||||||
|
return newUsages;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImagePackContent {
|
||||||
|
// we want to preserve potential custom keys in this object
|
||||||
|
final Map<String, dynamic> _json;
|
||||||
|
|
||||||
|
Map<String, ImagePackImageContent> images;
|
||||||
|
ImagePackPackContent pack;
|
||||||
|
|
||||||
|
ImagePackContent({required this.images, required this.pack}) : _json = {};
|
||||||
|
|
||||||
|
ImagePackContent.fromJson(Map<String, dynamic> json)
|
||||||
|
: _json = Map.fromEntries(json.entries.where(
|
||||||
|
(e) => !['images', 'pack', 'emoticons', 'short'].contains(e.key))),
|
||||||
|
pack = ImagePackPackContent.fromJson(
|
||||||
|
json.tryGetMap<String, dynamic>('pack', TryGet.optional) ?? {}),
|
||||||
|
images = json
|
||||||
|
.tryGetMap<String, dynamic>('images', TryGet.optional)
|
||||||
|
?.catchMap(
|
||||||
|
(k, v) => MapEntry(k, ImagePackImageContent.fromJson(v))) ??
|
||||||
|
// the "emoticons" key needs a small migration on the key, ":string:" --> "string"
|
||||||
|
json
|
||||||
|
.tryGetMap<String, dynamic>('emoticons', TryGet.optional)
|
||||||
|
?.catchMap((k, v) => MapEntry(
|
||||||
|
k.startsWith(':') && k.endsWith(':')
|
||||||
|
? k.substring(1, k.length - 1)
|
||||||
|
: k,
|
||||||
|
ImagePackImageContent.fromJson(v))) ??
|
||||||
|
// the "short" key was still just a map from shortcode to mxc uri
|
||||||
|
json.tryGetMap<String, String>('short', TryGet.optional)?.catchMap(
|
||||||
|
(k, v) => MapEntry(
|
||||||
|
k.startsWith(':') && k.endsWith(':')
|
||||||
|
? k.substring(1, k.length - 1)
|
||||||
|
: k,
|
||||||
|
ImagePackImageContent(url: Uri.parse(v)))) ??
|
||||||
|
{};
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
..._json,
|
||||||
|
'images': images.map((k, v) => MapEntry(k, v.toJson())),
|
||||||
|
'pack': pack.toJson(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImagePackImageContent {
|
||||||
|
// we want to preserve potential custom keys in this object
|
||||||
|
final Map<String, dynamic> _json;
|
||||||
|
|
||||||
|
Uri url;
|
||||||
|
String? body;
|
||||||
|
Map<String, dynamic>? info;
|
||||||
|
List<ImagePackUsage>? usage;
|
||||||
|
|
||||||
|
ImagePackImageContent({required this.url, this.body, this.info, this.usage})
|
||||||
|
: _json = {};
|
||||||
|
|
||||||
|
ImagePackImageContent.fromJson(Map<String, dynamic> json)
|
||||||
|
: _json = Map.fromEntries(json.entries
|
||||||
|
.where((e) => !['url', 'body', 'info'].contains(e.key))),
|
||||||
|
url = Uri.parse(json['url']),
|
||||||
|
body = json.tryGet('body', TryGet.optional),
|
||||||
|
info = json.tryGetMap<String, dynamic>('info', TryGet.optional),
|
||||||
|
usage = imagePackUsageFromJson(
|
||||||
|
json.tryGetList<String>('usage', TryGet.optional));
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
...Map.from(_json)..remove('usage'),
|
||||||
|
'url': url.toString(),
|
||||||
|
if (body != null) 'body': body,
|
||||||
|
if (info != null) 'info': info,
|
||||||
|
if (usage != null)
|
||||||
|
'usage': imagePackUsageToJson(
|
||||||
|
usage, _json.tryGetList<String>('usage', TryGet.optional)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImagePackPackContent {
|
||||||
|
// we want to preserve potential custom keys in this object
|
||||||
|
final Map<String, dynamic> _json;
|
||||||
|
|
||||||
|
String? displayName;
|
||||||
|
Uri? avatarUrl;
|
||||||
|
List<ImagePackUsage>? usage;
|
||||||
|
String? attribution;
|
||||||
|
|
||||||
|
ImagePackPackContent(
|
||||||
|
{this.displayName, this.avatarUrl, this.usage, this.attribution})
|
||||||
|
: _json = {};
|
||||||
|
|
||||||
|
ImagePackPackContent.fromJson(Map<String, dynamic> json)
|
||||||
|
: _json = Map.fromEntries(json.entries.where((e) =>
|
||||||
|
!['display_name', 'avatar_url', 'attribution'].contains(e.key))),
|
||||||
|
displayName = json.tryGet('display_name', TryGet.optional),
|
||||||
|
// we default to an invalid uri
|
||||||
|
avatarUrl =
|
||||||
|
Uri.tryParse(json.tryGet('avatar_url', TryGet.optional) ?? '.::'),
|
||||||
|
usage = imagePackUsageFromJson(
|
||||||
|
json.tryGetList<String>('usage', TryGet.optional)),
|
||||||
|
attribution = json.tryGet('attribution', TryGet.optional);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
...Map.from(_json)..remove('usage'),
|
||||||
|
if (displayName != null) 'display_name': displayName,
|
||||||
|
if (avatarUrl != null) 'avatar_url': avatarUrl.toString(),
|
||||||
|
if (usage != null)
|
||||||
|
'usage': imagePackUsageToJson(
|
||||||
|
usage, _json.tryGetList<String>('usage', TryGet.optional)),
|
||||||
|
if (attribution != null) 'attribution': attribution,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -167,5 +167,84 @@ void main() {
|
||||||
json = jsonDecode(jsonEncode(json));
|
json = jsonDecode(jsonEncode(json));
|
||||||
expect(OlmPlaintextPayload.fromJson(json!).toJson(), json);
|
expect(OlmPlaintextPayload.fromJson(json!).toJson(), json);
|
||||||
});
|
});
|
||||||
|
test('Image Pack Content', () {
|
||||||
|
// basic parse / unparse
|
||||||
|
var json = <String, dynamic>{
|
||||||
|
'type': 'some type',
|
||||||
|
'content': {
|
||||||
|
'images': {
|
||||||
|
'emote': {
|
||||||
|
'url': 'mxc://example.org/beep',
|
||||||
|
'usage': ['emoticon'],
|
||||||
|
'org.custom': 'beep',
|
||||||
|
},
|
||||||
|
'sticker': {
|
||||||
|
'url': 'mxc://example.org/boop',
|
||||||
|
'usage': ['org.custom', 'sticker', 'org.other.custom'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'pack': {
|
||||||
|
'display_name': 'Awesome Pack',
|
||||||
|
'org.custom': 'boop',
|
||||||
|
},
|
||||||
|
'org.custom': 'blah',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
json = jsonDecode(jsonEncode(json));
|
||||||
|
expect(BasicEvent.fromJson(json).parsedImagePackContent.toJson(),
|
||||||
|
json['content']);
|
||||||
|
|
||||||
|
// emoticons migration
|
||||||
|
json = <String, dynamic>{
|
||||||
|
'type': 'some type',
|
||||||
|
'content': {
|
||||||
|
'emoticons': {
|
||||||
|
':emote:': {
|
||||||
|
'url': 'mxc://example.org/beep',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
json = jsonDecode(jsonEncode(json));
|
||||||
|
expect(
|
||||||
|
BasicEvent.fromJson(json)
|
||||||
|
.parsedImagePackContent
|
||||||
|
.images['emote']
|
||||||
|
?.toJson(),
|
||||||
|
{
|
||||||
|
'url': 'mxc://example.org/beep',
|
||||||
|
});
|
||||||
|
|
||||||
|
json = <String, dynamic>{
|
||||||
|
'type': 'some type',
|
||||||
|
'content': {
|
||||||
|
'short': {
|
||||||
|
':emote:': 'mxc://example.org/beep',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
json = jsonDecode(jsonEncode(json));
|
||||||
|
expect(
|
||||||
|
BasicEvent.fromJson(json)
|
||||||
|
.parsedImagePackContent
|
||||||
|
.images['emote']
|
||||||
|
?.toJson(),
|
||||||
|
{
|
||||||
|
'url': 'mxc://example.org/beep',
|
||||||
|
});
|
||||||
|
|
||||||
|
// invalid url for image
|
||||||
|
json = <String, dynamic>{
|
||||||
|
'type': 'some type',
|
||||||
|
'content': {
|
||||||
|
'images': {
|
||||||
|
'emote': {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
json = jsonDecode(jsonEncode(json));
|
||||||
|
expect(BasicEvent.fromJson(json).parsedImagePackContent.images['emote'],
|
||||||
|
null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue