metatext/ServiceLayer/Sources/ServiceLayer/Services/EmojiPickerService.swift
2021-01-17 18:42:03 -08:00

128 lines
4.1 KiB
Swift

// Copyright © 2020 Metabolist. All rights reserved.
import Combine
import DB
import Foundation
import Mastodon
public enum EmojiPickerError: Error {
case emojisFileMissing
case invalidSystemEmojiGroup
case annotationsAndTagsFileMissing
}
public struct EmojiPickerService {
private let contentDatabase: ContentDatabase
init(contentDatabase: ContentDatabase) {
self.contentDatabase = contentDatabase
}
}
public extension EmojiPickerService {
func customEmojiPublisher() -> AnyPublisher<[PickerEmoji.Category: [PickerEmoji]], Error> {
contentDatabase.pickerEmojisPublisher().map {
var typed = [PickerEmoji.Category: [PickerEmoji]]()
for emoji in $0 {
let category: PickerEmoji.Category
if let categoryName = emoji.category {
category = .customNamed(categoryName)
} else {
category = .custom
}
if typed[category] == nil {
typed[category] = [.custom(emoji, inFrequentlyUsed: false)]
} else {
typed[category]?.append(.custom(emoji, inFrequentlyUsed: false))
}
}
return typed
}
.eraseToAnyPublisher()
}
func systemEmojiPublisher() -> AnyPublisher<[PickerEmoji.Category: [PickerEmoji]], Error> {
Future { promise in
DispatchQueue.global(qos: .userInteractive).async {
guard let url = Bundle.module.url(forResource: "emojis", withExtension: "json") else {
promise(.failure(EmojiPickerError.emojisFileMissing))
return
}
do {
let data = try Data(contentsOf: url)
let decoded = try JSONDecoder().decode([String: [SystemEmoji]].self, from: data)
var typed = [PickerEmoji.Category: [PickerEmoji]]()
for (groupString, emoji) in decoded {
guard let rawValue = Int(groupString),
let group = SystemEmoji.Group(rawValue: rawValue)
else {
promise(.failure(EmojiPickerError.invalidSystemEmojiGroup))
return
}
typed[.systemGroup(group)] = emoji
.filter { !($0.version > Self.maxEmojiVersion) }
.map {
PickerEmoji.system(
$0.withMaxVersionForSkinToneVariations(Self.maxEmojiVersion),
inFrequentlyUsed: false)
}
}
return promise(.success(typed))
} catch {
promise(.failure(error))
}
}
}
.eraseToAnyPublisher()
}
func systemEmojiAnnotationsAndTagsPublisher(languageCode: String) -> AnyPublisher<[String: String], Error> {
Future { promise in
guard let url = Bundle.module.url(forResource: languageCode, withExtension: "json") else {
promise(.failure(EmojiPickerError.annotationsAndTagsFileMissing))
return
}
do {
let data = try Data(contentsOf: url)
let decoded = try JSONDecoder().decode([String: String].self, from: data)
promise(.success(decoded))
} catch {
promise(.failure(error))
}
}
.print()
.eraseToAnyPublisher()
}
func emojiUses(limit: Int) -> AnyPublisher<[EmojiUse], Error> {
contentDatabase.emojiUses(limit: limit)
}
func updateUse(emoji: PickerEmoji) -> AnyPublisher<Never, Error> {
contentDatabase.updateUse(emoji: emoji.name, system: emoji.system)
}
}
private extension EmojiPickerService {
static var maxEmojiVersion: Float = {
if #available(iOS 14.2, *) {
return 13.0
}
return 12.1
}()
}