This commit is contained in:
John Estropia
2026-07-15 11:50:43 +09:00
parent 7db1cfecfb
commit 890e150b95
135 changed files with 2895 additions and 2526 deletions
@@ -2,7 +2,6 @@
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import Combine
import CoreStore
import SwiftUI
@@ -22,19 +21,19 @@ extension Classic.ColorsDemo {
}
// MARK: UIViewControllerRepresentable
typealias UIViewControllerType = Classic.ColorsDemo.DetailViewController
func makeUIViewController(context: Self.Context) -> UIViewControllerType {
return UIViewControllerType(self.palette)
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Self.Context) {
uiViewController.palette = self.palette
}
static func dismantleUIViewController(_ uiViewController: UIViewControllerType, coordinator: Void) {}
func makeCoordinator() -> ObjectMonitor<Classic.ColorsDemo.Palette> {
@@ -48,32 +47,3 @@ extension Classic.ColorsDemo {
private let palette: ObjectMonitor<Classic.ColorsDemo.Palette>
}
}
#if DEBUG
struct _Demo_Classic_ColorsDemo_DetailView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
try! Classic.ColorsDemo.dataStack.perform(
synchronous: { transaction in
guard (try transaction.fetchCount(From<Modern.ColorsDemo.Palette>())) <= 0 else {
return
}
let palette = transaction.create(Into<Modern.ColorsDemo.Palette>())
palette.setRandomHue()
}
)
return Classic.ColorsDemo.DetailView(
Classic.ColorsDemo.dataStack.monitorObject(
Classic.ColorsDemo.palettesMonitor[0, 0]
)
)
}
}
#endif
@@ -55,35 +55,29 @@ extension Classic.ColorsDemo {
}
}
#if DEBUG
struct _Demo_Classic_ColorsDemo_ListView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
let minimumSamples = 10
try! Classic.ColorsDemo.dataStack.perform(
synchronous: { transaction in
// MARK: - Preview
let missing = minimumSamples
- (try transaction.fetchCount(From<Classic.ColorsDemo.Palette>()))
guard missing > 0 else {
return
}
for _ in 0..<missing {
let palette = transaction.create(Into<Classic.ColorsDemo.Palette>())
palette.setRandomHue()
}
#Preview {
let minimumSamples = 10
try! Classic.ColorsDemo.dataStack.perform(
synchronous: { transaction in
let missing = minimumSamples
- (try transaction.fetchCount(From<Classic.ColorsDemo.Palette>()))
guard missing > 0 else {
return
}
)
return Classic.ColorsDemo.ListView(
listMonitor: Classic.ColorsDemo.palettesMonitor,
onPaletteTapped: { _ in }
)
}
}
for _ in 0..<missing {
#endif
let palette = transaction.create(Into<Classic.ColorsDemo.Palette>())
palette.setRandomHue()
}
}
)
return Classic.ColorsDemo.ListView(
listMonitor: Classic.ColorsDemo.palettesMonitor,
onPaletteTapped: { _ in }
)
}
@@ -3,6 +3,7 @@
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import Observation
import SwiftUI
// MARK: - Classic.ColorsDemo
@@ -12,97 +13,85 @@ extension Classic.ColorsDemo {
// MARK: - Classic.ColorsDemo.MainView
struct MainView: View {
// MARK: Internal
init() {
let listMonitor = Classic.ColorsDemo.palettesMonitor
self.listMonitor = listMonitor
self.listHelper = .init(listMonitor: listMonitor)
self._listHelper = State(initialValue: .init(listMonitor: listMonitor))
self._filter = Binding(
get: { Classic.ColorsDemo.filter },
set: { Classic.ColorsDemo.filter = $0 }
)
}
// MARK: View
var body: some View {
let detailView: AnyView
if let selectedObject = self.listHelper.selectedObject() {
detailView = AnyView(
Classic.ColorsDemo.DetailView(selectedObject)
VStack(spacing: 0) {
Classic.ColorsDemo.ListView(
listMonitor: self.listMonitor,
onPaletteTapped: {
self.listHelper.setSelectedPalette($0)
}
)
}
else {
detailView = AnyView(EmptyView())
}
let listMonitor = self.listMonitor
return VStack(spacing: 0) {
Classic.ColorsDemo.ListView
.init(
listMonitor: listMonitor,
onPaletteTapped: {
self.listHelper.setSelectedPalette($0)
}
)
.navigationBarTitle(
Text("Colors (\(self.listHelper.count) objects)")
)
.frame(minHeight: 0, maxHeight: .infinity)
.edgesIgnoringSafeArea(.vertical)
detailView
.edgesIgnoringSafeArea(.all)
.frame(minHeight: 0, maxHeight: .infinity)
}
.navigationBarItems(
leading: HStack {
EditButton()
Button(
action: { self.clearColors() },
label: { Text("Clear") }
)
},
trailing: HStack {
Button(
action: { self.changeFilter() },
label: { Text(self.filter.rawValue) }
)
Button(
action: { self.shuffleColors() },
label: { Text("Shuffle") }
)
Button(
action: { self.addColor() },
label: { Text("Add") }
)
.frame(minHeight: 0, maxHeight: .infinity)
.ignoresSafeArea(.container, edges: .vertical)
if let selectedObject = self.listHelper.selectedObject() {
Classic.ColorsDemo.DetailView(selectedObject)
.ignoresSafeArea()
.frame(minHeight: 0, maxHeight: .infinity)
}
)
}
.navigationTitle("Colors (\(self.listHelper.count) objects)")
.toolbar {
ToolbarItemGroup(placement: .topBarLeading) {
EditButton()
Button("Clear") {
self.clearColors()
}
}
ToolbarItemGroup(placement: .topBarTrailing) {
Button(self.filter.rawValue) {
self.changeFilter()
}
Button("Shuffle") {
self.shuffleColors()
}
Button("Add") {
self.addColor()
}
}
}
}
// MARK: Private
private let listMonitor: ListMonitor<Classic.ColorsDemo.Palette>
@ObservedObject
@State
private var listHelper: ListHelper
@Binding
private var filter: Classic.ColorsDemo.Filter
private func changeFilter() {
Classic.ColorsDemo.filter = Classic.ColorsDemo.filter.next()
}
private func clearColors() {
Classic.ColorsDemo.dataStack.perform(
asynchronous: { transaction in
@@ -111,7 +100,7 @@ extension Classic.ColorsDemo {
completion: { _ in }
)
}
private func addColor() {
Classic.ColorsDemo.dataStack.perform(
@@ -122,7 +111,7 @@ extension Classic.ColorsDemo {
completion: { _ in }
)
}
private func shuffleColors() {
Classic.ColorsDemo.dataStack.perform(
@@ -136,112 +125,93 @@ extension Classic.ColorsDemo {
completion: { _ in }
)
}
// MARK: - Classic.ColorsDemo.MainView.ListHelper
fileprivate final class ListHelper: ObservableObject, ListObjectObserver {
@MainActor
@Observable
fileprivate final class ListHelper: ListObjectObserver {
// MARK: FilePrivate
fileprivate private(set) var count: Int = 0
fileprivate init(listMonitor: ListMonitor<Classic.ColorsDemo.Palette>) {
listMonitor.addObserver(self)
self.count = listMonitor.numberOfObjects()
}
fileprivate func selectedObject() -> ObjectMonitor<Classic.ColorsDemo.Palette>? {
return self.selectedPalette.flatMap {
self.selectedPalette.flatMap {
guard !$0.isDeleted else {
return nil
}
return Classic.ColorsDemo.dataStack.monitorObject($0)
}
}
fileprivate func setSelectedPalette(_ palette: Classic.ColorsDemo.Palette?) {
guard self.selectedPalette != palette else {
return
}
self.objectWillChange.send()
if let palette = palette, !palette.isDeleted {
if let palette, !palette.isDeleted {
self.selectedPalette = palette
}
else {
self.selectedPalette = nil
}
}
// MARK: ListObserver
typealias ListEntityType = Classic.ColorsDemo.Palette
func listMonitorDidChange(_ monitor: ListMonitor<Classic.ColorsDemo.Palette>) {
self.objectWillChange.send()
self.count = monitor.numberOfObjects()
}
func listMonitorDidRefetch(_ monitor: ListMonitor<ListEntityType>) {
self.objectWillChange.send()
self.count = monitor.numberOfObjects()
}
// MARK: ListObjectObserver
func listMonitor(_ monitor: ListMonitor<Classic.ColorsDemo.Palette>, didDeleteObject object: Classic.ColorsDemo.Palette, fromIndexPath indexPath: IndexPath) {
if self.selectedPalette == object {
self.setSelectedPalette(nil)
// MARK: ListObserver
typealias ListEntityType = Classic.ColorsDemo.Palette
nonisolated func listMonitorDidChange(_ monitor: ListMonitor<Classic.ColorsDemo.Palette>) {
let count = monitor.numberOfObjects()
Task { @MainActor in
self.count = count
}
}
nonisolated func listMonitorDidRefetch(_ monitor: ListMonitor<ListEntityType>) {
let count = monitor.numberOfObjects()
Task { @MainActor in
self.count = count
}
}
// MARK: ListObjectObserver
nonisolated func listMonitor(
_ monitor: ListMonitor<Classic.ColorsDemo.Palette>,
didDeleteObject object: Classic.ColorsDemo.Palette,
fromIndexPath indexPath: IndexPath
) {
let deletedObjectURI = object.objectID.uriRepresentation()
Task { @MainActor in
if self.selectedPalette?.objectID.uriRepresentation() == deletedObjectURI {
self.setSelectedPalette(nil)
}
}
}
// MARK: Private
private var selectedPalette: Classic.ColorsDemo.Palette?
}
}
}
#if DEBUG
struct _Demo_Classic_ColorsDemo_MainView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
let minimumSamples = 10
try! Classic.ColorsDemo.dataStack.perform(
synchronous: { transaction in
let missing = minimumSamples
- (try transaction.fetchCount(From<Classic.ColorsDemo.Palette>()))
guard missing > 0 else {
return
}
for _ in 0..<missing {
let palette = transaction.create(Into<Classic.ColorsDemo.Palette>())
palette.setRandomHue()
}
}
)
return Classic.ColorsDemo.MainView()
}
}
#endif
@@ -38,6 +38,7 @@ extension Classic {
return dataStack
}()
@MainActor
static let palettesMonitor: ListMonitor<Classic.ColorsDemo.Palette> = Classic.ColorsDemo.dataStack.monitorSectionedList(
From<Classic.ColorsDemo.Palette>()
.sectionBy(\.colorGroup)
@@ -45,6 +46,7 @@ extension Classic {
.orderBy(.ascending(\.hue))
)
@MainActor
static var filter: Classic.ColorsDemo.Filter = .all {
didSet {