Add functionality for updating the favorites state of a pad
This commit is contained in:
parent
8ba7a51628
commit
ad1f1b1420
|
@ -16,6 +16,7 @@
|
||||||
F32ED62623E0D525006A5195 /* Pad+Computeds.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED62523E0D525006A5195 /* Pad+Computeds.swift */; };
|
F32ED62623E0D525006A5195 /* Pad+Computeds.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED62523E0D525006A5195 /* Pad+Computeds.swift */; };
|
||||||
F32ED62923E0F0C6006A5195 /* MapSnapshottingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED62823E0F0C6006A5195 /* MapSnapshottingService.swift */; };
|
F32ED62923E0F0C6006A5195 /* MapSnapshottingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED62823E0F0C6006A5195 /* MapSnapshottingService.swift */; };
|
||||||
F32ED62B23E0F0F1006A5195 /* MapSnapshotServicing.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED62A23E0F0F1006A5195 /* MapSnapshotServicing.swift */; };
|
F32ED62B23E0F0F1006A5195 /* MapSnapshotServicing.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED62A23E0F0F1006A5195 /* MapSnapshotServicing.swift */; };
|
||||||
|
F32ED62E23E163BB006A5195 /* CypherPoetPropertyWrappers in Frameworks */ = {isa = PBXBuildFile; productRef = F32ED62D23E163BB006A5195 /* CypherPoetPropertyWrappers */; };
|
||||||
F331C45C23DDB0AE0061925E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C45B23DDB0AE0061925E /* AppDelegate.swift */; };
|
F331C45C23DDB0AE0061925E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C45B23DDB0AE0061925E /* AppDelegate.swift */; };
|
||||||
F331C45E23DDB0AE0061925E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C45D23DDB0AE0061925E /* SceneDelegate.swift */; };
|
F331C45E23DDB0AE0061925E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C45D23DDB0AE0061925E /* SceneDelegate.swift */; };
|
||||||
F331C46223DDB0B00061925E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F331C46123DDB0B00061925E /* Assets.xcassets */; };
|
F331C46223DDB0B00061925E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F331C46123DDB0B00061925E /* Assets.xcassets */; };
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
F331C49823DE10010061925E /* PreviewData+AppStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C49723DE10010061925E /* PreviewData+AppStore.swift */; };
|
F331C49823DE10010061925E /* PreviewData+AppStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C49723DE10010061925E /* PreviewData+AppStore.swift */; };
|
||||||
F331C49C23DE18650061925E /* PadsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C49B23DE18650061925E /* PadsListView.swift */; };
|
F331C49C23DE18650061925E /* PadsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C49B23DE18650061925E /* PadsListView.swift */; };
|
||||||
F331C49E23DE187A0061925E /* PadsListView+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C49D23DE187A0061925E /* PadsListView+ViewModel.swift */; };
|
F331C49E23DE187A0061925E /* PadsListView+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C49D23DE187A0061925E /* PadsListView+ViewModel.swift */; };
|
||||||
|
F356E61E23E25E7A008553B0 /* PadDetailsContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F356E61D23E25E7A008553B0 /* PadDetailsContainerView.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
@ -66,6 +68,7 @@
|
||||||
F331C49723DE10010061925E /* PreviewData+AppStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PreviewData+AppStore.swift"; sourceTree = "<group>"; };
|
F331C49723DE10010061925E /* PreviewData+AppStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PreviewData+AppStore.swift"; sourceTree = "<group>"; };
|
||||||
F331C49B23DE18650061925E /* PadsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadsListView.swift; sourceTree = "<group>"; };
|
F331C49B23DE18650061925E /* PadsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadsListView.swift; sourceTree = "<group>"; };
|
||||||
F331C49D23DE187A0061925E /* PadsListView+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PadsListView+ViewModel.swift"; sourceTree = "<group>"; };
|
F331C49D23DE187A0061925E /* PadsListView+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PadsListView+ViewModel.swift"; sourceTree = "<group>"; };
|
||||||
|
F356E61D23E25E7A008553B0 /* PadDetailsContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadDetailsContainerView.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -74,6 +77,7 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
F331C49123DDF0A30061925E /* CypherPoetNetStack in Frameworks */,
|
F331C49123DDF0A30061925E /* CypherPoetNetStack in Frameworks */,
|
||||||
|
F32ED62E23E163BB006A5195 /* CypherPoetPropertyWrappers in Frameworks */,
|
||||||
F331C48123DDDE080061925E /* CypherPoetSwiftUIKit in Frameworks */,
|
F331C48123DDDE080061925E /* CypherPoetSwiftUIKit in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -86,6 +90,7 @@
|
||||||
children = (
|
children = (
|
||||||
F32ED61C23DF9B46006A5195 /* PadDetailsView.swift */,
|
F32ED61C23DF9B46006A5195 /* PadDetailsView.swift */,
|
||||||
F32ED61E23DF9B7C006A5195 /* PadDetailsView+ViewModel.swift */,
|
F32ED61E23DF9B7C006A5195 /* PadDetailsView+ViewModel.swift */,
|
||||||
|
F356E61D23E25E7A008553B0 /* PadDetailsContainerView.swift */,
|
||||||
);
|
);
|
||||||
path = "Pad Details";
|
path = "Pad Details";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -270,6 +275,7 @@
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
F331C48023DDDE080061925E /* CypherPoetSwiftUIKit */,
|
F331C48023DDDE080061925E /* CypherPoetSwiftUIKit */,
|
||||||
F331C49023DDF0A30061925E /* CypherPoetNetStack */,
|
F331C49023DDF0A30061925E /* CypherPoetNetStack */,
|
||||||
|
F32ED62D23E163BB006A5195 /* CypherPoetPropertyWrappers */,
|
||||||
);
|
);
|
||||||
productName = PadFinder;
|
productName = PadFinder;
|
||||||
productReference = F331C45823DDB0AE0061925E /* PadFinder.app */;
|
productReference = F331C45823DDB0AE0061925E /* PadFinder.app */;
|
||||||
|
@ -302,6 +308,7 @@
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
F331C47F23DDDE080061925E /* XCRemoteSwiftPackageReference "CypherPoetSwiftUIKit" */,
|
F331C47F23DDDE080061925E /* XCRemoteSwiftPackageReference "CypherPoetSwiftUIKit" */,
|
||||||
F331C48F23DDF0A30061925E /* XCRemoteSwiftPackageReference "CypherPoetNetStack" */,
|
F331C48F23DDF0A30061925E /* XCRemoteSwiftPackageReference "CypherPoetNetStack" */,
|
||||||
|
F32ED62C23E163BB006A5195 /* XCRemoteSwiftPackageReference "CypherPoetPropertyWrappers" */,
|
||||||
);
|
);
|
||||||
productRefGroup = F331C45923DDB0AE0061925E /* Products */;
|
productRefGroup = F331C45923DDB0AE0061925E /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
|
@ -331,6 +338,7 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
F331C47C23DDB1710061925E /* PadsListContainerView.swift in Sources */,
|
F331C47C23DDB1710061925E /* PadsListContainerView.swift in Sources */,
|
||||||
|
F356E61E23E25E7A008553B0 /* PadDetailsContainerView.swift in Sources */,
|
||||||
F331C48523DDDE710061925E /* AppState.swift in Sources */,
|
F331C48523DDDE710061925E /* AppState.swift in Sources */,
|
||||||
F32ED62923E0F0C6006A5195 /* MapSnapshottingService.swift in Sources */,
|
F32ED62923E0F0C6006A5195 /* MapSnapshottingService.swift in Sources */,
|
||||||
F32ED61F23DF9B7C006A5195 /* PadDetailsView+ViewModel.swift in Sources */,
|
F32ED61F23DF9B7C006A5195 /* PadDetailsView+ViewModel.swift in Sources */,
|
||||||
|
@ -548,6 +556,14 @@
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
F32ED62C23E163BB006A5195 /* XCRemoteSwiftPackageReference "CypherPoetPropertyWrappers" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/CypherPoet/CypherPoetPropertyWrappers.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 0.0.1;
|
||||||
|
};
|
||||||
|
};
|
||||||
F331C47F23DDDE080061925E /* XCRemoteSwiftPackageReference "CypherPoetSwiftUIKit" */ = {
|
F331C47F23DDDE080061925E /* XCRemoteSwiftPackageReference "CypherPoetSwiftUIKit" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/CypherPoet/CypherPoetSwiftUIKit.git";
|
repositoryURL = "https://github.com/CypherPoet/CypherPoetSwiftUIKit.git";
|
||||||
|
@ -567,6 +583,11 @@
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
F32ED62D23E163BB006A5195 /* CypherPoetPropertyWrappers */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = F32ED62C23E163BB006A5195 /* XCRemoteSwiftPackageReference "CypherPoetPropertyWrappers" */;
|
||||||
|
productName = CypherPoetPropertyWrappers;
|
||||||
|
};
|
||||||
F331C48023DDDE080061925E /* CypherPoetSwiftUIKit */ = {
|
F331C48023DDDE080061925E /* CypherPoetSwiftUIKit */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = F331C47F23DDDE080061925E /* XCRemoteSwiftPackageReference "CypherPoetSwiftUIKit" */;
|
package = F331C47F23DDDE080061925E /* XCRemoteSwiftPackageReference "CypherPoetSwiftUIKit" */;
|
||||||
|
|
|
@ -10,6 +10,15 @@
|
||||||
"version": "0.0.27"
|
"version": "0.0.27"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"package": "CypherPoetPropertyWrappers",
|
||||||
|
"repositoryURL": "https://github.com/CypherPoet/CypherPoetPropertyWrappers.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "9e1c62432f5a25a159e80dee28965e8cc20b7939",
|
||||||
|
"version": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"package": "CypherPoetSwiftUIKit",
|
"package": "CypherPoetSwiftUIKit",
|
||||||
"repositoryURL": "https://github.com/CypherPoet/CypherPoetSwiftUIKit.git",
|
"repositoryURL": "https://github.com/CypherPoet/CypherPoetSwiftUIKit.git",
|
||||||
|
|
|
@ -10,10 +10,14 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import CypherPoetSwiftUIKit_DataFlowUtils
|
import CypherPoetSwiftUIKit_DataFlowUtils
|
||||||
|
import CypherPoetPropertyWrappers_UserDefault
|
||||||
|
|
||||||
|
|
||||||
struct PadsState {
|
struct PadsState {
|
||||||
var dataFetchingState: DataFetchingState = .inactive
|
var dataFetchingState: DataFetchingState = .inactive
|
||||||
|
|
||||||
|
@UserDefault("pads-state-favorites", defaultValue: [Pad.ID]())
|
||||||
|
var favorites: [Pad.ID]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,6 +31,7 @@ extension PadsState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension PadsState.DataFetchingState: Equatable {
|
extension PadsState.DataFetchingState: Equatable {
|
||||||
|
|
||||||
static func == (
|
static func == (
|
||||||
|
@ -71,6 +76,8 @@ enum PadsAction {
|
||||||
case padsFetchStart
|
case padsFetchStart
|
||||||
case fetchedPadsSet([Pad])
|
case fetchedPadsSet([Pad])
|
||||||
case fetchErrorSet(Error)
|
case fetchErrorSet(Error)
|
||||||
|
case favoriteAdded(Pad.ID)
|
||||||
|
case favoriteRemoved(Pad.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,7 +91,10 @@ let padsReducer: Reducer<PadsState, PadsAction> = Reducer(
|
||||||
state.dataFetchingState = .fetching
|
state.dataFetchingState = .fetching
|
||||||
case .fetchErrorSet(let error):
|
case .fetchErrorSet(let error):
|
||||||
state.dataFetchingState = .errored(error)
|
state.dataFetchingState = .errored(error)
|
||||||
|
case .favoriteAdded(let padID):
|
||||||
|
state.favorites.append(padID)
|
||||||
|
case .favoriteRemoved(let padID):
|
||||||
|
state.favorites.removeAll(where: { $0 == padID })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// PadDetailsContainerView.swift
|
||||||
|
// PadFinder
|
||||||
|
//
|
||||||
|
// Created by CypherPoet on 1/29/20.
|
||||||
|
// ✌️
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import MapKit
|
||||||
|
|
||||||
|
|
||||||
|
struct PadDetailsContainerView {
|
||||||
|
@EnvironmentObject private var store: AppStore
|
||||||
|
|
||||||
|
let pad: Pad
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - View
|
||||||
|
extension PadDetailsContainerView: View {
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
PadDetailsView(
|
||||||
|
viewModel: .init(
|
||||||
|
pad: pad,
|
||||||
|
isPadFavorited: isPadFavorited,
|
||||||
|
snapshotService: MapSnapshottingService(
|
||||||
|
snapshotOptions: makeSnapshotOptions(for: pad)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
onFavoriteToggled: toggleFavorite(for:)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Computeds
|
||||||
|
extension PadDetailsContainerView {
|
||||||
|
var padsState: PadsState { store.state.padsState }
|
||||||
|
var isPadFavorited: Bool { padsState.favorites.contains(pad.id) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - View Variables
|
||||||
|
extension PadDetailsContainerView {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Private Helpers
|
||||||
|
private extension PadDetailsContainerView {
|
||||||
|
|
||||||
|
func makeSnapshotOptions(for pad: Pad) -> MKMapSnapshotter.Options {
|
||||||
|
let snapshotOptions = pad.baseSnapshotOptions
|
||||||
|
|
||||||
|
return snapshotOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func toggleFavorite(for pad: Pad) {
|
||||||
|
if padsState.favorites.contains(pad.id) {
|
||||||
|
store.send(.pads(.favoriteRemoved(pad.id)))
|
||||||
|
} else {
|
||||||
|
store.send(.pads(.favoriteAdded(pad.id)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//// MARK: - Preview
|
||||||
|
//struct PadDetailsContainerView_Previews: PreviewProvider {
|
||||||
|
//
|
||||||
|
// static var previews: some View {
|
||||||
|
// PadDetailsContainerView()
|
||||||
|
// }
|
||||||
|
//}
|
|
@ -16,7 +16,8 @@ extension PadDetailsView {
|
||||||
final class ViewModel: ObservableObject {
|
final class ViewModel: ObservableObject {
|
||||||
private var subscriptions = Set<AnyCancellable>()
|
private var subscriptions = Set<AnyCancellable>()
|
||||||
|
|
||||||
private let pad: Pad
|
let pad: Pad
|
||||||
|
let isPadFavorited: Bool
|
||||||
private let snapshotService: MapSnapshotServicing
|
private let snapshotService: MapSnapshotServicing
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,9 +28,11 @@ extension PadDetailsView {
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
init(
|
init(
|
||||||
pad: Pad,
|
pad: Pad,
|
||||||
|
isPadFavorited: Bool,
|
||||||
snapshotService: MapSnapshotServicing
|
snapshotService: MapSnapshotServicing
|
||||||
) {
|
) {
|
||||||
self.pad = pad
|
self.pad = pad
|
||||||
|
self.isPadFavorited = isPadFavorited
|
||||||
self.snapshotService = snapshotService
|
self.snapshotService = snapshotService
|
||||||
|
|
||||||
setupSubscribers()
|
setupSubscribers()
|
||||||
|
@ -46,15 +49,7 @@ extension PadDetailsView.ViewModel {
|
||||||
// MARK: - Computeds
|
// MARK: - Computeds
|
||||||
extension PadDetailsView.ViewModel {
|
extension PadDetailsView.ViewModel {
|
||||||
|
|
||||||
// TODO: Replace with MapKit Snapshot
|
var padNameText: String { pad.name }
|
||||||
var mapImageName: String {
|
|
||||||
"CanaveralMapSample"
|
|
||||||
}
|
|
||||||
|
|
||||||
var padNameText: String {
|
|
||||||
pad.name
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var latitudeText: String {
|
var latitudeText: String {
|
||||||
NumberFormatters.padCoordinateDisplay.string(for: pad.latitude) ?? ""
|
NumberFormatters.padCoordinateDisplay.string(for: pad.latitude) ?? ""
|
||||||
|
@ -64,9 +59,7 @@ extension PadDetailsView.ViewModel {
|
||||||
NumberFormatters.padCoordinateDisplay.string(for: pad.longitude) ?? ""
|
NumberFormatters.padCoordinateDisplay.string(for: pad.longitude) ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var wikipediaURL: URL? {
|
var wikipediaURL: URL? { pad.wikiURL }
|
||||||
pad.wikiURL
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var webLinkData: [(hostName: String, url: URL)] {
|
var webLinkData: [(hostName: String, url: URL)] {
|
||||||
|
@ -80,6 +73,11 @@ extension PadDetailsView.ViewModel {
|
||||||
return (hostName: strippedHostName(from: hostName), url: url)
|
return (hostName: strippedHostName(from: hostName), url: url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var favoritesButtonText: String {
|
||||||
|
return isPadFavorited ? "Remove From Favorites" : "Add to Favorites"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,11 @@ import CypherPoetSwiftUIKit
|
||||||
|
|
||||||
|
|
||||||
struct PadDetailsView {
|
struct PadDetailsView {
|
||||||
|
@EnvironmentObject private var store: AppStore
|
||||||
|
|
||||||
@ObservedObject var viewModel: ViewModel
|
@ObservedObject var viewModel: ViewModel
|
||||||
|
|
||||||
|
var onFavoriteToggled: ((Pad) -> Void)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,25 +25,22 @@ extension PadDetailsView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
VStack {
|
List {
|
||||||
if self.viewModel.mapSnapshotImage != nil {
|
if self.viewModel.mapSnapshotImage != nil {
|
||||||
Image(uiImage: self.viewModel.mapSnapshotImage!)
|
Image(uiImage: self.viewModel.mapSnapshotImage!)
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
|
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
Group {
|
|
||||||
self.coordinateHeader
|
self.coordinateHeader
|
||||||
|
.padding(.vertical)
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
if self.viewModel.webLinkData.isEmpty == false {
|
if self.viewModel.webLinkData.isEmpty == false {
|
||||||
self.linksSection
|
self.linksSection
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
Spacer()
|
self.optionsSection
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
self.viewModel.takeMapSnapshot(
|
self.viewModel.takeMapSnapshot(
|
||||||
|
@ -66,6 +67,8 @@ extension PadDetailsView {
|
||||||
|
|
||||||
private var coordinateHeader: some View {
|
private var coordinateHeader: some View {
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
Image(systemName: "mappin")
|
Image(systemName: "mappin")
|
||||||
.padding()
|
.padding()
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
@ -80,8 +83,9 @@ extension PadDetailsView {
|
||||||
+ Text(self.viewModel.longitudeText)
|
+ Text(self.viewModel.longitudeText)
|
||||||
}
|
}
|
||||||
.embedInCompactableStack()
|
.embedInCompactableStack()
|
||||||
|
|
||||||
|
Spacer()
|
||||||
}
|
}
|
||||||
.padding(.top)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,7 +93,7 @@ extension PadDetailsView {
|
||||||
Section(
|
Section(
|
||||||
header: Text("Links").font(.headline)
|
header: Text("Links").font(.headline)
|
||||||
) {
|
) {
|
||||||
List(viewModel.webLinkData, id: \.0) { linkItem in
|
ForEach(viewModel.webLinkData, id: \.0) { linkItem in
|
||||||
Button(action: {
|
Button(action: {
|
||||||
UIApplication.shared.open(linkItem.url)
|
UIApplication.shared.open(linkItem.url)
|
||||||
}) {
|
}) {
|
||||||
|
@ -99,6 +103,25 @@ extension PadDetailsView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var optionsSection: some View {
|
||||||
|
Section(
|
||||||
|
header: Text("Options").font(.headline)
|
||||||
|
) {
|
||||||
|
favoritesButton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var favoritesButton: some View {
|
||||||
|
Button(action: {
|
||||||
|
self.onFavoriteToggled?(self.viewModel.pad)
|
||||||
|
}) {
|
||||||
|
Text(viewModel.favoritesButtonText)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -120,6 +143,7 @@ struct PadDetailsView_Previews: PreviewProvider {
|
||||||
PadDetailsView(
|
PadDetailsView(
|
||||||
viewModel: .init(
|
viewModel: .init(
|
||||||
pad: PreviewData.Pads.pad1,
|
pad: PreviewData.Pads.pad1,
|
||||||
|
isPadFavorited: false,
|
||||||
snapshotService: MapSnapshottingService(
|
snapshotService: MapSnapshottingService(
|
||||||
snapshotOptions: snapshotOptions
|
snapshotOptions: snapshotOptions
|
||||||
)
|
)
|
||||||
|
@ -127,5 +151,6 @@ struct PadDetailsView_Previews: PreviewProvider {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
|
.environmentObject(PreviewData.AppStores.withPads)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ extension PadsListContainerView: View {
|
||||||
buildDestination: buildDestination(forPad:)
|
buildDestination: buildDestination(forPad:)
|
||||||
)
|
)
|
||||||
.navigationBarTitle("Launch Pads")
|
.navigationBarTitle("Launch Pads")
|
||||||
|
.environmentObject(store)
|
||||||
|
|
||||||
WelcomeView()
|
WelcomeView()
|
||||||
}
|
}
|
||||||
|
@ -60,15 +61,17 @@ private extension PadsListContainerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func toggleFavorite(for pad: Pad) {
|
||||||
|
if padsState.favorites.contains(pad.id) {
|
||||||
|
store.send(.pads(.favoriteRemoved(pad.id)))
|
||||||
|
} else {
|
||||||
|
store.send(.pads(.favoriteAdded(pad.id)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func buildDestination(forPad pad: Pad) -> some View {
|
func buildDestination(forPad pad: Pad) -> some View {
|
||||||
PadDetailsView(
|
PadDetailsContainerView(pad: pad)
|
||||||
viewModel: .init(
|
|
||||||
pad: pad,
|
|
||||||
snapshotService: MapSnapshottingService(
|
|
||||||
snapshotOptions: makeSnapshotOptions(for: pad)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ extension PadsListView {
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
init(
|
init(
|
||||||
padsState: PadsState = .init()
|
padsState: PadsState
|
||||||
) {
|
) {
|
||||||
self.padsState = padsState
|
self.padsState = padsState
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue