mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 20:09:04 +00:00
Render Room and Message Pills (#3809)
* added a way to render the room and the message pills, but is WIP * permalinks now get converted into pills! * fixed an issue where room address mentions were not adding a URL properly but a string * updated tests * c * Revert "c" This reverts commit 5c80252fa23dba7e4d44f2a07fbf1e9500e37c82. * updated tests * more tests * created APIs to get a specific RoomSummary given the id or the alias * small mention builder improvement * pr suggestions
This commit is contained in:
@@ -10,75 +10,77 @@ import Foundation
|
||||
|
||||
@MainActor
|
||||
final class PillContext: ObservableObject {
|
||||
struct PillViewState: Equatable {
|
||||
let isOwnMention: Bool
|
||||
let displayText: String
|
||||
}
|
||||
@Published var viewState: PillViewState = .undefined
|
||||
|
||||
@Published private(set) var viewState: PillViewState
|
||||
|
||||
private var cancellable: AnyCancellable?
|
||||
let data: PillTextAttachmentData
|
||||
var cancellable: AnyCancellable?
|
||||
|
||||
init(timelineContext: TimelineViewModel.Context, data: PillTextAttachmentData) {
|
||||
switch data.type {
|
||||
case let .user(id):
|
||||
let isOwnMention = id == timelineContext.viewState.ownUserID
|
||||
if let profile = timelineContext.viewState.members[id] {
|
||||
var name = id
|
||||
if let displayName = profile.displayName {
|
||||
name = "@\(displayName)"
|
||||
}
|
||||
viewState = PillViewState(isOwnMention: isOwnMention, displayText: name)
|
||||
} else {
|
||||
viewState = PillViewState(isOwnMention: isOwnMention, displayText: id)
|
||||
cancellable = timelineContext.$viewState.sink { [weak self] viewState in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let profile = viewState.members[id] {
|
||||
var name = id
|
||||
if let displayName = profile.displayName {
|
||||
name = "@\(displayName)"
|
||||
}
|
||||
self.viewState = PillViewState(isOwnMention: isOwnMention, displayText: name)
|
||||
cancellable = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
case .allUsers:
|
||||
viewState = PillViewState(isOwnMention: true, displayText: PillConstants.atRoom)
|
||||
}
|
||||
self.data = data
|
||||
timelineContext.viewState.pillContextUpdater?(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension PillContext {
|
||||
enum MockType {
|
||||
case loadUser(isOwn: Bool)
|
||||
case loadedUser(isOwn: Bool)
|
||||
case allUsers
|
||||
static func mock(viewState: PillViewState, delay: Duration? = nil) -> PillContext {
|
||||
// This is just for previews so the internal data doesn't really matter
|
||||
let viewModel = PillContext(timelineContext: TimelineViewModel.mock.context, data: PillTextAttachmentData(type: .allUsers, font: .preferredFont(forTextStyle: .body)))
|
||||
if let delay {
|
||||
viewModel.viewState = .mention(isOwnMention: false, displayText: "placeholder")
|
||||
Task {
|
||||
try? await Task.sleep(for: delay)
|
||||
viewModel.viewState = viewState
|
||||
}
|
||||
} else {
|
||||
viewModel.viewState = viewState
|
||||
}
|
||||
return viewModel
|
||||
}
|
||||
}
|
||||
|
||||
enum PillViewState: Equatable {
|
||||
enum PillImage: Equatable {
|
||||
case link
|
||||
case roomAvatar(RoomAvatar)
|
||||
}
|
||||
|
||||
static func mock(type: MockType) -> PillContext {
|
||||
let testID = "@test:test.com"
|
||||
let pillType: PillType
|
||||
switch type {
|
||||
case .loadUser(let isOwn):
|
||||
pillType = .user(userID: testID)
|
||||
let viewModel = PillContext(timelineContext: TimelineViewModel.mock.context, data: PillTextAttachmentData(type: pillType, font: .preferredFont(forTextStyle: .body)))
|
||||
viewModel.viewState = PillViewState(isOwnMention: isOwn, displayText: testID)
|
||||
Task {
|
||||
try? await Task.sleep(for: .seconds(2))
|
||||
viewModel.viewState = PillViewState(isOwnMention: isOwn, displayText: "@Test Long Display Text")
|
||||
}
|
||||
return viewModel
|
||||
case .loadedUser(let isOwn):
|
||||
pillType = .user(userID: "@test:test.com")
|
||||
let viewModel = PillContext(timelineContext: TimelineViewModel.mock.context, data: PillTextAttachmentData(type: pillType, font: .preferredFont(forTextStyle: .body)))
|
||||
viewModel.viewState = PillViewState(isOwnMention: isOwn, displayText: "@Very Very Long Test Display Text")
|
||||
return viewModel
|
||||
case .allUsers:
|
||||
pillType = .allUsers
|
||||
return PillContext(timelineContext: TimelineViewModel.mock.context, data: PillTextAttachmentData(type: pillType, font: .preferredFont(forTextStyle: .body)))
|
||||
case mention(isOwnMention: Bool, displayText: String)
|
||||
case reference(avatar: PillImage, displayText: String)
|
||||
case undefined
|
||||
|
||||
var isOwnMention: Bool {
|
||||
switch self {
|
||||
case .mention(let isOwnMention, _):
|
||||
return isOwnMention
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var displayText: String {
|
||||
switch self {
|
||||
case .mention(_, let displayText), .reference(_, let displayText):
|
||||
return displayText
|
||||
case .undefined:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
var isUndefined: Bool {
|
||||
switch self {
|
||||
case .undefined:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var image: PillImage? {
|
||||
switch self {
|
||||
case .reference(let avatar, _):
|
||||
return avatar
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user