mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
Hook up the emoji picker to the custom status row. (#5902)
* Refactor EmojiPickerScreen to take a continuation instead of a timeline/itemID. * Hook up the emoji picker to the custom status row.
This commit is contained in:
@@ -10,10 +10,11 @@ import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct EmojiPickerScreenCoordinatorParameters {
|
||||
let itemID: TimelineItemIdentifier
|
||||
/// Any emojis that should be displayed as already selected.
|
||||
let selectedEmojis: Set<String>
|
||||
let emojiProvider: EmojiProviderProtocol
|
||||
let timelineController: TimelineControllerProtocol
|
||||
/// A continuation that yields the selected emoji.
|
||||
let continuation: EmojiPickerScreenContinuation
|
||||
}
|
||||
|
||||
enum EmojiPickerScreenCoordinatorAction {
|
||||
@@ -31,10 +32,9 @@ final class EmojiPickerScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
|
||||
init(parameters: EmojiPickerScreenCoordinatorParameters) {
|
||||
viewModel = EmojiPickerScreenViewModel(itemID: parameters.itemID,
|
||||
selectedEmojis: parameters.selectedEmojis,
|
||||
viewModel = EmojiPickerScreenViewModel(selectedEmojis: parameters.selectedEmojis,
|
||||
emojiProvider: parameters.emojiProvider,
|
||||
timelineController: parameters.timelineController)
|
||||
continuation: parameters.continuation)
|
||||
}
|
||||
|
||||
func start() {
|
||||
@@ -50,6 +50,10 @@ final class EmojiPickerScreenCoordinator: CoordinatorProtocol {
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func stop() {
|
||||
viewModel.stop()
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(EmojiPickerScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
typealias EmojiPickerScreenContinuation = AsyncStream<String>.Continuation
|
||||
|
||||
enum EmojiPickerScreenViewModelAction {
|
||||
case dismiss
|
||||
}
|
||||
|
||||
@@ -12,26 +12,28 @@ import SwiftUI
|
||||
typealias EmojiPickerScreenViewModelType = StateStoreViewModelV2<EmojiPickerScreenViewState, EmojiPickerScreenViewAction>
|
||||
|
||||
class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScreenViewModelProtocol {
|
||||
private let itemID: TimelineItemIdentifier
|
||||
private let emojiProvider: EmojiProviderProtocol
|
||||
private let timelineController: TimelineControllerProtocol
|
||||
private let continuation: EmojiPickerScreenContinuation
|
||||
|
||||
private var actionsSubject: PassthroughSubject<EmojiPickerScreenViewModelAction, Never> = .init()
|
||||
var actions: AnyPublisher<EmojiPickerScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(itemID: TimelineItemIdentifier, selectedEmojis: Set<String>, emojiProvider: EmojiProviderProtocol, timelineController: TimelineControllerProtocol) {
|
||||
init(selectedEmojis: Set<String>, emojiProvider: EmojiProviderProtocol, continuation: EmojiPickerScreenContinuation) {
|
||||
let initialViewState = EmojiPickerScreenViewState(categories: [], selectedEmojis: selectedEmojis)
|
||||
self.itemID = itemID
|
||||
self.emojiProvider = emojiProvider
|
||||
self.timelineController = timelineController
|
||||
self.continuation = continuation
|
||||
super.init(initialViewState: initialViewState)
|
||||
loadEmojis()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func stop() {
|
||||
continuation.finish() // Ensure the continuation always finishes even without a selection.
|
||||
}
|
||||
|
||||
override func process(viewAction: EmojiPickerScreenViewAction) {
|
||||
switch viewAction {
|
||||
case let .search(searchString: searchString):
|
||||
@@ -40,7 +42,7 @@ class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScr
|
||||
state.categories = convert(emojiCategories: categories)
|
||||
}
|
||||
case let .emojiTapped(emoji: emoji):
|
||||
Task { await selectEmoji(emoji) }
|
||||
selectEmoji(emoji)
|
||||
case .dismiss:
|
||||
actionsSubject.send(.dismiss)
|
||||
}
|
||||
@@ -66,16 +68,12 @@ class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScr
|
||||
}
|
||||
}
|
||||
|
||||
private func selectEmoji(_ emoji: EmojiPickerEmojiViewData) async {
|
||||
MXLog.debug("Selected \(emoji) for \(itemID)")
|
||||
private func selectEmoji(_ emoji: EmojiPickerEmojiViewData) {
|
||||
emojiProvider.markEmojiAsFrequentlyUsed(emoji.value)
|
||||
|
||||
guard case let .event(_, eventOrTransactionID) = itemID else { fatalError("Attempted to react to a virtual item.") }
|
||||
continuation.yield(emoji.value)
|
||||
continuation.finish()
|
||||
|
||||
// There aren't any local echoes when the toggle redacts, so dismiss the screen early
|
||||
// until we have them: https://github.com/matrix-org/matrix-rust-sdk/issues/4162
|
||||
actionsSubject.send(.dismiss)
|
||||
|
||||
await timelineController.toggleReaction(emoji.value, to: eventOrTransactionID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,4 +11,6 @@ import Combine
|
||||
protocol EmojiPickerScreenViewModelProtocol {
|
||||
var actions: AnyPublisher<EmojiPickerScreenViewModelAction, Never> { get }
|
||||
var context: EmojiPickerScreenViewModelType.Context { get }
|
||||
|
||||
func stop()
|
||||
}
|
||||
|
||||
@@ -102,10 +102,9 @@ struct EmojiPickerScreen: View {
|
||||
|
||||
@available(iOS 26.0, *)
|
||||
struct EmojiPickerScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = EmojiPickerScreenViewModel(itemID: .randomEvent,
|
||||
selectedEmojis: ["😀", "😄"],
|
||||
static let viewModel = EmojiPickerScreenViewModel(selectedEmojis: ["😀", "😄"],
|
||||
emojiProvider: EmojiProvider(appSettings: .volatile()),
|
||||
timelineController: TimelineControllerMock(.init()))
|
||||
continuation: AsyncStream<String>.makeStream().continuation)
|
||||
|
||||
static var previews: some View {
|
||||
EmojiPickerScreen(context: viewModel.context)
|
||||
@@ -115,10 +114,9 @@ struct EmojiPickerScreen_Previews: PreviewProvider, TestablePreview {
|
||||
}
|
||||
|
||||
struct EmojiPickerScreenSheet_Previews: PreviewProvider {
|
||||
static let viewModel = EmojiPickerScreenViewModel(itemID: .randomEvent,
|
||||
selectedEmojis: ["😀", "😄"],
|
||||
static let viewModel = EmojiPickerScreenViewModel(selectedEmojis: ["😀", "😄"],
|
||||
emojiProvider: EmojiProvider(appSettings: .volatile()),
|
||||
timelineController: TimelineControllerMock(.init()))
|
||||
continuation: AsyncStream<String>.makeStream().continuation)
|
||||
|
||||
static var previews: some View {
|
||||
Text("Timeline view")
|
||||
|
||||
Reference in New Issue
Block a user