mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 08:15:24 +00:00
More tweaks for User Status. (#6051)
* Make sure user profiles are up to date when mentioning. * Automatically focus the custom status text field. * Make sure to clear the previous custom status text before input. * Attempt to fix the user status sheet sometimes disappearing.
This commit is contained in:
@@ -27,6 +27,8 @@ final class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private var updateMembersTask: Task<Void, Never>?
|
||||
|
||||
init(roomProxy: JoinedRoomProxyProtocol,
|
||||
roomListPublisher: AnyPublisher<[RoomSummary], Never>) {
|
||||
self.roomProxy = roomProxy
|
||||
@@ -66,7 +68,17 @@ final class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
|
||||
}
|
||||
|
||||
func setSuggestionTrigger(_ suggestionTrigger: SuggestionTrigger?) {
|
||||
let isNewUserMention = suggestionTrigger?.type == .user && suggestionTriggerSubject.value?.type != .user
|
||||
|
||||
suggestionTriggerSubject.value = suggestionTrigger
|
||||
|
||||
// Refresh the members each time a new user suggestion is triggered to pull in the latest profiles.
|
||||
guard isNewUserMention, updateMembersTask == nil else { return }
|
||||
|
||||
updateMembersTask = Task { [weak self, roomProxy] in
|
||||
await roomProxy.updateMembers()
|
||||
self?.updateMembersTask = nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
@@ -46,6 +46,13 @@ struct SettingsScreen: View {
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbarVisibility(context.viewState.navigationBarVisibility, for: .navigationBar)
|
||||
.toolbar { toolbar }
|
||||
.sheet(isPresented: $context.isPresentingStatusPicker) {
|
||||
SettingsScreenUserStatusPickerView { action in
|
||||
context.send(viewAction: .userStatus(action))
|
||||
}
|
||||
.presentationDetents([.medium])
|
||||
.presentationBackground(.compound.bgCanvasDefault)
|
||||
}
|
||||
}
|
||||
|
||||
private var userSection: some View {
|
||||
@@ -94,13 +101,6 @@ struct SettingsScreen: View {
|
||||
SettingsScreenUserStatusRow(mode: context.viewState.userStatusRowMode) { action in
|
||||
context.send(viewAction: .userStatus(action))
|
||||
}
|
||||
.sheet(isPresented: $context.isPresentingStatusPicker) {
|
||||
SettingsScreenUserStatusPickerView { action in
|
||||
context.send(viewAction: .userStatus(action))
|
||||
}
|
||||
.presentationDetents([.medium]) // Stop using a List and calculate the exact height?
|
||||
.presentationBackground(.compound.bgCanvasDefault)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-1
@@ -9,13 +9,33 @@ import Compound
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsScreenUserStatusRow: View {
|
||||
enum Mode: Equatable { case pickStatusButton, customStatusInput(emoji: Character), showingStatus(UserStatus.Displayed) }
|
||||
enum Mode: Equatable {
|
||||
case pickStatusButton, customStatusInput(emoji: Character), showingStatus(UserStatus.Displayed)
|
||||
|
||||
var isCustomStatusInput: Bool {
|
||||
switch self {
|
||||
case .customStatusInput: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mode: Mode
|
||||
let action: (SettingsScreenViewAction.UserStatusAction) -> Void
|
||||
|
||||
@State private var customText = ""
|
||||
@FocusState private var isCustomFieldFocused: Bool
|
||||
|
||||
var body: some View {
|
||||
rowContent
|
||||
.onChange(of: mode.isCustomStatusInput) { _, newValue in
|
||||
guard newValue else { return }
|
||||
customText = ""
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var rowContent: some View {
|
||||
switch mode {
|
||||
case .pickStatusButton:
|
||||
ListRow(label: .default(title: L10n.screenSettingsUserStatusPlaceholder, icon: \.reaction),
|
||||
@@ -34,6 +54,8 @@ struct SettingsScreenUserStatusRow: View {
|
||||
|
||||
TextField(L10n.screenSettingsUserStatusCustomHint, text: $customText)
|
||||
.textFieldStyle(.compound(.raised))
|
||||
.focused($isCustomFieldFocused)
|
||||
.onAppear { isCustomFieldFocused = true }
|
||||
.accessibilityLabel(L10n.screenSettingsUserStatusCustomHint)
|
||||
.padding(.vertical, 3)
|
||||
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
@testable import ElementX
|
||||
import Testing
|
||||
|
||||
@MainActor
|
||||
struct CompletionSuggestionServiceTests {
|
||||
// MARK: User Suggestions
|
||||
|
||||
@Test
|
||||
func userSuggestions() async throws {
|
||||
let alice: RoomMemberProxyMock = .mockAlice
|
||||
@@ -178,6 +179,40 @@ struct CompletionSuggestionServiceTests {
|
||||
try await deffered.fulfill()
|
||||
}
|
||||
|
||||
@Test
|
||||
func userSuggestionRefreshesMembers() async throws {
|
||||
let members: [RoomMemberProxyMock] = [.mockAlice, .mockBob, .mockMe]
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(id: "roomID", name: "test", members: members))
|
||||
let roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||
|
||||
let (refreshesStream, refreshesContinuation) = AsyncStream<Void>.makeStream()
|
||||
roomProxyMock.updateMembersClosure = { refreshesContinuation.yield() }
|
||||
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock,
|
||||
roomListPublisher: roomSummaryProvider.roomListPublisher.eraseToAnyPublisher())
|
||||
#expect(!roomProxyMock.updateMembersCalled)
|
||||
|
||||
// Beginning a mention refreshes the members once…
|
||||
var deferred = deferFulfillment(refreshesStream) { _ in true }
|
||||
service.setSuggestionTrigger(.init(type: .user, text: "", range: .init()))
|
||||
try await deferred.fulfill()
|
||||
#expect(roomProxyMock.updateMembersCallsCount == 1)
|
||||
|
||||
// …but typing further within the same mention doesn't refresh again.
|
||||
service.setSuggestionTrigger(.init(type: .user, text: "a", range: .init()))
|
||||
service.setSuggestionTrigger(.init(type: .user, text: "al", range: .init()))
|
||||
#expect(roomProxyMock.updateMembersCallsCount == 1)
|
||||
|
||||
// Clearing and starting a new mention refreshes a second time.
|
||||
deferred = deferFulfillment(refreshesStream) { _ in true }
|
||||
service.setSuggestionTrigger(nil)
|
||||
service.setSuggestionTrigger(.init(type: .user, text: "", range: .init()))
|
||||
try await deferred.fulfill()
|
||||
#expect(roomProxyMock.updateMembersCallsCount == 2)
|
||||
}
|
||||
|
||||
// MARK: Room Suggestions
|
||||
|
||||
@Test
|
||||
func roomSuggestions() async throws {
|
||||
let alice: RoomMemberProxyMock = .mockAlice
|
||||
@@ -353,6 +388,8 @@ struct CompletionSuggestionServiceTests {
|
||||
try await deffered.fulfill()
|
||||
}
|
||||
|
||||
// MARK: Common
|
||||
|
||||
@Test
|
||||
func suggestionsWithMultipleDifferentTriggers() async throws {
|
||||
let alice: RoomMemberProxyMock = .mockAlice
|
||||
|
||||
Reference in New Issue
Block a user