mirror of
https://github.com/yattee/yattee.git
synced 2026-07-20 14:22:02 +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
140 lines
3.8 KiB
Swift
140 lines
3.8 KiB
Swift
//
|
|
// BookmarkRowView.swift
|
|
// Yattee
|
|
//
|
|
// Row view for displaying bookmarked videos with tags and notes.
|
|
//
|
|
|
|
import SwiftData
|
|
import SwiftUI
|
|
|
|
/// Row view for displaying bookmarked videos.
|
|
/// Uses VideoRowView for consistent presentation with additional tags/notes display.
|
|
/// Automatically handles DeArrow integration.
|
|
/// Supports optional queue context for auto-play functionality.
|
|
struct BookmarkRowView: View {
|
|
@Environment(\.appEnvironment) private var appEnvironment
|
|
|
|
let bookmark: Bookmark
|
|
var style: VideoRowStyle = .regular
|
|
var watchProgress: Double? = nil
|
|
let onRemove: () -> Void
|
|
|
|
// Queue context (optional, enables auto-play when provided)
|
|
var queueSource: QueueSource? = nil
|
|
var sourceLabel: String? = nil
|
|
var videoList: [Video]? = nil
|
|
var videoIndex: Int? = nil
|
|
var loadMoreVideos: LoadMoreVideosCallback? = nil
|
|
|
|
private var accentColor: Color {
|
|
appEnvironment?.settingsManager.resolvedAccentColor ?? .accentColor
|
|
}
|
|
|
|
private var isBookmarkValid: Bool {
|
|
bookmark.modelContext != nil && !bookmark.isDeleted
|
|
}
|
|
|
|
private var video: Video {
|
|
bookmark.toVideo()
|
|
}
|
|
|
|
private var hasTags: Bool {
|
|
guard isBookmarkValid else { return false }
|
|
return !bookmark.tags.isEmpty
|
|
}
|
|
|
|
private var hasNote: Bool {
|
|
guard isBookmarkValid else { return false }
|
|
if let note = bookmark.note, !note.isEmpty {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
private var hasMetadata: Bool {
|
|
hasTags || hasNote
|
|
}
|
|
|
|
/// Leading padding to align tags/notes with the text content (past thumbnail)
|
|
private var metadataLeadingPadding: CGFloat {
|
|
let hstackSpacing: CGFloat = 12
|
|
return style.thumbnailWidth + hstackSpacing
|
|
}
|
|
|
|
var body: some View {
|
|
if isBookmarkValid {
|
|
content
|
|
}
|
|
}
|
|
|
|
private var content: some View {
|
|
VStack(alignment: .leading, spacing: 0) {
|
|
// Video row content
|
|
videoRowContent
|
|
|
|
// Tags and notes (text-aligned, past thumbnail) - one line
|
|
if hasMetadata && style != .compact {
|
|
bookmarkMetadataLine
|
|
.padding(.leading, metadataLeadingPadding)
|
|
.padding(.top, 4)
|
|
}
|
|
}
|
|
.tappableVideo(
|
|
video,
|
|
queueSource: queueSource,
|
|
sourceLabel: sourceLabel,
|
|
videoList: videoList,
|
|
videoIndex: videoIndex,
|
|
loadMoreVideos: loadMoreVideos
|
|
)
|
|
.videoContextMenu(
|
|
video: video,
|
|
customActions: [
|
|
VideoContextAction(
|
|
String(localized: "home.bookmarks.remove"),
|
|
systemImage: "trash",
|
|
role: .destructive,
|
|
action: onRemove
|
|
)
|
|
],
|
|
context: .bookmarks
|
|
)
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var videoRowContent: some View {
|
|
VideoRowView(
|
|
video: video,
|
|
style: style,
|
|
watchProgress: watchProgress
|
|
)
|
|
}
|
|
|
|
/// Single line with tags and note separated by dot
|
|
@ViewBuilder
|
|
private var bookmarkMetadataLine: some View {
|
|
HStack(spacing: 4) {
|
|
if hasTags {
|
|
BookmarkTagsView(
|
|
tags: bookmark.tags,
|
|
maxVisible: style == .large ? 4 : 3
|
|
)
|
|
}
|
|
|
|
if hasTags && hasNote {
|
|
Text(verbatim: "·")
|
|
.font(.caption2)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
|
|
if hasNote {
|
|
Text(bookmark.note!)
|
|
.font(.caption2)
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(1)
|
|
}
|
|
}
|
|
}
|
|
}
|