Compare commits

...

2 Commits

6 changed files with 232 additions and 28 deletions

View File

@ -42,6 +42,57 @@
}
}
},
"settings.button.add-repository.text" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Add repository"
}
}
}
},
"settings.column.active.text" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Active"
}
}
}
},
"settings.column.folder.text" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Folder"
}
}
}
},
"settings.column.name.text" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Name"
}
}
}
},
"settings.item.active.text" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : " "
}
}
}
},
"settings.tab-bar.repositories.text" : {
"localizations" : {
"en" : {

View File

@ -17,22 +17,32 @@ final class Repository {
@Attribute(.unique) var path: URL
var addedAt: Date
var sortOrder: Int
var active: Bool
// MARK: Initialisers
init(
_ path: URL,
sortOrder: Int,
active: Bool = true,
addedAt: Date = .now
) {
self.path = path
self.addedAt = addedAt
self.sortOrder = sortOrder
self.active = active
}
// MARK: Computed
@Transient var name: String { path.lastPathComponent }
@Transient var folder: String {
path.deletingLastPathComponent().relativePath
}
@Transient var name: String {
path.lastPathComponent
}
}
// MARK: - Identifiable
extension Repository: Identifiable {}

View File

@ -0,0 +1,37 @@
//
// RepositoriesViewModel.swift
// Piper ~ App
//
// Created by Javier Cicchelli on 21/10/2024.
// Copyright © 2024 Röck+Cöde. All rights reserved.
//
import Foundation
import Observation
import SwiftData
@Observable
final class RepositoriesViewModel {
// MARK: Properties
var fileImporterOpened: Bool = false
var rowsSelected: Set<Repository.ID> = []
// MARK: Functions
func addRepository(
_ result: Result<URL, any Error>,
into context: ModelContext
) {
do {
let url = try result.get()
let repository = Repository(url)
context.insert(repository)
} catch {
// TODO: Handle this error gracefully.
}
}
}

View File

@ -18,18 +18,16 @@ extension Repository {
let context = container.mainContext
context.insert(Repository(
URL(filePath: "/full/path/to/repository/name-0.git"),
sortOrder: 0
URL(filePath: "/full/path/to/repository/name-0.git")
))
context.insert(Repository(
URL(filePath: "/full/path/to/repository/name-1.git"),
sortOrder: 1
active: false
))
context.insert(Repository(
URL(filePath: "/full/path/to/repository/name-2.git"),
sortOrder: 2
URL(filePath: "/full/path/to/repository/name-2.git")
))
}

View File

@ -62,24 +62,15 @@ struct ItemLabelStyle: LabelStyle {
#Preview("List Item component") {
List {
ListItem(
repository: .init(
.init(filePath: "/full/path/to/repository/name.git")!,
sortOrder: 0
)
)
ListItem(
repository: .init(
.init(filePath: "/full/path/to/repository/name.git")!,
sortOrder: 0
)
)
ListItem(
repository: .init(
.init(filePath: "/full/path/to/repository/name.git")!,
sortOrder: 0
)
)
ListItem(repository: .init(
.init(filePath: "/full/path/to/repository/name.git")!
))
ListItem(repository: .init(
.init(filePath: "/full/path/to/repository/name.git")!
))
ListItem(repository: .init(
.init(filePath: "/full/path/to/repository/name.git")!
))
}
.frame(width: 400, height: 220)
}

View File

@ -0,0 +1,117 @@
//
// RepositoriesView.swift
// Piper ~ App
//
// Created by Javier Cicchelli on 14/10/2024.
// Copyright © 2024 Röck+Cöde. All rights reserved.
//
import SwiftData
import SwiftUI
struct RepositoriesView: View {
// MARK: Properties
@Environment(\.modelContext) private var modelContext
@Query(sort: \Repository.addedAt)
private var repositories: [Repository]
@State private var viewModel: RepositoriesViewModel = .init()
// MARK: Body
var body: some View {
HStack(
alignment: .top,
spacing: Layout.spacingHorizontal
) {
Table(
repositories,
selection: $viewModel.rowsSelected
) {
TableColumn("settings.column.active.text") { repository in
HStack {
Toggle("settings.item.active.text", isOn: .init {
repository.active
} set: {
repository.active = $0
})
}
}
TableColumn("settings.column.name.text") { repository in
Text(repository.name)
.fontWeight(.semibold)
.foregroundStyle(.primary)
}
TableColumn("settings.column.folder.text") { repository in
Text(repository.folder)
.fontWeight(.regular)
.foregroundStyle(.secondary)
}
}
VStack {
Button("settings.button.add-repository.text") {
viewModel.fileImporterOpened = true
}
.fileImporter(
isPresented: $viewModel.fileImporterOpened,
allowedContentTypes: [.pdf]
) {
viewModel.addRepository($0, into: modelContext)
}
}
}
}
}
// MARK: - Layout
private extension RepositoriesView {
enum Layout {
static let spacingHorizontal = CGFloat(16)
}
}
// MARK: - Previews
@available(macOS 15.0, *)
#Preview(
"Repositories view when no repositories found (macOS 15)",
traits: .emptyData
) {
RepositoriesView()
.scenePadding()
}
@available(macOS, obsoleted: 15)
#Preview("Repositories view when no repositories found (macOS 14)") {
MenuBarView()
.modelContainer(.preview)
.scenePadding()
}
@available(macOS 15.0, *)
#Preview(
"Repositories view when repositories found (macOS 15)",
traits: .sampleData
) {
RepositoriesView()
.scenePadding()
}
@available(macOS, obsoleted: 15)
#Preview("Repositories view when repositories found (macOS 14)") {
let container = ModelContainer.preview
Repository.samples(in: container)
return RepositoriesView()
.modelContainer(container)
.scenePadding()
}