Skip to content

Commit 44a0ec8

Browse files
Merge branch 'trunk' into task/15305-split-shipments-ui
2 parents 34ee101 + 53a313e commit 44a0ec8

File tree

66 files changed

+2596
-444
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+2596
-444
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<!--
22
Contains editorialized release notes. Raw release notes should go into `RELEASE-NOTES.txt`.
33
-->
4+
## 22.0
5+
This update adds the option to set different notification preferences for each store. In Orders and Products, your filters stay around as you move through the app, and we've fixed a crash that could happen when you choose pictures for your products. Payments is improved too, with support for Puerto Rico in WooPayments, and better accessibility.
6+
47
## 21.9
58
We're dedicated to enhancing your WooCommerce app experience! We've ironed out the issues related to discarding images during uploads in the Product Form. Plus, we've ramped up our login flow accessibility to make your journey smoother and more intuitive. Enjoy the fresh update and newly polished features!
69

Experiments/Experiments/DefaultFeatureFlagService.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
9292
case .hideSitesInStorePicker:
9393
return true
9494
case .filterHistoryOnOrderAndProductLists:
95-
return buildConfig == .localDeveloper || buildConfig == .alpha
95+
return true
9696
case .backgroundProductImageUpload:
9797
return buildConfig == .localDeveloper || buildConfig == .alpha
9898
case .notificationSettings:
99-
return buildConfig == .localDeveloper || buildConfig == .alpha
99+
return true
100100
default:
101101
return true
102102
}

Networking/Networking/Model/Copiable/Models+Copiable.generated.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,51 @@ extension Networking.NoteRange {
13111311
}
13121312
}
13131313

1314+
extension Networking.NotificationSettings {
1315+
public func copy(
1316+
blogs: CopiableProp<[NotificationSettings.Blog]> = .copy
1317+
) -> Networking.NotificationSettings {
1318+
let blogs = blogs ?? self.blogs
1319+
1320+
return Networking.NotificationSettings(
1321+
blogs: blogs
1322+
)
1323+
}
1324+
}
1325+
1326+
extension Networking.NotificationSettings.Blog {
1327+
public func copy(
1328+
blogID: CopiableProp<Int64> = .copy,
1329+
devices: CopiableProp<[NotificationSettings.Device]> = .copy
1330+
) -> Networking.NotificationSettings.Blog {
1331+
let blogID = blogID ?? self.blogID
1332+
let devices = devices ?? self.devices
1333+
1334+
return Networking.NotificationSettings.Blog(
1335+
blogID: blogID,
1336+
devices: devices
1337+
)
1338+
}
1339+
}
1340+
1341+
extension Networking.NotificationSettings.Device {
1342+
public func copy(
1343+
deviceID: CopiableProp<Int64> = .copy,
1344+
newComment: CopiableProp<Bool> = .copy,
1345+
storeOrder: CopiableProp<Bool> = .copy
1346+
) -> Networking.NotificationSettings.Device {
1347+
let deviceID = deviceID ?? self.deviceID
1348+
let newComment = newComment ?? self.newComment
1349+
let storeOrder = storeOrder ?? self.storeOrder
1350+
1351+
return Networking.NotificationSettings.Device(
1352+
deviceID: deviceID,
1353+
newComment: newComment,
1354+
storeOrder: storeOrder
1355+
)
1356+
}
1357+
}
1358+
13141359
extension Networking.Order {
13151360
public func copy(
13161361
siteID: CopiableProp<Int64> = .copy,

Networking/Networking/Model/NotificationSettings.swift

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import Foundation
2+
import Codegen
23

34
/// Notification settings for a user
45
///
5-
public struct NotificationSettings: Equatable, Encodable {
6+
public struct NotificationSettings: Equatable, Codable, GeneratedCopiable {
67

78
/// Settings for different blogs connected to the user.
89
public let blogs: [Blog]
@@ -36,7 +37,7 @@ public struct NotificationSettings: Equatable, Encodable {
3637

3738
public extension NotificationSettings {
3839
/// Notification settings for a blog
39-
struct Blog: Equatable, Encodable {
40+
struct Blog: Equatable, Codable, GeneratedCopiable {
4041
/// ID of the blog
4142
public let blogID: Int64
4243

@@ -46,11 +47,37 @@ public extension NotificationSettings {
4647
enum CodingKeys: String, CodingKey {
4748
case blogID = "blog_id"
4849
case devices
50+
case device
51+
}
52+
53+
public init(blogID: Int64, devices: [Device]) {
54+
self.blogID = blogID
55+
self.devices = devices
56+
}
57+
58+
public func encode(to encoder: any Encoder) throws {
59+
var container = encoder.container(keyedBy: CodingKeys.self)
60+
try container.encode(blogID, forKey: .blogID)
61+
try container.encode(devices, forKey: .devices)
62+
}
63+
64+
public init(from decoder: any Decoder) throws {
65+
let container: KeyedDecodingContainer<NotificationSettings.Blog.CodingKeys> = try decoder.container(keyedBy: NotificationSettings.Blog.CodingKeys.self)
66+
self.blogID = try container.decode(Int64.self, forKey: NotificationSettings.Blog.CodingKeys.blogID)
67+
self.devices = try {
68+
if let array = try container.decodeIfPresent([NotificationSettings.Device].self, forKey: NotificationSettings.Blog.CodingKeys.devices) {
69+
return array
70+
} else if let device = try container.decodeIfPresent(NotificationSettings.Device.self, forKey: NotificationSettings.Blog.CodingKeys.device) {
71+
return [device]
72+
}
73+
return []
74+
}()
4975
}
5076
}
5177

5278
/// Notification settings for a device
53-
struct Device: Equatable, Encodable {
79+
struct Device: Equatable, Codable, GeneratedCopiable {
80+
5481
/// Unique ID of the device
5582
public let deviceID: Int64
5683

@@ -60,6 +87,12 @@ public extension NotificationSettings {
6087
/// Whether a notification should be sent when there is a new order on the store.
6188
public let storeOrder: Bool
6289

90+
public init(deviceID: Int64, newComment: Bool, storeOrder: Bool) {
91+
self.deviceID = deviceID
92+
self.newComment = newComment
93+
self.storeOrder = storeOrder
94+
}
95+
6396
enum CodingKeys: String, CodingKey {
6497
case deviceID = "device_id"
6598
case newComment = "new_comment"

Networking/Networking/Model/Site.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ public struct Site: Decodable, Equatable, Hashable, GeneratedFakeable, Generated
208208
}
209209
}
210210

211+
extension Site: Identifiable {
212+
public var id: Int64 { siteID }
213+
}
214+
211215
public extension Site {
212216
/// Whether the site is connected to Jetpack with Jetpack Connection Package, and not with Jetpack-the-plugin.
213217
///

Networking/Networking/Remote/AccountRemote.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public protocol AccountRemoteProtocol {
1616
func loadSitePlan(for siteID: Int64, completion: @escaping (Result<SitePlan, Error>) -> Void)
1717
func loadUsernameSuggestions(from text: String) async throws -> [String]
1818

19+
func loadNotificationSettings(deviceID: Int64) async throws -> NotificationSettings
20+
1921
func updateNotificationSettings(with settings: NotificationSettings) async throws
2022

2123
/// Creates a WPCOM account with the given email and password.
@@ -147,6 +149,13 @@ public class AccountRemote: Remote, AccountRemoteProtocol {
147149
return suggestions
148150
}
149151

152+
public func loadNotificationSettings(deviceID: Int64) async throws -> NotificationSettings {
153+
let path = Path.notificationSettings
154+
let parameters = [ParameterKey.deviceID: deviceID]
155+
let request = DotcomRequest(wordpressApiVersion: .mark1_1, method: .get, path: path, parameters: parameters)
156+
return try await enqueue(request)
157+
}
158+
150159
public func updateNotificationSettings(with settings: NotificationSettings) async throws {
151160
let path = Path.notificationSettings
152161
let parameters = try settings.toDictionary()
@@ -203,6 +212,7 @@ private extension AccountRemote {
203212

204213
enum ParameterKey {
205214
static let name = "name"
215+
static let deviceID = "device_id"
206216
}
207217

208218
enum Path {

Networking/NetworkingTests/Remote/AccountRemoteTests.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,41 @@ final class AccountRemoteTests: XCTestCase {
333333
// Then
334334
XCTAssertEqual(expectedError, errorCaught as? NetworkError)
335335
}
336+
337+
func test_loadNotificationSettings_succeeds_on_request_success() async throws {
338+
// Given
339+
let remote = AccountRemote(network: network)
340+
let deviceID: Int64 = 664
341+
network.simulateResponse(requestUrlSuffix: "me/notifications/settings",
342+
filename: "load-notification-settings-success")
343+
344+
// When
345+
let notificationSettings = try await remote.loadNotificationSettings(deviceID: deviceID)
346+
347+
// Then
348+
let expectedResult = NotificationSettings(blogs: [
349+
.init(blogID: 113, devices: [.init(deviceID: deviceID, newComment: true, storeOrder: true)]),
350+
.init(blogID: 72, devices: [.init(deviceID: deviceID, newComment: false, storeOrder: true)])
351+
])
352+
XCTAssertEqual(notificationSettings, expectedResult)
353+
}
354+
355+
func test_loadNotificationSettings_relays_error_on_request_failure() async {
356+
// Given
357+
let remote = AccountRemote(network: network)
358+
let deviceID: Int64 = 664
359+
let expectedError = NetworkError.timeout()
360+
network.simulateError(requestUrlSuffix: "me/notifications/settings", error: expectedError)
361+
362+
// When
363+
var errorCaught: Error?
364+
do {
365+
_ = try await remote.loadNotificationSettings(deviceID: deviceID)
366+
} catch {
367+
errorCaught = error
368+
}
369+
370+
// Then
371+
XCTAssertEqual(expectedError, errorCaught as? NetworkError)
372+
}
336373
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
{
2+
"blogs": [
3+
{
4+
"blog_id": 113,
5+
"timeline": {
6+
"new_comment": true,
7+
"comment_like": true,
8+
"post_like": true,
9+
"follow": true,
10+
"achievement": true,
11+
"mentions": true,
12+
"scheduled_publicize": true,
13+
"store_order": true,
14+
"blogging_prompt": false,
15+
"draft_post_prompt": true
16+
},
17+
"email": {
18+
"new_comment": true,
19+
"comment_like": true,
20+
"post_like": true,
21+
"follow": true,
22+
"achievement": true,
23+
"mentions": true,
24+
"scheduled_publicize": true,
25+
"store_order": true,
26+
"blogging_prompt": false,
27+
"draft_post_prompt": true
28+
},
29+
"device": {
30+
"device_id": 664,
31+
"new_comment": true,
32+
"comment_like": true,
33+
"post_like": true,
34+
"follow": true,
35+
"achievement": true,
36+
"mentions": true,
37+
"scheduled_publicize": true,
38+
"store_order": true,
39+
"blogging_prompt": false,
40+
"draft_post_prompt": true
41+
}
42+
},
43+
{
44+
"blog_id": 72,
45+
"timeline": {
46+
"new_comment": true,
47+
"comment_like": true,
48+
"post_like": true,
49+
"follow": true,
50+
"achievement": true,
51+
"mentions": true,
52+
"scheduled_publicize": true,
53+
"store_order": true,
54+
"blogging_prompt": false,
55+
"draft_post_prompt": true
56+
},
57+
"email": {
58+
"new_comment": true,
59+
"comment_like": true,
60+
"post_like": true,
61+
"follow": true,
62+
"achievement": true,
63+
"mentions": true,
64+
"scheduled_publicize": true,
65+
"store_order": true,
66+
"blogging_prompt": false,
67+
"draft_post_prompt": true
68+
},
69+
"device": {
70+
"device_id": 664,
71+
"new_comment": false,
72+
"comment_like": true,
73+
"post_like": true,
74+
"follow": true,
75+
"achievement": true,
76+
"mentions": true,
77+
"scheduled_publicize": true,
78+
"store_order": true,
79+
"blogging_prompt": false,
80+
"draft_post_prompt": true
81+
}
82+
}
83+
]
84+
}

RELEASE-NOTES.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
*** PLEASE FOLLOW THIS FORMAT: [<priority indicator, more stars = higher priority>] <description> [<PR URL>]
22
*** Use [*****] to indicate smoke tests of all critical flows should be run on the final IPA before release (e.g. major library or OS update).
33

4+
22.1
5+
-----
6+
7+
48
22.0
59
-----
610
- [*] Payments: Puerto Rico is now available in the list of countries that are supported by In-Person Payments, when using WooPayments [https://github.com/woocommerce/woocommerce-ios/pull/14972]
711
- [*] Product Form: Fix crash related to picking photos [https://github.com/woocommerce/woocommerce-ios/pull/15275]
812
- [internal] Assign `siteID` and `productID` to image product upload statuses. [https://github.com/woocommerce/woocommerce-ios/pull/15196]
913
- [*] Payments: Improved payment views' adaptability to larger accessibility font sizes [https://github.com/woocommerce/woocommerce-ios/pull/15328].
14+
- [*] Added an entry point to Troubleshoot Connection from the Settings screen. [https://github.com/woocommerce/woocommerce-ios/pull/15391]
15+
- [**] Filters in Orders and Products lists are now persisted. [https://github.com/woocommerce/woocommerce-ios/issues/14791]
1016
- [internal] Add site related properties to crowdsignal surveys. [https://github.com/woocommerce/woocommerce-ios/pull/15353]
17+
- [**] Notification settings are now available for configuring notifications for each store. [https://github.com/woocommerce/woocommerce-ios/pull/15386]
1118

1219
21.9
1320
-----

WooCommerce/Classes/Analytics/WooAnalyticsEvent+ConnectivityTool.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ extension WooAnalyticsEvent {
2020
.init(statName: .ordersListTopBannerTroubleshootTapped, properties: [:])
2121
}
2222

23+
static func settingsTroubleshootTapped() -> WooAnalyticsEvent {
24+
.init(statName: .settingsTroubleshootConnectionTapped, properties: [:])
25+
}
26+
2327
static func requestResponse(test: Test, success: Bool, timeTaken: Double) -> WooAnalyticsEvent {
2428
.init(statName: .connectivityToolRequestResponse,
2529
properties: [

0 commit comments

Comments
 (0)