Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,8 @@ extension Networking.Booking {
allDay: CopiableProp<Bool> = .copy,
cost: CopiableProp<String> = .copy,
customerID: CopiableProp<Int64> = .copy,
dateCreated: CopiableProp<Date> = .copy,
dateModified: CopiableProp<Date> = .copy,
dateCreated: NullableCopiableProp<Date> = .copy,
dateModified: NullableCopiableProp<Date> = .copy,
endDate: CopiableProp<Date> = .copy,
googleCalendarEventID: NullableCopiableProp<String> = .copy,
orderID: CopiableProp<Int64> = .copy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import SwiftUI

struct POSSettingsLocalCatalogDetailView: View {
// TODO: WOOMOB-1335 - implement full sync cellular data setting functionality
@State private var allowFullSyncOnCellular: Bool = true
private let viewModel: POSSettingsLocalCatalogViewModel

init(viewModel: POSSettingsLocalCatalogViewModel) {
Expand Down Expand Up @@ -51,11 +50,12 @@ private extension POSSettingsLocalCatalogDetailView {

@ViewBuilder
var managingDataUsage: some View {
@Bindable var viewModel = viewModel
VStack(spacing: POSSpacing.none) {
sectionHeaderView(title: Localization.managingDataUsage)

VStack(spacing: POSSpacing.medium) {
toggleRowView(label: Localization.allowFullSyncOnCellular, isOn: $allowFullSyncOnCellular)
toggleRowView(label: Localization.allowFullSyncOnCellular, isOn: $viewModel.allowFullSyncOnCellular)
}
.padding(.bottom, POSPadding.medium)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import CocoaLumberjackSwift
import Yosemite
import Foundation
import Storage

@Observable
final class POSSettingsLocalCatalogViewModel {
Expand All @@ -14,19 +15,31 @@ final class POSSettingsLocalCatalogViewModel {
private let siteID: Int64
private let catalogSettingsService: POSCatalogSettingsServiceProtocol
private let catalogSyncCoordinator: POSCatalogSyncCoordinatorProtocol
private let siteSettings: SiteSpecificAppSettingsStoreMethodsProtocol
private let dateFormatter: RelativeDateTimeFormatter = {
let formatter = RelativeDateTimeFormatter()
formatter.dateTimeStyle = .named
formatter.unitsStyle = .full
return formatter
}()

var allowFullSyncOnCellular: Bool {
get {
siteSettings.getPOSLocalCatalogCellularDataAllowed(siteID: siteID)
}
set {
siteSettings.setPOSLocalCatalogCellularDataAllowed(siteID: siteID, allowed: newValue)
}
}

init(siteID: Int64,
catalogSettingsService: POSCatalogSettingsServiceProtocol,
catalogSyncCoordinator: POSCatalogSyncCoordinatorProtocol) {
catalogSyncCoordinator: POSCatalogSyncCoordinatorProtocol,
siteSettings: SiteSpecificAppSettingsStoreMethodsProtocol? = nil) {
self.siteID = siteID
self.catalogSettingsService = catalogSettingsService
self.catalogSyncCoordinator = catalogSyncCoordinator
self.siteSettings = siteSettings ?? SiteSpecificAppSettingsStoreMethods(fileStorage: PListFileStorage())
}

@MainActor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ extension Storage.GeneralStoreSettings {
searchTermsByKey: CopiableProp<[String: [String]]> = .copy,
isPOSTabVisible: NullableCopiableProp<Bool> = .copy,
lastPOSOpenedDate: NullableCopiableProp<Date> = .copy,
firstPOSCatalogSyncDate: NullableCopiableProp<Date> = .copy
firstPOSCatalogSyncDate: NullableCopiableProp<Date> = .copy,
syncPOSCatalogOverCellular: CopiableProp<Bool> = .copy
) -> Storage.GeneralStoreSettings {
let storeID = storeID ?? self.storeID
let isTelemetryAvailable = isTelemetryAvailable ?? self.isTelemetryAvailable
Expand All @@ -151,6 +152,7 @@ extension Storage.GeneralStoreSettings {
let isPOSTabVisible = isPOSTabVisible ?? self.isPOSTabVisible
let lastPOSOpenedDate = lastPOSOpenedDate ?? self.lastPOSOpenedDate
let firstPOSCatalogSyncDate = firstPOSCatalogSyncDate ?? self.firstPOSCatalogSyncDate
let syncPOSCatalogOverCellular = syncPOSCatalogOverCellular ?? self.syncPOSCatalogOverCellular

return Storage.GeneralStoreSettings(
storeID: storeID,
Expand All @@ -174,7 +176,8 @@ extension Storage.GeneralStoreSettings {
searchTermsByKey: searchTermsByKey,
isPOSTabVisible: isPOSTabVisible,
lastPOSOpenedDate: lastPOSOpenedDate,
firstPOSCatalogSyncDate: firstPOSCatalogSyncDate
firstPOSCatalogSyncDate: firstPOSCatalogSyncDate,
syncPOSCatalogOverCellular: syncPOSCatalogOverCellular
)
}
}
Expand Down
9 changes: 8 additions & 1 deletion Modules/Sources/Storage/Model/GeneralStoreSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ public struct GeneralStoreSettings: Codable, Equatable, GeneratedCopiable {
///
public var firstPOSCatalogSyncDate: Date?

/// Whether we should sync catalog data over cellular connections for this store
///
public var syncPOSCatalogOverCellular: Bool

public init(storeID: String? = nil,
isTelemetryAvailable: Bool = false,
telemetryLastReportedTime: Date? = nil,
Expand All @@ -115,7 +119,8 @@ public struct GeneralStoreSettings: Codable, Equatable, GeneratedCopiable {
searchTermsByKey: [String: [String]] = [:],
isPOSTabVisible: Bool? = nil,
lastPOSOpenedDate: Date? = nil,
firstPOSCatalogSyncDate: Date? = nil) {
firstPOSCatalogSyncDate: Date? = nil,
syncPOSCatalogOverCellular: Bool = true) {
self.storeID = storeID
self.isTelemetryAvailable = isTelemetryAvailable
self.telemetryLastReportedTime = telemetryLastReportedTime
Expand All @@ -138,6 +143,7 @@ public struct GeneralStoreSettings: Codable, Equatable, GeneratedCopiable {
self.isPOSTabVisible = isPOSTabVisible
self.lastPOSOpenedDate = lastPOSOpenedDate
self.firstPOSCatalogSyncDate = firstPOSCatalogSyncDate
self.syncPOSCatalogOverCellular = syncPOSCatalogOverCellular
}

public func erasingSelectedTaxRateID() -> GeneralStoreSettings {
Expand Down Expand Up @@ -198,6 +204,7 @@ extension GeneralStoreSettings {
self.isPOSTabVisible = try container.decodeIfPresent(Bool.self, forKey: .isPOSTabVisible)
self.lastPOSOpenedDate = try container.decodeIfPresent(Date.self, forKey: .lastPOSOpenedDate)
self.firstPOSCatalogSyncDate = try container.decodeIfPresent(Date.self, forKey: .firstPOSCatalogSyncDate)
self.syncPOSCatalogOverCellular = try container.decodeIfPresent(Bool.self, forKey: .syncPOSCatalogOverCellular) ?? true

// Decode new properties with `decodeIfPresent` and provide a default value if necessary.
}
Expand Down
7 changes: 7 additions & 0 deletions Modules/Sources/Yosemite/Actions/AppSettingsAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -414,4 +414,11 @@ public enum AppSettingsAction: Action {
/// Gets the date of the first POS catalog sync for a specific site
///
case getFirstPOSCatalogSyncDate(siteID: Int64, onCompletion: (Date?) -> Void)

/// Sets whether we should allow cellular data use downloading POS catalogs for a specific site
///
case setPOSLocalCatalogCellularDataAllowed(siteID: Int64, allowed: Bool, onCompletion: () -> Void)

/// Gets whether we should allow cellular data use downloading POS catalogs for a specific site
case getPOSLocalCatalogCellularDataAllowed(siteID: Int64, onCompletion: (Bool) -> Void)
}
17 changes: 17 additions & 0 deletions Modules/Sources/Yosemite/Stores/AppSettingsStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,10 @@ public class AppSettingsStore: Store {
setFirstPOSCatalogSyncDate(siteID: siteID, date: date, onCompletion: onCompletion)
case .getFirstPOSCatalogSyncDate(siteID: let siteID, onCompletion: let onCompletion):
getFirstPOSCatalogSyncDate(siteID: siteID, onCompletion: onCompletion)
case .setPOSLocalCatalogCellularDataAllowed(let siteID, let allowed, let onCompletion):
setPOSLocalCatalogCellularDataAllowed(siteID: siteID, allowed: allowed, onCompletion: onCompletion)
case .getPOSLocalCatalogCellularDataAllowed(let siteID, let onCompletion):
getPOSLocalCatalogCellularDataAllowed(siteID: siteID, onCompletion: onCompletion)
}
}
}
Expand Down Expand Up @@ -1365,7 +1369,10 @@ private extension AppSettingsStore {
onCompletion(.failure(error))
}
}
}

// MARK: - Point of Sale local catalog settings
private extension AppSettingsStore {
func setPOSLastOpenedDate(siteID: Int64, date: Date, onCompletion: () -> Void) {
siteSpecificAppSettingsStoreMethods.setPOSLastOpenedDate(siteID: siteID, date: date)
onCompletion()
Expand All @@ -1385,6 +1392,16 @@ private extension AppSettingsStore {
let date = siteSpecificAppSettingsStoreMethods.getFirstPOSCatalogSyncDate(siteID: siteID)
onCompletion(date)
}

func setPOSLocalCatalogCellularDataAllowed(siteID: Int64, allowed: Bool, onCompletion: () -> Void) {
siteSpecificAppSettingsStoreMethods.setPOSLocalCatalogCellularDataAllowed(siteID: siteID, allowed: allowed)
onCompletion()
}

func getPOSLocalCatalogCellularDataAllowed(siteID: Int64, onCompletion: (Bool) -> Void) {
let allowed = siteSpecificAppSettingsStoreMethods.getPOSLocalCatalogCellularDataAllowed(siteID: siteID)
onCompletion(allowed)
}
}

// MARK: - Errors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ public protocol SiteSpecificAppSettingsStoreMethodsProtocol {
func setPOSLastOpenedDate(siteID: Int64, date: Date)
func getFirstPOSCatalogSyncDate(siteID: Int64) -> Date?
func setFirstPOSCatalogSyncDate(siteID: Int64, date: Date)

// POS local catalog cellular data
func setPOSLocalCatalogCellularDataAllowed(siteID: Int64, allowed: Bool)
func getPOSLocalCatalogCellularDataAllowed(siteID: Int64) -> Bool
}

/// Methods for managing site-specific app settings
///
struct SiteSpecificAppSettingsStoreMethods: SiteSpecificAppSettingsStoreMethodsProtocol {
public struct SiteSpecificAppSettingsStoreMethods: SiteSpecificAppSettingsStoreMethodsProtocol {
private let fileStorage: FileStorage
private let generalStoreSettingsFileURL: URL

Expand All @@ -40,7 +44,7 @@ struct SiteSpecificAppSettingsStoreMethods: SiteSpecificAppSettingsStoreMethodsP

// MARK: - Store Settings
extension SiteSpecificAppSettingsStoreMethods {
func getStoreSettings(for siteID: Int64) -> GeneralStoreSettings {
public func getStoreSettings(for siteID: Int64) -> GeneralStoreSettings {
guard let existingData: GeneralStoreSettingsBySite = try? fileStorage.data(for: generalStoreSettingsFileURL),
let storeSettings = existingData.storeSettingsBySite[siteID] else {
return GeneralStoreSettings()
Expand All @@ -49,7 +53,7 @@ extension SiteSpecificAppSettingsStoreMethods {
return storeSettings
}

func setStoreSettings(settings: GeneralStoreSettings, for siteID: Int64, onCompletion: ((Result<Void, Error>) -> Void)? = nil) {
public func setStoreSettings(settings: GeneralStoreSettings, for siteID: Int64, onCompletion: ((Result<Void, Error>) -> Void)? = nil) {
var storeSettingsBySite: [Int64: GeneralStoreSettings] = [:]
if let existingData: GeneralStoreSettingsBySite = try? fileStorage.data(for: generalStoreSettingsFileURL) {
storeSettingsBySite = existingData.storeSettingsBySite
Expand All @@ -66,64 +70,80 @@ extension SiteSpecificAppSettingsStoreMethods {
}
}

func resetStoreSettings() {
public func resetStoreSettings() {
do {
try fileStorage.deleteFile(at: generalStoreSettingsFileURL)
} catch {
DDLogError("⛔️ Deleting store settings file failed. Error: \(error)")
}
}

func setStoreID(siteID: Int64, id: String?) {
public func setStoreID(siteID: Int64, id: String?) {
let storeSettings = getStoreSettings(for: siteID)
let updatedSettings = storeSettings.copy(storeID: id)
setStoreSettings(settings: updatedSettings, for: siteID)
}

func getStoreID(siteID: Int64, onCompletion: (String?) -> Void) {
public func getStoreID(siteID: Int64, onCompletion: (String?) -> Void) {
let storeSettings = getStoreSettings(for: siteID)
onCompletion(storeSettings.storeID)
}
}

// MARK: - Search History
extension SiteSpecificAppSettingsStoreMethods {
func getSearchTerms(for itemType: POSItemType, siteID: Int64) -> [String] {
public func getSearchTerms(for itemType: POSItemType, siteID: Int64) -> [String] {
let storeSettings = getStoreSettings(for: siteID)
let key = itemType.storedSearchHistoryKey
return storeSettings.searchTermsByKey[key] ?? []
}

func setSearchTerms(_ terms: [String], for itemType: POSItemType, siteID: Int64) {
public func setSearchTerms(_ terms: [String], for itemType: POSItemType, siteID: Int64) {
let storeSettings = getStoreSettings(for: siteID)
let key = itemType.storedSearchHistoryKey
var updatedSearchTermsByKey = storeSettings.searchTermsByKey
updatedSearchTermsByKey[key] = terms
let updatedSettings = storeSettings.copy(searchTermsByKey: updatedSearchTermsByKey)
setStoreSettings(settings: updatedSettings, for: siteID)
}
}

func getPOSLastOpenedDate(siteID: Int64) -> Date? {
// MARK: - POS sync eligibility tracking
extension SiteSpecificAppSettingsStoreMethods {
public func getPOSLastOpenedDate(siteID: Int64) -> Date? {
getStoreSettings(for: siteID).lastPOSOpenedDate
}

func setPOSLastOpenedDate(siteID: Int64, date: Date) {
public func setPOSLastOpenedDate(siteID: Int64, date: Date) {
let storeSettings = getStoreSettings(for: siteID)
let updatedSettings = storeSettings.copy(lastPOSOpenedDate: date)
setStoreSettings(settings: updatedSettings, for: siteID)
}

func getFirstPOSCatalogSyncDate(siteID: Int64) -> Date? {
public func getFirstPOSCatalogSyncDate(siteID: Int64) -> Date? {
getStoreSettings(for: siteID).firstPOSCatalogSyncDate
}

func setFirstPOSCatalogSyncDate(siteID: Int64, date: Date) {
public func setFirstPOSCatalogSyncDate(siteID: Int64, date: Date) {
let storeSettings = getStoreSettings(for: siteID)
let updatedSettings = storeSettings.copy(firstPOSCatalogSyncDate: date)
setStoreSettings(settings: updatedSettings, for: siteID)
}
}

// MARK: - POS local catalog cellular data
extension SiteSpecificAppSettingsStoreMethods {
public func setPOSLocalCatalogCellularDataAllowed(siteID: Int64, allowed: Bool) {
let storeSettings = getStoreSettings(for: siteID)
let updatedSettings = storeSettings.copy(syncPOSCatalogOverCellular: allowed)
setStoreSettings(settings: updatedSettings, for: siteID)
}

public func getPOSLocalCatalogCellularDataAllowed(siteID: Int64) -> Bool {
getStoreSettings(for: siteID).syncPOSCatalogOverCellular
}
}

// MARK: - Constants
private enum Constants {
static let generalStoreSettingsFileName = "general-store-settings.plist"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
@testable import Yosemite
import Foundation
import Storage

/// Minimal mock for SiteSpecificAppSettingsStoreMethodsProtocol
/// Only implements the methods needed for POS local catalog tests
final class MockSiteSpecificAppSettingsStoreMethods: SiteSpecificAppSettingsStoreMethodsProtocol {
var currentSiteID: Int64 = 1

// POS local catalog cellular data properties
var getPOSLocalCatalogCellularDataAllowedCalled = false
var setPOSLocalCatalogCellularDataAllowedCalled = false
var mockPOSLocalCatalogCellularDataAllowed: Bool?

// Implement only the methods we actually use
func setPOSLocalCatalogCellularDataAllowed(siteID: Int64, allowed: Bool) {
setPOSLocalCatalogCellularDataAllowedCalled = true
mockPOSLocalCatalogCellularDataAllowed = allowed
}

func getPOSLocalCatalogCellularDataAllowed(siteID: Int64) -> Bool {
getPOSLocalCatalogCellularDataAllowedCalled = true
return mockPOSLocalCatalogCellularDataAllowed ?? false
}

// Protocol requirements - minimal implementations
func getStoreSettings(for siteID: Int64) -> GeneralStoreSettings {
GeneralStoreSettings()
}

func setStoreSettings(settings: GeneralStoreSettings, for siteID: Int64, onCompletion: ((Result<Void, Error>) -> Void)?) {
onCompletion?(.success(()))
}

func resetStoreSettings() {}

func setStoreID(siteID: Int64, id: String?) {}

func getStoreID(siteID: Int64, onCompletion: (String?) -> Void) {
onCompletion(nil)
}

func getSearchTerms(for itemType: POSItemType, siteID: Int64) -> [String] {
[]
}

func setSearchTerms(_ terms: [String], for itemType: POSItemType, siteID: Int64) {}

func getPOSLastOpenedDate(siteID: Int64) -> Date? {
nil
}

func setPOSLastOpenedDate(siteID: Int64, date: Date) {}

func getFirstPOSCatalogSyncDate(siteID: Int64) -> Date? {
nil
}

func setFirstPOSCatalogSyncDate(siteID: Int64, date: Date) {}
}
Loading