mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 20:09:04 +00:00
Plain timeline styler (#94)
* #92 Create `TimelineStyle` and store it in user defaults * #92 Create `TimelineStyler` observable environment object * #92 Use timeline list row insets from the styler * #92 remove sender view, move header into the styler views * #92 Add timeline style option in settings * #92 Add changelog * #92 refactor some code duplications * #92 Fix `TimelineStyle`targets * #92 Fix `TimelineStyle` targets * #92 Fix PR remarks * #92 Fix inline PR comments * #92 Fix UI tests * #92 Rename bubbled to bubbles
This commit is contained in:
@@ -13,18 +13,14 @@ struct EmoteRoomTimelineView: View {
|
||||
let timelineItem: EmoteRoomTimelineItem
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
TimelineItemStylerView(timelineItem: timelineItem) {
|
||||
EventBasedTimelineSenderView(timelineItem: timelineItem)
|
||||
} content: {
|
||||
HStack(alignment: .top) {
|
||||
Image(systemName: "face.dashed").padding(.top, 1.0)
|
||||
if let attributedComponents = timelineItem.attributedComponents {
|
||||
FormattedBodyText(attributedComponents: attributedComponents)
|
||||
} else {
|
||||
Text(timelineItem.text)
|
||||
.foregroundColor(.element.primaryContent)
|
||||
}
|
||||
TimelineStyler(timelineItem: timelineItem) {
|
||||
HStack(alignment: .top) {
|
||||
Image(systemName: "face.dashed").padding(.top, 1.0)
|
||||
if let attributedComponents = timelineItem.attributedComponents {
|
||||
FormattedBodyText(attributedComponents: attributedComponents)
|
||||
} else {
|
||||
Text(timelineItem.text)
|
||||
.foregroundColor(.element.primaryContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
//
|
||||
// EventBasedTimelineSenderView.swift
|
||||
// ElementX
|
||||
//
|
||||
// Created by Stefan Ceriu on 18/03/2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct EventBasedTimelineSenderView: View {
|
||||
let timelineItem: EventBasedTimelineItemProtocol
|
||||
|
||||
@ScaledMetric private var avatarSize = 26
|
||||
|
||||
var body: some View {
|
||||
if timelineItem.shouldShowSenderDetails {
|
||||
VStack {
|
||||
Spacer()
|
||||
.frame(height: 8)
|
||||
HStack(alignment: .top, spacing: 4) {
|
||||
avatar
|
||||
Text(timelineItem.senderDisplayName ?? timelineItem.senderId)
|
||||
.font(.body)
|
||||
.foregroundColor(.element.primaryContent)
|
||||
.fontWeight(.semibold)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private var avatar: some View {
|
||||
ZStack(alignment: .center) {
|
||||
if let avatar = timelineItem.senderAvatar {
|
||||
Image(uiImage: avatar)
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.overlay(Circle().stroke(Color.element.accent))
|
||||
} else {
|
||||
PlaceholderAvatarImage(text: timelineItem.senderDisplayName ?? timelineItem.senderId)
|
||||
}
|
||||
}
|
||||
.clipShape(Circle())
|
||||
.frame(width: avatarSize, height: avatarSize)
|
||||
.overlay(
|
||||
Circle()
|
||||
.stroke(Color.element.background, lineWidth: 2)
|
||||
)
|
||||
|
||||
.animation(.default, value: timelineItem.senderAvatar)
|
||||
}
|
||||
}
|
||||
|
||||
struct EventBasedTimelineSenderView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
body.preferredColorScheme(.light)
|
||||
body.preferredColorScheme(.dark)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
static var body: some View {
|
||||
VStack(alignment: .leading, spacing: 20.0) {
|
||||
EventBasedTimelineSenderView(timelineItem: item1)
|
||||
|
||||
EventBasedTimelineSenderView(timelineItem: item2)
|
||||
}
|
||||
.frame(maxHeight: 160)
|
||||
.previewLayout(.sizeThatFits)
|
||||
}
|
||||
|
||||
private static var item1: EventBasedTimelineItemProtocol {
|
||||
TextRoomTimelineItem(id: UUID().uuidString,
|
||||
text: "Some text",
|
||||
timestamp: "",
|
||||
shouldShowSenderDetails: true,
|
||||
isOutgoing: false,
|
||||
senderId: "",
|
||||
senderDisplayName: "Bob")
|
||||
}
|
||||
|
||||
private static var item2: EventBasedTimelineItemProtocol {
|
||||
TextRoomTimelineItem(id: UUID().uuidString,
|
||||
text: "Some text",
|
||||
timestamp: "",
|
||||
shouldShowSenderDetails: true,
|
||||
isOutgoing: false,
|
||||
senderId: "",
|
||||
senderDisplayName: "Some long display name for a user")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,43 +14,37 @@ struct ImageRoomTimelineView: View {
|
||||
|
||||
var body: some View {
|
||||
if timelineItem.image != nil || timelineItem.blurhash != nil { // Fixes view heights after loading finishes
|
||||
VStack(alignment: .leading) {
|
||||
TimelineItemStylerView(timelineItem: timelineItem) {
|
||||
EventBasedTimelineSenderView(timelineItem: timelineItem)
|
||||
} content: {
|
||||
if let image = timelineItem.image {
|
||||
if let aspectRatio = timelineItem.aspectRatio {
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.aspectRatio(aspectRatio, contentMode: .fit)
|
||||
} else {
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
}
|
||||
} else if let blurhash = timelineItem.blurhash,
|
||||
// Build a small blurhash image so that it's fast
|
||||
let image = UIImage(blurHash: blurhash, size: .init(width: 10.0, height: 10.0)) {
|
||||
TimelineStyler(timelineItem: timelineItem) {
|
||||
if let image = timelineItem.image {
|
||||
if let aspectRatio = timelineItem.aspectRatio {
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
||||
.aspectRatio(aspectRatio, contentMode: .fit)
|
||||
} else {
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
}
|
||||
} else if let blurhash = timelineItem.blurhash,
|
||||
// Build a small blurhash image so that it's fast
|
||||
let image = UIImage(blurHash: blurhash, size: .init(width: 10.0, height: 10.0)) {
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
||||
}
|
||||
}
|
||||
.id(timelineItem.id)
|
||||
.animation(.default, value: timelineItem.image)
|
||||
.frame(maxHeight: 1000.0)
|
||||
} else {
|
||||
VStack(alignment: .leading) {
|
||||
TimelineItemStylerView(timelineItem: timelineItem) {
|
||||
EventBasedTimelineSenderView(timelineItem: timelineItem)
|
||||
} content: {
|
||||
HStack {
|
||||
Spacer()
|
||||
ProgressView("Loading")
|
||||
Spacer()
|
||||
}
|
||||
TimelineStyler(timelineItem: timelineItem) {
|
||||
HStack {
|
||||
Spacer()
|
||||
ProgressView("Loading")
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.id(timelineItem.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,18 +13,14 @@ struct NoticeRoomTimelineView: View {
|
||||
let timelineItem: NoticeRoomTimelineItem
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
TimelineItemStylerView(timelineItem: timelineItem) {
|
||||
EventBasedTimelineSenderView(timelineItem: timelineItem)
|
||||
} content: {
|
||||
HStack(alignment: .top) {
|
||||
Image(systemName: "exclamationmark.bubble").padding(.top, 2.0)
|
||||
if let attributedComponents = timelineItem.attributedComponents {
|
||||
FormattedBodyText(attributedComponents: attributedComponents)
|
||||
} else {
|
||||
Text(timelineItem.text)
|
||||
.foregroundColor(.element.primaryContent)
|
||||
}
|
||||
TimelineStyler(timelineItem: timelineItem) {
|
||||
HStack(alignment: .top) {
|
||||
Image(systemName: "exclamationmark.bubble").padding(.top, 2.0)
|
||||
if let attributedComponents = timelineItem.attributedComponents {
|
||||
FormattedBodyText(attributedComponents: attributedComponents)
|
||||
} else {
|
||||
Text(timelineItem.text)
|
||||
.foregroundColor(.element.primaryContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,7 @@ struct TextRoomTimelineView: View {
|
||||
let timelineItem: TextRoomTimelineItem
|
||||
|
||||
var body: some View {
|
||||
TimelineItemStylerView(timelineItem: timelineItem) {
|
||||
EventBasedTimelineSenderView(timelineItem: timelineItem)
|
||||
} content: {
|
||||
TimelineStyler(timelineItem: timelineItem) {
|
||||
if let attributedComponents = timelineItem.attributedComponents {
|
||||
FormattedBodyText(attributedComponents: attributedComponents)
|
||||
} else {
|
||||
@@ -42,12 +40,26 @@ struct TextRoomTimelineView_Previews: PreviewProvider {
|
||||
shouldShowSenderDetails: true,
|
||||
isOutgoing: false,
|
||||
senderId: "Bob"))
|
||||
|
||||
|
||||
TextRoomTimelineView(timelineItem: itemWith(text: "Some other text",
|
||||
timestamp: "Later",
|
||||
shouldShowSenderDetails: true,
|
||||
isOutgoing: true,
|
||||
senderId: "Anne"))
|
||||
|
||||
TextRoomTimelineView(timelineItem: itemWith(text: "Short loin ground round tongue hamburger, fatback salami shoulder. Beef turkey sausage kielbasa strip steak. Alcatra capicola pig tail pancetta chislic.",
|
||||
timestamp: "Now",
|
||||
shouldShowSenderDetails: true,
|
||||
isOutgoing: false,
|
||||
senderId: "Bob"))
|
||||
.timelineStyle(.plain)
|
||||
|
||||
TextRoomTimelineView(timelineItem: itemWith(text: "Some other text",
|
||||
timestamp: "Later",
|
||||
shouldShowSenderDetails: true,
|
||||
isOutgoing: true,
|
||||
senderId: "Anne"))
|
||||
.timelineStyle(.plain)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user