98 lines
3.4 KiB
Swift

//
// DraftEditor.swift
// ComposeUI
//
// Created by Shadowfacts on 11/16/24.
//
import SwiftUI
import Pachyderm
import TuskerComponents
import TuskerPreferences
struct DraftEditor: View {
@ObservedObject var draft: Draft
@FocusState.Binding var focusedField: FocusableField?
@Environment(\.currentAccount) private var currentAccount
var body: some View {
HStack(alignment: .top, spacing: 8) {
// TODO: scroll effect?
AvatarView(account: currentAccount)
VStack(alignment: .leading, spacing: 4) {
if let currentAccount {
AccountNameView(account: currentAccount)
}
if draft.contentWarningEnabled {
ContentWarningTextField(draft: draft, focusedField: $focusedField)
.transition(.opacity)
}
DraftContentEditor(draft: draft, focusedField: $focusedField)
if let poll = draft.poll,
draft.pollEnabled {
PollEditor(poll: poll, focusedField: $focusedField)
.padding(.bottom, 4)
// So that during the appearance transition, it's behind the text view.
.zIndex(-1)
.transition(.move(edge: .top).combined(with: .opacity))
}
AttachmentsSection(draft: draft)
// We want the padding between the poll/attachments to be part of the poll, so it animates in/out with the transition.
// Otherwise, when the poll is added, its bottom edge is aligned with the top edge of the attachments
.padding(.top, draft.pollEnabled ? -4 : 0)
}
// These animations are here, because the height of the VStack and the positions of the lower views needs to animate too.
.animation(.snappy, value: draft.pollEnabled)
.modifier(PollAnimatingModifier(poll: draft.poll))
.animation(.snappy, value: draft.contentWarningEnabled)
}
}
}
private struct AvatarView: View {
let account: (any AccountProtocol)?
@PreferenceObserving(\.$avatarStyle) private var avatarStyle
@Environment(\.composeUIConfig.fetchAvatar) private var fetchAvatar
var body: some View {
AvatarImageView(
url: account?.avatar,
size: 50,
style: avatarStyle == .circle ? .circle : .roundRect,
fetchAvatar: fetchAvatar
)
.accessibilityHidden(true)
}
}
private struct AccountNameView: View {
let account: any AccountProtocol
@Environment(\.composeUIConfig.displayNameLabel) private var displayNameLabel
var body: some View {
HStack(spacing: 4) {
displayNameLabel(account, .body, 16)
.lineLimit(1)
Text(verbatim: "@\(account.acct)")
.font(.body.weight(.light))
.foregroundColor(.secondary)
.lineLimit(1)
}
}
}
// Separate modifier because we need to observe the Poll itself, not the draft
private struct PollAnimatingModifier: ViewModifier {
@OptionalObservedObject var poll: Poll?
func body(content: Content) -> some View {
content.animation(.snappy, value: poll?.pollOptions.count)
}
}