session-ios/Session/Emoji/EmojiWithSkinTones.swift

74 lines
2.9 KiB
Swift
Raw Normal View History

2022-06-10 08:51:37 +02:00
//
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
//
public struct EmojiWithSkinTones: Hashable {
let baseEmoji: Emoji
let skinTones: [Emoji.SkinTone]?
init(baseEmoji: Emoji, skinTones: [Emoji.SkinTone]? = nil) {
self.baseEmoji = baseEmoji
// Deduplicate skin tones, while preserving order. This allows for
// multi-skin tone emoji, where if you have for example the permutation
// [.dark, .dark], it is consolidated to just [.dark], to be initialized
// with either variant and result in the correct emoji.
self.skinTones = skinTones?.reduce(into: [Emoji.SkinTone]()) { result, skinTone in
guard !result.contains(skinTone) else { return }
result.append(skinTone)
}
}
var rawValue: String {
if let skinTones = skinTones {
return baseEmoji.emojiPerSkinTonePermutation?[skinTones] ?? baseEmoji.rawValue
} else {
return baseEmoji.rawValue
}
}
}
extension Emoji {
private static let keyValueStore = SDSKeyValueStore(collection: "Emoji+PreferredSkinTonePermutation")
static func allSendableEmojiByCategoryWithPreferredSkinTones(transaction: YapDatabaseReadTransaction) -> [Category: [EmojiWithSkinTones]] {
return Category.allCases.reduce(into: [Category: [EmojiWithSkinTones]]()) { result, category in
result[category] = category.normalizedEmoji.filter { $0.available }.map { $0.withPreferredSkinTones(transaction: transaction) }
}
}
func withPreferredSkinTones(transaction: YapDatabaseReadTransaction) -> EmojiWithSkinTones {
guard let rawSkinTones = Self.keyValueStore.getObject(forKey: rawValue, transaction: transaction) as? [String] else {
return EmojiWithSkinTones(baseEmoji: self, skinTones: nil)
}
return EmojiWithSkinTones(baseEmoji: self, skinTones: rawSkinTones.compactMap { SkinTone(rawValue: $0) })
}
func setPreferredSkinTones(_ preferredSkinTonePermutation: [SkinTone]?, transaction: YapDatabaseReadWriteTransaction) {
if let preferredSkinTonePermutation = preferredSkinTonePermutation {
Self.keyValueStore.setObject(preferredSkinTonePermutation.map { $0.rawValue }, key: rawValue, transaction: transaction)
} else {
Self.keyValueStore.removeValue(forKey: rawValue, transaction: transaction)
}
}
init?(_ string: String) {
guard let emojiWithSkinTonePermutation = EmojiWithSkinTones(rawValue: string) else { return nil }
self = emojiWithSkinTonePermutation.baseEmoji
}
}
// MARK: -
extension String {
// This is slightly more accurate than String.isSingleEmoji,
// but slower.
//
// * This will reject "lone modifiers".
// * This will reject certain edge cases such as 🌈.
var isSingleEmojiUsingEmojiWithSkinTones: Bool {
EmojiWithSkinTones(rawValue: self) != nil
}
}