Complete Challenge 3: Implementing filtering and sorting for the pads list.
This commit is contained in:
parent
1fc29fc8a1
commit
cfc1135f0e
|
@ -41,6 +41,8 @@
|
||||||
F3596BC923E3F72200FA2C66 /* Cache+Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3596BC823E3F72200FA2C66 /* Cache+Entry.swift */; };
|
F3596BC923E3F72200FA2C66 /* Cache+Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3596BC823E3F72200FA2C66 /* Cache+Entry.swift */; };
|
||||||
F3596BCB23E4298300FA2C66 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3596BCA23E4298300FA2C66 /* RootView.swift */; };
|
F3596BCB23E4298300FA2C66 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3596BCA23E4298300FA2C66 /* RootView.swift */; };
|
||||||
F3596BCD23E42BED00FA2C66 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3596BCC23E42BEC00FA2C66 /* WelcomeView.swift */; };
|
F3596BCD23E42BED00FA2C66 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3596BCC23E42BEC00FA2C66 /* WelcomeView.swift */; };
|
||||||
|
F3596BCF23E4756500FA2C66 /* PadsListView+ViewModel+SortMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3596BCE23E4756500FA2C66 /* PadsListView+ViewModel+SortMode.swift */; };
|
||||||
|
F3596BD123E475A700FA2C66 /* PadsListView+ViewModel+ActivityTypeFilterMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3596BD023E475A700FA2C66 /* PadsListView+ViewModel+ActivityTypeFilterMode.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
@ -77,6 +79,8 @@
|
||||||
F3596BC823E3F72200FA2C66 /* Cache+Entry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Cache+Entry.swift"; sourceTree = "<group>"; };
|
F3596BC823E3F72200FA2C66 /* Cache+Entry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Cache+Entry.swift"; sourceTree = "<group>"; };
|
||||||
F3596BCA23E4298300FA2C66 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
|
F3596BCA23E4298300FA2C66 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
|
||||||
F3596BCC23E42BEC00FA2C66 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
|
F3596BCC23E42BEC00FA2C66 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
|
||||||
|
F3596BCE23E4756500FA2C66 /* PadsListView+ViewModel+SortMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PadsListView+ViewModel+SortMode.swift"; sourceTree = "<group>"; };
|
||||||
|
F3596BD023E475A700FA2C66 /* PadsListView+ViewModel+ActivityTypeFilterMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PadsListView+ViewModel+ActivityTypeFilterMode.swift"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -225,6 +229,8 @@
|
||||||
F32ED61B23DF9B36006A5195 /* Pad Details */,
|
F32ED61B23DF9B36006A5195 /* Pad Details */,
|
||||||
F331C49B23DE18650061925E /* PadsListView.swift */,
|
F331C49B23DE18650061925E /* PadsListView.swift */,
|
||||||
F331C49D23DE187A0061925E /* PadsListView+ViewModel.swift */,
|
F331C49D23DE187A0061925E /* PadsListView+ViewModel.swift */,
|
||||||
|
F3596BCE23E4756500FA2C66 /* PadsListView+ViewModel+SortMode.swift */,
|
||||||
|
F3596BD023E475A700FA2C66 /* PadsListView+ViewModel+ActivityTypeFilterMode.swift */,
|
||||||
);
|
);
|
||||||
path = Pads;
|
path = Pads;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -363,6 +369,7 @@
|
||||||
F32ED62923E0F0C6006A5195 /* MapSnapshottingService.swift in Sources */,
|
F32ED62923E0F0C6006A5195 /* MapSnapshottingService.swift in Sources */,
|
||||||
F3596BCB23E4298300FA2C66 /* RootView.swift in Sources */,
|
F3596BCB23E4298300FA2C66 /* RootView.swift in Sources */,
|
||||||
F32ED61F23DF9B7C006A5195 /* PadDetailsView+ViewModel.swift in Sources */,
|
F32ED61F23DF9B7C006A5195 /* PadDetailsView+ViewModel.swift in Sources */,
|
||||||
|
F3596BCF23E4756500FA2C66 /* PadsListView+ViewModel+SortMode.swift in Sources */,
|
||||||
F331C49C23DE18650061925E /* PadsListView.swift in Sources */,
|
F331C49C23DE18650061925E /* PadsListView.swift in Sources */,
|
||||||
F3596BC723E3F71500FA2C66 /* Cache+WrappedKey.swift in Sources */,
|
F3596BC723E3F71500FA2C66 /* Cache+WrappedKey.swift in Sources */,
|
||||||
F331C48923DDDF0E0061925E /* Pad.swift in Sources */,
|
F331C48923DDDF0E0061925E /* Pad.swift in Sources */,
|
||||||
|
@ -379,6 +386,7 @@
|
||||||
F331C49323DDF0CF0061925E /* Endpoint+LaunchLibraryAPI.swift in Sources */,
|
F331C49323DDF0CF0061925E /* Endpoint+LaunchLibraryAPI.swift in Sources */,
|
||||||
F3596BC923E3F72200FA2C66 /* Cache+Entry.swift in Sources */,
|
F3596BC923E3F72200FA2C66 /* Cache+Entry.swift in Sources */,
|
||||||
F331C48E23DDE6C20061925E /* LaunchLibraryAPIServicing.swift in Sources */,
|
F331C48E23DDE6C20061925E /* LaunchLibraryAPIServicing.swift in Sources */,
|
||||||
|
F3596BD123E475A700FA2C66 /* PadsListView+ViewModel+ActivityTypeFilterMode.swift in Sources */,
|
||||||
F331C45E23DDB0AE0061925E /* SceneDelegate.swift in Sources */,
|
F331C45E23DDB0AE0061925E /* SceneDelegate.swift in Sources */,
|
||||||
F32ED61A23DF91C1006A5195 /* Pad+PadType.swift in Sources */,
|
F32ED61A23DF91C1006A5195 /* Pad+PadType.swift in Sources */,
|
||||||
F3596BC423E3EE6800FA2C66 /* Cache.swift in Sources */,
|
F3596BC423E3EE6800FA2C66 /* Cache.swift in Sources */,
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// PadsListView+ViewModel+ActivityTypeFilterMode.swift
|
||||||
|
// PadFinder
|
||||||
|
//
|
||||||
|
// Created by CypherPoet on 1/31/20.
|
||||||
|
// ✌️
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
extension PadsListView.ViewModel {
|
||||||
|
enum ActivityTypeFilterMode: String {
|
||||||
|
case all
|
||||||
|
case activePads
|
||||||
|
case retiredPads
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PadsListView.ViewModel.ActivityTypeFilterMode: CaseIterable {}
|
||||||
|
|
||||||
|
extension PadsListView.ViewModel.ActivityTypeFilterMode: Identifiable {
|
||||||
|
var id: String { self.rawValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Computeds
|
||||||
|
extension PadsListView.ViewModel.ActivityTypeFilterMode {
|
||||||
|
var displayName: String {
|
||||||
|
switch self {
|
||||||
|
case .all:
|
||||||
|
return "All"
|
||||||
|
case .activePads:
|
||||||
|
return "Active Pads"
|
||||||
|
case .retiredPads:
|
||||||
|
return "Retired Pads"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// PadsListView+ViewModel+SortingMode.swift
|
||||||
|
// PadFinder
|
||||||
|
//
|
||||||
|
// Created by CypherPoet on 1/31/20.
|
||||||
|
// ✌️
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
extension PadsListView.ViewModel {
|
||||||
|
enum SortMode: String {
|
||||||
|
case alphanumericAscending
|
||||||
|
case westToEast
|
||||||
|
case northToSouth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PadsListView.ViewModel.SortMode: CaseIterable {}
|
||||||
|
|
||||||
|
extension PadsListView.ViewModel.SortMode: Identifiable {
|
||||||
|
var id: String { self.rawValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Computeds
|
||||||
|
extension PadsListView.ViewModel.SortMode {
|
||||||
|
var displayName: String {
|
||||||
|
switch self {
|
||||||
|
case .alphanumericAscending:
|
||||||
|
return "A-Z"
|
||||||
|
case .westToEast:
|
||||||
|
return "West-East"
|
||||||
|
case .northToSouth:
|
||||||
|
return "North-South"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,8 +17,11 @@ extension PadsListView {
|
||||||
|
|
||||||
private let padsState: PadsState
|
private let padsState: PadsState
|
||||||
|
|
||||||
// MARK: - Published Outputs
|
|
||||||
|
|
||||||
|
// MARK: - Published Outputs
|
||||||
|
@Published var sortingMode: SortMode = .alphanumericAscending
|
||||||
|
@Published var activityTypeFilteringMode: ActivityTypeFilterMode = .all
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
init(
|
init(
|
||||||
|
@ -40,13 +43,37 @@ extension PadsListView.ViewModel {
|
||||||
// MARK: - Computeds
|
// MARK: - Computeds
|
||||||
extension PadsListView.ViewModel {
|
extension PadsListView.ViewModel {
|
||||||
|
|
||||||
var pads: [Pad] {
|
private var pads: [Pad] {
|
||||||
if case let .fetched(pads) = padsState.dataFetchingState {
|
if case let .fetched(pads) = padsState.dataFetchingState {
|
||||||
return pads
|
return pads
|
||||||
} else {
|
} else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var sortedPads: [Pad] {
|
||||||
|
switch sortingMode {
|
||||||
|
case .alphanumericAscending:
|
||||||
|
return pads.sorted { $0.name < $1.name }
|
||||||
|
case .westToEast:
|
||||||
|
return pads.sorted { $0.longitude < $1.longitude }
|
||||||
|
case .northToSouth:
|
||||||
|
return pads.sorted { $0.latitude < $1.latitude }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var displayedPads: [Pad] {
|
||||||
|
switch activityTypeFilteringMode {
|
||||||
|
case .all:
|
||||||
|
return sortedPads
|
||||||
|
case .activePads:
|
||||||
|
return sortedPads.filter { $0.isRetired == false }
|
||||||
|
case .retiredPads:
|
||||||
|
return sortedPads.filter { $0.isRetired }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,7 +82,6 @@ extension PadsListView.ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Private Helpers
|
// MARK: - Private Helpers
|
||||||
private extension PadsListView.ViewModel {
|
private extension PadsListView.ViewModel {
|
||||||
|
|
||||||
|
|
|
@ -19,19 +19,31 @@ struct PadsListView<Destination: View> {
|
||||||
extension PadsListView: View {
|
extension PadsListView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List(viewModel.pads) { pad in
|
List {
|
||||||
NavigationLink(destination: self.buildDestination(pad)) {
|
Section(header: Text("Sorting").font(.headline)) {
|
||||||
HStack {
|
sortingPicker
|
||||||
pad.padType.listItemImage
|
.padding()
|
||||||
|
}
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text(pad.name)
|
Section(header: Text("Filters").font(.headline)) {
|
||||||
.foregroundColor(.primary)
|
activityTypeFilterPicker
|
||||||
.font(.headline)
|
.padding()
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEach(viewModel.displayedPads) { pad in
|
||||||
|
NavigationLink(destination: self.buildDestination(pad)) {
|
||||||
|
HStack {
|
||||||
|
pad.padType.listItemImage
|
||||||
|
|
||||||
Text(pad.padType.displayName)
|
VStack(alignment: .leading) {
|
||||||
.font(.subheadline)
|
Text(pad.name)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.primary)
|
||||||
|
.font(.headline)
|
||||||
|
|
||||||
|
Text(pad.padType.displayName)
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,6 +59,30 @@ extension PadsListView {
|
||||||
|
|
||||||
// MARK: - View Variables
|
// MARK: - View Variables
|
||||||
extension PadsListView {
|
extension PadsListView {
|
||||||
|
|
||||||
|
private var sortingPicker: some View {
|
||||||
|
Picker("Sorting Mode", selection: $viewModel.sortingMode) {
|
||||||
|
ForEach(ViewModel.SortMode.allCases) { sortMode in
|
||||||
|
Text(sortMode.displayName).tag(sortMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pickerStyle(SegmentedPickerStyle())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var activityTypeFilterPicker: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Pad Activity Type").font(.headline)
|
||||||
|
|
||||||
|
Picker("Pad Activity Type Filtering Mode", selection: $viewModel.activityTypeFilteringMode) {
|
||||||
|
ForEach(ViewModel.ActivityTypeFilterMode.allCases) { filterMode in
|
||||||
|
Text(filterMode.displayName).tag(filterMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.labelsHidden()
|
||||||
|
.pickerStyle(SegmentedPickerStyle())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,10 +98,14 @@ struct PadsListView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let store = PreviewData.AppStores.withPads
|
let store = PreviewData.AppStores.withPads
|
||||||
|
|
||||||
return PadsListView(
|
return NavigationView {
|
||||||
viewModel: .init(padsState: store.state.padsState),
|
PadsListView(
|
||||||
buildDestination: { _ in EmptyView() }
|
viewModel: .init(padsState: store.state.padsState),
|
||||||
)
|
buildDestination: { _ in EmptyView() }
|
||||||
|
)
|
||||||
|
.navigationBarTitle("Launch Pads")
|
||||||
|
}
|
||||||
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
.environmentObject(store)
|
.environmentObject(store)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue