| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- import SwiftUI
- /// Editor for playlist grouping template.
- /// Lets the user type a custom template or pick a preset.
- struct GroupTemplateEditorSheet: View {
- let playlist: Playlist
- @EnvironmentObject private var theme: AppTheme
- @Environment(\.modelContext) private var modelContext
- @Environment(\.dismiss) private var dismiss
- @State private var template: String = ""
- var body: some View {
- NavigationStack {
- List {
- Section {
- TextField("Template (empty = no grouping)", text: $template)
- .font(.body.monospaced())
- .autocorrectionDisabled()
- .textInputAutocapitalization(.never)
- } header: {
- Text("Group Template")
- } footer: {
- Text("Use placeholders like {Album}, {Artist}, {Date}, etc. You can also use {Year} as an alias for {Date}. Tracks with the same resolved value will be grouped together.")
- }
- Section("Presets") {
- ForEach(GroupTemplateResolver.presets, id: \.template) { preset in
- Button {
- template = preset.template
- } label: {
- HStack {
- VStack(alignment: .leading, spacing: 2) {
- Text(preset.name)
- .foregroundStyle(theme.primaryText)
- if !preset.template.isEmpty {
- Text(preset.template)
- .font(.caption.monospaced())
- .foregroundStyle(theme.tertiaryText)
- }
- }
- Spacer()
- if template == preset.template {
- Image(systemName: "checkmark")
- .foregroundStyle(theme.accent)
- }
- }
- }
- }
- }
- Section("Available Placeholders") {
- ForEach(GroupTemplateResolver.placeholders, id: \.token) { placeholder in
- Button {
- template += placeholder.token
- } label: {
- HStack {
- Text(placeholder.token)
- .font(.body.monospaced())
- .foregroundStyle(theme.accent)
- Spacer()
- Text(placeholder.description)
- .font(.caption)
- .foregroundStyle(theme.tertiaryText)
- }
- }
- }
- }
- if !template.isEmpty {
- Section("Preview") {
- Text("Groups will look like:")
- .font(.caption)
- .foregroundStyle(theme.tertiaryText)
- Text(previewText)
- .font(.subheadline.weight(.semibold))
- .foregroundStyle(theme.groupHeaderText)
- }
- }
- }
- .navigationTitle("Grouping")
- .navigationBarTitleDisplayMode(.inline)
- .toolbar {
- ToolbarItem(placement: .cancellationAction) {
- Button("Cancel") { dismiss() }
- }
- ToolbarItem(placement: .confirmationAction) {
- Button("Save") {
- playlist.groupTemplate = template
- try? modelContext.save()
- dismiss()
- }
- }
- }
- .onAppear {
- template = playlist.groupTemplate
- }
- }
- }
- private var previewText: String {
- // Show a sample with dummy data
- let sample = template
- .replacingOccurrences(of: "{Artist}", with: "Raekwon")
- .replacingOccurrences(of: "{Album}", with: "Only Built 4 Cuban Linx")
- .replacingOccurrences(of: "{Genre}", with: "Hip Hop")
- .replacingOccurrences(of: "{Date}", with: "1995")
- .replacingOccurrences(of: "{Year}", with: "1995")
- .replacingOccurrences(of: "{Folder}", with: "Batch 1")
- .replacingOccurrences(of: "{Format}", with: "FLAC")
- .replacingOccurrences(of: "{BPM}", with: "90-100 BPM")
- .replacingOccurrences(of: "{Key}", with: "Am")
- return sample
- }
- }
|