Skip to content

Commit 35c21f9

Browse files
authored
Merge branch 'trunk' into woomob-692-woo-posbarcodes-scanning-on-the-order-complete-screen-should
2 parents 3c04fc1 + 300a2f9 commit 35c21f9

File tree

19 files changed

+1103
-409
lines changed

19 files changed

+1103
-409
lines changed

Modules/Sources/NetworkingCore/Model/Order.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ public struct Order: Decodable, Sendable, GeneratedCopiable, GeneratedFakeable {
6969
///
7070
public let createdVia: String?
7171

72+
public var salesChannel: SalesChannel? {
73+
guard let createdVia else { return nil }
74+
return SalesChannel(rawValue: createdVia)
75+
}
76+
7277
/// Order struct initializer.
7378
///
7479
public init(siteID: Int64,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import Foundation
2+
3+
public enum SalesChannel {
4+
case pointOfSale
5+
}
6+
7+
extension SalesChannel: RawRepresentable {
8+
public init?(rawValue: String) {
9+
switch rawValue {
10+
case "pos-rest-api":
11+
self = .pointOfSale
12+
default:
13+
return nil
14+
}
15+
}
16+
17+
public var rawValue: String {
18+
switch self {
19+
case .pointOfSale:
20+
return description
21+
}
22+
}
23+
24+
public var description: String {
25+
switch self {
26+
case .pointOfSale:
27+
return NSLocalizedString("salesChannel.pos.description",
28+
value: "POS",
29+
comment: "The acronym for 'Point of Sale'.")
30+
}
31+
}
32+
}

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- [*] Shipping Labels: Fixed a portion of layout issues caused by bigger accessibility content size categories. [https://github.com/woocommerce/woocommerce-ios/pull/15844]
99
- [*] Shipping Labels: Enable the confirm button on the payment method sheet even when there are no changes. [https://github.com/woocommerce/woocommerce-ios/pull/15856]
1010
- [*] POS: start a new cart by scanning a barcode on the payment success screen [https://github.com/woocommerce/woocommerce-ios/pull/15870]
11+
- [*] Watch app: Fixed connection issue upon fresh install [https://github.com/woocommerce/woocommerce-ios/pull/15867]
1112

1213
22.7
1314
-----

WooCommerce/Classes/Analytics/WooAnalyticsStat.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,7 @@ enum WooAnalyticsStat: String {
12481248
case watchAppOpened = "watch_app_opened"
12491249
case watchStoreDataSynced = "watch_store_data_synced"
12501250
case watchConnectingOpened = "watch_connecting_opened"
1251+
case watchSyncingFailed = "watch_syncing_failed"
12511252
case watchMyStoreOpened = "watch_my_store_opened"
12521253
case watchOrdersListOpened = "watch_orders_list_opened"
12531254
case watchPushNotificationTapped = "watch_push_notification_tapped"

WooCommerce/Classes/POS/Presentation/PointOfSaleBarcodeScannerInformationModal.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@ private extension PointOfSaleBarcodeScannerInformationModal {
8282
comment: "Accessible version of more details link in barcode info modal, announcing it as a link for screen readers"
8383
)
8484
static let barcodeInfoSecondaryMessage = NSLocalizedString(
85-
"pos.barcodeInfoModal.secondaryMessage",
86-
value: "• Refer to your Bluetooth barcode scanner's instructions to set HID mode.",
85+
"pos.barcodeInfoModal.secondaryMessage.2",
86+
value: "• Refer to your Bluetooth barcode scanner's instructions to set HID mode. This usually " +
87+
"requires scanning a special barcode in the manual.",
8788
comment: "Secondary bullet point in the barcode info modal in POS, instructing to set scanner to HID mode"
8889
)
8990
static let barcodeInfoTertiaryMessage = NSLocalizedString(
@@ -110,8 +111,9 @@ private extension PointOfSaleBarcodeScannerInformationModal {
110111
comment: "Accessible version of primary bullet point in barcode info modal, without bullet character for screen readers"
111112
)
112113
static let barcodeInfoSecondaryMessageAccessible = NSLocalizedString(
113-
"pos.barcodeInfoModal.secondaryMessage.accessible",
114-
value: "Second: Refer to your Bluetooth barcode scanner's instructions to set H-I-D mode.",
114+
"pos.barcodeInfoModal.secondaryMessage.accessible.2",
115+
value: "Second: Refer to your Bluetooth barcode scanner's instructions to set H-I-D mode. This usually " +
116+
"requires scanning a special barcode in the manual.",
115117
comment: "Accessible version of secondary bullet point in barcode info modal, without bullet character for screen readers"
116118
)
117119
static let barcodeInfoTertiaryMessageAccessible = NSLocalizedString(

WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,6 @@ struct POSIneligibleView: View {
7777

7878
private var suggestionText: String {
7979
switch reason {
80-
case .notTablet:
81-
return NSLocalizedString("pos.ineligible.suggestion.notTablet",
82-
value: "Please use a tablet to access POS features.",
83-
comment: "Suggestion for not tablet: use iPad")
8480
case .unsupportedIOSVersion:
8581
return NSLocalizedString("pos.ineligible.suggestion.unsupportedIOSVersion",
8682
value: "Point of Sale requires iOS 17 or later. Please update your device to iOS 17+ to use this feature.",
@@ -105,16 +101,6 @@ struct POSIneligibleView: View {
105101
return NSLocalizedString("pos.ineligible.suggestion.featureSwitchSyncFailure",
106102
value: "Try relaunching the app or check your internet connection and try again.",
107103
comment: "Suggestion for feature switch sync failure: relaunch or check connection")
108-
case let .unsupportedCountry(supportedCountries):
109-
let countryNames = supportedCountries.map { $0.readableCountry }
110-
let formattedCountryList = ListFormatter.localizedString(byJoining: countryNames)
111-
let format = NSLocalizedString(
112-
"pos.ineligible.suggestion.unsupportedCountry",
113-
value: "POS is currently only available in %1$@. Check back later for availability in your region.",
114-
comment: "Suggestion for unsupported country with list of supported countries. " +
115-
"%1$@ is a placeholder for the localized list of supported country names."
116-
)
117-
return String.localizedStringWithFormat(format, formattedCountryList)
118104
case let .unsupportedCurrency(supportedCurrencies):
119105
let currencyList = supportedCurrencies.map { $0.rawValue }
120106
let formattedCurrencyList = ListFormatter.localizedString(byJoining: currencyList)
@@ -130,10 +116,6 @@ struct POSIneligibleView: View {
130116
return NSLocalizedString("pos.ineligible.suggestion.siteSettingsNotAvailable",
131117
value: "Check your internet connection and try relaunching the app. If the issue persists, please contact support.",
132118
comment: "Suggestion for site settings unavailable: check connection or contact support")
133-
case .featureFlagDisabled:
134-
return NSLocalizedString("pos.ineligible.suggestion.featureFlagDisabled",
135-
value: "POS is currently disabled.",
136-
comment: "Suggestion for disabled feature flag: notify that POS is disabled remotely")
137119
case .selfDeallocated:
138120
return NSLocalizedString("pos.ineligible.suggestion.selfDeallocated",
139121
value: "Try relaunching the app to resolve this issue.",
@@ -176,24 +158,6 @@ private extension POSIneligibleView {
176158
}
177159
}
178160

179-
#Preview("Unsupported country") {
180-
if #available(iOS 17.0, *) {
181-
POSIneligibleView(
182-
reason: .unsupportedCountry(supportedCountries: [.US, .GB]),
183-
onRefresh: {}
184-
)
185-
}
186-
}
187-
188-
#Preview("Not a tablet") {
189-
if #available(iOS 17.0, *) {
190-
POSIneligibleView(
191-
reason: .notTablet,
192-
onRefresh: {}
193-
)
194-
}
195-
}
196-
197161
#Preview("Unsupported iOS version") {
198162
if #available(iOS 17.0, *) {
199163
POSIneligibleView(
@@ -212,15 +176,6 @@ private extension POSIneligibleView {
212176
}
213177
}
214178

215-
#Preview("Feature flag disabled") {
216-
if #available(iOS 17.0, *) {
217-
POSIneligibleView(
218-
reason: .featureFlagDisabled,
219-
onRefresh: {}
220-
)
221-
}
222-
}
223-
224179
#Preview("Feature switch disabled") {
225180
if #available(iOS 17.0, *) {
226181
POSIneligibleView(

WooCommerce/Classes/System/WatchDependenciesSynchronizer.swift

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import WatchConnectivity
22
import Combine
33
import Networking
4+
import protocol WooFoundation.Analytics
45
import class WooFoundation.CurrencySettings
56

67
/// Type that syncs the necessary dependencies to the watch session.
@@ -10,6 +11,8 @@ final class WatchDependenciesSynchronizer: NSObject, WCSessionDelegate {
1011
/// Current WatchKit Session
1112
private let watchSession: WCSession
1213

14+
private let analytics: Analytics
15+
1316
/// Subscriptions store for combine publishers
1417
///
1518
private var subscriptions = Set<AnyCancellable>()
@@ -34,16 +37,15 @@ final class WatchDependenciesSynchronizer: NSObject, WCSessionDelegate {
3437
///
3538
@Published var account: Account?
3639

37-
/// Tracks if the current watch session is active or not
38-
///
39-
@Published private var isSessionActive: Bool = false
40-
4140
/// Toggle this value to force a credentials sync.
4241
///
4342
@Published private var syncTrigger = false
4443

45-
init(watchSession: WCSession = WCSession.default, storedDependencies: WatchDependencies?) {
44+
init(watchSession: WCSession = WCSession.default,
45+
storedDependencies: WatchDependencies?,
46+
analytics: Analytics = ServiceLocator.analytics) {
4647
self.watchSession = watchSession
48+
self.analytics = analytics
4749
super.init()
4850

4951
self.storeID = storedDependencies?.storeID
@@ -80,9 +82,7 @@ final class WatchDependenciesSynchronizer: NSObject, WCSessionDelegate {
8082
let (storeID, storeName, credentials, currencySettings) = required
8183
let (enablesCrashReports, account) = configuration
8284

83-
guard let storeID, let storeName, let credentials else {
84-
return nil
85-
}
85+
guard let storeID, let storeName, let credentials else { return nil }
8686

8787
return .init(storeID: storeID,
8888
storeName: storeName,
@@ -95,16 +95,30 @@ final class WatchDependenciesSynchronizer: NSObject, WCSessionDelegate {
9595
.debounce(for: 0.5, scheduler: DispatchQueue.main)
9696

9797
// Syncs the dependencies to the paired counterpart when the session becomes available.
98-
Publishers.CombineLatest3(watchDependencies, $isSessionActive, $syncTrigger)
99-
.sink { [watchSession] dependencies, isSessionActive, forceSync in
98+
watchDependencies.combineLatest($syncTrigger)
99+
.sink { [weak self, watchSession] dependencies, forceSync in
100100

101101
// Do not update the context if the session is not active, the watch is not paired or the watch app is not installed.
102-
guard isSessionActive, watchSession.isPaired, watchSession.isWatchAppInstalled else { return }
102+
guard watchSession.activationState == .activated,
103+
watchSession.isPaired,
104+
watchSession.isWatchAppInstalled else {
105+
self?.analytics.track(
106+
.watchSyncingFailed,
107+
properties: [
108+
"session_active": watchSession.activationState == .activated,
109+
"session_paired": watchSession.isPaired,
110+
"watch_app_installed": watchSession.isWatchAppInstalled
111+
],
112+
error: SyncError.watchSessionInactiveOrNotPaired
113+
)
114+
return
115+
}
103116

104117
do {
105118

106119
// If dependencies is nil, send an empty dictionary. This is most likely a logged out state
107120
guard let dependencies else {
121+
self?.analytics.track(.watchSyncingFailed, withError: SyncError.noDependenciesFound)
108122
return try watchSession.updateApplicationContext([:])
109123
}
110124

@@ -118,9 +132,11 @@ final class WatchDependenciesSynchronizer: NSObject, WCSessionDelegate {
118132

119133
try watchSession.updateApplicationContext(jsonObject)
120134
} else {
135+
self?.analytics.track(.watchSyncingFailed, withError: SyncError.encodingApplicationContextFailed)
121136
DDLogError("⛔️ Unable to encode watch dependencies for synchronization. Resulting object is not a dictionary")
122137
}
123138
} catch {
139+
self?.analytics.track(.watchSyncingFailed, withError: error)
124140
DDLogError("⛔️ Error synchronizing credentials into watch session: \(error)")
125141
}
126142
}
@@ -129,7 +145,6 @@ final class WatchDependenciesSynchronizer: NSObject, WCSessionDelegate {
129145

130146
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
131147
DDLogInfo("🔵 WatchSession activated \(activationState)")
132-
self.isSessionActive = activationState == .activated
133148
}
134149

135150
func sessionDidBecomeInactive(_ session: WCSession) {
@@ -138,7 +153,6 @@ final class WatchDependenciesSynchronizer: NSObject, WCSessionDelegate {
138153

139154
func sessionDidDeactivate(_ session: WCSession) {
140155
// Try to guarantee an active session
141-
self.isSessionActive = false
142156
watchSession.activate()
143157
}
144158
}
@@ -149,13 +163,12 @@ extension WatchDependenciesSynchronizer {
149163
/// This is in order to not duplicate tracks configuration which involve quite a lot of information to be transmitted to the watch.
150164
///
151165
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any] = [:]) {
152-
153166
// The user info could contain a track event. Send it if we found one.
154167
guard let rawEvent = userInfo[WooConstants.watchTracksKey] as? String,
155168
let analyticEvent = WooAnalyticsStat(rawValue: rawEvent) else {
156169
return DDLogError("⛔️ Unsupported watch tracks event: \(userInfo)")
157170
}
158-
ServiceLocator.analytics.track(analyticEvent)
171+
analytics.track(analyticEvent)
159172
}
160173
}
161174

@@ -173,7 +186,14 @@ extension WatchDependenciesSynchronizer {
173186
guard message[WooConstants.watchSyncKey] as? Bool == true else {
174187
return DDLogError("⛔️ Unsupported sync request message: \(message)")
175188
}
176-
177189
syncTrigger.toggle()
178190
}
179191
}
192+
193+
extension WatchDependenciesSynchronizer {
194+
enum SyncError: Error {
195+
case watchSessionInactiveOrNotPaired
196+
case noDependenciesFound
197+
case encodingApplicationContextFailed
198+
}
199+
}

0 commit comments

Comments
 (0)