mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-05-19 08:48:16 +00:00
229 lines
6.9 KiB
Swift
229 lines
6.9 KiB
Swift
import DesignSystem
|
|
import Env
|
|
import Models
|
|
import Network
|
|
import NukeUI
|
|
import SwiftUI
|
|
|
|
@MainActor
|
|
struct ConversationMessageView: View {
|
|
@Environment(\.openWindow) private var openWindow
|
|
@Environment(QuickLook.self) private var quickLook
|
|
@Environment(RouterPath.self) private var routerPath
|
|
@Environment(CurrentAccount.self) private var currentAccount
|
|
@Environment(Client.self) private var client
|
|
@Environment(Theme.self) private var theme
|
|
|
|
let message: Status
|
|
let conversation: Conversation
|
|
|
|
@State private var isLiked: Bool = false
|
|
@State private var isBookmarked: Bool = false
|
|
|
|
var body: some View {
|
|
let isOwnMessage = message.account.id == currentAccount.account?.id
|
|
VStack {
|
|
HStack(alignment: .bottom) {
|
|
if isOwnMessage {
|
|
Spacer()
|
|
} else {
|
|
AvatarView(message.account.avatar)
|
|
.onTapGesture {
|
|
routerPath.navigate(to: .accountDetailWithAccount(account: message.account))
|
|
}
|
|
}
|
|
VStack(alignment: .leading) {
|
|
EmojiTextApp(message.content, emojis: message.emojis)
|
|
.font(.scaledBody)
|
|
.foregroundColor(theme.labelColor)
|
|
.emojiSize(Font.scaledBodyFont.emojiSize)
|
|
.emojiBaselineOffset(Font.scaledBodyFont.emojiBaselineOffset)
|
|
.padding(6)
|
|
.environment(\.openURL, OpenURLAction { url in
|
|
routerPath.handleStatus(status: message, url: url)
|
|
})
|
|
}
|
|
#if os(visionOS)
|
|
.background(isOwnMessage ? Material.ultraThick : Material.regular)
|
|
#else
|
|
.background(isOwnMessage ? theme.tintColor.opacity(0.2) : theme.secondaryBackgroundColor)
|
|
#endif
|
|
.cornerRadius(8)
|
|
.padding(.leading, isOwnMessage ? 24 : 0)
|
|
.padding(.trailing, isOwnMessage ? 0 : 24)
|
|
.overlay {
|
|
if isLiked, message.account.id != currentAccount.account?.id {
|
|
likeView
|
|
}
|
|
}
|
|
.contextMenu {
|
|
contextMenu
|
|
}
|
|
|
|
if !isOwnMessage {
|
|
Spacer()
|
|
}
|
|
}
|
|
|
|
ForEach(message.mediaAttachments) { media in
|
|
makeMediaView(media)
|
|
.padding(.leading, isOwnMessage ? 24 : 0)
|
|
.padding(.trailing, isOwnMessage ? 0 : 24)
|
|
}
|
|
|
|
if message.id == String(conversation.lastStatus?.id ?? "") {
|
|
HStack {
|
|
if isOwnMessage {
|
|
Spacer()
|
|
}
|
|
Group {
|
|
Text(message.createdAt.shortDateFormatted) +
|
|
Text(" ")
|
|
Text(message.createdAt.asDate, style: .time)
|
|
}
|
|
.font(.scaledFootnote)
|
|
.foregroundStyle(.secondary)
|
|
if !isOwnMessage {
|
|
Spacer()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.onAppear {
|
|
isLiked = message.favourited == true
|
|
isBookmarked = message.bookmarked == true
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var contextMenu: some View {
|
|
Button {
|
|
routerPath.navigate(to: .statusDetail(id: message.id))
|
|
} label: {
|
|
Label("conversations.action.view-detail", systemImage: "arrow.forward")
|
|
}
|
|
Button {
|
|
UIPasteboard.general.string = message.content.asRawText
|
|
} label: {
|
|
Label("status.action.copy-text", systemImage: "doc.on.doc")
|
|
}
|
|
Button {
|
|
Task {
|
|
do {
|
|
let status: Status
|
|
if isLiked {
|
|
status = try await client.post(endpoint: Statuses.unfavorite(id: message.id))
|
|
} else {
|
|
status = try await client.post(endpoint: Statuses.favorite(id: message.id))
|
|
}
|
|
withAnimation {
|
|
isLiked = status.favourited == true
|
|
}
|
|
} catch {}
|
|
}
|
|
} label: {
|
|
Label(isLiked ? "status.action.unfavorite" : "status.action.favorite",
|
|
systemImage: isLiked ? "star.fill" : "star")
|
|
}
|
|
Button { Task {
|
|
do {
|
|
let status: Status
|
|
if isBookmarked {
|
|
status = try await client.post(endpoint: Statuses.unbookmark(id: message.id))
|
|
} else {
|
|
status = try await client.post(endpoint: Statuses.bookmark(id: message.id))
|
|
}
|
|
withAnimation {
|
|
isBookmarked = status.bookmarked == true
|
|
}
|
|
} catch {}
|
|
} } label: {
|
|
Label(isBookmarked ? "status.action.unbookmark" : "status.action.bookmark",
|
|
systemImage: isBookmarked ? "bookmark.fill" : "bookmark")
|
|
}
|
|
Divider()
|
|
if message.account.id == currentAccount.account?.id {
|
|
Button("status.action.delete", role: .destructive) {
|
|
Task {
|
|
_ = try await client.delete(endpoint: Statuses.status(id: message.id))
|
|
}
|
|
}
|
|
} else {
|
|
Section(message.reblog?.account.acct ?? message.account.acct) {
|
|
Button {
|
|
routerPath.presentedSheet = .mentionStatusEditor(account: message.reblog?.account ?? message.account, visibility: .pub)
|
|
} label: {
|
|
Label("status.action.mention", systemImage: "at")
|
|
}
|
|
}
|
|
Section {
|
|
Button(role: .destructive) {
|
|
routerPath.presentedSheet = .report(status: message.reblogAsAsStatus ?? message)
|
|
} label: {
|
|
Label("status.action.report", systemImage: "exclamationmark.bubble")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func makeImageRequest(for url: URL, size: CGSize) -> ImageRequest {
|
|
ImageRequest(url: url, processors: [.resize(size: size)])
|
|
}
|
|
|
|
private func mediaWidth(proxy: GeometryProxy) -> CGFloat {
|
|
var width = proxy.frame(in: .local).width
|
|
if UIDevice.current.userInterfaceIdiom == .pad {
|
|
width = width * 0.60
|
|
}
|
|
return width
|
|
}
|
|
|
|
private func makeMediaView(_ attachement: MediaAttachment) -> some View {
|
|
GeometryReader { proxy in
|
|
let width = mediaWidth(proxy: proxy)
|
|
if let url = attachement.url {
|
|
LazyImage(request: makeImageRequest(for: url,
|
|
size: .init(width: width, height: 200)))
|
|
{ state in
|
|
if let image = state.image {
|
|
image
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fill)
|
|
.frame(height: 200)
|
|
.frame(maxWidth: width)
|
|
.clipped()
|
|
.cornerRadius(8)
|
|
.padding(8)
|
|
} else if state.isLoading {
|
|
RoundedRectangle(cornerRadius: 8)
|
|
.fill(Color.gray)
|
|
.frame(height: 200)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.frame(height: 200)
|
|
.contentShape(Rectangle())
|
|
.onTapGesture {
|
|
#if targetEnvironment(macCatalyst)
|
|
openWindow(value: WindowDestinationMedia.mediaViewer(attachments: [attachement],
|
|
selectedAttachment: attachement))
|
|
#else
|
|
quickLook.prepareFor(selectedMediaAttachment: attachement, mediaAttachments: [attachement])
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private var likeView: some View {
|
|
HStack {
|
|
Spacer()
|
|
VStack {
|
|
Image(systemName: "star.fill")
|
|
.foregroundColor(.yellow)
|
|
.offset(x: -16, y: -7)
|
|
Spacer()
|
|
}
|
|
}
|
|
}
|
|
}
|