mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Handle tap on user mentions (#1850)
* user mention routing implemented * more tests * better naming * fixed a test
This commit is contained in:
@@ -158,6 +158,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
|
||||
} else {
|
||||
navigationRootCoordinator.setSheetCoordinator(GenericCallLinkCoordinator(parameters: .init(url: url)))
|
||||
}
|
||||
case .roomMemberDetails:
|
||||
userSessionFlowCoordinator?.handleAppRoute(route, animated: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ enum AppRoute: Equatable {
|
||||
case roomList
|
||||
case room(roomID: String)
|
||||
case roomDetails(roomID: String)
|
||||
case roomMemberDetails(userID: String)
|
||||
case invites
|
||||
case genericCallLink(url: URL)
|
||||
}
|
||||
@@ -30,6 +31,7 @@ struct AppRouteURLParser {
|
||||
|
||||
init(appSettings: AppSettings) {
|
||||
urlParsers = [
|
||||
MatrixPermalinkParser(appSettings: appSettings),
|
||||
OIDCCallbackURLParser(appSettings: appSettings),
|
||||
ElementCallURLParser()
|
||||
]
|
||||
@@ -105,3 +107,17 @@ struct ElementCallURLParser: URLParser {
|
||||
return .genericCallLink(url: url)
|
||||
}
|
||||
}
|
||||
|
||||
struct MatrixPermalinkParser: URLParser {
|
||||
let appSettings: AppSettings
|
||||
|
||||
func route(from url: URL) -> AppRoute? {
|
||||
switch PermalinkBuilder.detectPermalink(in: url, baseURL: appSettings.permalinkBaseURL) {
|
||||
case .userIdentifier(let userID):
|
||||
return .roomMemberDetails(userID: userID)
|
||||
// Other cases will be handled in the future
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,17 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
stateMachine.tryEvent(.presentRoomDetails(roomID: roomID), userInfo: EventUserInfo(animated: animated))
|
||||
case .roomList:
|
||||
stateMachine.tryEvent(.dismissRoom, userInfo: EventUserInfo(animated: animated))
|
||||
case .roomMemberDetails(let userID):
|
||||
Task {
|
||||
switch await roomProxy?.getMember(userID: userID) {
|
||||
case .success(let member):
|
||||
stateMachine.tryEvent(.presentRoomMemberDetails(member: .init(value: member)))
|
||||
case .failure(let error):
|
||||
MXLog.error("[RoomFlowCoordinator] Failed to get member: \(error)")
|
||||
case .none:
|
||||
MXLog.error("[RoomFlowCoordinator] Failed to get member: RoomProxy is nil")
|
||||
}
|
||||
}
|
||||
case .invites:
|
||||
break
|
||||
case .genericCallLink, .oidcCallback:
|
||||
|
||||
@@ -115,7 +115,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
|
||||
switch appRoute {
|
||||
case .room, .roomDetails, .roomList:
|
||||
case .room, .roomDetails, .roomList, .roomMemberDetails:
|
||||
self.roomFlowCoordinator.handleAppRoute(appRoute, animated: animated)
|
||||
case .invites:
|
||||
if UIDevice.current.isPhone {
|
||||
|
||||
@@ -57,6 +57,7 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
|
||||
|
||||
let mutableAttributedString = NSMutableAttributedString(string: string)
|
||||
addLinks(mutableAttributedString)
|
||||
addAllUsersMention(mutableAttributedString)
|
||||
detectPermalinks(mutableAttributedString)
|
||||
removeLinkColors(mutableAttributedString)
|
||||
|
||||
@@ -110,6 +111,7 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
|
||||
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
|
||||
removeDefaultForegroundColor(mutableAttributedString)
|
||||
addLinks(mutableAttributedString)
|
||||
addAllUsersMention(mutableAttributedString)
|
||||
replaceMarkedBlockquotes(mutableAttributedString)
|
||||
replaceMarkedCodeBlocks(mutableAttributedString)
|
||||
detectPermalinks(mutableAttributedString)
|
||||
@@ -184,38 +186,42 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
|
||||
|
||||
let linkMatches = MatrixEntityRegex.linkRegex.matches(in: string, options: [])
|
||||
matches.append(contentsOf: linkMatches)
|
||||
if matches.count > 0 {
|
||||
// Sort the links by length so the longest one always takes priority
|
||||
matches.sorted { $0.range.length > $1.range.length }.forEach { match in
|
||||
guard let matchRange = Range(match.range, in: string) else {
|
||||
return
|
||||
}
|
||||
|
||||
var hasLink = false
|
||||
attributedString.enumerateAttribute(.link, in: match.range, options: []) { value, _, stop in
|
||||
if value != nil {
|
||||
hasLink = true
|
||||
stop.pointee = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasLink {
|
||||
return
|
||||
}
|
||||
|
||||
var link = String(string[matchRange])
|
||||
|
||||
if linkMatches.contains(match), !link.contains("://") {
|
||||
link.insert(contentsOf: "https://", at: link.startIndex)
|
||||
}
|
||||
|
||||
if let url = URL(string: link) {
|
||||
attributedString.addAttribute(.link, value: url, range: match.range)
|
||||
|
||||
guard matches.count > 0 else {
|
||||
return
|
||||
}
|
||||
// Sort the links by length so the longest one always takes priority
|
||||
matches.sorted { $0.range.length > $1.range.length }.forEach { match in
|
||||
guard let matchRange = Range(match.range, in: string) else {
|
||||
return
|
||||
}
|
||||
|
||||
var hasLink = false
|
||||
attributedString.enumerateAttribute(.link, in: match.range, options: []) { value, _, stop in
|
||||
if value != nil {
|
||||
hasLink = true
|
||||
stop.pointee = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasLink {
|
||||
return
|
||||
}
|
||||
|
||||
var link = String(string[matchRange])
|
||||
|
||||
if linkMatches.contains(match), !link.contains("://") {
|
||||
link.insert(contentsOf: "https://", at: link.startIndex)
|
||||
}
|
||||
|
||||
if let url = URL(string: link) {
|
||||
attributedString.addAttribute(.link, value: url, range: match.range)
|
||||
}
|
||||
}
|
||||
|
||||
MatrixEntityRegex.allUsersRegex.matches(in: string, options: []).forEach { match in
|
||||
}
|
||||
|
||||
private func addAllUsersMention(_ attributedString: NSMutableAttributedString) {
|
||||
MatrixEntityRegex.allUsersRegex.matches(in: attributedString.string, options: []).forEach { match in
|
||||
if attributedString.attribute(.link, at: 0, longestEffectiveRange: nil, in: match.range) == nil {
|
||||
attributedString.addAttribute(.MatrixAllUsersMention, value: true, range: match.range)
|
||||
}
|
||||
|
||||
@@ -37,13 +37,13 @@ struct MentionBuilder: MentionBuilderProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
var attributesToAdd: [NSAttributedString.Key: Any] = [.link: url, .MatrixUserID: userID]
|
||||
var attachmentAttributes: [NSAttributedString.Key: Any] = [.link: url, .MatrixUserID: userID]
|
||||
if let blockquote {
|
||||
// mentions can be in blockquotes, so if the replaced string was in one, we keep the attribute
|
||||
attributesToAdd[.MatrixBlockquote] = blockquote
|
||||
attachmentAttributes[.MatrixBlockquote] = blockquote
|
||||
}
|
||||
let attachmentString = NSMutableAttributedString(attachment: attachment)
|
||||
attachmentString.addAttributes(attributes, range: NSRange(location: 0, length: attachmentString.length))
|
||||
attachmentString.addAttributes(attachmentAttributes, range: NSRange(location: 0, length: attachmentString.length))
|
||||
attributedString.replaceCharacters(in: range, with: attachmentString)
|
||||
}
|
||||
|
||||
@@ -61,13 +61,13 @@ struct MentionBuilder: MentionBuilderProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
var attributesToAdd: [NSAttributedString.Key: Any] = [:]
|
||||
var attachmentAttributes: [NSAttributedString.Key: Any] = [:]
|
||||
if let blockquote {
|
||||
// mentions can be in blockquotes, so if the replaced string was in one, we keep the attribute
|
||||
attributesToAdd[.MatrixBlockquote] = blockquote
|
||||
attachmentAttributes[.MatrixBlockquote] = blockquote
|
||||
}
|
||||
let attachmentString = NSMutableAttributedString(attachment: attachment)
|
||||
attachmentString.addAttributes(attributes, range: NSRange(location: 0, length: attachmentString.length))
|
||||
attachmentString.addAttributes(attachmentAttributes, range: NSRange(location: 0, length: attachmentString.length))
|
||||
attributedString.replaceCharacters(in: range, with: attachmentString)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user