mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 19:44:05 +00:00
Added custom reply views for all message types as well as a loading state
This commit is contained in:
committed by
Stefan Ceriu
parent
1b4ef0b35c
commit
0608c68aee
@@ -25,58 +25,116 @@ struct TimelineReplyView: View {
|
|||||||
case .loaded(let sender, let content):
|
case .loaded(let sender, let content):
|
||||||
switch content {
|
switch content {
|
||||||
case .audio(let content):
|
case .audio(let content):
|
||||||
TimelineTextReplyView(sender: sender, plainBody: content.body, formattedBody: nil)
|
ReplyView(sender: sender, plainBody: content.body, formattedBody: nil, systemIconName: "waveform")
|
||||||
case .emote(let content):
|
case .emote(let content):
|
||||||
TimelineTextReplyView(sender: sender, plainBody: content.body, formattedBody: content.formattedBody)
|
ReplyView(sender: sender, plainBody: content.body, formattedBody: content.formattedBody)
|
||||||
case .file(let content):
|
case .file(let content):
|
||||||
TimelineTextReplyView(sender: sender, plainBody: content.body, formattedBody: nil)
|
ReplyView(sender: sender, plainBody: content.body, formattedBody: nil, systemIconName: "doc.text.fill")
|
||||||
case .image(let content):
|
case .image(let content):
|
||||||
TimelineTextReplyView(sender: sender, plainBody: content.body, formattedBody: nil)
|
ReplyView(sender: sender, plainBody: content.body, formattedBody: nil, mediaSource: content.thumbnailSource ?? content.source)
|
||||||
case .notice(let content):
|
case .notice(let content):
|
||||||
TimelineTextReplyView(sender: sender, plainBody: content.body, formattedBody: content.formattedBody)
|
ReplyView(sender: sender, plainBody: content.body, formattedBody: content.formattedBody)
|
||||||
case .text(let content):
|
case .text(let content):
|
||||||
TimelineTextReplyView(sender: sender, plainBody: content.body, formattedBody: content.formattedBody)
|
ReplyView(sender: sender, plainBody: content.body, formattedBody: content.formattedBody)
|
||||||
case .video(let content):
|
case .video(let content):
|
||||||
TimelineTextReplyView(sender: sender, plainBody: content.body, formattedBody: nil)
|
ReplyView(sender: sender, plainBody: content.body, formattedBody: nil, mediaSource: content.thumbnailSource)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
Text("Missing in-reply-to details")
|
LoadingReplyView()
|
||||||
.font(.compound.bodyMD)
|
|
||||||
.foregroundColor(.element.secondaryContent)
|
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct TimelineTextReplyView: View {
|
private struct LoadingReplyView: View {
|
||||||
|
var body: some View {
|
||||||
|
ReplyView(sender: .init(id: "@alice:matrix.org"), plainBody: "Hello world", formattedBody: nil)
|
||||||
|
.redacted(reason: .placeholder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct ReplyView: View {
|
||||||
|
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||||
|
@ScaledMetric private var imageContainerSize = 36.0
|
||||||
|
|
||||||
let sender: TimelineItemSender
|
let sender: TimelineItemSender
|
||||||
let plainBody: String
|
let plainBody: String
|
||||||
let formattedBody: AttributedString?
|
let formattedBody: AttributedString?
|
||||||
|
|
||||||
|
var systemIconName: String?
|
||||||
|
var mediaSource: MediaSourceProxy?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
HStack {
|
||||||
Text(sender.displayName ?? sender.id)
|
icon
|
||||||
.font(.compound.bodySMSemibold)
|
.frame(width: imageContainerSize, height: imageContainerSize)
|
||||||
.foregroundColor(.compound.textPrimary)
|
.foregroundColor(.element.primaryContent)
|
||||||
|
.background(Color.compound.bgSubtlePrimary)
|
||||||
|
.cornerRadius(9.0, corners: .allCorners)
|
||||||
|
|
||||||
Text(formattedBody ?? AttributedString(plainBody))
|
VStack(alignment: .leading) {
|
||||||
.font(.compound.bodyMD)
|
Text(sender.displayName ?? sender.id)
|
||||||
.foregroundColor(.compound.textPlaceholder)
|
.font(.compound.bodySMSemibold)
|
||||||
.tint(.element.links)
|
.foregroundColor(.compound.textPrimary)
|
||||||
.lineLimit(2)
|
|
||||||
|
Text(formattedBody ?? AttributedString(plainBody))
|
||||||
|
.font(.compound.bodyMD)
|
||||||
|
.foregroundColor(.compound.textPlaceholder)
|
||||||
|
.tint(.element.links)
|
||||||
|
.lineLimit(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var icon: some View {
|
||||||
|
if let mediaSource {
|
||||||
|
LoadableImage(mediaSource: mediaSource, size: .init(width: imageContainerSize, height: imageContainerSize), imageProvider: context.imageProvider) {
|
||||||
|
Image(systemName: "photo")
|
||||||
|
.padding(4.0)
|
||||||
|
}
|
||||||
|
.aspectRatio(contentMode: .fill)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let systemIconName {
|
||||||
|
Image(systemName: systemIconName)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.padding(4.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TimelineReplyView_Previews: PreviewProvider {
|
struct TimelineReplyView_Previews: PreviewProvider {
|
||||||
|
static let viewModel = RoomScreenViewModel.mock
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
TimelineReplyView(timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
VStack(alignment: .leading) {
|
||||||
content: .text(.init(body: "This is a reply"))))
|
TimelineReplyView(timelineItemReplyDetails: .notLoaded(eventID: ""))
|
||||||
.background(Color.element.background)
|
|
||||||
.cornerRadius(8)
|
TimelineReplyView(timelineItemReplyDetails: .loading(eventID: ""))
|
||||||
.padding(8)
|
|
||||||
.background(Color.element.bubblesYou)
|
TimelineReplyView(timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
.cornerRadius(12)
|
content: .text(.init(body: "This is a reply"))))
|
||||||
|
|
||||||
|
TimelineReplyView(timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
content: .emote(.init(body: "says hello"))))
|
||||||
|
|
||||||
|
TimelineReplyView(timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bot"),
|
||||||
|
content: .notice(.init(body: "Hello world"))))
|
||||||
|
|
||||||
|
TimelineReplyView(timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
content: .audio(.init(body: "Some audio", duration: 0, source: nil, contentType: nil))))
|
||||||
|
|
||||||
|
TimelineReplyView(timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
content: .file(.init(body: "Some file", source: nil, thumbnailSource: nil, contentType: nil))))
|
||||||
|
|
||||||
|
let imageSource = MediaSourceProxy(url: .init(staticString: "https://mock.com"), mimeType: "image/png")
|
||||||
|
|
||||||
|
TimelineReplyView(timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), content: .image(.init(body: "Some image", source: imageSource, thumbnailSource: imageSource))))
|
||||||
|
|
||||||
|
TimelineReplyView(timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), content: .video(.init(body: "Some video", duration: 0, source: nil, thumbnailSource: imageSource))))
|
||||||
|
}
|
||||||
|
.environmentObject(viewModel.context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,6 @@ extension View {
|
|||||||
func timelineQuoteBubbleFormatting() -> some View {
|
func timelineQuoteBubbleFormatting() -> some View {
|
||||||
foregroundColor(.compound.textPlaceholder)
|
foregroundColor(.compound.textPlaceholder)
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
.padding(EdgeInsets(top: 4, leading: 12, bottom: 4, trailing: 12))
|
.padding(4.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ struct TimelineItemPlainStylerView<Content: View>: View {
|
|||||||
let replyDetails = messageTimelineItem.replyDetails {
|
let replyDetails = messageTimelineItem.replyDetails {
|
||||||
HStack(spacing: 4.0) {
|
HStack(spacing: 4.0) {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.foregroundColor(.element.accent)
|
.foregroundColor(.global.melon)
|
||||||
.frame(width: 4.0)
|
.frame(width: 4.0)
|
||||||
TimelineReplyView(timelineItemReplyDetails: replyDetails)
|
TimelineReplyView(timelineItemReplyDetails: replyDetails)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user