Make categorized emoji picker (#1680)

Signed-off-by: Euigyom Kim <egkim@dehol.kr>
This commit is contained in:
Euigyom Kim 2023-11-19 16:09:41 +09:00 committed by GitHub
parent 1e35ffb82b
commit d3b52b3206
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 23 deletions

View file

@ -13,4 +13,5 @@ public struct Emoji: Codable, Hashable, Identifiable, Equatable, Sendable {
public let url: URL
public let staticUrl: URL
public let visibleInPicker: Bool
public let category: String?
}

View file

@ -120,7 +120,7 @@ struct StatusEditorAccessoryView: View {
}
}
if !viewModel.customEmojis.isEmpty {
if !viewModel.customEmojiContainer.isEmpty {
Button {
isCustomEmojisSheetDisplay = true
} label: {
@ -283,29 +283,37 @@ struct StatusEditorAccessoryView: View {
private var customEmojisSheet: some View {
NavigationStack {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 40))], spacing: 9) {
ForEach(viewModel.customEmojis) { emoji in
LazyImage(url: emoji.url) { state in
if let image = state.image {
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 40, height: 40)
.accessibilityLabel(emoji.shortcode.replacingOccurrences(of: "_", with: " "))
.accessibilityAddTraits(.isButton)
} else if state.isLoading {
Rectangle()
.fill(Color.gray)
.frame(width: 40, height: 40)
.accessibility(hidden: true)
.shimmering()
ForEach(viewModel.customEmojiContainer) { container in
VStack(alignment: .leading) {
Text(container.categoryName)
.font(.scaledFootnote)
LazyVGrid(columns: [GridItem(.adaptive(minimum: 40))], spacing: 9) {
ForEach(container.emojis) { emoji in
LazyImage(url: emoji.url) { state in
if let image = state.image {
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 40, height: 40)
.accessibilityLabel(emoji.shortcode.replacingOccurrences(of: "_", with: " "))
.accessibilityAddTraits(.isButton)
} else if state.isLoading {
Rectangle()
.fill(Color.gray)
.frame(width: 40, height: 40)
.accessibility(hidden: true)
.shimmering()
}
}
.onTapGesture {
viewModel.insertStatusText(text: " :\(emoji.shortcode): ")
}
}
}
.onTapGesture {
viewModel.insertStatusText(text: " :\(emoji.shortcode): ")
}
}
}.padding(.horizontal)
.padding(.horizontal)
.padding(.bottom)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {

View file

@ -0,0 +1,8 @@
import Foundation
import Models
struct StatusEditorCategorizedEmojiContainer: Identifiable, Equatable {
let id = UUID().uuidString
let categoryName: String
var emojis: [Emoji]
}

View file

@ -100,7 +100,7 @@ import SwiftUI
var replyToStatus: Status?
var embeddedStatus: Status?
var customEmojis: [Emoji] = []
var customEmojiContainer: [StatusEditorCategorizedEmojiContainer] = []
var postingError: String?
var showPostingErrorAlert: Bool = false
@ -726,9 +726,33 @@ import SwiftUI
// MARK: - Custom emojis
func fetchCustomEmojis() async {
typealias EmojiContainer = StatusEditorCategorizedEmojiContainer
guard let client else { return }
do {
customEmojis = try await client.get(endpoint: CustomEmojis.customEmojis) ?? []
let customEmojis: [Emoji] = try await client.get(endpoint: CustomEmojis.customEmojis) ?? []
var emojiContainers: [EmojiContainer] = []
customEmojis.reduce([String: [Emoji]]()) { currentDict, emoji in
var dict = currentDict
let category = emoji.category ?? "Uncategorized"
if let emojis = dict[category] {
dict[category] = emojis + [emoji]
} else {
dict[category] = [emoji]
}
return dict
}.sorted(by: { lhs, rhs in
if rhs.key == "Uncategorized" { return false }
else if lhs.key == "Uncategorized" { return true }
else { return lhs.key < rhs.key }
}).forEach { key, value in
emojiContainers.append(.init(categoryName: key, emojis: value))
}
customEmojiContainer = emojiContainers
} catch {}
}
}