Skip to content

Commit

Permalink
Compendium Document-related improvements in filtering & migration
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomvis committed Aug 31, 2023
1 parent 183a4a4 commit 989a3d3
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 145 deletions.
5 changes: 5 additions & 0 deletions App/App/Compendium/CompendiumImportFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1309,10 +1309,15 @@ enum CompendiumMetadataKey: DependencyKey {
public static var previewValue: CompendiumMetadata {
CompendiumMetadata {
[
CompendiumSourceDocument.srd5_1,
CompendiumSourceDocument.unspecifiedCore,
CompendiumSourceDocument.homebrew,
.init(id: Tagged("tob1"), displayName: "Tome of Beasts 1", realmId: Tagged("kp"))
]
} realms: {
[
CompendiumRealm.core,
CompendiumRealm.homebrew,
.init(id: Tagged("kp"), displayName: "Kobold Press")
]
} putRealm: { _ in
Expand Down
212 changes: 85 additions & 127 deletions App/App/Compendium/View/CompendiumFilterSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,79 +32,7 @@ struct CompendiumFilterSheet: View {
NavigationStack {
ScrollView {
VStack {
SectionContainer {
LabeledContent {
Menu {
if let realmsWithDocuments = viewStore.state.realmsWithDocuments.value {
let selection = viewStore.state.current.selectedSource

Button(action: {
viewStore.send(.selectedSource(nil))
}, label: {
Label("All", systemImage: sourcesMenuIcon(selection, nil, nil))
})

ForEach(realmsWithDocuments) { realm in
Menu {
Button(action: {
viewStore.send(
.selectedSource(
CompendiumFilterSheetState.Values.SelectedSource(
realmId: realm.realm.id,
documentId: nil,
displayName: realm.realm.displayName
)
)
)
}, label: {
Label("All", systemImage: sourcesMenuIcon(selection, realm.id, nil, true))
})

Divider()

ForEach(realm.documents) { document in
Button(action: {
viewStore.send(
.selectedSource(
CompendiumFilterSheetState.Values.SelectedSource(
realmId: realm.id,
documentId: document.id,
displayName: document.displayName
)
)
)
}, label: {
Label(document.displayName, systemImage: sourcesMenuIcon(selection, realm.id, document.id))
})
}
} label: {
Label(realm.realm.displayName, systemImage: sourcesMenuIcon(selection, realm.id, nil))
}
}
} else {
Text("Loading...")
}
} label: {
HStack(spacing: 4) {
if let selectedSource = viewStore.state.current.selectedSource {
Text(selectedSource.displayName)
} else {
Text("All")
}

Image(systemName: "chevron.up.chevron.down")
.font(.footnote)
}
.fontWeight(.regular)
.padding(.trailing, 12)
}
.frame(minHeight: 35)
} label: {
Text("Sources")
}
}
.bold()
.padding(8)
sourcesSection

SectionContainer {
LabeledContent {
Expand Down Expand Up @@ -191,10 +119,66 @@ struct CompendiumFilterSheet: View {
}
}
.onAppear {
viewStore.send(.realmsWithDocuments(.startLoading))
viewStore.send(.allSources(.startLoading))
}
}

@ViewBuilder
var sourcesSection: some View {
SectionContainer {
LabeledContent {
Menu {
Button("All") {
viewStore.send(.source(nil))
}

if let allSources = viewStore.state.allSources.value {
ForEach(allSources, id: \.document.id) { source in

// Add divider between realms
if needsDividerBefore(source, in: allSources) {
Divider()
}

Button(action: {
viewStore.send(.source(source))
}) {
if viewStore.state.current.source == source {
Label(source.document.displayName, systemImage: "checkmark")
} else {
Text(source.document.displayName)
}
if source.document.displayName != source.realm.displayName {
Text(source.realm.displayName)
}
}
}
} else {
Text("Loading...")
}
} label: {
HStack(spacing: 4) {
if let source = viewStore.state.current.source {
Text(source.document.displayName)
} else {
Text("All")
}

Image(systemName: "chevron.up.chevron.down")
.font(.footnote)
}
.fontWeight(.regular)
.padding(.trailing, 12)
}
.frame(minHeight: 35)
} label: {
Text("Sources")
}
}
.bold()
.padding(8)
}

func onEditingChanged(_ filter: CompendiumFilterSheetState.Filter) -> (Bool) -> Void {
return { b in
self.viewStore.send(.editing(filter, b))
Expand All @@ -217,41 +201,27 @@ struct CompendiumFilterSheet: View {
AnyView(self.eraseToAnyView)
}

func sourcesMenuIcon(
_ selection: CompendiumFilterSheetState.Values.SelectedSource?,
_ realmId: CompendiumRealm.Id?,
_ documentId: CompendiumSourceDocument.Id?,
_ allInRealmItem: Bool = false
) -> String {
let fullMatch = "checkmark.square"
let partialMatch = "minus.square"
let noMatch = "square"

guard let selection else {
return realmId == nil && documentId == nil ? fullMatch : noMatch
func needsDividerBefore(
_ source: CompendiumFilters.Source,
in sources: [CompendiumFilters.Source]
) -> Bool {
guard let idx = sources.firstIndex(where: { $0.document == source.document }) else {
return false
}

if selection.realmId == realmId {
if documentId == nil && selection.documentId != nil {
return allInRealmItem ? noMatch : partialMatch
}
return selection.documentId == documentId ? fullMatch : noMatch
}

return noMatch
return idx == 0 || sources[idx-1].realm != source.realm
}
}

struct CompendiumFilterSheetState: Equatable {
typealias AsyncRealmsWithDocuments = Async<[RealmWithDocuments], Error>
typealias AsyncSources = Async<[CompendiumFilters.Source], Error>

let challengeRatings = crToXpMapping.keys.sorted()
let allAllowedItemTypes: [CompendiumItemType]

let initial: Values
var current: Values

var realmsWithDocuments: AsyncRealmsWithDocuments = .initial
var allSources: AsyncSources = .initial

init() {
self.allAllowedItemTypes = CompendiumItemType.allCases
Expand All @@ -260,18 +230,11 @@ struct CompendiumFilterSheetState: Equatable {
}

struct Values: Equatable {
var selectedSource: SelectedSource?
var source: CompendiumFilters.Source?
var itemType: CompendiumItemType?
var minMonsterCR: Fraction?
var maxMonsterCR: Fraction?
var monsterType: MonsterType?

struct SelectedSource: Equatable {
var realmId: CompendiumRealm.Id
var documentId: CompendiumSourceDocument.Id?

var displayName: String
}
}

var compatibleFilters: [Filter] {
Expand All @@ -289,6 +252,7 @@ struct CompendiumFilterSheetState: Equatable {
var effectiveCurrentValues: Values {
let filters = compatibleFilters
return Values(
source: current.source,
itemType: current.itemType,
minMonsterCR: filters.contains(.minMonsterCR) ? current.minMonsterCR : nil,
maxMonsterCR: filters.contains(.maxMonsterCR) ? current.maxMonsterCR : nil,
Expand All @@ -297,19 +261,10 @@ struct CompendiumFilterSheetState: Equatable {
}

typealias Filter = CompendiumFilters.Property

struct RealmWithDocuments: Equatable, Identifiable {
let realm: CompendiumRealm
let documents: [CompendiumSourceDocument]

var id: CompendiumRealm.Id { realm.id }
}
}

extension CompendiumSourceDocument: Identifiable { }

enum CompendiumFilterSheetAction {
case selectedSource(CompendiumFilterSheetState.Values.SelectedSource?)
case source(CompendiumFilters.Source?)
case itemType(CompendiumItemType?)
case minMonsterCR(Double)
case maxMonsterCR(Double)
Expand All @@ -318,7 +273,7 @@ enum CompendiumFilterSheetAction {
case clear(CompendiumFilterSheetState.Filter)
case clearAll

case realmsWithDocuments(CompendiumFilterSheetState.AsyncRealmsWithDocuments.Action)
case allSources(CompendiumFilterSheetState.AsyncSources.Action)
}

typealias CompendiumFilterSheetEnvironment = EnvironmentWithCompendiumMetadata
Expand Down Expand Up @@ -395,8 +350,8 @@ extension CompendiumFilterSheetState {
state.minMonsterCrDouble = v
case .maxMonsterCR(let v):
state.maxMonsterCrDouble = v
case .selectedSource(let s):
state.current.selectedSource = s
case .source(let s):
state.current.source = s
case .monsterType(let t):
state.monsterType = t
case .editing(.minMonsterCR, false):
Expand All @@ -420,25 +375,28 @@ extension CompendiumFilterSheetState {
return Filter.allCases.publisher.map { f in
.clear(f)
}.eraseToEffect()
case .realmsWithDocuments: break // handled below
case .allSources: break // handled below
}
return .none
},
AsyncRealmsWithDocuments.reducer { (env: EnvironmentWithCompendiumMetadata) in
AsyncSources.reducer { (env: EnvironmentWithCompendiumMetadata) in
do {
let realms = try env.compendiumMetadata.realms()
let realms: [CompendiumRealm.Id: CompendiumRealm] = try env.compendiumMetadata.realms().reduce(into: [:]) { partialResult, realm in
partialResult[realm.id] = realm
}
let documents = try env.compendiumMetadata.sourceDocuments()
let realmsWithDocuments = realms.map { realm in
RealmWithDocuments(
let allSources: [CompendiumFilters.Source] = documents.compactMap { document -> CompendiumFilters.Source? in
guard let realm = realms[document.realmId] else { return nil }
return CompendiumFilters.Source(
realm: realm,
documents: documents.filter { $0.realmId == realm.id }
document: document
)
}
return Just(realmsWithDocuments).setFailureType(to: Swift.Error.self).eraseToAnyPublisher()
}.sorted(by: .combine([.init(\.realm.id), .init(\.document.id)]))
return Just(allSources).setFailureType(to: Swift.Error.self).eraseToAnyPublisher()
} catch {
return Fail(error: error).eraseToAnyPublisher()
}
}.pullback(state: \.realmsWithDocuments, action: /CompendiumFilterSheetAction.realmsWithDocuments, environment: { $0 })
}.pullback(state: \.allSources, action: /CompendiumFilterSheetAction.allSources, environment: { $0 })
)
}

Expand Down
2 changes: 2 additions & 0 deletions App/App/Compendium/View/CompendiumIndexView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ struct FilterButton: View {

self.sheet = CompendiumFilterSheet(store: Store(initialState: state, reducer: CompendiumFilterSheetState.reducer, environment: self.env)) { filterValues in
var filters = self.viewStore.state.filters ?? .init()
filters.source = filterValues.source
filters.types = filterValues.itemType.optionalArray
filters.minMonsterChallengeRating = filterValues.minMonsterCR
filters.maxMonsterChallengeRating = filterValues.maxMonsterCR
Expand Down Expand Up @@ -659,6 +660,7 @@ extension CompendiumItem {
fileprivate extension CompendiumFilterSheetState {
init(_ queryFilters: CompendiumFilters?, allAllowedItemTypes: [CompendiumItemType]) {
let values = Values(
source: queryFilters?.source,
itemType: queryFilters?.types?.single,
minMonsterCR: queryFilters?.minMonsterChallengeRating,
maxMonsterCR: queryFilters?.maxMonsterChallengeRating,
Expand Down
4 changes: 2 additions & 2 deletions App/App/Encouter/AddCombatantState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ struct AddCombatantState: Equatable {
definition.item,
origin: .created(.init(definition.item)),
document: .init(
id: CompendiumSourceDocument.unknownCore.id,
displayName: CompendiumSourceDocument.unknownCore.displayName
id: CompendiumSourceDocument.unspecifiedCore.id,
displayName: CompendiumSourceDocument.unspecifiedCore.displayName
)
)
}
Expand Down
13 changes: 13 additions & 0 deletions Sources/Compendium/Compendium.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,21 @@ public struct Order: Equatable {
}

public struct CompendiumFilters: Equatable {
public var source: Source?
public var types: [CompendiumItemType]?

public var minMonsterChallengeRating: Fraction? = nil
public var maxMonsterChallengeRating: Fraction? = nil
public var monsterType: MonsterType? = nil

public init(
source: Source? = nil,
types: [CompendiumItemType]? = nil,
minMonsterChallengeRating: Fraction? = nil,
maxMonsterChallengeRating: Fraction? = nil,
monsterType: MonsterType? = nil
) {
self.source = source
self.types = types
self.minMonsterChallengeRating = minMonsterChallengeRating
self.maxMonsterChallengeRating = maxMonsterChallengeRating
Expand All @@ -73,6 +76,16 @@ public struct CompendiumFilters: Equatable {
case maxMonsterCR
case monsterType
}

public struct Source: Hashable {
public var realm: CompendiumRealm
public var document: CompendiumSourceDocument

public init(realm: CompendiumRealm, document: CompendiumSourceDocument) {
self.realm = realm
self.document = document
}
}
}

public enum ReferenceResolveResult {
Expand Down
2 changes: 1 addition & 1 deletion Sources/GameModels/CompendiumImportJob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public struct CompendiumImportJob: Hashable, Codable {
public let sourceVersion: String?
public let documentId: CompendiumSourceDocument.Id

public let timestamp: Date
public var timestamp: Date
public let uuid: UUID

public var id: Id {
Expand Down
Loading

0 comments on commit 989a3d3

Please sign in to comment.