Skip to content

Commit

Permalink
Merge branch 'trunk' into use-ephemeral-session
Browse files Browse the repository at this point in the history
  • Loading branch information
crazytonyli committed Jan 30, 2025
2 parents 9469a80 + c4f564c commit 70be504
Show file tree
Hide file tree
Showing 46 changed files with 228 additions and 276 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ gem 'fastlane-plugin-sentry'
# gem 'fastlane-plugin-wpmreleasetoolkit', git: 'https://github.com/wordpress-mobile/release-toolkit', ref: ''
gem 'fastlane-plugin-wpmreleasetoolkit', '~> 12.4'
gem 'rake'
gem 'rubocop', '~> 1.69'
gem 'rubocop', '~> 1.71'
gem 'rubocop-rake', '~> 0.6'
gem 'xcpretty-travis-formatter'

Expand Down
16 changes: 8 additions & 8 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ GEM
xcpretty (~> 0.4.0)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-plugin-appcenter (2.1.2)
fastlane-plugin-sentry (1.25.1)
fastlane-plugin-sentry (1.27.1)
os (~> 1.1, >= 1.1.4)
fastlane-plugin-wpmreleasetoolkit (12.4.0)
activesupport (>= 6.1.7.1)
Expand Down Expand Up @@ -280,14 +280,14 @@ GEM
concurrent-ruby (~> 1.0)
java-properties (0.3.0)
jmespath (1.6.2)
json (2.9.0)
json (2.9.1)
jwt (2.9.3)
base64
kramdown (2.5.1)
rexml (>= 3.3.9)
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
language_server-protocol (3.17.0.3)
language_server-protocol (3.17.0.4)
logger (1.6.3)
mini_magick (4.13.2)
mini_mime (1.1.5)
Expand All @@ -312,7 +312,7 @@ GEM
optparse (0.6.0)
os (1.1.4)
parallel (1.26.3)
parser (3.3.6.0)
parser (3.3.7.0)
ast (~> 2.4.1)
racc
pkg-config (1.5.7)
Expand All @@ -328,7 +328,7 @@ GEM
rake-compiler (1.2.8)
rake
rchardet (1.8.0)
regexp_parser (2.9.3)
regexp_parser (2.10.0)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
Expand All @@ -338,7 +338,7 @@ GEM
rmagick (5.3.0)
pkg-config (~> 1.4)
rouge (3.28.0)
rubocop (1.69.2)
rubocop (1.71.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
Expand All @@ -348,7 +348,7 @@ GEM
rubocop-ast (>= 1.36.2, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.37.0)
rubocop-ast (1.38.0)
parser (>= 3.3.1.0)
rubocop-rake (0.6.0)
rubocop (~> 1.0)
Expand Down Expand Up @@ -410,7 +410,7 @@ DEPENDENCIES
fastlane-plugin-wpmreleasetoolkit (~> 12.4)
rake
rmagick (~> 5.3.0)
rubocop (~> 1.69)
rubocop (~> 1.71)
rubocop-rake (~> 0.6)
xcpretty-travis-formatter

Expand Down
9 changes: 6 additions & 3 deletions Modules/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ let package = Package(
.package(url: "https://github.com/wordpress-mobile/WordPressKit-iOS", branch: "wpios-edition"),
.package(url: "https://github.com/zendesk/support_sdk_ios", from: "8.0.3"),
// We can't use wordpress-rs branches nor commits here. Only tags work.
.package(url: "https://github.com/Automattic/wordpress-rs", revision: "alpha-20241116"),
.package(url: "https://github.com/wordpress-mobile/GutenbergKit", revision: "f8c5c417c789c8d052093838e622828ae4ec076f"),
.package(url: "https://github.com/Automattic/wordpress-rs", revision: "alpha-20250127"),
.package(url: "https://github.com/wordpress-mobile/GutenbergKit", revision: "fb31301ea6a94376237947afb4242f75c074f43c"),
.package(url: "https://github.com/Automattic/color-studio", branch: "trunk"),
],
targets: XcodeSupport.targets + [
Expand All @@ -63,6 +63,7 @@ let package = Package(
.product(name: "XCUITestHelpers", package: "XCUITestHelpers"),
], swiftSettings: [.swiftLanguageMode(.v5)]),
.target(name: "WordPressFlux", swiftSettings: [.swiftLanguageMode(.v5)]),
.target(name: "WordPressCore", dependencies: [.target(name: "WordPressShared"), .product(name: "WordPressAPI", package: "wordpress-rs")]),
.target(name: "WordPressSharedObjC", resources: [.process("Resources")], swiftSettings: [.swiftLanguageMode(.v5)]),
.target(name: "WordPressShared", dependencies: [.target(name: "WordPressSharedObjC")], resources: [.process("Resources")], swiftSettings: [.swiftLanguageMode(.v5)]),
.target(name: "WordPressTesting", resources: [.process("Resources")]),
Expand All @@ -85,7 +86,8 @@ let package = Package(
]),
.testTarget(name: "WordPressSharedTests", dependencies: [.target(name: "WordPressShared")], swiftSettings: [.swiftLanguageMode(.v5)]),
.testTarget(name: "WordPressSharedObjCTests", dependencies: [.target(name: "WordPressShared"), .target(name: "WordPressTesting")], swiftSettings: [.swiftLanguageMode(.v5)]),
.testTarget(name: "WordPressUITests", dependencies: [.target(name: "WordPressUI")], swiftSettings: [.swiftLanguageMode(.v5)]),
.testTarget(name: "WordPressUIUnitTests", dependencies: [.target(name: "WordPressUI")], swiftSettings: [.swiftLanguageMode(.v5)]),
.testTarget(name: "WordPressCoreTests", dependencies: [.target(name: "WordPressCore")]),
]
)

Expand Down Expand Up @@ -156,6 +158,7 @@ enum XcodeSupport {
"WordPressShared",
"AsyncImageKit",
"WordPressUI",
"WordPressCore",
.product(name: "Alamofire", package: "Alamofire"),
.product(name: "AutomatticAbout", package: "AutomatticAbout-swift"),
.product(name: "AutomatticTracks", package: "Automattic-Tracks-iOS"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation
/// An abstraction of local data storage, with CRUD operations.
public protocol DataStore: Actor {
associatedtype T: Identifiable & Sendable
associatedtype Query
associatedtype Query: Sendable

func list(query: Query) async throws -> [T]
func delete(query: Query) async throws
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import Combine
@preconcurrency import Combine

/// A `DataStore` type that stores data in memory.
public protocol InMemoryDataStore: DataStore {
Expand All @@ -16,11 +16,11 @@ public protocol InMemoryDataStore: DataStore {

public extension InMemoryDataStore {
func delete(query: Query) async throws {
var updated = Set<T.ID>()
let result = try await list(query: query)
result.forEach {
if storage.removeValue(forKey: $0.id) != nil {
updated.insert($0.id)
var updated = Set<T.ID>()
for item in result {
if storage.removeValue(forKey: item.id) != nil {
updated.insert(item.id)
}
}

Expand All @@ -31,9 +31,9 @@ public extension InMemoryDataStore {

func store(_ data: [T]) async throws {
var updated = Set<T.ID>()
data.forEach {
updated.insert($0.id)
self.storage[$0.id] = $0
for item in data {
updated.insert(item.id)
self.storage[item.id] = item
}

if !updated.isEmpty {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import Foundation
import WordPressShared

public struct DisplayUser: Identifiable, Codable, Hashable {
public let id: Int32
public struct DisplayUser: Identifiable, Codable, Hashable, Sendable {
public let id: Int64
public let handle: String
public let username: String
public let firstName: String
Expand All @@ -17,7 +16,7 @@ public struct DisplayUser: Identifiable, Codable, Hashable {
public let biography: String?

public init(
id: Int32,
id: Int64,
handle: String,
username: String,
firstName: String,
Expand All @@ -42,7 +41,7 @@ public struct DisplayUser: Identifiable, Codable, Hashable {
self.biography = biography
}

static let MockUser = DisplayUser(
public static let mockUser = DisplayUser(
id: 16,
handle: "@person",
username: "example",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import Foundation
import Combine
@preconcurrency import Combine
import WordPressShared

public protocol UserDataStore: DataStore where T == DisplayUser, Query == UserDataStoreQuery {
}

public enum UserDataStoreQuery: Equatable, Sendable {
case all
case id(Set<DisplayUser.ID>)
case search(String)
}

public actor InMemoryUserDataStore: UserDataStore, InMemoryDataStore {
public typealias T = DisplayUser
Expand All @@ -11,6 +21,8 @@ public actor InMemoryUserDataStore: UserDataStore, InMemoryDataStore {
updates.send(completion: .finished)
}

public init() {}

public func list(query: Query) throws -> [T] {
switch query {
case .all:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import Foundation
import Combine
import WordPressAPI
import WordPressUI

/// UserService is responsible for fetching user acounts via the .org REST API – it's the replacement for `UsersService` (the XMLRPC-based approach)
///
actor UserService: UserServiceProtocol, UserDataStoreProvider {
public actor UserService: UserServiceProtocol {
private let client: WordPressClient

private let _dataStore: InMemoryUserDataStore = .init()
var userDataStore: any UserDataStore { _dataStore }
private let userDataStore: InMemoryUserDataStore = .init()

private var _currentUser: UserWithEditContext?
private var currentUser: UserWithEditContext? {
Expand All @@ -21,47 +18,59 @@ actor UserService: UserServiceProtocol, UserDataStoreProvider {
}
}

init(client: WordPressClient) {
public init(client: WordPressClient) {
self.client = client
}

func fetchUsers() async throws {
public func fetchUsers() async throws {
let sequence = await client.api.users.sequenceWithEditContext(params: .init(perPage: 100))
var started = false
for try await users in sequence {
if !started {
try await _dataStore.delete(query: .all)
try await userDataStore.delete(query: .all)
}

try await _dataStore.store(users.compactMap { DisplayUser(user: $0) })
try await userDataStore.store(users.compactMap { DisplayUser(user: $0) })

started = true
}
}

func isCurrentUserCapableOf(_ capability: String) async -> Bool {
public func isCurrentUserCapableOf(_ capability: String) async -> Bool {
await currentUser?.capabilities.keys.contains(capability) == true
}

func deleteUser(id: Int32, reassigningPostsTo newUserId: Int32) async throws {
public func deleteUser(id: Int64, reassigningPostsTo newUserId: Int64) async throws {
let result = try await client.api.users.delete(
userId: id,
params: UserDeleteParams(reassign: newUserId)
).data

// Remove the deleted user from the cached users list.
if result.deleted {
try await _dataStore.delete(query: .id([id]))
try await userDataStore.delete(query: .id([id]))
}
}

func setNewPassword(id: Int32, newPassword: String) async throws {
public func setNewPassword(id: Int64, newPassword: String) async throws {
_ = try await client.api.users.update(
userId: Int32(id),
userId: id,
params: UserUpdateParams(password: newPassword)
)
}

public func allUsers() async throws -> [DisplayUser] {
try await userDataStore.list(query: .all)
}

public func streamSearchResult(input: String) async -> AsyncStream<Result<[DisplayUser], Error>> {
await userDataStore.listStream(query: .search(input))
}

public func streamAll() async -> AsyncStream<Result<[DisplayUser], Error>> {
await userDataStore.listStream(query: .all)
}

}

private extension DisplayUser {
Expand All @@ -86,12 +95,9 @@ private extension DisplayUser {
}

static func profilePhotoUrl(for user: UserWithEditContext) -> URL? {
// The key is the size of the avatar. Get the largetst one, which is 96x96px.
// https://github.com/WordPress/wordpress-develop/blob/6.6.2/src/wp-includes/rest-api.php#L1253-L1260
guard let url = user.avatarUrls?
.max(by: { $0.key.compare($1.key, options: .numeric) == .orderedAscending } )?
.value
else { return nil }
guard let url = user.avatarUrls?[.size96] ?? user.avatarUrls?[.size48] ?? user.avatarUrls?[.size24], let url else {
return nil
}

return URL(string: url)
}
Expand Down
18 changes: 18 additions & 0 deletions Modules/Sources/WordPressCore/Users/UserServiceProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation
import WordPressAPI

public protocol UserServiceProtocol: Actor {
func fetchUsers() async throws

func isCurrentUserCapableOf(_ capability: String) async -> Bool

func setNewPassword(id: UserId, newPassword: String) async throws

func deleteUser(id: UserId, reassigningPostsTo newUserId: UserId) async throws

func allUsers() async throws -> [DisplayUser]

func streamSearchResult(input: String) async -> AsyncStream<Result<[DisplayUser], Error>>

func streamAll() async -> AsyncStream<Result<[DisplayUser], Error>>
}
14 changes: 14 additions & 0 deletions Modules/Sources/WordPressCore/WordPressClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation
import WordPressAPI

public actor WordPressClient {

public let api: WordPressAPI
private let rootUrl: String

public init(api: WordPressAPI, rootUrl: ParsedUrl) {
self.api = api
self.rootUrl = rootUrl.url()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import OHHTTPStubsSwift
private let cache = MockMemoryCache()

init() async throws {
sut = ImageDownloader(cache: cache, authenticator: nil)
sut = ImageDownloader(cache: cache)
}

deinit {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Foundation
import Testing

@testable import WordPress
import WordPressCore

@Suite(.timeLimit(.minutes(1)))
struct InMemoryDataStoreTests {
Expand All @@ -25,7 +24,7 @@ struct InMemoryDataStoreTests {

Task.detached {
try await Task.sleep(for: .milliseconds(50))
try await store.store([.MockUser])
try await store.store([.mockUser])
}

await confirmation("The stream produces an update", expectedCount: 2) { confirmation in
Expand All @@ -38,7 +37,7 @@ struct InMemoryDataStoreTests {
@Test
func testUpdatesAfterDelete() async throws {
let store: InMemoryUserDataStore = InMemoryUserDataStore()
try await store.store([.MockUser])
try await store.store([.mockUser])

let stream = await store.listStream(query: .all)

Expand Down
2 changes: 2 additions & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
25.8
-----
* [*] Enable dismissing the virtual keyboard in the experimental editor [#23988]
* [*] Remove Submit Feedback form from the Me menu [#24020]


25.7
-----
Expand Down
Loading

0 comments on commit 70be504

Please sign in to comment.