mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-20 16:42:44 +02:00
Emoji picker: Handle Manu's comments.
This commit is contained in:
+16
-7
@@ -17,10 +17,19 @@
|
||||
import Foundation
|
||||
|
||||
extension EmojiItem: Decodable {
|
||||
|
||||
|
||||
/// JSON keys associated to EmojiItem properties.
|
||||
/// See https://github.com/missive/emoji-mart/blob/master/src/utils/data.js for minified letters informations.
|
||||
///
|
||||
/// - shortName: The commonly-agreed short name for the emoji, as supported in GitHub and others via the :short_name: syntax.
|
||||
/// - name: The offical Unicode name.
|
||||
/// - codepoint: The Unicode codepoint, as 4-5 hex digits. Where an emoji needs 2 or more codepoints, they are specified like 1F1EA-1F1F8.
|
||||
/// - shortNames: An array of all the other known short names.
|
||||
/// - keywords: Associated emoji keywords.
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case code = "b"
|
||||
case shortName
|
||||
case name = "a"
|
||||
case codepoint = "b"
|
||||
case shortNames = "n"
|
||||
case keywords = "j"
|
||||
}
|
||||
@@ -28,11 +37,11 @@ extension EmojiItem: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
guard let identifier = decoder.codingPath.last?.stringValue else {
|
||||
throw DecodingError.dataCorruptedError(forKey: .code, in: container, debugDescription: "Cannot initialize identifier")
|
||||
guard let shortName = decoder.codingPath.last?.stringValue else {
|
||||
throw DecodingError.dataCorruptedError(forKey: .shortName, in: container, debugDescription: "Cannot initialize short name")
|
||||
}
|
||||
|
||||
let emojiUnicodeStringValue = try container.decode(String.self, forKey: .code)
|
||||
let emojiUnicodeStringValue = try container.decode(String.self, forKey: .codepoint)
|
||||
|
||||
let unicodeStringComponents = emojiUnicodeStringValue.components(separatedBy: "-")
|
||||
|
||||
@@ -43,7 +52,7 @@ extension EmojiItem: Decodable {
|
||||
let emojiUnicodeScalar = UnicodeScalar(unicodeCodePoint) {
|
||||
emoji.append(String(emojiUnicodeScalar))
|
||||
} else {
|
||||
throw DecodingError.dataCorruptedError(forKey: .code, in: container, debugDescription: "Cannot initialize emoji")
|
||||
throw DecodingError.dataCorruptedError(forKey: .codepoint, in: container, debugDescription: "Cannot initialize emoji")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +74,7 @@ extension EmojiItem: Decodable {
|
||||
keywords = []
|
||||
}
|
||||
|
||||
self.init(identifier: identifier,
|
||||
self.init(shortName: shortName,
|
||||
value: emoji,
|
||||
name: name,
|
||||
shortNames: shortNames,
|
||||
+5
-3
@@ -16,19 +16,21 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EmojiJSONStore {
|
||||
struct EmojiMartStore {
|
||||
let categories: [EmojiJSONCategory]
|
||||
let emojis: [EmojiItem]
|
||||
}
|
||||
|
||||
// MARK: - Decodable
|
||||
extension EmojiJSONStore: Decodable {
|
||||
extension EmojiMartStore: Decodable {
|
||||
|
||||
/// JSON keys associated to EmojiJSONStore properties.
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case categories
|
||||
case emojis
|
||||
}
|
||||
|
||||
/// JSON key associated to emoji short name.
|
||||
struct EmojiKey: CodingKey {
|
||||
var stringValue: String
|
||||
|
||||
@@ -51,7 +53,7 @@ extension EmojiJSONStore: Decodable {
|
||||
do {
|
||||
emojiItem = try emojisContainer.decode(EmojiItem.self, forKey: emojiKey)
|
||||
} catch {
|
||||
print(error)
|
||||
print("[EmojiJSONStore] init(from decoder: Decoder) failed to parse emojiItem \(emojiKey) with error: \(error)")
|
||||
emojiItem = nil
|
||||
}
|
||||
|
||||
+11
-4
@@ -16,18 +16,25 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EmojiJSONCategory {
|
||||
struct EmojiMartCategory {
|
||||
|
||||
/// Emoji category identifier (e.g. "people")
|
||||
let identifier: String
|
||||
|
||||
/// Emoji category name in english (e.g. "Smiley & People")
|
||||
let name: String
|
||||
let emojiIdentifiers: [String]
|
||||
|
||||
/// List of emoji short names associated to the category (e.g. "people")
|
||||
let emojiShortNames: [String]
|
||||
}
|
||||
|
||||
// MARK: - Decodable
|
||||
extension EmojiJSONCategory: Decodable {
|
||||
extension EmojiMartCategory: Decodable {
|
||||
|
||||
/// JSON keys associated to EmojiJSONCategory properties.
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case identifier = "id"
|
||||
case name
|
||||
case emojiIdentifiers = "emojis"
|
||||
case emojiShortNames = "emojis"
|
||||
}
|
||||
}
|
||||
+9
-7
@@ -20,16 +20,18 @@ enum EmojiServiceError: Error {
|
||||
case emojiJSONFileNotFound
|
||||
}
|
||||
|
||||
final class EmojiService: EmojiServiceType {
|
||||
/// Emoji service powered by Emoji Mart data (https://github.com/missive/emoji-mart/)
|
||||
final class EmojiMartService: EmojiServiceType {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
/// Emoji data coming from https://github.com/missive/emoji-mart/blob/master/data/apple.json
|
||||
private static let jsonFilename = "apple_emojis_data"
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private let serializationService: SerializationServiceType = SerializationService()
|
||||
private let serviceQueue = DispatchQueue(label: "\(type(of: EmojiService.self))")
|
||||
private let serviceQueue = DispatchQueue(label: "\(type(of: EmojiMartService.self))")
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@@ -37,7 +39,7 @@ final class EmojiService: EmojiServiceType {
|
||||
self.serviceQueue.async {
|
||||
do {
|
||||
let emojiJSONData = try self.getEmojisJSONData()
|
||||
let emojiJSONStore: EmojiJSONStore = try self.serializationService.deserialize(emojiJSONData)
|
||||
let emojiJSONStore: EmojiMartStore = try self.serializationService.deserialize(emojiJSONData)
|
||||
let emojiCategories = self.emojiCategories(from: emojiJSONStore)
|
||||
completion(MXResponse.success(emojiCategories))
|
||||
} catch {
|
||||
@@ -49,19 +51,19 @@ final class EmojiService: EmojiServiceType {
|
||||
// MARK: - Private
|
||||
|
||||
private func getEmojisJSONData() throws -> Data {
|
||||
guard let jsonDataURL = Bundle.main.url(forResource: EmojiService.jsonFilename, withExtension: "json") else {
|
||||
guard let jsonDataURL = Bundle.main.url(forResource: EmojiMartService.jsonFilename, withExtension: "json") else {
|
||||
throw EmojiServiceError.emojiJSONFileNotFound
|
||||
}
|
||||
let jsonData = try Data(contentsOf: jsonDataURL)
|
||||
return jsonData
|
||||
}
|
||||
|
||||
private func emojiCategories(from emojiJSONStore: EmojiJSONStore) -> [EmojiCategory] {
|
||||
private func emojiCategories(from emojiJSONStore: EmojiMartStore) -> [EmojiCategory] {
|
||||
let allEmojiItems = emojiJSONStore.emojis
|
||||
|
||||
return emojiJSONStore.categories.map { (jsonCategory) -> EmojiCategory in
|
||||
let emojiItems = jsonCategory.emojiIdentifiers.compactMap({ (emojiIdentifier) -> EmojiItem? in
|
||||
return allEmojiItems.first(where: { $0.identifier == emojiIdentifier })
|
||||
let emojiItems = jsonCategory.emojiShortNames.compactMap({ (shortName) -> EmojiItem? in
|
||||
return allEmojiItems.first(where: { $0.shortName == shortName })
|
||||
})
|
||||
return EmojiCategory(identifier: jsonCategory.identifier, emojis: emojiItems)
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EmojiMartStore {
|
||||
let categories: [EmojiMartCategory]
|
||||
let emojis: [EmojiItem]
|
||||
}
|
||||
|
||||
// MARK: - Decodable
|
||||
extension EmojiMartStore: Decodable {
|
||||
|
||||
/// JSON keys associated to EmojiMartStore properties.
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case categories
|
||||
case emojis
|
||||
}
|
||||
|
||||
/// JSON key associated to emoji short name.
|
||||
struct EmojiKey: CodingKey {
|
||||
var stringValue: String
|
||||
|
||||
init?(stringValue: String) {
|
||||
self.stringValue = stringValue
|
||||
}
|
||||
|
||||
var intValue: Int? { return nil }
|
||||
init?(intValue: Int) { return nil }
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
let emojisContainer = try container.nestedContainer(keyedBy: EmojiKey.self, forKey: .emojis)
|
||||
|
||||
let emojis: [EmojiItem] = emojisContainer.allKeys.compactMap { (emojiKey) -> EmojiItem? in
|
||||
let emojiItem: EmojiItem?
|
||||
|
||||
do {
|
||||
emojiItem = try emojisContainer.decode(EmojiItem.self, forKey: emojiKey)
|
||||
} catch {
|
||||
print("[EmojiJSONStore] init(from decoder: Decoder) failed to parse emojiItem \(emojiKey) with error: \(error)")
|
||||
emojiItem = nil
|
||||
}
|
||||
|
||||
return emojiItem
|
||||
}
|
||||
|
||||
let categories = try container.decode([EmojiMartCategory].self, forKey: .categories)
|
||||
|
||||
self.init(categories: categories, emojis: emojis)
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,14 @@
|
||||
import Foundation
|
||||
|
||||
struct EmojiCategory {
|
||||
|
||||
/// Emoji category identifier (e.g. "people")
|
||||
let identifier: String
|
||||
|
||||
/// Emoji list associated to category
|
||||
let emojis: [EmojiItem]
|
||||
|
||||
/// Emoji category localized name
|
||||
var name: String {
|
||||
let categoryNameLocalizationKey = "emoji_picker_\(self.identifier)_category"
|
||||
return VectorL10n.tr("Vector", categoryNameLocalizationKey)
|
||||
|
||||
@@ -17,21 +17,37 @@
|
||||
import Foundation
|
||||
|
||||
struct EmojiItem {
|
||||
let identifier: String
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// The commonly-agreed short name for the emoji, as supported in GitHub and others via the :short_name: syntax (e.g. "grinning" for 😀).
|
||||
let shortName: String
|
||||
|
||||
/// The emoji string (e.g. 😀)
|
||||
let value: String
|
||||
|
||||
/// The offical Unicode name (e.g. "Grinning Face" for 😀)
|
||||
let name: String
|
||||
|
||||
/// An array of all the other known short names (e.g. ["running"] for 🏃♂️).
|
||||
let shortNames: [String]
|
||||
let keywords: [String]
|
||||
|
||||
/// Associated emoji keywords (e.g. ["face","smile","happy"] for 😀).
|
||||
let keywords: [String]
|
||||
|
||||
/// For emoji with multiple skin tone variations, a list of alternative emoji items.
|
||||
let variations: [EmojiItem]
|
||||
|
||||
init(identifier: String,
|
||||
// MARK: - Setup
|
||||
|
||||
init(shortName: String,
|
||||
value: String,
|
||||
name: String,
|
||||
shortNames: [String] = [],
|
||||
keywords: [String] = [],
|
||||
variations: [EmojiItem] = []) {
|
||||
|
||||
self.identifier = identifier
|
||||
self.shortName = shortName
|
||||
self.value = value
|
||||
self.name = name
|
||||
self.shortNames = shortNames
|
||||
|
||||
Reference in New Issue
Block a user