Skip to content

Dynamic Dashboard: Save and load last selected stock type for Stock card #12800

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Fakes/Fakes/Networking.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1411,7 +1411,7 @@ extension Networking.ProductImage {
extension Networking.ProductReport {
/// Returns a "ready to use" type filled with fake values.
///
static func fake() -> Networking.ProductReport {
public static func fake() -> Networking.ProductReport {
.init(
totals: .fake()
)
Expand Down Expand Up @@ -1440,7 +1440,7 @@ extension Networking.ProductReportSegment.Subtotals {
extension Networking.ProductReportTotals {
/// Returns a "ready to use" type filled with fake values.
///
static func fake() -> Networking.ProductReportTotals {
public static func fake() -> Networking.ProductReportTotals {
.init(
segments: .fake()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2281,7 +2281,7 @@ extension Networking.ProductImage {
}

extension Networking.ProductReport {
func copy(
public func copy(
totals: CopiableProp<ProductReportTotals> = .copy
) -> Networking.ProductReport {
let totals = totals ?? self.totals
Expand Down Expand Up @@ -2323,7 +2323,7 @@ extension Networking.ProductReportSegment.Subtotals {
}

extension Networking.ProductReportTotals {
func copy(
public func copy(
segments: CopiableProp<[ProductReportSegment]> = .copy
) -> Networking.ProductReportTotals {
let segments = segments ?? self.segments
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ extension Storage.GeneralStoreSettings {
dashboardCards: NullableCopiableProp<[DashboardCard]> = .copy,
lastSelectedPerformanceTimeRange: CopiableProp<String> = .copy,
lastSelectedTopPerformersTimeRange: CopiableProp<String> = .copy,
lastSelectedMostActiveCouponsTimeRange: CopiableProp<String> = .copy
lastSelectedMostActiveCouponsTimeRange: CopiableProp<String> = .copy,
lastSelectedStockType: NullableCopiableProp<String> = .copy
) -> Storage.GeneralStoreSettings {
let storeID = storeID ?? self.storeID
let isTelemetryAvailable = isTelemetryAvailable ?? self.isTelemetryAvailable
Expand All @@ -130,6 +131,7 @@ extension Storage.GeneralStoreSettings {
let lastSelectedPerformanceTimeRange = lastSelectedPerformanceTimeRange ?? self.lastSelectedPerformanceTimeRange
let lastSelectedTopPerformersTimeRange = lastSelectedTopPerformersTimeRange ?? self.lastSelectedTopPerformersTimeRange
let lastSelectedMostActiveCouponsTimeRange = lastSelectedMostActiveCouponsTimeRange ?? self.lastSelectedMostActiveCouponsTimeRange
let lastSelectedStockType = lastSelectedStockType ?? self.lastSelectedStockType

return Storage.GeneralStoreSettings(
storeID: storeID,
Expand All @@ -146,7 +148,8 @@ extension Storage.GeneralStoreSettings {
dashboardCards: dashboardCards,
lastSelectedPerformanceTimeRange: lastSelectedPerformanceTimeRange,
lastSelectedTopPerformersTimeRange: lastSelectedTopPerformersTimeRange,
lastSelectedMostActiveCouponsTimeRange: lastSelectedMostActiveCouponsTimeRange
lastSelectedMostActiveCouponsTimeRange: lastSelectedMostActiveCouponsTimeRange,
lastSelectedStockType: lastSelectedStockType
)
}
}
11 changes: 9 additions & 2 deletions Storage/Storage/Model/GeneralStoreSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ public struct GeneralStoreSettings: Codable, Equatable, GeneratedCopiable {
/// The raw value string of `MostActiveCouponsTimeRange` that indicates the last selected time range tab in Most active coupons card.
public var lastSelectedMostActiveCouponsTimeRange: String

/// The raw value of stock type indicating the last selected type in the Stock dashboard card.
public var lastSelectedStockType: String?

public init(storeID: String? = nil,
isTelemetryAvailable: Bool = false,
telemetryLastReportedTime: Date? = nil,
Expand All @@ -82,7 +85,8 @@ public struct GeneralStoreSettings: Codable, Equatable, GeneratedCopiable {
dashboardCards: [DashboardCard]? = nil,
lastSelectedPerformanceTimeRange: String = "",
lastSelectedTopPerformersTimeRange: String = "",
lastSelectedMostActiveCouponsTimeRange: String = "") {
lastSelectedMostActiveCouponsTimeRange: String = "",
lastSelectedStockType: String? = nil) {
self.storeID = storeID
self.isTelemetryAvailable = isTelemetryAvailable
self.telemetryLastReportedTime = telemetryLastReportedTime
Expand All @@ -98,6 +102,7 @@ public struct GeneralStoreSettings: Codable, Equatable, GeneratedCopiable {
self.lastSelectedPerformanceTimeRange = lastSelectedPerformanceTimeRange
self.lastSelectedTopPerformersTimeRange = lastSelectedTopPerformersTimeRange
self.lastSelectedMostActiveCouponsTimeRange = lastSelectedMostActiveCouponsTimeRange
self.lastSelectedStockType = lastSelectedStockType
}

public func erasingSelectedTaxRateID() -> GeneralStoreSettings {
Expand All @@ -114,7 +119,8 @@ public struct GeneralStoreSettings: Codable, Equatable, GeneratedCopiable {
analyticsHubCards: analyticsHubCards,
dashboardCards: dashboardCards,
lastSelectedPerformanceTimeRange: lastSelectedPerformanceTimeRange,
lastSelectedTopPerformersTimeRange: lastSelectedTopPerformersTimeRange)
lastSelectedTopPerformersTimeRange: lastSelectedTopPerformersTimeRange,
lastSelectedStockType: lastSelectedStockType)
}
}

Expand Down Expand Up @@ -142,6 +148,7 @@ extension GeneralStoreSettings {
self.lastSelectedPerformanceTimeRange = try container.decodeIfPresent(String.self, forKey: .lastSelectedPerformanceTimeRange) ?? ""
self.lastSelectedTopPerformersTimeRange = try container.decodeIfPresent(String.self, forKey: .lastSelectedTopPerformersTimeRange) ?? ""
self.lastSelectedMostActiveCouponsTimeRange = try container.decodeIfPresent(String.self, forKey: .lastSelectedMostActiveCouponsTimeRange) ?? ""
self.lastSelectedStockType = try container.decodeIfPresent(String.self, forKey: .lastSelectedStockType)

// Decode new properties with `decodeIfPresent` and provide a default value if necessary.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ final class ProductStockDashboardCardViewModel: ObservableObject {
self.analytics = analytics
self.stores = stores
self.storageManager = storageManager

Task { @MainActor in
selectedStockType = await loadLastSelectedStockType()
}
}

@MainActor
Expand All @@ -44,12 +48,29 @@ final class ProductStockDashboardCardViewModel: ObservableObject {

func updateStockType(_ type: StockType) {
selectedStockType = type
stores.dispatch(AppSettingsAction.setLastSelectedStockType(siteID: siteID, type: type.rawValue))
Task {
await reloadData()
}
}
}

private extension ProductStockDashboardCardViewModel {
@MainActor
func loadLastSelectedStockType() async -> StockType {
await withCheckedContinuation { continuation in
stores.dispatch(AppSettingsAction.loadLastSelectedStockType(siteID: siteID, onCompletion: { type in
guard let type,
let stockType = StockType(rawValue: type) else {
continuation.resume(returning: .lowStock)
return
}
continuation.resume(returning: stockType)
}))
}
}
}

extension ProductStockDashboardCardViewModel {
enum StockType: String, CaseIterable, Identifiable {
case lowStock = "lowstock"
Expand Down
8 changes: 8 additions & 0 deletions Yosemite/Yosemite/Actions/AppSettingsAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,12 @@ public enum AppSettingsAction: Action {
/// Loads the last selected time range for the Most Active coupons dashboard card.
///
case loadLastSelectedMostActiveCouponsTimeRange(siteID: Int64, onCompletion: (StatsTimeRangeV4?) -> Void)

/// Stores the last selected stock type for the Stock dashboard card.
///
case setLastSelectedStockType(siteID: Int64, type: String)

/// Loads the last selected stock type for the Stock dashboard card.
///
case loadLastSelectedStockType(siteID: Int64, onCompletion: (String?) -> Void)
}
16 changes: 16 additions & 0 deletions Yosemite/Yosemite/Stores/AppSettingsStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ public class AppSettingsStore: Store {
setLastSelectedMostActiveCouponsTimeRange(siteID: siteID, timeRange: timeRange)
case let .loadLastSelectedMostActiveCouponsTimeRange(siteID, onCompletion):
loadLastSelectedMostActiveCouponsTimeRange(siteID: siteID, onCompletion: onCompletion)
case let .setLastSelectedStockType(siteID, type):
setLastSelectedStockType(siteID: siteID, type: type)
case let .loadLastSelectedStockType(siteID, onCompletion):
loadLastSelectedStockType(siteID: siteID, onCompletion: onCompletion)
}
}
}
Expand Down Expand Up @@ -1037,6 +1041,18 @@ private extension AppSettingsStore {
let timeRange = StatsTimeRangeV4(rawValue: timeRangeRawValue)
onCompletion(timeRange)
}

func setLastSelectedStockType(siteID: Int64, type: String) {
let storeSettings = getStoreSettings(for: siteID)
let updatedSettings = storeSettings.copy(lastSelectedStockType: type)
setStoreSettings(settings: updatedSettings, for: siteID)
}

func loadLastSelectedStockType(siteID: Int64, onCompletion: (String?) -> Void) {
let storeSettings = getStoreSettings(for: siteID)
let stockType = storeSettings.lastSelectedStockType
onCompletion(stockType)
}
}

// MARK: - Errors
Expand Down
53 changes: 53 additions & 0 deletions Yosemite/YosemiteTests/Stores/AppSettingsStoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,59 @@ extension AppSettingsStoreTests {
// Then
XCTAssertNil(loadedTimeRange)
}

// MARK: - Last selected stock type for Stock dashboard card

func test_setLastSelectedStockType_works_correctly() throws {
// Given
let stockType = "lowstock"
let existingSettings = GeneralStoreSettingsBySite(storeSettingsBySite: [TestConstants.siteID: GeneralStoreSettings()])
try fileStorage?.write(existingSettings, to: expectedGeneralStoreSettingsFileURL)

// When
let action = AppSettingsAction.setLastSelectedStockType(siteID: TestConstants.siteID, type: stockType)
subject?.onAction(action)

// Then
let savedSettings: GeneralStoreSettingsBySite = try XCTUnwrap(fileStorage?.data(for: expectedGeneralStoreSettingsFileURL))
let settingsForSite = savedSettings.storeSettingsBySite[TestConstants.siteID]

assertEqual(stockType, settingsForSite?.lastSelectedStockType)
}

func test_loadLastSelectedStockType_works_correctly() throws {
// Given
let stockType = "lowstock"
let storeSettings = GeneralStoreSettings(lastSelectedStockType: stockType)
let existingSettings = GeneralStoreSettingsBySite(storeSettingsBySite: [TestConstants.siteID: storeSettings])
try fileStorage?.write(existingSettings, to: expectedGeneralStoreSettingsFileURL)

// When
var loadedStockType: String?
let action = AppSettingsAction.loadLastSelectedStockType(siteID: TestConstants.siteID) { type in
loadedStockType = type
}
subject?.onAction(action)

// Then
assertEqual(stockType, loadedStockType)
}

func test_loadLastSelectedStockType_returns_nil_when_no_data_was_saved() throws {
// Given
let existingSettings = GeneralStoreSettingsBySite(storeSettingsBySite: [TestConstants.siteID: GeneralStoreSettings()])
try fileStorage?.write(existingSettings, to: expectedGeneralStoreSettingsFileURL)

// When
var loadedStockType: String?
let action = AppSettingsAction.loadLastSelectedStockType(siteID: TestConstants.siteID) { type in
loadedStockType = type
}
subject?.onAction(action)

// Then
XCTAssertNil(loadedStockType)
}
}

// MARK: - Utils
Expand Down