Emoji picker: Handle Manu's comments.

This commit is contained in:
SBiOSoftWhare
2019-07-26 14:06:19 +02:00
parent 4a696887a2
commit dacee398cf
12 changed files with 191 additions and 59 deletions

View File

@@ -508,7 +508,7 @@
B1DCC62422E60CA900625807 /* EmojiPickerCategoryViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62322E60CA800625807 /* EmojiPickerCategoryViewData.swift */; };
B1DCC62622E60CC600625807 /* EmojiItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62522E60CC600625807 /* EmojiItem.swift */; };
B1DCC62822E60CE300625807 /* EmojiCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62722E60CE300625807 /* EmojiCategory.swift */; };
B1DCC62A22E60D1000625807 /* EmojiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62922E60D1000625807 /* EmojiService.swift */; };
B1DCC62A22E60D1000625807 /* EmojiMartService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62922E60D1000625807 /* EmojiMartService.swift */; };
B1DCC62D22E61EAF00625807 /* EmojiPickerViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62B22E61EAF00625807 /* EmojiPickerViewCell.swift */; };
B1DCC62E22E61EAF00625807 /* EmojiPickerViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1DCC62C22E61EAF00625807 /* EmojiPickerViewCell.xib */; };
B1DCC63122E7026F00625807 /* EmojiPickerHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC62F22E7026F00625807 /* EmojiPickerHeaderView.swift */; };
@@ -516,9 +516,9 @@
B1DCC63422E72C1B00625807 /* UISearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63322E72C1B00625807 /* UISearchBar.swift */; };
B1DCC63522E72D2200625807 /* UISearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63322E72C1B00625807 /* UISearchBar.swift */; };
B1DCC63722E8541700625807 /* EmojiStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63622E8541700625807 /* EmojiStore.swift */; };
B1DCC63922E85E9A00625807 /* EmojiJSONStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63822E85E9A00625807 /* EmojiJSONStore.swift */; };
B1DCC63B22E85EF800625807 /* EmojiJSONCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63A22E85EF800625807 /* EmojiJSONCategory.swift */; };
B1DCC63F22E9A3AE00625807 /* EmojiItem+Decodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63E22E9A3AE00625807 /* EmojiItem+Decodable.swift */; };
B1DCC63922E85E9A00625807 /* EmojiMartStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63822E85E9A00625807 /* EmojiMartStore.swift */; };
B1DCC63B22E85EF800625807 /* EmojiMartCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63A22E85EF800625807 /* EmojiMartCategory.swift */; };
B1DCC63F22E9A3AE00625807 /* EmojiItem+EmojiMart.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1DCC63E22E9A3AE00625807 /* EmojiItem+EmojiMart.swift */; };
B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E5368821FB1E20001F3AFF /* UIButton.swift */; };
B1E5368D21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E5368C21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift */; };
B1E5368F21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1E5368E21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard */; };
@@ -1293,16 +1293,16 @@
B1DCC62322E60CA800625807 /* EmojiPickerCategoryViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerCategoryViewData.swift; sourceTree = "<group>"; };
B1DCC62522E60CC600625807 /* EmojiItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItem.swift; sourceTree = "<group>"; };
B1DCC62722E60CE300625807 /* EmojiCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCategory.swift; sourceTree = "<group>"; };
B1DCC62922E60D1000625807 /* EmojiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiService.swift; sourceTree = "<group>"; };
B1DCC62922E60D1000625807 /* EmojiMartService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartService.swift; sourceTree = "<group>"; };
B1DCC62B22E61EAF00625807 /* EmojiPickerViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerViewCell.swift; sourceTree = "<group>"; };
B1DCC62C22E61EAF00625807 /* EmojiPickerViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EmojiPickerViewCell.xib; sourceTree = "<group>"; };
B1DCC62F22E7026F00625807 /* EmojiPickerHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerHeaderView.swift; sourceTree = "<group>"; };
B1DCC63022E7026F00625807 /* EmojiPickerHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EmojiPickerHeaderView.xib; sourceTree = "<group>"; };
B1DCC63322E72C1B00625807 /* UISearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UISearchBar.swift; sourceTree = "<group>"; };
B1DCC63622E8541700625807 /* EmojiStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiStore.swift; sourceTree = "<group>"; };
B1DCC63822E85E9A00625807 /* EmojiJSONStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiJSONStore.swift; sourceTree = "<group>"; };
B1DCC63A22E85EF800625807 /* EmojiJSONCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiJSONCategory.swift; sourceTree = "<group>"; };
B1DCC63E22E9A3AE00625807 /* EmojiItem+Decodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EmojiItem+Decodable.swift"; sourceTree = "<group>"; };
B1DCC63822E85E9A00625807 /* EmojiMartStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartStore.swift; sourceTree = "<group>"; };
B1DCC63A22E85EF800625807 /* EmojiMartCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartCategory.swift; sourceTree = "<group>"; };
B1DCC63E22E9A3AE00625807 /* EmojiItem+EmojiMart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EmojiItem+EmojiMart.swift"; sourceTree = "<group>"; };
B1E5368821FB1E20001F3AFF /* UIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = "<group>"; };
B1E5368C21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromPassphraseViewController.swift; sourceTree = "<group>"; };
B1E5368E21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = KeyBackupRecoverFromPassphraseViewController.storyboard; sourceTree = "<group>"; };
@@ -1855,9 +1855,8 @@
isa = PBXGroup;
children = (
B1B9DEDD22E9D9890065E677 /* EmojiServiceType.swift */,
B1DCC62922E60D1000625807 /* EmojiService.swift */,
B1DCC64022E9B37400625807 /* Store */,
B1B9DED722E9B56C0065E677 /* JSON */,
B1B9DED722E9B56C0065E677 /* EmojiMart */,
);
path = Data;
sourceTree = "<group>";
@@ -3243,14 +3242,15 @@
path = Analytics;
sourceTree = "<group>";
};
B1B9DED722E9B56C0065E677 /* JSON */ = {
B1B9DED722E9B56C0065E677 /* EmojiMart */ = {
isa = PBXGroup;
children = (
B1DCC63822E85E9A00625807 /* EmojiJSONStore.swift */,
B1DCC63A22E85EF800625807 /* EmojiJSONCategory.swift */,
B1DCC63E22E9A3AE00625807 /* EmojiItem+Decodable.swift */,
B1DCC62922E60D1000625807 /* EmojiMartService.swift */,
B1DCC63822E85E9A00625807 /* EmojiMartStore.swift */,
B1DCC63A22E85EF800625807 /* EmojiMartCategory.swift */,
B1DCC63E22E9A3AE00625807 /* EmojiItem+EmojiMart.swift */,
);
path = JSON;
path = EmojiMart;
sourceTree = "<group>";
};
B1B9DED822E9B7120065E677 /* Serialization */ = {
@@ -4123,7 +4123,7 @@
3232ABA3225730E100AD6A5C /* DeviceVerificationStartCoordinatorType.swift in Sources */,
3232AB4D2256558300AD6A5C /* TemplateScreenCoordinatorType.swift in Sources */,
B1B5581720EF625800210D55 /* PreviewRoomTitleView.m in Sources */,
B1DCC63F22E9A3AE00625807 /* EmojiItem+Decodable.swift in Sources */,
B1DCC63F22E9A3AE00625807 /* EmojiItem+EmojiMart.swift in Sources */,
B1DCC61C22E5E17100625807 /* EmojiPickerViewAction.swift in Sources */,
B1098BDF21ECE09F000DDA48 /* Strings.swift in Sources */,
B1B558C420EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
@@ -4218,14 +4218,14 @@
32F6B96B2270623100BBA352 /* DeviceVerificationDataLoadingViewAction.swift in Sources */,
B1B558C920EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */,
B1B5571B20EE6C4D00210D55 /* DeactivateAccountViewController.m in Sources */,
B1DCC63922E85E9A00625807 /* EmojiJSONStore.swift in Sources */,
B1DCC63922E85E9A00625807 /* EmojiMartStore.swift in Sources */,
B1B5590620EF768F00210D55 /* RoomMembershipCollapsedWithPaginationTitleBubbleCell.m in Sources */,
B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */,
F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */,
B1DCC62422E60CA900625807 /* EmojiPickerCategoryViewData.swift in Sources */,
324A2056225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift in Sources */,
B16932F720F3C50E00746532 /* RecentsDataSource.m in Sources */,
B1DCC63B22E85EF800625807 /* EmojiJSONCategory.swift in Sources */,
B1DCC63B22E85EF800625807 /* EmojiMartCategory.swift in Sources */,
3232AB4F2256558300AD6A5C /* TemplateScreenViewController.swift in Sources */,
B1B558FC20EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */,
B1B5572920EE6C4D00210D55 /* RoomFilesViewController.m in Sources */,
@@ -4327,7 +4327,7 @@
B1B5575A20EE6C4D00210D55 /* UnifiedSearchViewController.m in Sources */,
3232AB492256558300AD6A5C /* FlowTemplateCoordinatorBridgePresenter.swift in Sources */,
B1B5572820EE6C4D00210D55 /* RoomViewController.m in Sources */,
B1DCC62A22E60D1000625807 /* EmojiService.swift in Sources */,
B1DCC62A22E60D1000625807 /* EmojiMartService.swift in Sources */,
B1B558C720EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.m in Sources */,
B1B558F020EF768F00210D55 /* RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m in Sources */,
926FA53F1F4C132000F826C2 /* MXSession+Riot.m in Sources */,

View File

@@ -1359,6 +1359,23 @@
href="https://www.apache.org/licenses/LICENSE-2.0">https://www.apache.org/licenses/LICENSE-2.0</a>
<br/><br/>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.
</li>
<li>
<p><b>Emoji Mart</b> (<a href="https://github.com/missive/emoji-mart">https://github.com/missive/emoji-mart</a>)</p>
<pre>
Copyright (c) 2016, Missive
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</pre>
</li>
</ul>
</body>
</html>

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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"
}
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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)

View File

@@ -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

View File

@@ -24,7 +24,7 @@ protocol EmojiPickerCoordinatorDelegate: class {
func emojiPickerCoordinatorDidCancel(_ coordinator: EmojiPickerCoordinatorType)
}
/// `EmojiPickerCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
/// `EmojiPickerCoordinatorType` is a protocol describing a Coordinator that handle emoji picker navigation flow.
protocol EmojiPickerCoordinatorType: Coordinator, Presentable {
var delegate: EmojiPickerCoordinatorDelegate? { get }
}

View File

@@ -27,8 +27,9 @@ final class EmojiPickerViewModel: EmojiPickerViewModelType {
private let session: MXSession
private let roomId: String
private let eventId: String
private let emojiService: EmojiService
private let emojiService: EmojiServiceType
private let emojiStore: EmojiStore
private let processingQueue: DispatchQueue
private lazy var aggregatedReactionsByEmoji: [String: MXReactionCount] = {
return self.buildAggregatedReactionsByEmoji()
@@ -45,8 +46,9 @@ final class EmojiPickerViewModel: EmojiPickerViewModelType {
self.session = session
self.roomId = roomId
self.eventId = eventId
self.emojiService = EmojiService()
self.emojiService = EmojiMartService()
self.emojiStore = EmojiStore.shared
self.processingQueue = DispatchQueue(label: "\(type(of: self))")
}
// MARK: - Public
@@ -99,16 +101,21 @@ final class EmojiPickerViewModel: EmojiPickerViewModelType {
}
private func searchEmojis(with searchText: String?) {
let filteredEmojiCategories: [EmojiCategory]
if let searchText = searchText, searchText.isEmpty == false {
filteredEmojiCategories = self.emojiStore.findEmojiItemsSortedByCategory(with: searchText)
} else {
filteredEmojiCategories = self.emojiStore.getAll()
self.processingQueue.async {
let filteredEmojiCategories: [EmojiCategory]
if let searchText = searchText, searchText.isEmpty == false {
filteredEmojiCategories = self.emojiStore.findEmojiItemsSortedByCategory(with: searchText)
} else {
filteredEmojiCategories = self.emojiStore.getAll()
}
let emojiCatagoryViewDataList = self.emojiCatagoryViewDataList(from: filteredEmojiCategories)
DispatchQueue.main.async {
self.update(viewState: .loaded(emojiCategories: emojiCatagoryViewDataList))
}
}
let emojiCatagoryViewDataList = self.emojiCatagoryViewDataList(from: filteredEmojiCategories)
self.update(viewState: .loaded(emojiCategories: emojiCatagoryViewDataList))
}
private func update(viewState: EmojiPickerViewState) {
@@ -119,7 +126,7 @@ final class EmojiPickerViewModel: EmojiPickerViewModelType {
return emojiCategories.map { (emojiCategory) -> EmojiPickerCategoryViewData in
let emojiPickerViewDataList = emojiCategory.emojis.map({ (emojiItem) -> EmojiPickerItemViewData in
let isSelected = self.isUserReacted(with: emojiItem.value)
return EmojiPickerItemViewData(identifier: emojiItem.identifier, emoji: emojiItem.value, isSelected: isSelected)
return EmojiPickerItemViewData(identifier: emojiItem.shortName, emoji: emojiItem.value, isSelected: isSelected)
})
return EmojiPickerCategoryViewData(identifier: emojiCategory.identifier, name: emojiCategory.name, emojiViewDataList: emojiPickerViewDataList)
}

View File

@@ -103,7 +103,7 @@ class EmojiServiceTests: XCTestCase {
let expectation = self.expectation(description: "get Emoji categories")
let emojiService = EmojiService()
let emojiService = EmojiMartService()
emojiService.getEmojiCategories { (response) in
switch response {
case .success(let emojiCategories):
@@ -125,7 +125,7 @@ class EmojiServiceTests: XCTestCase {
let grinningEmoji = peopleEmojiCategory.emojis[0]
XCTAssertEqual(grinningEmoji.identifier, "grinning")
XCTAssertEqual(grinningEmoji.shortName, "grinning")
XCTAssertEqual(grinningEmoji.value, "😀")
XCTAssertEqual(grinningEmoji.keywords.count, 6)