mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
@room in user suggestions (#1880)
* for now I am using a local RTE solution, need to fix one test * fixed a test * new rte version * pr suggestions * @room added to the suggestions * removed empty space * revert * code improvement * project updated * package
This commit is contained in:
@@ -10,12 +10,18 @@ import Foundation
|
||||
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
|
||||
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||
public enum L10n {
|
||||
/// Delete
|
||||
public static var a11yDelete: String { return L10n.tr("Localizable", "a11y_delete") }
|
||||
/// Hide password
|
||||
public static var a11yHidePassword: String { return L10n.tr("Localizable", "a11y_hide_password") }
|
||||
/// Mentions only
|
||||
public static var a11yNotificationsMentionsOnly: String { return L10n.tr("Localizable", "a11y_notifications_mentions_only") }
|
||||
/// Muted
|
||||
public static var a11yNotificationsMuted: String { return L10n.tr("Localizable", "a11y_notifications_muted") }
|
||||
/// Pause
|
||||
public static var a11yPause: String { return L10n.tr("Localizable", "a11y_pause") }
|
||||
/// Play
|
||||
public static var a11yPlay: String { return L10n.tr("Localizable", "a11y_play") }
|
||||
/// Poll
|
||||
public static var a11yPoll: String { return L10n.tr("Localizable", "a11y_poll") }
|
||||
/// Ended poll
|
||||
@@ -26,6 +32,8 @@ public enum L10n {
|
||||
public static var a11yShowPassword: String { return L10n.tr("Localizable", "a11y_show_password") }
|
||||
/// User menu
|
||||
public static var a11yUserMenu: String { return L10n.tr("Localizable", "a11y_user_menu") }
|
||||
/// Record voice message. Double tap and hold to record. Release to end recording.
|
||||
public static var a11yVoiceMessageRecord: String { return L10n.tr("Localizable", "a11y_voice_message_record") }
|
||||
/// Accept
|
||||
public static var actionAccept: String { return L10n.tr("Localizable", "action_accept") }
|
||||
/// Add to timeline
|
||||
@@ -142,6 +150,8 @@ public enum L10n {
|
||||
public static var actionShare: String { return L10n.tr("Localizable", "action_share") }
|
||||
/// Share link
|
||||
public static var actionShareLink: String { return L10n.tr("Localizable", "action_share_link") }
|
||||
/// Sign in again
|
||||
public static var actionSignInAgain: String { return L10n.tr("Localizable", "action_sign_in_again") }
|
||||
/// Skip
|
||||
public static var actionSkip: String { return L10n.tr("Localizable", "action_skip") }
|
||||
/// Start
|
||||
@@ -192,6 +202,8 @@ public enum L10n {
|
||||
public static var commonEncryptionEnabled: String { return L10n.tr("Localizable", "common_encryption_enabled") }
|
||||
/// Error
|
||||
public static var commonError: String { return L10n.tr("Localizable", "common_error") }
|
||||
/// Everyone
|
||||
public static var commonEveryone: String { return L10n.tr("Localizable", "common_everyone") }
|
||||
/// File
|
||||
public static var commonFile: String { return L10n.tr("Localizable", "common_file") }
|
||||
/// Forward message
|
||||
@@ -284,8 +296,6 @@ public enum L10n {
|
||||
public static var commonSecureBackup: String { return L10n.tr("Localizable", "common_secure_backup") }
|
||||
/// Security
|
||||
public static var commonSecurity: String { return L10n.tr("Localizable", "common_security") }
|
||||
/// Select your server
|
||||
public static var commonSelectYourServer: String { return L10n.tr("Localizable", "common_select_your_server") }
|
||||
/// Sending…
|
||||
public static var commonSending: String { return L10n.tr("Localizable", "common_sending") }
|
||||
/// Server not supported
|
||||
@@ -498,8 +508,6 @@ public enum L10n {
|
||||
public static var preferenceRageshake: String { return L10n.tr("Localizable", "preference_rageshake") }
|
||||
/// You seem to be shaking the phone in frustration. Would you like to open the bug report screen?
|
||||
public static var rageshakeDetectionDialogContent: String { return L10n.tr("Localizable", "rageshake_detection_dialog_content") }
|
||||
/// You seem to be shaking the phone in frustration. Would you like to open the bug report screen?
|
||||
public static var rageshakeDialogContent: String { return L10n.tr("Localizable", "rageshake_dialog_content") }
|
||||
/// This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages.
|
||||
public static var reportContentExplanation: String { return L10n.tr("Localizable", "report_content_explanation") }
|
||||
/// Reason for reporting this content
|
||||
@@ -560,8 +568,6 @@ public enum L10n {
|
||||
}
|
||||
/// Change account provider
|
||||
public static var screenAccountProviderChange: String { return L10n.tr("Localizable", "screen_account_provider_change") }
|
||||
/// Continue
|
||||
public static var screenAccountProviderContinue: String { return L10n.tr("Localizable", "screen_account_provider_continue") }
|
||||
/// Homeserver address
|
||||
public static var screenAccountProviderFormHint: String { return L10n.tr("Localizable", "screen_account_provider_form_hint") }
|
||||
/// Enter a search term or a domain address.
|
||||
@@ -660,8 +666,6 @@ public enum L10n {
|
||||
public static func screenChangeServerFormNotice(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "screen_change_server_form_notice", String(describing: p1))
|
||||
}
|
||||
/// Continue
|
||||
public static var screenChangeServerSubmit: String { return L10n.tr("Localizable", "screen_change_server_submit") }
|
||||
/// What is the address of your server?
|
||||
public static var screenChangeServerSubtitle: String { return L10n.tr("Localizable", "screen_change_server_subtitle") }
|
||||
/// Select your server
|
||||
@@ -760,10 +764,6 @@ public enum L10n {
|
||||
public static var screenLoginErrorUnsupportedAuthentication: String { return L10n.tr("Localizable", "screen_login_error_unsupported_authentication") }
|
||||
/// Enter your details
|
||||
public static var screenLoginFormHeader: String { return L10n.tr("Localizable", "screen_login_form_header") }
|
||||
/// Password
|
||||
public static var screenLoginPasswordHint: String { return L10n.tr("Localizable", "screen_login_password_hint") }
|
||||
/// Continue
|
||||
public static var screenLoginSubmit: String { return L10n.tr("Localizable", "screen_login_submit") }
|
||||
/// Matrix is an open network for secure, decentralised communication.
|
||||
public static var screenLoginSubtitle: String { return L10n.tr("Localizable", "screen_login_subtitle") }
|
||||
/// Welcome back!
|
||||
@@ -772,8 +772,6 @@ public enum L10n {
|
||||
public static func screenLoginTitleWithHomeserver(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "screen_login_title_with_homeserver", String(describing: p1))
|
||||
}
|
||||
/// Username
|
||||
public static var screenLoginUsernameHint: String { return L10n.tr("Localizable", "screen_login_username_hint") }
|
||||
/// Failed selecting media, please try again.
|
||||
public static var screenMediaPickerErrorFailedSelection: String { return L10n.tr("Localizable", "screen_media_picker_error_failed_selection") }
|
||||
/// Failed processing media to upload, please try again.
|
||||
@@ -844,8 +842,6 @@ public enum L10n {
|
||||
public static var screenOnboardingSignInWithQrCode: String { return L10n.tr("Localizable", "screen_onboarding_sign_in_with_qr_code") }
|
||||
/// Create account
|
||||
public static var screenOnboardingSignUp: String { return L10n.tr("Localizable", "screen_onboarding_sign_up") }
|
||||
/// Communicate and collaborate securely
|
||||
public static var screenOnboardingSubtitle: String { return L10n.tr("Localizable", "screen_onboarding_subtitle") }
|
||||
/// Welcome to the fastest Element ever. Supercharged for speed and simplicity.
|
||||
public static var screenOnboardingWelcomeMessage: String { return L10n.tr("Localizable", "screen_onboarding_welcome_message") }
|
||||
/// Welcome to %1$@. Supercharged, for speed and simplicity.
|
||||
@@ -906,8 +902,6 @@ public enum L10n {
|
||||
public static var screenRoomDetailsNotificationModeDefault: String { return L10n.tr("Localizable", "screen_room_details_notification_mode_default") }
|
||||
/// Notifications
|
||||
public static var screenRoomDetailsNotificationTitle: String { return L10n.tr("Localizable", "screen_room_details_notification_title") }
|
||||
/// People
|
||||
public static var screenRoomDetailsPeopleTitle: String { return L10n.tr("Localizable", "screen_room_details_people_title") }
|
||||
/// Room name
|
||||
public static var screenRoomDetailsRoomNameLabel: String { return L10n.tr("Localizable", "screen_room_details_room_name_label") }
|
||||
/// Security
|
||||
@@ -984,8 +978,6 @@ public enum L10n {
|
||||
public static var screenRoomReactionsShowLess: String { return L10n.tr("Localizable", "screen_room_reactions_show_less") }
|
||||
/// Show more
|
||||
public static var screenRoomReactionsShowMore: String { return L10n.tr("Localizable", "screen_room_reactions_show_more") }
|
||||
/// Remove
|
||||
public static var screenRoomRetrySendMenuRemoveAction: String { return L10n.tr("Localizable", "screen_room_retry_send_menu_remove_action") }
|
||||
/// Send again
|
||||
public static var screenRoomRetrySendMenuSendAgainAction: String { return L10n.tr("Localizable", "screen_room_retry_send_menu_send_again_action") }
|
||||
/// Your message failed to send
|
||||
@@ -1020,8 +1012,6 @@ public enum L10n {
|
||||
}
|
||||
/// Something doesn’t seem right. Either the request timed out or the request was denied.
|
||||
public static var screenSessionVerificationCancelledSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_cancelled_subtitle") }
|
||||
/// Verification cancelled
|
||||
public static var screenSessionVerificationCancelledTitle: String { return L10n.tr("Localizable", "screen_session_verification_cancelled_title") }
|
||||
/// Confirm that the emojis below match those shown on your other session.
|
||||
public static var screenSessionVerificationCompareEmojisSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_compare_emojis_subtitle") }
|
||||
/// Compare emojis
|
||||
@@ -1036,8 +1026,6 @@ public enum L10n {
|
||||
public static var screenSessionVerificationPositiveButtonCanceled: String { return L10n.tr("Localizable", "screen_session_verification_positive_button_canceled") }
|
||||
/// I am ready
|
||||
public static var screenSessionVerificationPositiveButtonInitial: String { return L10n.tr("Localizable", "screen_session_verification_positive_button_initial") }
|
||||
/// Start
|
||||
public static var screenSessionVerificationPositiveButtonReady: String { return L10n.tr("Localizable", "screen_session_verification_positive_button_ready") }
|
||||
/// Waiting to match
|
||||
public static var screenSessionVerificationPositiveButtonVerifyingOngoing: String { return L10n.tr("Localizable", "screen_session_verification_positive_button_verifying_ongoing") }
|
||||
/// Compare the unique emoji, ensuring they appear in the same order.
|
||||
@@ -1050,8 +1038,6 @@ public enum L10n {
|
||||
public static var screenSessionVerificationWaitingToAcceptSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_waiting_to_accept_subtitle") }
|
||||
/// Waiting to accept request
|
||||
public static var screenSessionVerificationWaitingToAcceptTitle: String { return L10n.tr("Localizable", "screen_session_verification_waiting_to_accept_title") }
|
||||
/// Account and devices
|
||||
public static var screenSettingsOidcAccount: String { return L10n.tr("Localizable", "screen_settings_oidc_account") }
|
||||
/// Share location
|
||||
public static var screenShareLocationTitle: String { return L10n.tr("Localizable", "screen_share_location_title") }
|
||||
/// Share my location
|
||||
@@ -1064,6 +1050,18 @@ public enum L10n {
|
||||
public static var screenShareOpenOsmMaps: String { return L10n.tr("Localizable", "screen_share_open_osm_maps") }
|
||||
/// Share this location
|
||||
public static var screenShareThisLocationAction: String { return L10n.tr("Localizable", "screen_share_this_location_action") }
|
||||
/// You’ve changed your password on another session
|
||||
public static var screenSignedOutReason1: String { return L10n.tr("Localizable", "screen_signed_out_reason_1") }
|
||||
/// You have deleted the session from another session
|
||||
public static var screenSignedOutReason2: String { return L10n.tr("Localizable", "screen_signed_out_reason_2") }
|
||||
/// Your server’s administrator has invalidated your access
|
||||
public static var screenSignedOutReason3: String { return L10n.tr("Localizable", "screen_signed_out_reason_3") }
|
||||
/// You might have been signed out for one of the reasons listed below. Please sign in again to continue using %@.
|
||||
public static func screenSignedOutSubtitle(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "screen_signed_out_subtitle", String(describing: p1))
|
||||
}
|
||||
/// You’re signed out
|
||||
public static var screenSignedOutTitle: String { return L10n.tr("Localizable", "screen_signed_out_title") }
|
||||
/// Are you sure you want to sign out?
|
||||
public static var screenSignoutConfirmationDialogContent: String { return L10n.tr("Localizable", "screen_signout_confirmation_dialog_content") }
|
||||
/// Sign out
|
||||
@@ -1114,8 +1112,6 @@ public enum L10n {
|
||||
public static var settingsRageshake: String { return L10n.tr("Localizable", "settings_rageshake") }
|
||||
/// Detection threshold
|
||||
public static var settingsRageshakeDetectionThreshold: String { return L10n.tr("Localizable", "settings_rageshake_detection_threshold") }
|
||||
/// General
|
||||
public static var settingsTitleGeneral: String { return L10n.tr("Localizable", "settings_title_general") }
|
||||
/// Version: %1$@ (%2$@)
|
||||
public static func settingsVersionNumber(_ p1: Any, _ p2: Any) -> String {
|
||||
return L10n.tr("Localizable", "settings_version_number", String(describing: p1), String(describing: p2))
|
||||
|
||||
@@ -1749,6 +1749,27 @@ class RoomProxyMock: RoomProxyProtocol {
|
||||
return canUserRedactUserIDReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - canUserTriggerRoomNotification
|
||||
|
||||
var canUserTriggerRoomNotificationUserIDCallsCount = 0
|
||||
var canUserTriggerRoomNotificationUserIDCalled: Bool {
|
||||
return canUserTriggerRoomNotificationUserIDCallsCount > 0
|
||||
}
|
||||
var canUserTriggerRoomNotificationUserIDReceivedUserID: String?
|
||||
var canUserTriggerRoomNotificationUserIDReceivedInvocations: [String] = []
|
||||
var canUserTriggerRoomNotificationUserIDReturnValue: Result<Bool, RoomProxyError>!
|
||||
var canUserTriggerRoomNotificationUserIDClosure: ((String) async -> Result<Bool, RoomProxyError>)?
|
||||
|
||||
func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError> {
|
||||
canUserTriggerRoomNotificationUserIDCallsCount += 1
|
||||
canUserTriggerRoomNotificationUserIDReceivedUserID = userID
|
||||
canUserTriggerRoomNotificationUserIDReceivedInvocations.append(userID)
|
||||
if let canUserTriggerRoomNotificationUserIDClosure = canUserTriggerRoomNotificationUserIDClosure {
|
||||
return await canUserTriggerRoomNotificationUserIDClosure(userID)
|
||||
} else {
|
||||
return canUserTriggerRoomNotificationUserIDReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - createPoll
|
||||
|
||||
var createPollQuestionAnswersPollKindCallsCount = 0
|
||||
|
||||
@@ -35,6 +35,7 @@ struct RoomProxyMockConfiguration {
|
||||
var inviter: RoomMemberProxyMock?
|
||||
var memberForID: RoomMemberProxyMock = .mockMe
|
||||
var ownUserID = "@alice:somewhere.org"
|
||||
var canUserTriggerRoomNotification = false
|
||||
|
||||
var invitedMembersCount = 100
|
||||
var joinedMembersCount = 50
|
||||
@@ -81,5 +82,6 @@ extension RoomProxyMock {
|
||||
setTopicClosure = { _ in .success(()) }
|
||||
getMemberUserIDReturnValue = .success(configuration.memberForID)
|
||||
canUserRedactUserIDReturnValue = .success(false)
|
||||
canUserTriggerRoomNotificationUserIDReturnValue = .success(configuration.canUserTriggerRoomNotification)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,3 +73,13 @@ extension Array where Element == RoomTimelineItemProtocol {
|
||||
first { $0.id.timelineID == id.timelineID }
|
||||
}
|
||||
}
|
||||
|
||||
extension Array {
|
||||
func appending(_ element: Element) -> [Element] {
|
||||
self + [element]
|
||||
}
|
||||
|
||||
func appending(_ elements: [Element]) -> [Element] {
|
||||
self + elements
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ enum MatrixEntityRegex: String {
|
||||
case .eventId:
|
||||
return "\\$[a-z0-9_\\-\\/]+(:[a-z0-9]+\\.[a-z0-9]+)?"
|
||||
case .allUsers:
|
||||
return "@room"
|
||||
return PillConstants.atRoom
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum PillConstants {
|
||||
static let atRoom = "@room"
|
||||
static var everyone: String {
|
||||
L10n.commonEveryone
|
||||
}
|
||||
|
||||
/// Used by the WYSIWYG as the urlString value to identify @room mentions
|
||||
static let composerAtRoomURLString = "#"
|
||||
}
|
||||
@@ -52,7 +52,7 @@ final class PillContext: ObservableObject {
|
||||
}
|
||||
}
|
||||
case .allUsers:
|
||||
viewState = PillViewState(contentID: roomContext.viewState.roomID, isOwnMention: true, name: roomContext.viewState.roomTitle, displayText: "@room", avatarURL: roomContext.viewState.roomAvatarURL)
|
||||
viewState = PillViewState(contentID: roomContext.viewState.roomID, isOwnMention: true, name: roomContext.viewState.roomTitle, displayText: PillConstants.atRoom, avatarURL: roomContext.viewState.roomAvatarURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,23 @@ import WysiwygComposer
|
||||
|
||||
enum SuggestionItem: Identifiable, Equatable {
|
||||
case user(item: MentionSuggestionItem)
|
||||
case allUsers(item: MentionSuggestionItem)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case .user(let user):
|
||||
return user.id
|
||||
case .allUsers:
|
||||
return PillConstants.atRoom
|
||||
}
|
||||
}
|
||||
|
||||
var name: String? {
|
||||
switch self {
|
||||
case .user(let user):
|
||||
return user.displayName
|
||||
case .allUsers:
|
||||
return PillConstants.everyone
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +45,10 @@ struct MentionSuggestionItem: Identifiable, Equatable {
|
||||
let id: String
|
||||
let displayName: String?
|
||||
let avatarURL: URL?
|
||||
|
||||
static func allUsersMention(roomAvatar: URL?) -> Self {
|
||||
MentionSuggestionItem(id: PillConstants.atRoom, displayName: PillConstants.everyone, avatarURL: roomAvatar)
|
||||
}
|
||||
}
|
||||
|
||||
extension WysiwygComposer.SuggestionPattern {
|
||||
|
||||
@@ -18,50 +18,70 @@ import Combine
|
||||
import Foundation
|
||||
|
||||
final class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
|
||||
private var canMentionAllUsers = false
|
||||
let areSuggestionsEnabled: Bool
|
||||
let suggestionsPublisher: AnyPublisher<[SuggestionItem], Never>
|
||||
private(set) var suggestionsPublisher: AnyPublisher<[SuggestionItem], Never> = Empty().eraseToAnyPublisher()
|
||||
|
||||
private let suggestionTriggerSubject = CurrentValueSubject<SuggestionPattern?, Never>(nil)
|
||||
|
||||
init(roomProxy: RoomProxyProtocol, areSuggestionsEnabled: Bool) {
|
||||
self.areSuggestionsEnabled = areSuggestionsEnabled
|
||||
guard areSuggestionsEnabled else {
|
||||
suggestionsPublisher = Empty().eraseToAnyPublisher()
|
||||
return
|
||||
}
|
||||
suggestionsPublisher = suggestionTriggerSubject
|
||||
.combineLatest(roomProxy.members)
|
||||
.map { suggestionTrigger, members -> [SuggestionItem] in
|
||||
guard let suggestionTrigger else {
|
||||
.map { [weak self] suggestionPattern, members -> [SuggestionItem] in
|
||||
guard let suggestionPattern else {
|
||||
return []
|
||||
}
|
||||
|
||||
switch suggestionTrigger.type {
|
||||
switch suggestionPattern.type {
|
||||
case .user:
|
||||
return members.filter { member in
|
||||
guard !member.isAccountOwner && member.membership == .join else {
|
||||
return false
|
||||
var membersSuggestion = members
|
||||
.compactMap { member -> SuggestionItem? in
|
||||
guard !member.isAccountOwner,
|
||||
member.membership == .join,
|
||||
Self.isIncluded(searchText: suggestionPattern.text, userID: member.userID, displayName: member.displayName) else {
|
||||
return nil
|
||||
}
|
||||
return SuggestionItem.user(item: .init(id: member.userID, displayName: member.displayName, avatarURL: member.avatarURL))
|
||||
}
|
||||
|
||||
let containedInUserID = member.userID.localizedStandardContains(suggestionTrigger.text.lowercased())
|
||||
|
||||
let containedInDisplayName: Bool
|
||||
if let displayName = member.displayName {
|
||||
containedInDisplayName = displayName.localizedStandardContains(suggestionTrigger.text.lowercased())
|
||||
} else {
|
||||
containedInDisplayName = false
|
||||
}
|
||||
|
||||
return containedInUserID || containedInDisplayName
|
||||
if self?.canMentionAllUsers == true,
|
||||
Self.isIncluded(searchText: suggestionPattern.text, userID: PillConstants.atRoom, displayName: PillConstants.everyone) {
|
||||
membersSuggestion
|
||||
.append(SuggestionItem.allUsers(item: .allUsersMention(roomAvatar: roomProxy.avatarURL)))
|
||||
}
|
||||
.map { SuggestionItem.user(item: .init(id: $0.userID, displayName: $0.displayName, avatarURL: $0.avatarURL)) }
|
||||
return membersSuggestion
|
||||
}
|
||||
}
|
||||
.debounce(for: 0.5, scheduler: DispatchQueue.main)
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
Task {
|
||||
switch await roomProxy.canUserTriggerRoomNotification(userID: roomProxy.ownUserID) {
|
||||
case .success(let value):
|
||||
canMentionAllUsers = value
|
||||
case .failure:
|
||||
canMentionAllUsers = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setSuggestionTrigger(_ suggestionTrigger: SuggestionPattern?) {
|
||||
suggestionTriggerSubject.value = suggestionTrigger
|
||||
}
|
||||
|
||||
private static func isIncluded(searchText: String, userID: String, displayName: String?) -> Bool {
|
||||
let containedInUserID = userID.localizedStandardContains(searchText.lowercased())
|
||||
|
||||
let containedInDisplayName: Bool
|
||||
if let displayName {
|
||||
containedInDisplayName = displayName.localizedStandardContains(searchText.lowercased())
|
||||
} else {
|
||||
containedInDisplayName = false
|
||||
}
|
||||
|
||||
return containedInUserID || containedInDisplayName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +166,8 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
|
||||
|
||||
wysiwygViewModel.mentionReplacer = ComposerMentionReplacer { urlString, string in
|
||||
let attributedString: NSMutableAttributedString
|
||||
// This is the all room mention case
|
||||
if urlString == "#" {
|
||||
// This is the all room mention special case
|
||||
if urlString == PillConstants.composerAtRoomURLString {
|
||||
attributedString = NSMutableAttributedString(string: string, attributes: [.MatrixAllUsersMention: true])
|
||||
} else {
|
||||
attributedString = NSMutableAttributedString(string: string, attributes: [.link: URL(string: urlString) as Any])
|
||||
@@ -188,6 +188,8 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
|
||||
return
|
||||
}
|
||||
wysiwygViewModel.setMention(url: url.absoluteString, name: item.displayName ?? item.id, mentionType: .user)
|
||||
case .allUsers:
|
||||
wysiwygViewModel.setAtRoomMention()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ struct CompletionSuggestionView: View {
|
||||
onTap(item)
|
||||
} label: {
|
||||
switch item {
|
||||
case .user(let mention):
|
||||
case .user(let mention), .allUsers(let mention):
|
||||
MentionSuggestionItemView(imageProvider: imageProvider, item: mention)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,6 +661,15 @@ class RoomProxy: RoomProxyProtocol {
|
||||
return .failure(.failedCheckingPermission)
|
||||
}
|
||||
}
|
||||
|
||||
func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError> {
|
||||
do {
|
||||
return try await .success(room.canUserTriggerRoomNotification(userId: userID))
|
||||
} catch {
|
||||
MXLog.error("Failed checking if the user can trigger room notification with error: \(error)")
|
||||
return .failure(.failedCheckingPermission)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Polls
|
||||
|
||||
|
||||
@@ -168,6 +168,8 @@ protocol RoomProxyProtocol {
|
||||
func uploadAvatar(media: MediaInfo) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func canUserRedact(userID: String) async -> Result<Bool, RoomProxyError>
|
||||
|
||||
func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError>
|
||||
|
||||
func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, RoomProxyError>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user