Skip to content

Commit

Permalink
Simplifty User Service
Browse files Browse the repository at this point in the history
  • Loading branch information
jkmassel committed Nov 18, 2024
1 parent 41b8062 commit 14f860c
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 159 deletions.
4 changes: 2 additions & 2 deletions Modules/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ let package = Package(
.package(url: "https://github.com/wordpress-mobile/WordPressKit-iOS", branch: "task/reader-discover"),
.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-swift-20240813"),
.package(url: "https://github.com/Automattic/wordpress-rs", revision: "alpha-20241116"),
.package(url: "https://github.com/wordpress-mobile/GutenbergKit", revision: "6cc307e7fc24910697be5f71b7d70f465a9c0f63"),
.package(url: "https://github.com/Automattic/color-studio", branch: "trunk"),
],
Expand Down Expand Up @@ -161,7 +161,7 @@ enum XcodeSupport {
.product(name: "ZendeskSupportSDK", package: "support_sdk_ios"),
.product(name: "ZIPFoundation", package: "ZIPFoundation"),
.product(name: "WordPressAPI", package: "wordpress-rs"),
.product(name: "ColorStudio", package: "color-studio"),
.product(name: "ColorStudio", package: "color-studio")
]),
.xcodeTarget("XcodeTarget_WordPressTests", dependencies: testDependencies + [
"WordPressShared",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct UserListItem: View {
}
}
}

#Preview {
UserListItem(user: DisplayUser.MockUser, userService: MockUserProvider())
}
//
//#Preview {
// UserListItem(user: DisplayUser.MockUser, userService: MockUserProvider())
//}
109 changes: 49 additions & 60 deletions Modules/Sources/WordPressUI/Views/Users/UserProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,59 @@ import Foundation
import Combine

public protocol UserServiceProtocol: Actor {
var users: [DisplayUser]? { get }
nonisolated var usersUpdates: AsyncStream<[DisplayUser]> { get }

func fetchUsers() async throws -> [DisplayUser]
func fetchPaginatedUsers() -> AsyncThrowingStream<[DisplayUser], Error>

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

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

func deleteUser(id: Int32, reassigningPostsTo newUserId: Int32) async throws
}

package actor MockUserProvider: UserServiceProtocol {

enum Scenario {
case infinitLoading
case dummyData
case error
}

var scenario: Scenario

package nonisolated let usersUpdates: AsyncStream<[DisplayUser]>
private let usersUpdatesContinuation: AsyncStream<[DisplayUser]>.Continuation

package private(set) var users: [DisplayUser]? {
didSet {
if let users {
usersUpdatesContinuation.yield(users)
}
}
}

init(scenario: Scenario = .dummyData) {
self.scenario = scenario
(usersUpdates, usersUpdatesContinuation) = AsyncStream<[DisplayUser]>.makeStream()
}

package func fetchUsers() async throws -> [DisplayUser] {
switch scenario {
case .infinitLoading:
// Do nothing
try await Task.sleep(for: .seconds(24 * 60 * 60))
return []
case .dummyData:
let dummyDataUrl = URL(string: "https://my.api.mockaroo.com/users.json?key=067c9730")!
let response = try await URLSession.shared.data(from: dummyDataUrl)
let users = try JSONDecoder().decode([DisplayUser].self, from: response.0)
self.users = users
return users
case .error:
throw URLError(.timedOut)
}
}

package func isCurrentUserCapableOf(_ capability: String) async throws -> Bool {
true
}

package func setNewPassword(id: Int32, newPassword: String) async throws {
// Not used in Preview
}

package func deleteUser(id: Int32, reassigningPostsTo newUserId: Int32) async throws {
// Not used in Preview
}
}
//
//package actor MockUserProvider: UserServiceProtocol {
//
// enum Scenario {
// case infinitLoading
// case dummyData
// case error
// }
//
// var scenario: Scenario
//
// init(scenario: Scenario = .dummyData) {
// self.scenario = scenario
// }
//
// public func fetchUsers() async throws -> any AsyncSequence {
// [DisplayUser]().async
// }
//
//
// package func fetchUsers() async throws -> [DisplayUser] {
// switch scenario {
// case .infinitLoading:
// // Do nothing
// try await Task.sleep(for: .seconds(24 * 60 * 60))
// return []
// case .dummyData:
// let dummyDataUrl = URL(string: "https://my.api.mockaroo.com/users.json?key=067c9730")!
// let response = try await URLSession.shared.data(from: dummyDataUrl)
// let users = try JSONDecoder().decode([DisplayUser].self, from: response.0)
// return users
// case .error:
// throw URLError(.timedOut)
// }
// }
//
// package func isCurrentUserCapableOf(_ capability: String) async throws -> Bool {
// true
// }
//
// package func setNewPassword(id: Int32, newPassword: String) async throws {
// // Not used in Preview
// }
//
// package func deleteUser(id: Int32, reassigningPostsTo newUserId: Int32) async throws {
// // Not used in Preview
// }
//}
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ public class UserDeleteViewModel: ObservableObject {
}

do {
let users = try await userService.fetchUsers()
self.otherUsers = users
let users = await userService.fetchPaginatedUsers()
self.otherUsers = try await users
.reduce(into: [DisplayUser](), { partialResult, users in
partialResult.append(contentsOf: users)
})
.filter { $0.id != self.user.id } // Don't allow re-assigning to yourself
.sorted(using: KeyPathComparator(\.username))
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ class UserListViewModel: ObservableObject {
}

/// The initial set of users fetched by `fetchItems`
private var users: [DisplayUser] = [] {
private var users: [Int32: DisplayUser] = [:] {
didSet {
sortedUsers = self.sortUsers(users)
sortedUsers = self.sortUsers(Array(users.values))
}
}
private var updateUsersTask: Task<Void, Never>?
Expand All @@ -36,7 +36,7 @@ class UserListViewModel: ObservableObject {
if searchTerm.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
setSearchResults(sortUsers(users))
} else {
let searchResults = users.search(searchTerm, using: \.searchString)
let searchResults = users.values.search(searchTerm, using: \.searchString)
setSearchResults([Section(role: "Search Results", users: searchResults)])
}
}
Expand All @@ -51,41 +51,40 @@ class UserListViewModel: ObservableObject {
}

func onAppear() async {
if updateUsersTask == nil {
updateUsersTask = Task { @MainActor [weak self, usersUpdates = userService.usersUpdates] in
for await users in usersUpdates {
guard let self else { break }

self.users = users
}
}
}

if !initialLoad {
initialLoad = true
await fetchItems()
}
}

private func fetchItems() async {
isLoadingItems = true
defer { isLoadingItems = false }

_ = try? await userService.fetchUsers()
do {
for try await page in await userService.fetchPaginatedUsers() {
for user in page {
self.users[user.id] = user
}

// Show results after the first page has loaded
isLoadingItems = false
}
} catch {
self.error = error
}
}

@Sendable
func refreshItems() async {
_ = try? await userService.fetchUsers()
await fetchItems()
}

func setUsers(_ newValue: [DisplayUser]) {
withAnimation {
self.users = newValue
self.sortedUsers = sortUsers(newValue)
isLoadingItems = false
}
}
// func setUsers(_ newValue: [DisplayUser]) {
// withAnimation {
// self.users = newValue
// self.sortedUsers = sortUsers(newValue)
// isLoadingItems = false
// }
// }

func setSearchResults(_ newValue: [Section]) {
withAnimation {
Expand All @@ -98,4 +97,10 @@ class UserListViewModel: ObservableObject {
.map { Section(role: $0.key, users: $0.value.sorted(by: { $0.username < $1.username })) }
.sorted { $0.role < $1.role }
}

private func sortUsers(_ users: [Int32: DisplayUser]) -> [Section] {
Dictionary(grouping: users.values, by: { $0.role })
.map { Section(role: $0.key, users: $0.value.sorted(by: { $0.username < $1.username })) }
.sorted { $0.role < $1.role }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,8 @@ private extension String {
}
}

#Preview {
NavigationStack {
UserDetailsView(user: DisplayUser.MockUser, userService: MockUserProvider())
}
}
//#Preview {
// NavigationStack {
// UserDetailsView(user: DisplayUser.MockUser, userService: MockUserProvider())
// }
//}
36 changes: 18 additions & 18 deletions Modules/Sources/WordPressUI/Views/Users/Views/UserListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,21 @@ public struct UserListView: View {
)
}
}

#Preview("Loading") {
NavigationView {
UserListView(userService: MockUserProvider())
}
}

#Preview("Error") {
NavigationView {
UserListView(userService: MockUserProvider(scenario: .error))
}
}

#Preview("List") {
NavigationView {
UserListView(userService: MockUserProvider(scenario: .dummyData))
}
}
//
//#Preview("Loading") {
// NavigationView {
// UserListView(userService: MockUserProvider())
// }
//}
//
//#Preview("Error") {
// NavigationView {
// UserListView(userService: MockUserProvider(scenario: .error))
// }
//}
//
//#Preview("List") {
// NavigationView {
// UserListView(userService: MockUserProvider(scenario: .dummyData))
// }
//}
4 changes: 2 additions & 2 deletions WordPress.xcworkspace/xcshareddata/swiftpm/Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/Automattic/wordpress-rs",
"state" : {
"branch" : "alpha-swift-20240813",
"revision" : "b51c560d83a61917eb3361441e65779b71fb96ce"
"branch" : "alpha-20241116",
"revision" : "1249ae77fcea2e836b7878a1b1cffdf6c1080256"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion WordPress/Classes/Networking/WordPressClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ actor WordPressClient {
try await self.api.plugins.create(params: PluginCreateParams(
slug: "InstallJetpack",
status: .active
))
)).data
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import WordPressAPI
}

private func fetchTokens(forUserId userId: Int32) async throws -> [ApplicationPasswordWithEditContext] {
try await apiClient.api.applicationPasswords.listWithEditContext(userId: userId)
try await apiClient.api.applicationPasswords.listWithEditContext(userId: userId).data
}
}

Expand Down
Loading

0 comments on commit 14f860c

Please sign in to comment.