LNIMEmojiManager.swift 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. //
  2. // LNIMEmojiManager.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/12/18.
  6. //
  7. import Foundation
  8. private class LNEmojiConfig: Codable {
  9. var face_name: String = ""
  10. var face_file: String = ""
  11. var face_id: String = ""
  12. }
  13. class LNIMEmojiManager {
  14. static let shared = LNIMEmojiManager()
  15. private let resourcePath = URL(fileURLWithPath: Bundle.main.path(forResource: "TUIChatFace", ofType: "bundle")!)
  16. private var recentEmoji: [String] = [] {
  17. didSet {
  18. LNUserDefaults[.imRecentEmojis] = recentEmoji
  19. }
  20. }
  21. private(set) var emojiGroup: LNEmojiGroup?
  22. var recentGroup: LNEmojiGroup? {
  23. guard !recentEmoji.isEmpty else { return nil }
  24. let emojis = recentEmoji.compactMap {
  25. emojiGroup?.emojiFor($0)
  26. }
  27. guard !emojis.isEmpty else { return nil }
  28. let group = LNEmojiGroup()
  29. group.emojis = emojis
  30. group.groupName = TIMLocalizedText.shared.commonText("TUIChatFaceGroupRecentEmojiName")
  31. return group
  32. }
  33. private var faceCache: [String: UIImage] = [:]
  34. private init() {
  35. DispatchQueue.global().async { [weak self] in
  36. guard let self else { return }
  37. reloadEmoji()
  38. loadRecent()
  39. }
  40. }
  41. func addRecentEmoji(name: String) {
  42. if recentEmoji.contains(name) {
  43. recentEmoji.removeAll { $0 == name }
  44. } else {
  45. recentEmoji.removeLast()
  46. }
  47. recentEmoji.insert(name, at: 0)
  48. }
  49. func getFaceFromCache(path: String?) -> UIImage? {
  50. guard let path, !path.isEmpty else { return nil }
  51. if let image = faceCache[path] { return image }
  52. if let loaded = UIImage(contentsOfFile: path) {
  53. faceCache[path] = loaded
  54. return loaded
  55. }
  56. return nil
  57. }
  58. }
  59. extension LNIMEmojiManager {
  60. private func reloadEmoji() {
  61. let plistPath = resourcePath.appendingPathComponent("emoji/emoji.plist").path
  62. guard let data = try? Data(contentsOf: URL(fileURLWithPath: plistPath)) else { return }
  63. let decoder = PropertyListDecoder()
  64. guard let infos: [LNEmojiConfig] = try? decoder.decode([LNEmojiConfig].self, from: data) else { return }
  65. var emojis: [LNEmojiData] = []
  66. for info in infos {
  67. let data = LNEmojiData()
  68. data.name = info.face_name
  69. data.path = resourcePath.appendingPathComponent(String(format: "emoji/%@", info.face_file)).path
  70. data.localizableName = TIMLocalizedText.shared.faceText(info.face_name)
  71. addFaceToCache(path: data.path)
  72. emojis.append(data)
  73. }
  74. guard !emojis.isEmpty else { return }
  75. let group = LNEmojiGroup()
  76. group.emojis = emojis
  77. group.groupName = TIMLocalizedText.shared.commonText("TUIChatFaceGroupAllEmojiName")
  78. emojiGroup = group
  79. }
  80. private func loadRecent() {
  81. let cached: [String] = LNUserDefaults[.imRecentEmojis, []]
  82. if !cached.isEmpty {
  83. recentEmoji = Array(cached.prefix(6))
  84. return
  85. }
  86. let plistPath = resourcePath.appendingPathComponent("emoji/emojiRecentDefaultList.plist").path
  87. guard let data = try? Data(contentsOf: URL(fileURLWithPath: plistPath)) else { return }
  88. let decoder = PropertyListDecoder()
  89. guard let infos: [LNEmojiConfig] = try? decoder.decode([LNEmojiConfig].self, from: data) else { return }
  90. let allNames: [String] = infos.map { $0.face_name }
  91. recentEmoji = Array(allNames.prefix(6))
  92. }
  93. private func addFaceToCache(path: String) {
  94. faceCache[path] = UIImage(contentsOfFile: path)
  95. }
  96. }