Compare commits
2 Commits
234434f102
...
44c5d91620
| Author | SHA1 | Date | |
|---|---|---|---|
|
44c5d91620
|
|||
|
7a5a2407b7
|
@@ -46,26 +46,34 @@ struct AttachmentView: View {
|
|||||||
}
|
}
|
||||||
} else if mimeType.starts(with: "video") {
|
} else if mimeType.starts(with: "video") {
|
||||||
if let serverUrl = appState.serverUrl, let videoUrl = getAttachmentUrl(for: attachment.id, serverUrl: serverUrl) {
|
if let serverUrl = appState.serverUrl, let videoUrl = getAttachmentUrl(for: attachment.id, serverUrl: serverUrl) {
|
||||||
let thumbnailUrl = videoUrl.appendingPathComponent("thumbnail") // Construct thumbnail URL
|
|
||||||
NavigationLink(destination: VideoPlayerView(videoUrl: videoUrl)) {
|
NavigationLink(destination: VideoPlayerView(videoUrl: videoUrl)) {
|
||||||
AsyncImage(url: thumbnailUrl) { phase in
|
if imageLoader.isLoading {
|
||||||
if let image = phase.image {
|
ProgressView()
|
||||||
|
} else if let image = imageLoader.image {
|
||||||
|
ZStack {
|
||||||
image
|
image
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
} else if phase.error != nil {
|
|
||||||
Image(systemName: "play.rectangle.fill") // Placeholder for video thumbnail
|
Image(systemName: "play.circle.fill")
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fit)
|
.scaledToFit()
|
||||||
.frame(maxWidth: .infinity)
|
.frame(width: 36, height: 36)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.white)
|
||||||
.cornerRadius(8)
|
.shadow(color: .black.opacity(0.6), radius: 4, x: 0, y: 2)
|
||||||
} else {
|
|
||||||
ProgressView()
|
|
||||||
.cornerRadius(8)
|
|
||||||
}
|
}
|
||||||
|
} else if imageLoader.errorMessage != nil {
|
||||||
|
Image(systemName: "play.rectangle.fill")
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
.cornerRadius(8)
|
||||||
|
} else {
|
||||||
|
ProgressView()
|
||||||
|
.cornerRadius(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
@@ -90,6 +98,11 @@ struct AttachmentView: View {
|
|||||||
if attachment.mimeType?.starts(with: "image") == true {
|
if attachment.mimeType?.starts(with: "image") == true {
|
||||||
await imageLoader.loadImage(from: attachmentUrl, token: token)
|
await imageLoader.loadImage(from: attachmentUrl, token: token)
|
||||||
}
|
}
|
||||||
|
if attachment.mimeType?.starts(with: "video") == true {
|
||||||
|
let thumbnailUrl = attachmentUrl
|
||||||
|
.appending(queryItems: [URLQueryItem(name: "thumbnail", value: "true")]) // Construct thumbnail URL
|
||||||
|
await imageLoader.loadImage(from: thumbnailUrl, token: token)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,28 +15,26 @@ struct PostRowView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
HStack {
|
HStack {
|
||||||
if let serverUrl = appState.serverUrl, let pictureId = post.publisher.picture?.id, let imageUrl = getAttachmentUrl(for: pictureId, serverUrl: serverUrl), let token = appState.token {
|
if imageLoader.isLoading {
|
||||||
if imageLoader.isLoading {
|
ProgressView()
|
||||||
ProgressView()
|
.frame(width: 24, height: 24)
|
||||||
.frame(width: 24, height: 24)
|
} else if let image = imageLoader.image {
|
||||||
} else if let image = imageLoader.image {
|
image
|
||||||
image
|
.resizable()
|
||||||
.resizable()
|
.frame(width: 24, height: 24)
|
||||||
.frame(width: 24, height: 24)
|
.clipShape(Circle())
|
||||||
.clipShape(Circle())
|
} else if let errorMessage = imageLoader.errorMessage {
|
||||||
} else if let errorMessage = imageLoader.errorMessage {
|
Text("Failed: \(errorMessage)")
|
||||||
Text("Failed: \(errorMessage)")
|
.font(.caption)
|
||||||
.font(.caption)
|
.foregroundColor(.red)
|
||||||
.foregroundColor(.red)
|
.frame(width: 24, height: 24)
|
||||||
.frame(width: 24, height: 24)
|
} else {
|
||||||
} else {
|
// Placeholder if no image and not loading
|
||||||
// Placeholder if no image and not loading
|
Image(systemName: "person.circle.fill")
|
||||||
Image(systemName: "person.circle.fill")
|
.resizable()
|
||||||
.resizable()
|
.frame(width: 24, height: 24)
|
||||||
.frame(width: 24, height: 24)
|
.clipShape(Circle())
|
||||||
.clipShape(Circle())
|
.foregroundColor(.gray)
|
||||||
.foregroundColor(.gray)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Text(post.publisher.nick ?? post.publisher.name)
|
Text(post.publisher.nick ?? post.publisher.name)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
@@ -57,7 +55,21 @@ struct PostRowView: View {
|
|||||||
Text(content)
|
Text(content)
|
||||||
.font(.body)
|
.font(.body)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if !post.attachments.isEmpty {
|
||||||
|
AttachmentView(attachment: post.attachments[0])
|
||||||
|
if post.attachments.count > 1 {
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
Image(systemName: "paperclip.circle.fill")
|
||||||
|
.frame(width: 12, height: 12)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
Text("\(post.attachments.count - 1)+ attachments")
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.padding(.vertical)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,32 +82,31 @@ struct PostDetailView: View {
|
|||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
HStack {
|
HStack {
|
||||||
if let serverUrl = appState.serverUrl, let pictureId = post.publisher.picture?.id, let imageUrl = getAttachmentUrl(for: pictureId, serverUrl: serverUrl), let token = appState.token {
|
if publisherImageLoader.isLoading {
|
||||||
if publisherImageLoader.isLoading {
|
ProgressView()
|
||||||
ProgressView()
|
.frame(width: 32, height: 32)
|
||||||
.frame(width: 32, height: 32)
|
} else if let image = publisherImageLoader.image {
|
||||||
} else if let image = publisherImageLoader.image {
|
image
|
||||||
image
|
.resizable()
|
||||||
.resizable()
|
.frame(width: 32, height: 32)
|
||||||
.frame(width: 32, height: 32)
|
.clipShape(Circle())
|
||||||
.clipShape(Circle())
|
} else if let errorMessage = publisherImageLoader.errorMessage {
|
||||||
} else if let errorMessage = publisherImageLoader.errorMessage {
|
Text("Failed: \(errorMessage)")
|
||||||
Text("Failed: \(errorMessage)")
|
.font(.caption)
|
||||||
.font(.caption)
|
.foregroundColor(.red)
|
||||||
.foregroundColor(.red)
|
.frame(width: 32, height: 32)
|
||||||
.frame(width: 32, height: 32)
|
} else {
|
||||||
} else {
|
Image(systemName: "person.circle.fill")
|
||||||
Image(systemName: "person.circle.fill")
|
.resizable()
|
||||||
.resizable()
|
.frame(width: 32, height: 32)
|
||||||
.frame(width: 32, height: 32)
|
.clipShape(Circle())
|
||||||
.clipShape(Circle())
|
.foregroundColor(.gray)
|
||||||
.foregroundColor(.gray)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Text("@\(post.publisher.name)")
|
Text("@\(post.publisher.name)")
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
}
|
}
|
||||||
.task(id: post.publisher.picture?.id) { // Use task(id:) to reload image when pictureId changes
|
// Use task(id:) to reload image when pictureId changes
|
||||||
|
.task(id: post.publisher.picture?.id) {
|
||||||
if let serverUrl = appState.serverUrl, let pictureId = post.publisher.picture?.id, let imageUrl = getAttachmentUrl(for: pictureId, serverUrl: serverUrl), let token = appState.token {
|
if let serverUrl = appState.serverUrl, let pictureId = post.publisher.picture?.id, let imageUrl = getAttachmentUrl(for: pictureId, serverUrl: serverUrl), let token = appState.token {
|
||||||
await publisherImageLoader.loadImage(from: imageUrl, token: token)
|
await publisherImageLoader.loadImage(from: imageUrl, token: token)
|
||||||
}
|
}
|
||||||
@@ -113,7 +124,6 @@ struct PostDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !post.attachments.isEmpty {
|
if !post.attachments.isEmpty {
|
||||||
Divider()
|
|
||||||
Text("Attachments").font(.headline)
|
Text("Attachments").font(.headline)
|
||||||
ForEach(post.attachments) { attachment in
|
ForEach(post.attachments) { attachment in
|
||||||
AttachmentView(attachment: attachment)
|
AttachmentView(attachment: attachment)
|
||||||
@@ -121,13 +131,12 @@ struct PostDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !post.tags.isEmpty {
|
if !post.tags.isEmpty {
|
||||||
Divider()
|
|
||||||
Text("Tags").font(.headline)
|
Text("Tags").font(.headline)
|
||||||
FlowLayout(alignment: .leading, spacing: 4) {
|
FlowLayout(alignment: .leading, spacing: 4) {
|
||||||
ForEach(post.tags) { tag in
|
ForEach(post.tags) { tag in
|
||||||
Text("#\(tag.name ?? tag.slug)")
|
Text("#\(tag.name ?? tag.slug)")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.padding(.horizontal, 6)
|
.padding(.horizontal, 8)
|
||||||
.padding(.vertical, 3)
|
.padding(.vertical, 3)
|
||||||
.background(Capsule().fill(Color.accentColor.opacity(0.2)))
|
.background(Capsule().fill(Color.accentColor.opacity(0.2)))
|
||||||
.cornerRadius(5)
|
.cornerRadius(5)
|
||||||
@@ -136,6 +145,7 @@ struct PostDetailView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
|
.frame(width: .infinity)
|
||||||
}
|
}
|
||||||
.navigationTitle("Post")
|
.navigationTitle("Post")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user