[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: #5 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This commit is contained in:
parent
3d78c599d1
commit
58ac646e25
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -35,6 +35,10 @@ struct PiperApp: App {
|
||||
Image(systemName: "circle.fill")
|
||||
}
|
||||
.menuBarExtraStyle(.window)
|
||||
|
||||
Settings {
|
||||
SettingsView()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,6 +15,5 @@ extension Schema {
|
||||
static let entities = Schema([
|
||||
Repository.self
|
||||
])
|
||||
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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 () {
|
||||
|
||||
}
|
||||
|
||||
}
|
20
Piper/Sources/Logic/ViewModels/SettingsViewModel.swift
Normal file
20
Piper/Sources/Logic/ViewModels/SettingsViewModel.swift
Normal file
@ -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
|
||||
|
||||
}
|
@ -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.
|
||||
|
@ -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())
|
||||
|
||||
}
|
@ -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.
|
||||
|
27
Piper/Sources/Previews/Modifiers/EmptyDataModifier.swift
Normal file
27
Piper/Sources/Previews/Modifiers/EmptyDataModifier.swift
Normal file
@ -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)
|
||||
}
|
||||
|
||||
}
|
31
Piper/Sources/Previews/Modifiers/SampleDataModifier.swift
Normal file
31
Piper/Sources/Previews/Modifiers/SampleDataModifier.swift
Normal file
@ -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)
|
||||
}
|
||||
|
||||
}
|
35
Piper/Sources/UI/Enumerations/SettingsItem.swift
Normal file
35
Piper/Sources/UI/Enumerations/SettingsItem.swift
Normal file
@ -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
|
||||
}
|
||||
|
||||
}
|
27
Piper/Sources/UI/Extensions/SettingsItem+Properties.swift
Normal file
27
Piper/Sources/UI/Extensions/SettingsItem+Properties.swift
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
|
||||
|
74
Piper/Sources/UI/Views/SettingsView.swift
Normal file
74
Piper/Sources/UI/Views/SettingsView.swift
Normal file
@ -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()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user