mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
//
|
||||
// Copyright 2022-2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EmojiCategory: Equatable, Identifiable {
|
||||
let id: String
|
||||
let emojis: [EmojiItem]
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// Copyright 2022-2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EmojiItem: Equatable, Identifiable {
|
||||
var id: String {
|
||||
label
|
||||
}
|
||||
|
||||
let label: String
|
||||
let unicode: String
|
||||
let keywords: [String]
|
||||
let shortcodes: [String]
|
||||
}
|
||||
@@ -7,31 +7,38 @@
|
||||
|
||||
import Emojibase
|
||||
import Foundation
|
||||
|
||||
@MainActor
|
||||
protocol EmojiProviderProtocol {
|
||||
func categories(searchString: String?) async -> [EmojiCategory]
|
||||
}
|
||||
|
||||
private enum EmojiProviderState {
|
||||
case notLoaded
|
||||
case inProgress(Task<[EmojiCategory], Never>)
|
||||
case loaded([EmojiCategory])
|
||||
}
|
||||
import OrderedCollections
|
||||
|
||||
class EmojiProvider: EmojiProviderProtocol {
|
||||
private let loader: EmojiLoaderProtocol
|
||||
private var state: EmojiProviderState = .notLoaded
|
||||
private let appSettings: AppSettings
|
||||
|
||||
init(loader: EmojiLoaderProtocol = EmojibaseDatasource()) {
|
||||
private(set) var state: EmojiProviderState = .notLoaded
|
||||
|
||||
init(loader: EmojiLoaderProtocol = EmojibaseDatasource(), appSettings: AppSettings) {
|
||||
self.loader = loader
|
||||
self.appSettings = appSettings
|
||||
|
||||
Task {
|
||||
await loadIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
func categories(searchString: String? = nil) async -> [EmojiCategory] {
|
||||
let emojiCategories = await loadIfNeeded()
|
||||
var emojiCategories = await loadIfNeeded()
|
||||
|
||||
let allEmojis = emojiCategories.reduce([]) { partialResult, category in
|
||||
partialResult + category.emojis
|
||||
}
|
||||
|
||||
// Map frequently used system unicode emojis to our emoji provider ones
|
||||
let frequentlyUsedEmojis = frequentlyUsedSystemEmojis().prefix(20)
|
||||
let emojis = allEmojis.filter { frequentlyUsedEmojis.contains($0.unicode) }
|
||||
|
||||
if !emojis.isEmpty {
|
||||
emojiCategories.insert(.init(id: EmojiCategory.frequentlyUsedCategoryIdentifier, emojis: emojis), at: 0)
|
||||
}
|
||||
|
||||
if let searchString, searchString.isEmpty == false {
|
||||
return search(searchString: searchString, emojiCategories: emojiCategories)
|
||||
} else {
|
||||
@@ -39,6 +46,40 @@ class EmojiProvider: EmojiProviderProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func frequentlyUsedSystemEmojis() -> [String] {
|
||||
guard appSettings.frequentEmojisEnabled, !ProcessInfo.processInfo.isiOSAppOnMac else {
|
||||
return []
|
||||
}
|
||||
|
||||
guard let preferences = UserDefaults(suiteName: "com.apple.EmojiPreferences"),
|
||||
let defaults = preferences.dictionary(forKey: "EMFDefaultsKey"),
|
||||
let recents = defaults["EMFRecentsKey"] as? [String]
|
||||
else {
|
||||
return []
|
||||
}
|
||||
|
||||
return recents
|
||||
}
|
||||
|
||||
func markEmojiAsFrequentlyUsed(_ emoji: String) {
|
||||
guard appSettings.frequentEmojisEnabled else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let preferences = UserDefaults(suiteName: "com.apple.EmojiPreferences"),
|
||||
let defaults = preferences.dictionary(forKey: "EMFDefaultsKey"),
|
||||
let recents = defaults["EMFRecentsKey"] as? [String] else {
|
||||
return
|
||||
}
|
||||
|
||||
var uniqueOrderedRecents = OrderedSet(recents)
|
||||
uniqueOrderedRecents.insert(emoji, at: 0)
|
||||
|
||||
preferences.setValue(["EMFRecentsKey": Array(uniqueOrderedRecents)], forKey: "EMFDefaultsKey")
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func search(searchString: String, emojiCategories: [EmojiCategory]) -> [EmojiCategory] {
|
||||
emojiCategories.compactMap { category in
|
||||
let emojis = category.emojis.filter { emoji in
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct EmojiItem: Equatable, Identifiable {
|
||||
var id: String {
|
||||
label
|
||||
}
|
||||
|
||||
let label: String
|
||||
let unicode: String
|
||||
let keywords: [String]
|
||||
let shortcodes: [String]
|
||||
}
|
||||
|
||||
struct EmojiCategory: Equatable, Identifiable {
|
||||
static let frequentlyUsedCategoryIdentifier = "io.element.elementx.frequently_used"
|
||||
|
||||
let id: String
|
||||
let emojis: [EmojiItem]
|
||||
}
|
||||
|
||||
enum EmojiProviderState {
|
||||
case notLoaded
|
||||
case inProgress(Task<[EmojiCategory], Never>)
|
||||
case loaded([EmojiCategory])
|
||||
}
|
||||
|
||||
@MainActor
|
||||
protocol EmojiProviderProtocol {
|
||||
var state: EmojiProviderState { get }
|
||||
|
||||
func categories(searchString: String?) async -> [EmojiCategory]
|
||||
|
||||
func frequentlyUsedSystemEmojis() -> [String]
|
||||
func markEmojiAsFrequentlyUsed(_ emoji: String)
|
||||
}
|
||||
Reference in New Issue
Block a user