Files
yattee/Yattee/Views/Components/CommentsPillView.swift
Arkadiusz Fal 0b2cb4deeb Honor the comments avatars setting in the overlay and pill, align replies without avatars
The "Author Avatars" toggle only reached the iPad/macOS comments page.
The expanded comments overlay now takes the same flag (hosts pass it in
the next commit), and the expanded comments pill shows the comments icon
instead of the author avatar when avatars are off.

The replies block in a comment used a hardcoded 44pt leading inset for
the avatar column regardless of the toggle, so "Show replies" was
indented past the comment text when avatars were hidden. The toggle now
aligns with the text (44pt with avatars, 0 without) and nested replies
keep a smaller 16pt inset without avatars so the hierarchy still reads.
2026-09-26 23:21:37 +02:00

98 lines
3.3 KiB
Swift

//
// CommentsPillView.swift
// Yattee
//
// A rounded pill showing a comment preview that slides in from the bottom.
// Tapping expands to full comments view.
// Collapses to just avatar when isCollapsed is true.
//
import SwiftUI
import NukeUI
struct CommentsPillView: View {
let comment: Comment
let isCollapsed: Bool
var fillWidth: Bool = false
/// When true, uses smaller sizing for the collapsed state (e.g. on narrow devices).
var compact: Bool = false
/// Whether the expanded pill leads with the author's avatar; when the
/// comments avatars setting is off it shows the comments icon instead.
var showAvatar: Bool = true
let onTap: () -> Void
var body: some View {
Button(action: onTap) {
HStack(spacing: isCollapsed ? 0 : 12) {
// Show icon when collapsed (or avatars are off), avatar when expanded
if isCollapsed || !showAvatar {
collapsedIconView
} else {
avatarView
}
// Text content - only in layout when expanded
if !isCollapsed {
textContent
}
}
.frame(maxWidth: (!isCollapsed && fillWidth) ? .infinity : nil, alignment: .leading)
.padding(.horizontal, isCollapsed ? (compact ? 6 : 10) : 16)
.padding(.vertical, isCollapsed ? (compact ? 6 : 10) : 12)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.contentShape(Capsule())
.glassBackground(.regular, in: .capsule, fallback: .thinMaterial)
.shadow(color: .black.opacity(0.1), radius: 8, y: 2)
}
private var collapsedIconView: some View {
let size: CGFloat = compact ? 28 : 32
return Image(systemName: "bubble.left.and.bubble.right")
.font(.system(size: compact ? 16 : 18, weight: .medium))
.foregroundStyle(.primary)
.frame(width: size, height: size)
}
private var avatarView: some View {
LazyImage(url: comment.author.thumbnailURL) { state in
if let image = state.image {
image
.resizable()
.aspectRatio(contentMode: .fill)
} else {
Circle()
.fill(.quaternary)
.overlay {
Text(String(comment.author.name.prefix(1)))
.font(.caption2)
.fontWeight(.medium)
.foregroundStyle(.secondary)
}
}
}
.frame(width: 32, height: 32)
.clipShape(Circle())
}
private var textContent: some View {
VStack(alignment: .leading, spacing: 2) {
// Author name
Text(comment.author.name)
.font(.caption2)
.fontWeight(.semibold)
.foregroundStyle(.primary)
.lineLimit(1)
// 2-line excerpt of comment
Text(comment.content)
.font(.footnote)
.foregroundStyle(.secondary)
.lineLimit(2)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}