mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 19:44:05 +00:00
Allow clearing your own call status. (#5959)
* Allow clearing your own call status. * Clarify the names of the SettingsScreenUserStatusRow.Mode cases.
This commit is contained in:
@@ -3333,6 +3333,34 @@ nonisolated class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
|
|||||||
return removeUserStatusReturnValue
|
return removeUserStatusReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//MARK: - removeCallStatus
|
||||||
|
|
||||||
|
private let removeCallStatusCallsCountLock = NSLock()
|
||||||
|
private nonisolated(unsafe) var removeCallStatusUnderlyingCallsCount = 0
|
||||||
|
var removeCallStatusCallsCount: Int {
|
||||||
|
get { removeCallStatusCallsCountLock.withLock { removeCallStatusUnderlyingCallsCount } }
|
||||||
|
set { removeCallStatusCallsCountLock.withLock { removeCallStatusUnderlyingCallsCount = newValue } }
|
||||||
|
}
|
||||||
|
var removeCallStatusCalled: Bool {
|
||||||
|
return removeCallStatusCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private let removeCallStatusReturnValueLock = NSLock()
|
||||||
|
private nonisolated(unsafe) var removeCallStatusUnderlyingReturnValue: Result<Void, ClientProxyError>!
|
||||||
|
var removeCallStatusReturnValue: Result<Void, ClientProxyError>! {
|
||||||
|
get { removeCallStatusReturnValueLock.withLock { removeCallStatusUnderlyingReturnValue } }
|
||||||
|
set { removeCallStatusReturnValueLock.withLock { removeCallStatusUnderlyingReturnValue = newValue } }
|
||||||
|
}
|
||||||
|
nonisolated(unsafe) var removeCallStatusClosure: (() async -> Result<Void, ClientProxyError>)?
|
||||||
|
|
||||||
|
@concurrent func removeCallStatus() async -> Result<Void, ClientProxyError> {
|
||||||
|
removeCallStatusCallsCountLock.withLock { removeCallStatusUnderlyingCallsCount += 1 }
|
||||||
|
if let removeCallStatusClosure = removeCallStatusClosure {
|
||||||
|
return await removeCallStatusClosure()
|
||||||
|
} else {
|
||||||
|
return removeCallStatusReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
//MARK: - linkNewDeviceService
|
//MARK: - linkNewDeviceService
|
||||||
|
|
||||||
private let linkNewDeviceServiceCallsCountLock = NSLock()
|
private let linkNewDeviceServiceCallsCountLock = NSLock()
|
||||||
|
|||||||
@@ -56,11 +56,11 @@ struct SettingsScreenViewState: BindableState {
|
|||||||
|
|
||||||
var userStatusRowMode: SettingsScreenUserStatusRow.Mode {
|
var userStatusRowMode: SettingsScreenUserStatusRow.Mode {
|
||||||
if bindings.isShowingCustomStatusField {
|
if bindings.isShowingCustomStatusField {
|
||||||
.custom(emoji: bindings.customStatusEmoji)
|
.customStatusInput(emoji: bindings.customStatusEmoji)
|
||||||
} else if let rawStatus = userProfile.status.raw {
|
} else if let displayedStatus = userProfile.status.displayed {
|
||||||
.show(rawStatus)
|
.showingStatus(displayedStatus)
|
||||||
} else {
|
} else {
|
||||||
.pick
|
.pickStatusButton
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,7 +107,9 @@ enum SettingsScreenViewAction {
|
|||||||
/// Show the emoji picker to select the emoji for the custom status.
|
/// Show the emoji picker to select the emoji for the custom status.
|
||||||
case pickCustomEmoji
|
case pickCustomEmoji
|
||||||
/// Set the user's status to the provided value.
|
/// Set the user's status to the provided value.
|
||||||
case set(UserStatus.Raw?)
|
case set(UserStatus.Raw)
|
||||||
|
/// Clears the user's currently displayed status.
|
||||||
|
case clear(UserStatus.Displayed)
|
||||||
/// Cancel user status picking/input.
|
/// Cancel user status picking/input.
|
||||||
case cancel
|
case cancel
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
|
|||||||
pickCustomEmoji()
|
pickCustomEmoji()
|
||||||
case .userStatus(.set(let status)):
|
case .userStatus(.set(let status)):
|
||||||
Task { await setUserStatus(status) }
|
Task { await setUserStatus(status) }
|
||||||
|
case .userStatus(.clear(let status)):
|
||||||
|
Task { await clearUserStatus(status) }
|
||||||
case .userStatus(.cancel):
|
case .userStatus(.cancel):
|
||||||
state.bindings.isPresentingStatusPicker = false
|
state.bindings.isPresentingStatusPicker = false
|
||||||
state.bindings.isShowingCustomStatusField = false
|
state.bindings.isShowingCustomStatusField = false
|
||||||
@@ -159,15 +161,32 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
|
|||||||
.asCancellable()
|
.asCancellable()
|
||||||
}
|
}
|
||||||
|
|
||||||
func setUserStatus(_ status: UserStatus.Raw?) async {
|
func setUserStatus(_ status: UserStatus.Raw) async {
|
||||||
// Loading state tbc
|
// Loading state tbc
|
||||||
state.bindings.isPresentingStatusPicker = false
|
state.bindings.isPresentingStatusPicker = false
|
||||||
state.bindings.isShowingCustomStatusField = false
|
state.bindings.isShowingCustomStatusField = false
|
||||||
|
|
||||||
let result = if let status {
|
switch await clientProxy.setUserStatus(status) {
|
||||||
await clientProxy.setUserStatus(status)
|
case .success:
|
||||||
} else {
|
break // Loading/error state tbc
|
||||||
|
case .failure:
|
||||||
|
userIndicatorController.submitIndicator(.init(id: UUID().uuidString,
|
||||||
|
type: .toast,
|
||||||
|
title: L10n.errorUnknown,
|
||||||
|
icon: \.close))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearUserStatus(_ status: UserStatus.Displayed) async {
|
||||||
|
// Loading state tbc
|
||||||
|
state.bindings.isPresentingStatusPicker = false
|
||||||
|
state.bindings.isShowingCustomStatusField = false
|
||||||
|
|
||||||
|
let result = switch status {
|
||||||
|
case .userSet:
|
||||||
await clientProxy.removeUserStatus()
|
await clientProxy.removeUserStatus()
|
||||||
|
case .inCall:
|
||||||
|
await clientProxy.removeCallStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
|
|||||||
+13
-9
@@ -9,7 +9,7 @@ import Compound
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct SettingsScreenUserStatusRow: View {
|
struct SettingsScreenUserStatusRow: View {
|
||||||
enum Mode: Equatable { case pick, custom(emoji: Character), show(UserStatus.Raw) }
|
enum Mode: Equatable { case pickStatusButton, customStatusInput(emoji: Character), showingStatus(UserStatus.Displayed) }
|
||||||
let mode: Mode
|
let mode: Mode
|
||||||
let action: (SettingsScreenViewAction.UserStatusAction) -> Void
|
let action: (SettingsScreenViewAction.UserStatusAction) -> Void
|
||||||
|
|
||||||
@@ -17,10 +17,10 @@ struct SettingsScreenUserStatusRow: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
switch mode {
|
switch mode {
|
||||||
case .pick:
|
case .pickStatusButton:
|
||||||
ListRow(label: .default(title: L10n.screenSettingsUserStatusPlaceholder, icon: \.reaction),
|
ListRow(label: .default(title: L10n.screenSettingsUserStatusPlaceholder, icon: \.reaction),
|
||||||
kind: .button { action(.pickStatus) })
|
kind: .button { action(.pickStatus) })
|
||||||
case .custom(let emoji):
|
case .customStatusInput(let emoji):
|
||||||
ListRow(kind: .custom {
|
ListRow(kind: .custom {
|
||||||
HStack(spacing: ListRowPadding.labelIconSpacing) {
|
HStack(spacing: ListRowPadding.labelIconSpacing) {
|
||||||
Button { action(.pickCustomEmoji) } label: {
|
Button { action(.pickCustomEmoji) } label: {
|
||||||
@@ -47,7 +47,7 @@ struct SettingsScreenUserStatusRow: View {
|
|||||||
}
|
}
|
||||||
.listRowBackground(Color.clear)
|
.listRowBackground(Color.clear)
|
||||||
})
|
})
|
||||||
case .show(let status):
|
case .showingStatus(let status):
|
||||||
ListRow(kind: .custom {
|
ListRow(kind: .custom {
|
||||||
HStack(spacing: ListRowPadding.labelIconSpacing) {
|
HStack(spacing: ListRowPadding.labelIconSpacing) {
|
||||||
Button { action(.pickStatus) } label: {
|
Button { action(.pickStatus) } label: {
|
||||||
@@ -71,7 +71,7 @@ struct SettingsScreenUserStatusRow: View {
|
|||||||
.accessibilityValue(Text(String(status.emoji)) + Text(status.text))
|
.accessibilityValue(Text(String(status.emoji)) + Text(status.text))
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
|
||||||
Button(L10n.actionClear) { action(.set(nil)) }
|
Button(L10n.actionClear) { action(.clear(status)) }
|
||||||
.buttonStyle(.compound(.textLink))
|
.buttonStyle(.compound(.textLink))
|
||||||
}
|
}
|
||||||
.padding(ListRowPadding.insets)
|
.padding(ListRowPadding.insets)
|
||||||
@@ -80,7 +80,7 @@ struct SettingsScreenUserStatusRow: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func saveCustomStatus() {
|
private func saveCustomStatus() {
|
||||||
guard case let .custom(emoji) = mode else { return }
|
guard case let .customStatusInput(emoji) = mode else { return }
|
||||||
action(.set(.init(text: customText, emoji: emoji)))
|
action(.set(.init(text: customText, emoji: emoji)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,15 +104,19 @@ struct SettingsScreenUserStatusRow_Previews: PreviewProvider, TestablePreview {
|
|||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
Form {
|
Form {
|
||||||
Section {
|
Section {
|
||||||
SettingsScreenUserStatusRow(mode: .pick) { _ in }
|
SettingsScreenUserStatusRow(mode: .pickStatusButton) { _ in }
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
SettingsScreenUserStatusRow(mode: .custom(emoji: "😄")) { _ in }
|
SettingsScreenUserStatusRow(mode: .customStatusInput(emoji: "😄")) { _ in }
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
SettingsScreenUserStatusRow(mode: .show(.init(text: "Away", emoji: "🌴"))) { _ in }
|
SettingsScreenUserStatusRow(mode: .showingStatus(.userSet(.init(text: "Away", emoji: "🌴")))) { _ in }
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
SettingsScreenUserStatusRow(mode: .showingStatus(.inCall(.init(joinedDate: nil)))) { _ in }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.compoundList()
|
.compoundList()
|
||||||
|
|||||||
@@ -766,7 +766,6 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// periphery:ignore - might be useful to have
|
|
||||||
func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError> {
|
func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError> {
|
||||||
do {
|
do {
|
||||||
try await client.setUserStatus(status: status.rustValue)
|
try await client.setUserStatus(status: status.rustValue)
|
||||||
@@ -778,11 +777,10 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// periphery:ignore - might be useful to have
|
|
||||||
func removeUserStatus() async -> Result<Void, ClientProxyError> {
|
func removeUserStatus() async -> Result<Void, ClientProxyError> {
|
||||||
do {
|
do {
|
||||||
try await client.clearUserStatus()
|
try await client.clearUserStatus()
|
||||||
Task { await self.loadUserProfileIfNeeded() }
|
// No need to refresh the profile, we only support user status with the profiles /sync extension.
|
||||||
return .success(())
|
return .success(())
|
||||||
} catch {
|
} catch {
|
||||||
MXLog.error("Failed removing user status with error: \(error)")
|
MXLog.error("Failed removing user status with error: \(error)")
|
||||||
@@ -790,6 +788,17 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeCallStatus() async -> Result<Void, ClientProxyError> {
|
||||||
|
do {
|
||||||
|
try await client.clearCallStatus()
|
||||||
|
// No need to refresh the profile, we only support user status with the profiles /sync extension.
|
||||||
|
return .success(())
|
||||||
|
} catch {
|
||||||
|
MXLog.error("Failed removing call status with error: \(error)")
|
||||||
|
return .failure(.sdkError(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func linkNewDeviceService() -> LinkNewDeviceServiceProtocol {
|
func linkNewDeviceService() -> LinkNewDeviceServiceProtocol {
|
||||||
LinkNewDeviceService(handler: client.newGrantLoginWithQrCodeHandler())
|
LinkNewDeviceService(handler: client.newGrantLoginWithQrCodeHandler())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ protocol ClientProxyProtocol: AnyObject {
|
|||||||
func isUserStatusSupported() async -> Result<Bool, ClientProxyError>
|
func isUserStatusSupported() async -> Result<Bool, ClientProxyError>
|
||||||
func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError>
|
func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError>
|
||||||
func removeUserStatus() async -> Result<Void, ClientProxyError>
|
func removeUserStatus() async -> Result<Void, ClientProxyError>
|
||||||
|
func removeCallStatus() async -> Result<Void, ClientProxyError>
|
||||||
|
|
||||||
func linkNewDeviceService() -> LinkNewDeviceServiceProtocol
|
func linkNewDeviceService() -> LinkNewDeviceServiceProtocol
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:db0bccb08800ef06f6ae7b5521e8e40aa16dbd2970a06d011793edf51f99362b
|
oid sha256:65f575cb43e5414644bb6a1fb18f3835963b854e79dec5933e1a98ee2720c555
|
||||||
size 109977
|
size 118179
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:73e00f26cd04d00add4ece3493d536e696296fc9fbd345ddca000ff9e9abea73
|
oid sha256:79d120979962c68981a690969b7dbca5778a20fb0dc93d83256208a94985fccc
|
||||||
size 112058
|
size 121012
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:797aba33bb05cf3f15e382125bb5f951bde4edcd9b2f1439b0c6cf3f37c1003c
|
oid sha256:8bcecc78d5d75d8574819df65af3d1e524a5959488be16cd1e4ab0075a7f53fc
|
||||||
size 61947
|
size 68717
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a41e5ee59832811b7a3a95e3a526d8060246e37b8d434126b416144fde459ae5
|
oid sha256:5b5261aa94be9b9e99bf5d8dc19ebd64e0d9ac16507efc9218ba46024857e022
|
||||||
size 68057
|
size 77192
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ struct SettingsScreenViewModelTests {
|
|||||||
setupViewModel()
|
setupViewModel()
|
||||||
#expect(!context.viewState.bindings.isPresentingStatusPicker)
|
#expect(!context.viewState.bindings.isPresentingStatusPicker)
|
||||||
#expect(!context.viewState.bindings.isShowingCustomStatusField)
|
#expect(!context.viewState.bindings.isShowingCustomStatusField)
|
||||||
#expect(context.viewState.userStatusRowMode == .pick)
|
#expect(context.viewState.userStatusRowMode == .pickStatusButton)
|
||||||
|
|
||||||
// When choosing to pick a status.
|
// When choosing to pick a status.
|
||||||
context.send(viewAction: .userStatus(.pickStatus))
|
context.send(viewAction: .userStatus(.pickStatus))
|
||||||
@@ -34,7 +34,7 @@ struct SettingsScreenViewModelTests {
|
|||||||
// Then the status picker should be presented.
|
// Then the status picker should be presented.
|
||||||
#expect(context.viewState.bindings.isPresentingStatusPicker)
|
#expect(context.viewState.bindings.isPresentingStatusPicker)
|
||||||
#expect(!context.viewState.bindings.isShowingCustomStatusField)
|
#expect(!context.viewState.bindings.isShowingCustomStatusField)
|
||||||
#expect(context.viewState.userStatusRowMode == .pick)
|
#expect(context.viewState.userStatusRowMode == .pickStatusButton)
|
||||||
|
|
||||||
// When choosing to enter a custom status.
|
// When choosing to enter a custom status.
|
||||||
context.send(viewAction: .userStatus(.customStatus))
|
context.send(viewAction: .userStatus(.customStatus))
|
||||||
@@ -42,7 +42,7 @@ struct SettingsScreenViewModelTests {
|
|||||||
// Then the picker should be dismissed and the custom status field shown.
|
// Then the picker should be dismissed and the custom status field shown.
|
||||||
#expect(!context.viewState.bindings.isPresentingStatusPicker)
|
#expect(!context.viewState.bindings.isPresentingStatusPicker)
|
||||||
#expect(context.viewState.bindings.isShowingCustomStatusField)
|
#expect(context.viewState.bindings.isShowingCustomStatusField)
|
||||||
#expect(context.viewState.userStatusRowMode == .custom(emoji: "😄"))
|
#expect(context.viewState.userStatusRowMode == .customStatusInput(emoji: "😄"))
|
||||||
|
|
||||||
// When cancelling.
|
// When cancelling.
|
||||||
context.send(viewAction: .userStatus(.cancel))
|
context.send(viewAction: .userStatus(.cancel))
|
||||||
@@ -50,7 +50,7 @@ struct SettingsScreenViewModelTests {
|
|||||||
// Then all status editing should be dismissed.
|
// Then all status editing should be dismissed.
|
||||||
#expect(!context.viewState.bindings.isPresentingStatusPicker)
|
#expect(!context.viewState.bindings.isPresentingStatusPicker)
|
||||||
#expect(!context.viewState.bindings.isShowingCustomStatusField)
|
#expect(!context.viewState.bindings.isShowingCustomStatusField)
|
||||||
#expect(context.viewState.userStatusRowMode == .pick)
|
#expect(context.viewState.userStatusRowMode == .pickStatusButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -82,12 +82,36 @@ struct SettingsScreenViewModelTests {
|
|||||||
return .success(())
|
return .success(())
|
||||||
}
|
}
|
||||||
let statusRemoved = deferFulfillment(removeStream) { _ in true }
|
let statusRemoved = deferFulfillment(removeStream) { _ in true }
|
||||||
context.send(viewAction: .userStatus(.set(nil)))
|
context.send(viewAction: .userStatus(.clear(.userSet(status))))
|
||||||
|
|
||||||
|
// Then only the remove user status endpoint should be called.
|
||||||
|
try await statusRemoved.fulfill()
|
||||||
|
#expect(clientProxy.removeUserStatusCallsCount == 1)
|
||||||
|
#expect(!clientProxy.removeCallStatusCalled)
|
||||||
|
#expect(clientProxy.setUserStatusCallsCount == 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
mutating func clearCallStatus() async throws {
|
||||||
|
// Given a screen where the user has a call status set.
|
||||||
|
let callStatus = UserStatus.mockCall
|
||||||
|
setupViewModel(status: callStatus)
|
||||||
|
let displayedStatus = try #require(callStatus.displayed)
|
||||||
|
|
||||||
|
// When clearing the status.
|
||||||
|
let (removeStream, removeContinuation) = AsyncStream<Void>.makeStream()
|
||||||
|
clientProxy.removeCallStatusClosure = {
|
||||||
|
removeContinuation.yield()
|
||||||
|
return .success(())
|
||||||
|
}
|
||||||
|
let statusRemoved = deferFulfillment(removeStream) { _ in true }
|
||||||
|
context.send(viewAction: .userStatus(.clear(displayedStatus)))
|
||||||
|
|
||||||
// Then only the remove endpoint should be called, leaving the set call untouched.
|
// Then only the remove endpoint should be called, leaving the set call untouched.
|
||||||
try await statusRemoved.fulfill()
|
try await statusRemoved.fulfill()
|
||||||
#expect(clientProxy.removeUserStatusCallsCount == 1)
|
#expect(clientProxy.removeCallStatusCallsCount == 1)
|
||||||
#expect(clientProxy.setUserStatusCallsCount == 1)
|
#expect(!clientProxy.removeUserStatusCalled)
|
||||||
|
#expect(!clientProxy.setUserStatusCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -97,8 +121,8 @@ struct SettingsScreenViewModelTests {
|
|||||||
setupViewModel(status: status)
|
setupViewModel(status: status)
|
||||||
|
|
||||||
// Then that status should be shown.
|
// Then that status should be shown.
|
||||||
let rawStatus = try #require(status.raw)
|
let displayedStatus = try #require(status.displayed)
|
||||||
#expect(context.viewState.userStatusRowMode == .show(rawStatus))
|
#expect(context.viewState.userStatusRowMode == .showingStatus(displayedStatus))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -106,20 +130,20 @@ struct SettingsScreenViewModelTests {
|
|||||||
// Given a custom status field showing the default emoji.
|
// Given a custom status field showing the default emoji.
|
||||||
setupViewModel()
|
setupViewModel()
|
||||||
context.send(viewAction: .userStatus(.customStatus))
|
context.send(viewAction: .userStatus(.customStatus))
|
||||||
#expect(context.viewState.userStatusRowMode == .custom(emoji: "😄"))
|
#expect(context.viewState.userStatusRowMode == .customStatusInput(emoji: "😄"))
|
||||||
|
|
||||||
// When selecting an emoji from the picker.
|
// When selecting an emoji from the picker.
|
||||||
try await selectCustomStatusEmoji("🎉")
|
try await selectCustomStatusEmoji("🎉")
|
||||||
|
|
||||||
// Then the custom status field should show the selected emoji.
|
// Then the custom status field should show the selected emoji.
|
||||||
#expect(context.viewState.userStatusRowMode == .custom(emoji: "🎉"))
|
#expect(context.viewState.userStatusRowMode == .customStatusInput(emoji: "🎉"))
|
||||||
|
|
||||||
// When cancelling and returning to the custom status field.
|
// When cancelling and returning to the custom status field.
|
||||||
context.send(viewAction: .userStatus(.cancel))
|
context.send(viewAction: .userStatus(.cancel))
|
||||||
context.send(viewAction: .userStatus(.customStatus))
|
context.send(viewAction: .userStatus(.customStatus))
|
||||||
|
|
||||||
// Then the emoji should have been reset to the default.
|
// Then the emoji should have been reset to the default.
|
||||||
#expect(context.viewState.userStatusRowMode == .custom(emoji: "😄"))
|
#expect(context.viewState.userStatusRowMode == .customStatusInput(emoji: "😄"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user