mirror of
https://github.com/yattee/yattee.git
synced 2026-07-19 22:02:10 +00:00
Add a Custom swatch to the appearance settings accent color grid, backed by a hex value stored in settings (synced via iCloud like the rest). iOS uses the native ColorPicker wheel; macOS uses a circular swatch that opens NSColorPanel, since the SwiftUI color well looks out of place among the circles. All accent consumers now read the resolved color through SettingsManager.resolvedAccentColor. The indigo preset is retired from the grid but kept in the enum, so users who selected it keep their color until they pick another one. Claude-Session: https://claude.ai/code/session_0154KH8RAVAvm6iVanhmoW8o
126 lines
3.2 KiB
Swift
126 lines
3.2 KiB
Swift
//
|
|
// BookmarkCardView.swift
|
|
// Yattee
|
|
//
|
|
// Grid card view for displaying bookmarked videos with tags and notes.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
/// A bookmark card for grid/horizontal scroll layouts.
|
|
/// Displays video content via VideoCardView with additional tags/notes.
|
|
struct BookmarkCardView: View {
|
|
@Environment(\.appEnvironment) private var appEnvironment
|
|
|
|
let bookmark: Bookmark
|
|
var watchProgress: Double? = nil
|
|
/// Use compact styling for dense grids (3+ columns).
|
|
var isCompact: Bool = false
|
|
|
|
private var accentColor: Color {
|
|
appEnvironment?.settingsManager.resolvedAccentColor ?? .accentColor
|
|
}
|
|
|
|
private var video: Video {
|
|
bookmark.toVideo()
|
|
}
|
|
|
|
private var hasTags: Bool {
|
|
!bookmark.tags.isEmpty
|
|
}
|
|
|
|
private var hasNote: Bool {
|
|
if let note = bookmark.note, !note.isEmpty {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
private var hasMetadata: Bool {
|
|
hasTags || hasNote
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: isCompact ? 4 : 8) {
|
|
// Video card content
|
|
VideoCardView(
|
|
video: video,
|
|
watchProgress: watchProgress,
|
|
isCompact: isCompact
|
|
)
|
|
|
|
// Tags and notes - one line
|
|
if hasMetadata {
|
|
bookmarkMetadataLine
|
|
}
|
|
}
|
|
.contentShape(Rectangle())
|
|
}
|
|
|
|
/// Single line with tags and note separated by dot (or icons in compact mode)
|
|
@ViewBuilder
|
|
private var bookmarkMetadataLine: some View {
|
|
if isCompact {
|
|
// Compact mode: show only icons
|
|
HStack(spacing: 4) {
|
|
if hasTags {
|
|
Image(systemName: "tag.fill")
|
|
.font(.caption2)
|
|
.foregroundStyle(accentColor)
|
|
}
|
|
if hasNote {
|
|
Image(systemName: "text.page")
|
|
.font(.caption2)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
} else {
|
|
// Regular mode: tags and note in one line
|
|
HStack(spacing: 4) {
|
|
if hasTags {
|
|
BookmarkTagsView(
|
|
tags: bookmark.tags,
|
|
maxVisible: 2
|
|
)
|
|
}
|
|
|
|
if hasTags && hasNote {
|
|
Text(verbatim: "·")
|
|
.font(.caption2)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
|
|
if hasNote {
|
|
Text(bookmark.note!)
|
|
.font(.caption2)
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Preview
|
|
|
|
#Preview("Card") {
|
|
BookmarkCardView(
|
|
bookmark: .preview,
|
|
watchProgress: 0.4
|
|
)
|
|
.frame(width: 280)
|
|
.padding()
|
|
.appEnvironment(.preview)
|
|
}
|
|
|
|
#Preview("Card Compact") {
|
|
BookmarkCardView(
|
|
bookmark: .preview,
|
|
watchProgress: 0.4,
|
|
isCompact: true
|
|
)
|
|
.frame(width: 110)
|
|
.padding()
|
|
.appEnvironment(.preview)
|
|
}
|