allow sending emoji/stickers from emote packs
This commit is contained in:
parent
868023a4a1
commit
3754bc2feb
6 changed files with 701 additions and 74 deletions
|
|
@ -16,10 +16,24 @@ class const AccountData({
|
|||
|
||||
@JsonKey(name: AccountData.recentEmojiKey)
|
||||
final RecentEmojiData recentEmoji = const RecentEmojiData(),
|
||||
|
||||
/// Room image packs the user has enabled everywhere
|
||||
@JsonKey(
|
||||
name: AccountData.imagePackRoomsKey,
|
||||
fromJson: ImagePackRooms.tryFromJson,
|
||||
)
|
||||
final ImagePackRooms? imagePackRooms,
|
||||
@JsonKey(
|
||||
name: AccountData.legacyImagePackRoomsKey,
|
||||
fromJson: ImagePackRooms.tryFromJson,
|
||||
)
|
||||
final ImagePackRooms? legacyImagePackRooms,
|
||||
}) with _$AccountData {
|
||||
static const invitePermissionConfigKey = "m.invite_permission_config";
|
||||
static const directKey = "m.direct";
|
||||
static const recentEmojiKey = "m.recent_emoji";
|
||||
static const imagePackRoomsKey = "m.image_pack.rooms";
|
||||
static const legacyImagePackRoomsKey = "im.ponies.emote_rooms";
|
||||
|
||||
Map<String, Object?> toJson() => _$AccountDataToJson(this);
|
||||
|
||||
|
|
@ -38,6 +52,28 @@ class const RecentEmojiData({
|
|||
_$RecentEmojiDataFromJson(json);
|
||||
}
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
@JsonSerializable()
|
||||
class const ImagePackRooms({
|
||||
/// Room IDs to the state keys of their enabled packs, each to a (currently
|
||||
/// empty) object
|
||||
final IMap<String, IMap<String, Map<String, dynamic>>> rooms = const .empty(),
|
||||
}) with _$ImagePackRooms {
|
||||
static ImagePackRooms? tryFromJson(Map<String, dynamic>? json) {
|
||||
if (json == null) return null;
|
||||
try {
|
||||
return .fromJson(json);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object?> toJson() => _$ImagePackRoomsToJson(this);
|
||||
|
||||
factory ImagePackRooms.fromJson(Map<String, Object?> json) =>
|
||||
_$ImagePackRoomsFromJson(json);
|
||||
}
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
@JsonSerializable()
|
||||
class const InvitePermissionConfig({
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import "package:nexus/models/content/server_acl.dart";
|
|||
import "package:nexus/models/content/topic.dart";
|
||||
import "package:nexus/models/content/sticker.dart";
|
||||
import "package:nexus/models/content/history_visibility.dart";
|
||||
import "package:nexus/models/content/image_pack.dart";
|
||||
|
||||
class const Content({final String? parseError, final StackTrace? stackTrace}) {
|
||||
factory Content.fromJson(Map<String, dynamic> json) => const Content();
|
||||
|
|
@ -61,5 +62,7 @@ enum EventType(
|
|||
tombstone("m.room.tombstone", Content.fromJson),
|
||||
reaction("m.reaction", ReactionContent.fromJson),
|
||||
pinnedEvents("m.room.pinned_events", PinnedEventsContent.fromJson),
|
||||
imagePack("m.room.image_pack", ImagePackContent.fromJson),
|
||||
legacyImagePack("im.ponies.room_emotes", ImagePackContent.fromJson),
|
||||
message("m.room.message", MessageContent.fromJson),
|
||||
}
|
||||
|
|
|
|||
68
lib/models/content/image_pack.dart
Normal file
68
lib/models/content/image_pack.dart
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
import "package:nexus/models/content/content.dart";
|
||||
import "package:nexus/models/info/image.dart";
|
||||
part "image_pack.freezed.dart";
|
||||
part "image_pack.g.dart";
|
||||
|
||||
/// An image pack (MSC2545), from a room's state.
|
||||
@freezed
|
||||
sealed class ImagePackContent extends Content with _$ImagePackContent {
|
||||
ImagePackContent._();
|
||||
factory ImagePackContent({
|
||||
@Default(IMap.empty()) IMap<String, PackImage> images,
|
||||
PackInfo? pack,
|
||||
}) = _ImagePackContent;
|
||||
|
||||
IMap<String, PackImage> imagesFor(ImageUsage usage) => images.where(
|
||||
(_, image) => switch ((image.usage, pack?.usage ?? const .empty())) {
|
||||
(final usages, _) when usages.isNotEmpty => usages.contains(usage),
|
||||
(_, final packUsages) when packUsages.isNotEmpty => packUsages.contains(
|
||||
usage,
|
||||
),
|
||||
_ => true,
|
||||
},
|
||||
);
|
||||
|
||||
factory ImagePackContent.fromJson(Map<String, Object?> json) =>
|
||||
_$ImagePackContentFromJson(json);
|
||||
}
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
@JsonSerializable()
|
||||
class const PackImage({
|
||||
required final Uri url,
|
||||
final String? body,
|
||||
final ImageInfo? info,
|
||||
@JsonKey(fromJson: ImageUsage.setFromJson)
|
||||
final ISet<ImageUsage> usage = const .empty(),
|
||||
}) with _$PackImage {
|
||||
Map<String, Object?> toJson() => _$PackImageToJson(this);
|
||||
|
||||
factory PackImage.fromJson(Map<String, Object?> json) =>
|
||||
_$PackImageFromJson(json);
|
||||
}
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
@JsonSerializable()
|
||||
class const PackInfo({
|
||||
final String? displayName,
|
||||
final Uri? avatarUrl,
|
||||
final String? attribution,
|
||||
@JsonKey(fromJson: ImageUsage.setFromJson)
|
||||
final ISet<ImageUsage> usage = const .empty(),
|
||||
}) with _$PackInfo {
|
||||
Map<String, Object?> toJson() => _$PackInfoToJson(this);
|
||||
|
||||
factory PackInfo.fromJson(Map<String, Object?> json) =>
|
||||
_$PackInfoFromJson(json);
|
||||
}
|
||||
|
||||
enum ImageUsage {
|
||||
emoticon,
|
||||
sticker;
|
||||
|
||||
/// Ignores usages we don't know about.
|
||||
static ISet<ImageUsage> setFromJson(List<dynamic>? json) =>
|
||||
ISet(values.where((value) => json?.contains(value.name) ?? false));
|
||||
}
|
||||
|
|
@ -11,11 +11,15 @@ sealed class ReactionContent extends Content with _$ReactionContent {
|
|||
|
||||
factory ReactionContent({
|
||||
@JsonKey(readValue: ReactionContent.keyJsonFromJson) String? key,
|
||||
|
||||
/// For custom emoji reactions
|
||||
@JsonKey(name: "com.beeper.reaction.shortcode") String? shortcode,
|
||||
}) = _ReactionContent;
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => {
|
||||
"m.relates_to": {"key": key},
|
||||
"com.beeper.reaction.shortcode": ?shortcode,
|
||||
};
|
||||
|
||||
factory ReactionContent.fromJson(Map<String, Object?> json) =>
|
||||
|
|
|
|||
33
lib/models/image_pack.dart
Normal file
33
lib/models/image_pack.dart
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
import "package:nexus/models/content/image_pack.dart";
|
||||
|
||||
part "image_pack.freezed.dart";
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
class const ImagePack({
|
||||
required final String id,
|
||||
required final String name,
|
||||
final Uri? avatar,
|
||||
|
||||
/// Map of shortcode to images
|
||||
required final IMap<String, PackImage> emoticons,
|
||||
required final IMap<String, PackImage> stickers,
|
||||
}) with _$ImagePack {
|
||||
static String emojiMarkdown(String shortcode, String url) {
|
||||
final title = ":$shortcode:".replaceAll(r"\", r"\\").replaceAll('"', r'\"');
|
||||
return '';
|
||||
}
|
||||
|
||||
factory ImagePack.fromContent(
|
||||
ImagePackContent content, {
|
||||
required String id,
|
||||
required String fallbackName,
|
||||
}) => .new(
|
||||
id: id,
|
||||
name: content.pack?.displayName ?? fallbackName,
|
||||
avatar: content.pack?.avatarUrl,
|
||||
emoticons: content.imagesFor(.emoticon),
|
||||
stickers: content.imagesFor(.sticker),
|
||||
);
|
||||
}
|
||||
29
lib/models/requests/get_specific_room_state.dart
Normal file
29
lib/models/requests/get_specific_room_state.dart
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
|
||||
part "get_specific_room_state.freezed.dart";
|
||||
part "get_specific_room_state.g.dart";
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
@JsonSerializable()
|
||||
class const GetSpecificRoomStateRequest({
|
||||
required final IList<RoomStateKey> keys,
|
||||
}) with _$GetSpecificRoomStateRequest {
|
||||
Map<String, Object?> toJson() => _$GetSpecificRoomStateRequestToJson(this);
|
||||
|
||||
factory GetSpecificRoomStateRequest.fromJson(Map<String, Object?> json) =>
|
||||
_$GetSpecificRoomStateRequestFromJson(json);
|
||||
}
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
@JsonSerializable()
|
||||
class const RoomStateKey({
|
||||
required final String roomId,
|
||||
required final String type,
|
||||
required final String stateKey,
|
||||
}) with _$RoomStateKey {
|
||||
Map<String, Object?> toJson() => _$RoomStateKeyToJson(this);
|
||||
|
||||
factory RoomStateKey.fromJson(Map<String, Object?> json) =>
|
||||
_$RoomStateKeyFromJson(json);
|
||||
}
|
||||
Loading…
Reference in a new issue