mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +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
|
||||
}
|
||||
}
|
||||
//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
|
||||
|
||||
private let linkNewDeviceServiceCallsCountLock = NSLock()
|
||||
|
||||
@@ -56,11 +56,11 @@ struct SettingsScreenViewState: BindableState {
|
||||
|
||||
var userStatusRowMode: SettingsScreenUserStatusRow.Mode {
|
||||
if bindings.isShowingCustomStatusField {
|
||||
.custom(emoji: bindings.customStatusEmoji)
|
||||
} else if let rawStatus = userProfile.status.raw {
|
||||
.show(rawStatus)
|
||||
.customStatusInput(emoji: bindings.customStatusEmoji)
|
||||
} else if let displayedStatus = userProfile.status.displayed {
|
||||
.showingStatus(displayedStatus)
|
||||
} else {
|
||||
.pick
|
||||
.pickStatusButton
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,9 @@ enum SettingsScreenViewAction {
|
||||
/// Show the emoji picker to select the emoji for the custom status.
|
||||
case pickCustomEmoji
|
||||
/// 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.
|
||||
case cancel
|
||||
}
|
||||
|
||||
@@ -108,6 +108,8 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
|
||||
pickCustomEmoji()
|
||||
case .userStatus(.set(let status)):
|
||||
Task { await setUserStatus(status) }
|
||||
case .userStatus(.clear(let status)):
|
||||
Task { await clearUserStatus(status) }
|
||||
case .userStatus(.cancel):
|
||||
state.bindings.isPresentingStatusPicker = false
|
||||
state.bindings.isShowingCustomStatusField = false
|
||||
@@ -159,15 +161,32 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
|
||||
.asCancellable()
|
||||
}
|
||||
|
||||
func setUserStatus(_ status: UserStatus.Raw?) async {
|
||||
func setUserStatus(_ status: UserStatus.Raw) async {
|
||||
// Loading state tbc
|
||||
state.bindings.isPresentingStatusPicker = false
|
||||
state.bindings.isShowingCustomStatusField = false
|
||||
|
||||
let result = if let status {
|
||||
await clientProxy.setUserStatus(status)
|
||||
} else {
|
||||
switch await clientProxy.setUserStatus(status) {
|
||||
case .success:
|
||||
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()
|
||||
case .inCall:
|
||||
await clientProxy.removeCallStatus()
|
||||
}
|
||||
|
||||
switch result {
|
||||
|
||||
+13
-9
@@ -9,7 +9,7 @@ import Compound
|
||||
import SwiftUI
|
||||
|
||||
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 action: (SettingsScreenViewAction.UserStatusAction) -> Void
|
||||
|
||||
@@ -17,10 +17,10 @@ struct SettingsScreenUserStatusRow: View {
|
||||
|
||||
var body: some View {
|
||||
switch mode {
|
||||
case .pick:
|
||||
case .pickStatusButton:
|
||||
ListRow(label: .default(title: L10n.screenSettingsUserStatusPlaceholder, icon: \.reaction),
|
||||
kind: .button { action(.pickStatus) })
|
||||
case .custom(let emoji):
|
||||
case .customStatusInput(let emoji):
|
||||
ListRow(kind: .custom {
|
||||
HStack(spacing: ListRowPadding.labelIconSpacing) {
|
||||
Button { action(.pickCustomEmoji) } label: {
|
||||
@@ -47,7 +47,7 @@ struct SettingsScreenUserStatusRow: View {
|
||||
}
|
||||
.listRowBackground(Color.clear)
|
||||
})
|
||||
case .show(let status):
|
||||
case .showingStatus(let status):
|
||||
ListRow(kind: .custom {
|
||||
HStack(spacing: ListRowPadding.labelIconSpacing) {
|
||||
Button { action(.pickStatus) } label: {
|
||||
@@ -71,7 +71,7 @@ struct SettingsScreenUserStatusRow: View {
|
||||
.accessibilityValue(Text(String(status.emoji)) + Text(status.text))
|
||||
.buttonStyle(.plain)
|
||||
|
||||
Button(L10n.actionClear) { action(.set(nil)) }
|
||||
Button(L10n.actionClear) { action(.clear(status)) }
|
||||
.buttonStyle(.compound(.textLink))
|
||||
}
|
||||
.padding(ListRowPadding.insets)
|
||||
@@ -80,7 +80,7 @@ struct SettingsScreenUserStatusRow: View {
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
@@ -104,15 +104,19 @@ struct SettingsScreenUserStatusRow_Previews: PreviewProvider, TestablePreview {
|
||||
static var previews: some View {
|
||||
Form {
|
||||
Section {
|
||||
SettingsScreenUserStatusRow(mode: .pick) { _ in }
|
||||
SettingsScreenUserStatusRow(mode: .pickStatusButton) { _ in }
|
||||
}
|
||||
|
||||
Section {
|
||||
SettingsScreenUserStatusRow(mode: .custom(emoji: "😄")) { _ in }
|
||||
SettingsScreenUserStatusRow(mode: .customStatusInput(emoji: "😄")) { _ in }
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -766,7 +766,6 @@ class ClientProxy: ClientProxyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
// periphery:ignore - might be useful to have
|
||||
func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError> {
|
||||
do {
|
||||
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> {
|
||||
do {
|
||||
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(())
|
||||
} catch {
|
||||
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 {
|
||||
LinkNewDeviceService(handler: client.newGrantLoginWithQrCodeHandler())
|
||||
}
|
||||
|
||||
@@ -230,6 +230,7 @@ protocol ClientProxyProtocol: AnyObject {
|
||||
func isUserStatusSupported() async -> Result<Bool, ClientProxyError>
|
||||
func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError>
|
||||
func removeUserStatus() async -> Result<Void, ClientProxyError>
|
||||
func removeCallStatus() async -> Result<Void, ClientProxyError>
|
||||
|
||||
func linkNewDeviceService() -> LinkNewDeviceServiceProtocol
|
||||
|
||||
|
||||
Reference in New Issue
Block a user