A bit better timeline UI
This commit is contained in:
parent
b7ce9648d5
commit
eb4dc011b6
|
@ -1,7 +1,7 @@
|
|||
import HTML2Markdown
|
||||
import Foundation
|
||||
|
||||
extension Status {
|
||||
extension AnyStatus {
|
||||
private static var createdAtDateFormatter: DateFormatter {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.calendar = .init(identifier: .iso8601)
|
||||
|
@ -16,6 +16,12 @@ extension Status {
|
|||
return dateFormatter
|
||||
}
|
||||
|
||||
private static var createdAtShortDateFormatted: DateFormatter {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateStyle = .medium
|
||||
return dateFormatter
|
||||
}
|
||||
|
||||
public var contentAsMarkdown: String {
|
||||
do {
|
||||
let dom = try HTMLParser().parse(html: content)
|
||||
|
@ -30,6 +36,21 @@ extension Status {
|
|||
}
|
||||
|
||||
public var createdAtFormatted: String {
|
||||
Self.createdAtRelativeFormatter.localizedString(for: createdAtDate, relativeTo: Date())
|
||||
let calendar = Calendar(identifier: .gregorian)
|
||||
if calendar.numberOfDaysBetween(createdAtDate, and: Date()) > 1 {
|
||||
return Self.createdAtShortDateFormatted.string(from: createdAtDate)
|
||||
} else {
|
||||
return Self.createdAtRelativeFormatter.localizedString(for: createdAtDate, relativeTo: Date())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Calendar {
|
||||
func numberOfDaysBetween(_ from: Date, and to: Date) -> Int {
|
||||
let fromDate = startOfDay(for: from)
|
||||
let toDate = startOfDay(for: to)
|
||||
let numberOfDays = dateComponents([.day], from: fromDate, to: toDate)
|
||||
|
||||
return numberOfDays.day!
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,21 @@
|
|||
import Foundation
|
||||
|
||||
public struct Status: Codable, Identifiable {
|
||||
public protocol AnyStatus {
|
||||
var id: String { get }
|
||||
var content: String { get }
|
||||
var account: Account { get }
|
||||
var createdAt: String { get }
|
||||
}
|
||||
|
||||
public struct Status: AnyStatus, Codable, Identifiable {
|
||||
public let id: String
|
||||
public let content: String
|
||||
public let account: Account
|
||||
public let createdAt: String
|
||||
public let reblog: ReblogStatus?
|
||||
}
|
||||
|
||||
public struct ReblogStatus: AnyStatus, Codable, Identifiable {
|
||||
public let id: String
|
||||
public let content: String
|
||||
public let account: Account
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import Routeur
|
||||
|
||||
struct StatusActionsView: View {
|
||||
let status: Status
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Image(systemName: "bubble.right")
|
||||
}
|
||||
Spacer()
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Image(systemName: "arrow.left.arrow.right.circle")
|
||||
}
|
||||
Spacer()
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Image(systemName: "star")
|
||||
}
|
||||
Spacer()
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,26 +9,45 @@ struct StatusRowView: View {
|
|||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack(alignment: .top) {
|
||||
Button {
|
||||
routeurPath.navigate(to: .accountDetail(id: status.account.id))
|
||||
} label: {
|
||||
accountView
|
||||
}.buttonStyle(.plain)
|
||||
|
||||
Spacer()
|
||||
Text(status.createdAtFormatted)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
NavigationLink(value: RouteurDestinations.statusDetail(id: status.id)) {
|
||||
Text(try! AttributedString(markdown: status.contentAsMarkdown))
|
||||
}
|
||||
reblogView
|
||||
statusView
|
||||
StatusActionsView(status: status)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var accountView: some View {
|
||||
private var reblogView: some View {
|
||||
if status.reblog != nil {
|
||||
HStack(spacing: 2) {
|
||||
Image(systemName:"arrow.left.arrow.right.circle")
|
||||
Text("\(status.account.displayName) reblogged")
|
||||
}
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var statusView: some View {
|
||||
if let status: AnyStatus = status.reblog ?? status {
|
||||
Button {
|
||||
routeurPath.navigate(to: .accountDetail(id: status.account.id))
|
||||
} label: {
|
||||
makeAccountView(status: status)
|
||||
}.buttonStyle(.plain)
|
||||
|
||||
Text(try! AttributedString(markdown: status.contentAsMarkdown))
|
||||
.font(.body)
|
||||
.onTapGesture {
|
||||
routeurPath.navigate(to: .statusDetail(id: status.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func makeAccountView(status: AnyStatus) -> some View {
|
||||
AsyncImage(
|
||||
url: status.account.avatar,
|
||||
content: { image in
|
||||
|
@ -45,9 +64,15 @@ struct StatusRowView: View {
|
|||
VStack(alignment: .leading) {
|
||||
Text(status.account.displayName)
|
||||
.font(.headline)
|
||||
Text("@\(status.account.acct)")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
HStack {
|
||||
Text("@\(status.account.acct)")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
Spacer()
|
||||
Text(status.createdAtFormatted)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue