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:
@@ -11,7 +11,7 @@ import Testing
|
||||
|
||||
@MainActor
|
||||
struct EmojiPickerScreenViewModelTests {
|
||||
var timelineProxy: TimelineProxyMock!
|
||||
var emojiPickerStream: AsyncStream<String>!
|
||||
|
||||
var viewModel: EmojiPickerScreenViewModel!
|
||||
var context: EmojiPickerScreenViewModel.Context {
|
||||
@@ -19,33 +19,43 @@ struct EmojiPickerScreenViewModelTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func toggleReaction() async throws {
|
||||
mutating func selectEmoji() async throws {
|
||||
// Given a freshly presented emoji picker.
|
||||
setupViewModel()
|
||||
let reaction = "👋"
|
||||
|
||||
// When the user taps an emoji.
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .dismiss }
|
||||
context.send(viewAction: .emojiTapped(emoji: .init(id: "wave", value: reaction)))
|
||||
|
||||
await waitForConfirmation(timeout: .seconds(5)) { confirmation in
|
||||
timelineProxy.toggleReactionToClosure = { toggledReaction, _ in
|
||||
defer { confirmation() }
|
||||
#expect(toggledReaction == reaction)
|
||||
return .success(())
|
||||
}
|
||||
|
||||
context.send(viewAction: .emojiTapped(emoji: .init(id: "wave", value: reaction)))
|
||||
}
|
||||
|
||||
// Then the screen should dismiss and yield the emoji before finishing the stream.
|
||||
try await deferred.fulfill()
|
||||
var iterator = emojiPickerStream.makeAsyncIterator()
|
||||
#expect(await iterator.next() == reaction)
|
||||
#expect(await iterator.next() == nil)
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func stopFinishesTheStream() async {
|
||||
// Given a freshly presented emoji picker.
|
||||
setupViewModel()
|
||||
|
||||
// When it is stopped without a selection (e.g. the picker is dismissed).
|
||||
viewModel.stop()
|
||||
|
||||
// Then the stream should finish without yielding an emoji.
|
||||
var iterator = emojiPickerStream.makeAsyncIterator()
|
||||
#expect(await iterator.next() == nil)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private mutating func setupViewModel(selectedEmojis: Set<String> = []) {
|
||||
timelineProxy = TimelineProxyMock(.init())
|
||||
let (stream, continuation) = AsyncStream<String>.makeStream()
|
||||
emojiPickerStream = stream
|
||||
|
||||
viewModel = EmojiPickerScreenViewModel(itemID: .randomEvent,
|
||||
selectedEmojis: selectedEmojis,
|
||||
viewModel = EmojiPickerScreenViewModel(selectedEmojis: selectedEmojis,
|
||||
emojiProvider: EmojiProvider(appSettings: .volatile()),
|
||||
timelineController: TimelineControllerMock(.init(timelineProxy: timelineProxy)))
|
||||
continuation: continuation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ struct SettingsScreenViewModelTests {
|
||||
// Then the picker should be dismissed and the custom status field shown.
|
||||
#expect(!context.viewState.bindings.isPresentingStatusPicker)
|
||||
#expect(context.viewState.bindings.isShowingCustomStatusField)
|
||||
#expect(context.viewState.userStatusRowMode == .custom)
|
||||
#expect(context.viewState.userStatusRowMode == .custom(emoji: "😄"))
|
||||
|
||||
// When cancelling.
|
||||
context.send(viewAction: .userStatus(.cancel))
|
||||
@@ -101,11 +101,32 @@ struct SettingsScreenViewModelTests {
|
||||
#expect(context.viewState.userStatusRowMode == .show(rawStatus))
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func selectingCustomStatusEmoji() async throws {
|
||||
// Given a custom status field showing the default emoji.
|
||||
setupViewModel()
|
||||
context.send(viewAction: .userStatus(.customStatus))
|
||||
#expect(context.viewState.userStatusRowMode == .custom(emoji: "😄"))
|
||||
|
||||
// When selecting an emoji from the picker.
|
||||
try await selectCustomStatusEmoji("🎉")
|
||||
|
||||
// Then the custom status field should show the selected emoji.
|
||||
#expect(context.viewState.userStatusRowMode == .custom(emoji: "🎉"))
|
||||
|
||||
// When cancelling and returning to the custom status field.
|
||||
context.send(viewAction: .userStatus(.cancel))
|
||||
context.send(viewAction: .userStatus(.customStatus))
|
||||
|
||||
// Then the emoji should have been reset to the default.
|
||||
#expect(context.viewState.userStatusRowMode == .custom(emoji: "😄"))
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func reportBug() async throws {
|
||||
setupViewModel()
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .reportBug }
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0.isReportBug }
|
||||
context.send(viewAction: .reportBug)
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
@@ -114,7 +135,7 @@ struct SettingsScreenViewModelTests {
|
||||
mutating func analytics() async throws {
|
||||
setupViewModel()
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .analytics }
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0.isAnalytics }
|
||||
context.send(viewAction: .analytics)
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
@@ -123,7 +144,7 @@ struct SettingsScreenViewModelTests {
|
||||
mutating func logout() async throws {
|
||||
setupViewModel()
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .logout }
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0.isLogout }
|
||||
context.send(viewAction: .logout)
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
@@ -138,4 +159,48 @@ struct SettingsScreenViewModelTests {
|
||||
isInSecondaryWindow: false,
|
||||
userIndicatorController: UserIndicatorControllerMock())
|
||||
}
|
||||
|
||||
/// Shows the emoji picker and selects the provided emoji through its continuation.
|
||||
private func selectCustomStatusEmoji(_ emoji: String) async throws {
|
||||
let pickerPresented = deferFulfillment(viewModel.actions) { $0.isUserStatusEmojiPicker }
|
||||
context.send(viewAction: .userStatus(.pickCustomEmoji))
|
||||
guard case let .userStatusEmojiPicker(continuation) = try await pickerPresented.fulfill() else {
|
||||
Issue.record("Expected the emoji picker to be presented.")
|
||||
return
|
||||
}
|
||||
|
||||
let emojiUpdated = deferFulfillment(context.observe(\.viewState.bindings.customStatusEmoji)) { $0 == Character(emoji) }
|
||||
continuation.yield(emoji)
|
||||
try await emojiUpdated.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
private extension SettingsScreenViewModelAction {
|
||||
var isUserStatusEmojiPicker: Bool {
|
||||
switch self {
|
||||
case .userStatusEmojiPicker: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
var isReportBug: Bool {
|
||||
switch self {
|
||||
case .reportBug: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
var isAnalytics: Bool {
|
||||
switch self {
|
||||
case .analytics: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
var isLogout: Bool {
|
||||
switch self {
|
||||
case .logout: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user