From 58ac646e25d1230cec654ecbbc849ce8f9905d3a Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Sun, 13 Oct 2024 22:17:49 +0000 Subject: [PATCH] [App] Open settings (#5) This PR contains the work done to open the *Settings* view and also, implemented the rendering of its tab items. Reviewed-on: https://repo.rock-n-code.com/rock-n-code/piper-app/pulls/5 Co-authored-by: Javier Cicchelli Co-committed-by: Javier Cicchelli --- Piper.xcodeproj/project.pbxproj | 4 +- .../Resources/Catalogs/Localizable.xcstrings | 17 +++-- Piper/Sources/App/PiperApp.swift | 4 + .../Logic/Extensions/Schema+Constants.swift | 1 - Piper/Sources/Logic/Models/Item.swift | 19 ----- .../Logic/ViewModels/MenuBarViewModel.swift | 20 ----- .../Logic/ViewModels/SettingsViewModel.swift | 20 +++++ .../Extensions/ModelContainer+Constants.swift | 2 +- .../Extensions/PreviewTrait+Properties.swift | 19 +++++ .../Extensions/Repository+Samples.swift | 2 +- .../Modifiers/EmptyDataModifier.swift | 27 +++++++ .../Modifiers/SampleDataModifier.swift | 31 ++++++++ .../UI/Enumerations/SettingsItem.swift | 35 +++++++++ .../Extensions/SettingsItem+Properties.swift | 27 +++++++ Piper/Sources/UI/Views/ContentView.swift | 60 --------------- Piper/Sources/UI/Views/MenuBarView.swift | 26 ++++++- Piper/Sources/UI/Views/SettingsView.swift | 74 +++++++++++++++++++ 17 files changed, 272 insertions(+), 116 deletions(-) delete mode 100644 Piper/Sources/Logic/Models/Item.swift delete mode 100644 Piper/Sources/Logic/ViewModels/MenuBarViewModel.swift create mode 100644 Piper/Sources/Logic/ViewModels/SettingsViewModel.swift create mode 100644 Piper/Sources/Previews/Extensions/PreviewTrait+Properties.swift create mode 100644 Piper/Sources/Previews/Modifiers/EmptyDataModifier.swift create mode 100644 Piper/Sources/Previews/Modifiers/SampleDataModifier.swift create mode 100644 Piper/Sources/UI/Enumerations/SettingsItem.swift create mode 100644 Piper/Sources/UI/Extensions/SettingsItem+Properties.swift delete mode 100644 Piper/Sources/UI/Views/ContentView.swift create mode 100644 Piper/Sources/UI/Views/SettingsView.swift diff --git a/Piper.xcodeproj/project.pbxproj b/Piper.xcodeproj/project.pbxproj index e9c7c3f..cbb4c68 100644 --- a/Piper.xcodeproj/project.pbxproj +++ b/Piper.xcodeproj/project.pbxproj @@ -431,7 +431,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "Piper/Sources/Previews/Extensions/Repository+Samples.swift Piper/Sources/Previews/Extensions/ModelContainer+Constants.swift Piper/Resources/Catalogs/Previews.xcassets"; + DEVELOPMENT_ASSET_PATHS = "Piper/Sources/Previews/Extensions/PreviewTrait+Properties.swift Piper/Sources/Previews/Extensions/ModelContainer+Constants.swift Piper/Resources/Catalogs/Previews.xcassets Piper/Sources/Previews/Extensions/Repository+Samples.swift Piper/Sources/Previews/Modifiers/SampleDataModifier.swift Piper/Sources/Previews/Modifiers/EmptyDataModifier.swift"; DEVELOPMENT_TEAM = 7FMNM89WKG; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -463,7 +463,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "Piper/Sources/Previews/Extensions/Repository+Samples.swift Piper/Sources/Previews/Extensions/ModelContainer+Constants.swift Piper/Resources/Catalogs/Previews.xcassets"; + DEVELOPMENT_ASSET_PATHS = "Piper/Sources/Previews/Extensions/PreviewTrait+Properties.swift Piper/Sources/Previews/Extensions/ModelContainer+Constants.swift Piper/Resources/Catalogs/Previews.xcassets Piper/Sources/Previews/Extensions/Repository+Samples.swift Piper/Sources/Previews/Modifiers/SampleDataModifier.swift Piper/Sources/Previews/Modifiers/EmptyDataModifier.swift"; DEVELOPMENT_TEAM = 7FMNM89WKG; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; diff --git a/Piper/Resources/Catalogs/Localizable.xcstrings b/Piper/Resources/Catalogs/Localizable.xcstrings index f26d581..ab81321 100644 --- a/Piper/Resources/Catalogs/Localizable.xcstrings +++ b/Piper/Resources/Catalogs/Localizable.xcstrings @@ -1,12 +1,6 @@ { "sourceLanguage" : "en", "strings" : { - "Add Item" : { - - }, - "Item at %@" : { - - }, "menu-bar.item.empty.button.text" : { "localizations" : { "en" : { @@ -48,8 +42,15 @@ } } }, - "Select an item" : { - + "settings.tab-bar.repositories.text" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repositories" + } + } + } } }, "version" : "1.0" diff --git a/Piper/Sources/App/PiperApp.swift b/Piper/Sources/App/PiperApp.swift index b8975a9..50d81e5 100644 --- a/Piper/Sources/App/PiperApp.swift +++ b/Piper/Sources/App/PiperApp.swift @@ -35,6 +35,10 @@ struct PiperApp: App { Image(systemName: "circle.fill") } .menuBarExtraStyle(.window) + + Settings { + SettingsView() + } } } diff --git a/Piper/Sources/Logic/Extensions/Schema+Constants.swift b/Piper/Sources/Logic/Extensions/Schema+Constants.swift index 934a1c5..4022270 100644 --- a/Piper/Sources/Logic/Extensions/Schema+Constants.swift +++ b/Piper/Sources/Logic/Extensions/Schema+Constants.swift @@ -15,6 +15,5 @@ extension Schema { static let entities = Schema([ Repository.self ]) - } diff --git a/Piper/Sources/Logic/Models/Item.swift b/Piper/Sources/Logic/Models/Item.swift deleted file mode 100644 index f2fca05..0000000 --- a/Piper/Sources/Logic/Models/Item.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// Item.swift -// Piper ~ App -// -// Created by Javier Cicchelli on 04/10/2024. -// Copyright © 2024 Röck+Cöde. All rights reserved. -// - -import Foundation -import SwiftData - -@Model -final class Item { - var timestamp: Date - - init(timestamp: Date) { - self.timestamp = timestamp - } -} diff --git a/Piper/Sources/Logic/ViewModels/MenuBarViewModel.swift b/Piper/Sources/Logic/ViewModels/MenuBarViewModel.swift deleted file mode 100644 index cb04301..0000000 --- a/Piper/Sources/Logic/ViewModels/MenuBarViewModel.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// MenuBarViewModel.swift -// Piper ~ App -// -// Created by Javier Cicchelli on 06/10/2024. -// Copyright © 2024 Röck+Cöde. All rights reserved. -// - -import Observation - -@Observable -final class MenuBarViewModel { - - // MARK: Initialisers - - init () { - - } - -} diff --git a/Piper/Sources/Logic/ViewModels/SettingsViewModel.swift b/Piper/Sources/Logic/ViewModels/SettingsViewModel.swift new file mode 100644 index 0000000..609b857 --- /dev/null +++ b/Piper/Sources/Logic/ViewModels/SettingsViewModel.swift @@ -0,0 +1,20 @@ +// +// SettingsViewModel.swift +// Piper ~ App +// +// Created by Javier Cicchelli on 13/10/2024. +// Copyright © 2024 Röck+Cöde. All rights reserved. +// + +import Observation + +@Observable +final class SettingsViewModel { + + // MARK: Properties + + var tabSelected: SettingsItem = .repositories + + let tabs: [SettingsItem] = SettingsItem.allCases + +} diff --git a/Piper/Sources/Previews/Extensions/ModelContainer+Constants.swift b/Piper/Sources/Previews/Extensions/ModelContainer+Constants.swift index 21d51a5..6ae202c 100644 --- a/Piper/Sources/Previews/Extensions/ModelContainer+Constants.swift +++ b/Piper/Sources/Previews/Extensions/ModelContainer+Constants.swift @@ -1,6 +1,6 @@ // // ModelContainer+Constants.swift -// Piper +// Piper ~ App // // Created by Javier Cicchelli on 06/10/2024. // Copyright © 2024 Röck+Cöde. All rights reserved. diff --git a/Piper/Sources/Previews/Extensions/PreviewTrait+Properties.swift b/Piper/Sources/Previews/Extensions/PreviewTrait+Properties.swift new file mode 100644 index 0000000..d41ef68 --- /dev/null +++ b/Piper/Sources/Previews/Extensions/PreviewTrait+Properties.swift @@ -0,0 +1,19 @@ +// +// PreviewTrait+Properties.swift +// Piper +// +// Created by Javier Cicchelli on 13/10/2024. +// Copyright © 2024 Röck+Cöde. All rights reserved. +// + +import SwiftUI + +@available(macOS 15.0, *) +extension PreviewTrait where T == Preview.ViewTraits { + + // MARK: Properties + + @MainActor static var emptyData: PreviewTrait = .modifier(EmptyDataModifier()) + @MainActor static var sampleData: PreviewTrait = .modifier(SampleDataModifier()) + +} diff --git a/Piper/Sources/Previews/Extensions/Repository+Samples.swift b/Piper/Sources/Previews/Extensions/Repository+Samples.swift index 9264b5b..f275d5b 100644 --- a/Piper/Sources/Previews/Extensions/Repository+Samples.swift +++ b/Piper/Sources/Previews/Extensions/Repository+Samples.swift @@ -1,6 +1,6 @@ // // Repository+Samples.swift -// Piper +// Piper ~ App // // Created by Javier Cicchelli on 06/10/2024. // Copyright © 2024 Röck+Cöde. All rights reserved. diff --git a/Piper/Sources/Previews/Modifiers/EmptyDataModifier.swift b/Piper/Sources/Previews/Modifiers/EmptyDataModifier.swift new file mode 100644 index 0000000..4ad5c1a --- /dev/null +++ b/Piper/Sources/Previews/Modifiers/EmptyDataModifier.swift @@ -0,0 +1,27 @@ +// +// EmptyDataModifier.swift +// Piper ~ App +// +// Created by Javier Cicchelli on 13/10/2024. +// Copyright © 2024 Röck+Cöde. All rights reserved. +// + +import SwiftData +import SwiftUI + +struct EmptyDataModifier: PreviewModifier { + + // MARK: Functions + + static func makeSharedContext() async throws -> ModelContainer { + ModelContainer.preview + } + + func body( + content: Content, + context: ModelContainer + ) -> some View { + content.modelContainer(context) + } + +} diff --git a/Piper/Sources/Previews/Modifiers/SampleDataModifier.swift b/Piper/Sources/Previews/Modifiers/SampleDataModifier.swift new file mode 100644 index 0000000..118b0a9 --- /dev/null +++ b/Piper/Sources/Previews/Modifiers/SampleDataModifier.swift @@ -0,0 +1,31 @@ +// +// SampleDataModifier.swift +// Piper ~ App +// +// Created by Javier Cicchelli on 13/10/2024. +// Copyright © 2024 Röck+Cöde. All rights reserved. +// + +import SwiftData +import SwiftUI + +struct SampleDataModifier: PreviewModifier { + + // MARK: Functions + + static func makeSharedContext() async throws -> ModelContainer { + let container = ModelContainer.preview + + Repository.samples(in: container) + + return container + } + + func body( + content: Content, + context: ModelContainer + ) -> some View { + content.modelContainer(context) + } + +} diff --git a/Piper/Sources/UI/Enumerations/SettingsItem.swift b/Piper/Sources/UI/Enumerations/SettingsItem.swift new file mode 100644 index 0000000..71092fd --- /dev/null +++ b/Piper/Sources/UI/Enumerations/SettingsItem.swift @@ -0,0 +1,35 @@ +// +// SettingsItem.swift +// Piper ~ App +// +// Created by Javier Cicchelli on 13/10/2024. +// Copyright © 2024 Röck+Cöde. All rights reserved. +// + +enum SettingsItem: Int, Hashable { + case repositories = 0 +} + +// MARK: - CaseIterable + +extension SettingsItem: CaseIterable { + + // MARK: Computed + + var allCases: [SettingsItem] { + [.repositories] + } + +} + +// MARK: - Identifiable + +extension SettingsItem: Identifiable { + + // MARK: Computed + + var id: Int { + rawValue + } + +} diff --git a/Piper/Sources/UI/Extensions/SettingsItem+Properties.swift b/Piper/Sources/UI/Extensions/SettingsItem+Properties.swift new file mode 100644 index 0000000..80ec6b1 --- /dev/null +++ b/Piper/Sources/UI/Extensions/SettingsItem+Properties.swift @@ -0,0 +1,27 @@ +// +// SettingsItem+Properties.swift +// Piper ~ App +// +// Created by Javier Cicchelli on 13/10/2024. +// Copyright © 2024 Röck+Cöde. All rights reserved. +// + +import SwiftUI + +extension SettingsItem { + + // MARK: Computed + + var icon: String { + switch self { + case .repositories: "folder" + } + } + + var title: LocalizedStringKey { + switch self { + case .repositories: "settings.tab-bar.repositories.text" + } + } + +} diff --git a/Piper/Sources/UI/Views/ContentView.swift b/Piper/Sources/UI/Views/ContentView.swift deleted file mode 100644 index 64a5ef2..0000000 --- a/Piper/Sources/UI/Views/ContentView.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// ContentView.swift -// Piper ~ App -// -// Created by Javier Cicchelli on 04/10/2024. -// Copyright © 2024 Röck+Cöde. All rights reserved. -// - -import SwiftUI -import SwiftData - -struct ContentView: View { - @Environment(\.modelContext) private var modelContext - @Query private var items: [Item] - - var body: some View { - NavigationSplitView { - List { - ForEach(items) { item in - NavigationLink { - Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") - } label: { - Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard)) - } - } - .onDelete(perform: deleteItems) - } - .navigationSplitViewColumnWidth(min: 180, ideal: 200) - .toolbar { - ToolbarItem { - Button(action: addItem) { - Label("Add Item", systemImage: "plus") - } - } - } - } detail: { - Text("Select an item") - } - } - - private func addItem() { - withAnimation { - let newItem = Item(timestamp: Date()) - modelContext.insert(newItem) - } - } - - private func deleteItems(offsets: IndexSet) { - withAnimation { - for index in offsets { - modelContext.delete(items[index]) - } - } - } -} - -#Preview { - ContentView() - .modelContainer(for: Item.self, inMemory: true) -} diff --git a/Piper/Sources/UI/Views/MenuBarView.swift b/Piper/Sources/UI/Views/MenuBarView.swift index 9040121..f0d89d1 100644 --- a/Piper/Sources/UI/Views/MenuBarView.swift +++ b/Piper/Sources/UI/Views/MenuBarView.swift @@ -13,11 +13,11 @@ struct MenuBarView: View { // MARK: Properties + @Environment(\.openSettings) private var openSettings + @Query(sort: \Repository.sortOrder) private var repositories: [Repository] - - @State private var viewMode = MenuBarViewModel() - + // MARK: Body var body: some View { @@ -28,7 +28,7 @@ struct MenuBarView: View { title: "menu-bar.item.empty.title.text", button: "menu-bar.item.empty.button.text" ) { - // ... + openSettings() } .frame(height: Layout.heightEmpty) } else { @@ -66,11 +66,29 @@ private extension MenuBarView { // MARK: - Previews +@available(macOS 15.0, *) +#Preview( + "Menu Bar view when no repositories found", + traits: .emptyData +) { + MenuBarView() +} + +@available(macOS, obsoleted: 15) #Preview("Menu Bar view when no repositories found") { MenuBarView() .modelContainer(.preview) } +@available(macOS 15.0, *) +#Preview( + "Menu Bar view when some repositories found", + traits: .sampleData +) { + MenuBarView() +} + +@available(macOS, obsoleted: 15) #Preview("Menu Bar view when some repositories found") { let container = ModelContainer.preview diff --git a/Piper/Sources/UI/Views/SettingsView.swift b/Piper/Sources/UI/Views/SettingsView.swift new file mode 100644 index 0000000..b960b87 --- /dev/null +++ b/Piper/Sources/UI/Views/SettingsView.swift @@ -0,0 +1,74 @@ +// +// SettingsView.swift +// Piper ~ App +// +// Created by Javier Cicchelli on 13/10/2024. +// Copyright © 2024 Röck+Cöde. All rights reserved. +// + +import SwiftUI + +struct SettingsView: View { + + // MARK: Properties + + @State private var viewModel: SettingsViewModel = .init() + + // MARK: Body + + var body: some View { + Group { + if #available(macOS 15.0, *) { + TabView(selection: $viewModel.tabSelected) { + ForEach(viewModel.tabs) { tabItem in + Tab( + tabItem.title, + systemImage: tabItem.icon, + value: tabItem + ) { + switch tabItem { + case .repositories: + Text(tabItem.title) + } + } + } + } + } else { + TabView(selection: $viewModel.tabSelected) { + ForEach(viewModel.tabs) { tabItem in + Group { + switch tabItem { + case .repositories: + Text(tabItem.title) + } + } + .tabItem { + Text(tabItem.title) + } + .tag(tabItem) + } + } + } + } + .scenePadding() + .frame( + width: Layout.sizeView.width, + height: Layout.sizeView.height + ) + } + +} + +// MARK: - Layout + +private extension SettingsView { + enum Layout { + static let sizeView = CGSize(width: 350, height: 250) + } +} + +// MARK: - Previews + +#Preview { + SettingsView() +}