Skip to content

Conversation

@crazytonyli
Copy link
Contributor

The majority of code in this PR is taken from #23572. See the first comment.

I added some minor changes in the following two commits: Removing a couple of global static variables and adding a feature flag for the new User Management feature.

I will make more changes to fine-tune both the UI and the implementation.

Regression Notes

  1. Potential unintended areas of impact

  2. What I did to test those areas of impact (or what existing automated tests I relied on)

  3. What automated tests I added (or what prevented me from doing so)

PR submission checklist:

  • I have completed the Regression Notes.
  • I have considered adding unit tests for my changes.
  • I have considered adding accessibility improvements for my changes.
  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

Testing checklist:

  • WordPress.com sites and self-hosted Jetpack sites.
  • Portrait and landscape orientations.
  • Light and dark modes.
  • Fonts: Larger, smaller and bold text.
  • High contrast.
  • VoiceOver.
  • Languages with large words or with letters/accents not frequently used in English.
  • Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
  • iPhone and iPad.
  • Multi-tasking: Split view and Slide over. (iPad)

@crazytonyli crazytonyli added this to the 25.6 milestone Nov 5, 2024
@crazytonyli crazytonyli requested review from jkmassel and kean November 5, 2024 05:58
@dangermattic
Copy link
Collaborator

3 Warnings
⚠️ Modules/Package.swift was changed without updating its corresponding Package.resolved. Please resolve the Swift packages as appropriate to your project setup (e.g. in Xcode or by running swift package resolve).
⚠️ This PR is larger than 500 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.
⚠️ This PR is assigned to the milestone 25.6. This milestone is due in less than 4 days.
Please make sure to get it merged by then or assign it to a milestone with a later deadline.

Generated by 🚫 Danger

@wpmobilebot
Copy link
Contributor

WordPress Alpha📲 You can test the changes from this Pull Request in WordPress Alpha by scanning the QR code below to install the corresponding build.
App NameWordPress Alpha WordPress Alpha
ConfigurationRelease-Alpha
Build Numberpr23768-f656ec1
Version25.4.2
Bundle IDorg.wordpress.alpha
Commitf656ec1
App Center BuildWPiOS - One-Offs #10986
Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.

@wpmobilebot
Copy link
Contributor

Jetpack Alpha📲 You can test the changes from this Pull Request in Jetpack Alpha by scanning the QR code below to install the corresponding build.
App NameJetpack Alpha Jetpack Alpha
ConfigurationRelease-Alpha
Build Numberpr23768-f656ec1
Version25.4.2
Bundle IDcom.jetpack.alpha
Commitf656ec1
App Center Buildjetpack-installable-builds #10026
Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.

@kean
Copy link
Contributor

kean commented Nov 5, 2024

  • Email is displayed in two lines – looks off
  • Profile picture low quality
  • I just deleted myself and it was the only user on my local tests site, so can no longer login in my site 🤣 I think it needs a bit more confirmation/alerts if you are deleting yourself.

@crazytonyli
Copy link
Contributor Author

Yep. I have a TODO list for those issues. As I mentioned in the PR description, they'll be addressed in future PRs.

I just deleted myself

🤣 I applaud your adventurous spirit! I saw the button but didn't have the guts to tap it. I'm surprised REST API would allow you to do that...

@crazytonyli crazytonyli mentioned this pull request Nov 6, 2024
14 tasks
}

/// Objects conforming to `StringRankedSearchable` can be searched by calling `search(query:)` on a collection of them
public protocol StringRankedSearchable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest removing the protocol and sticking with closures. The same type can have different search criteria depending on the context, so passing a closure is more flexible. This is similar to the way filter(:) uses closures and not protocols.

It may be worth adding a generic to Collection or something like this, but I'm not sure it's worth it.

extension Collection {
    func search(searchTerm: query, _ closure: (Element) -> String)
}

var searchString: String { get }
}

public extension Collection where Iterator.Element: StringRankedSearchable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) I think Collection should have Element, so no need to reach for the Iterator

@ScaledMetric(relativeTo: .headline)
var height: CGFloat = 48

@Environment(\.sizeCategory)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private let userProvider: UserDataProvider
private let actionDispatcher: UserManagementActionDispatcher

init(user: DisplayUser, userProvider: UserDataProvider, actionDispatcher: UserManagementActionDispatcher) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) I usually take advantage of the auto-generated init for structs by making the required parameters internal.

/// to perform user management actions.
///
/// The default implementation is set up for testing with SwiftUI Previews
open class UserManagementActionDispatcher: ObservableObject {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The app doesn't use this type of naming elsewhere ("Dispatcher"). I'd suggest sticking with the current naming conventions. I would also suggest avoiding subclassing by adding a protocol like this:

protocol UserManagementServiceProtocol {
    // Changed from "fetchCurrentUserCan" and made non-async (we aren't going to perform a fetch for every individual capability?)
    func hasCapability(_ capability: String) -> Bool

    // Or something else other that a separate CachedUserListCallback closure
    func getCachedUsers() -> [User]

    func fetchUsers() async throws -> [WordPressUI.DisplayUser]
    
    // Why was it async throws? Noting that it seems to be unused.
    func invalidateCaches() 

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

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

Copy link
Contributor

@kean kean Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also not clear what WordPressUI.DisplayUser is and what UserDataProvider belongs to.

If it's a model layer, it should returns users as is: User, SiteUser?. Perhaps there needs to be a namespace for the types associated with the WordPress sites to disambiguate. The name DisplayUser doesn't make much sense.

If it's a ViewModel layer, it should be UserView(Cell)Model.

var body: some View {
Form {
Section(Strings.nameSectionTitle) {
LabeledContent(Strings.roleFieldTitle, value: user.role)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL didn't know about LabeledContent, nice.

} label: {
HStack {
Spacer()
Text("Delete User")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing localization (in the whole file).

}

NavigationLink {
// Pass this view's dismiss action, because if we delete a user, we want that screen *and* this one gone
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both of these actions – "Set New Password" and "Delete User" should be shown as modal screens.
"Delete User" should be destructive.

}

Section(Strings.contactInfoSectionTitle) {
LabeledContent(Strings.emailAddressFieldTitle, value: user.emailAddress)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not the best way to display it. I'd suggest a vertical stack.
Screenshot 2024-11-06 at 11 49 50 AM

}, label: {
HStack {
Spacer()
Text("Save Changes")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Localization (entire file).

@crazytonyli
Copy link
Contributor Author

crazytonyli commented Nov 6, 2024

@kean Thanks for the review! I have addressed most of your comments in their own commits at #23777. A couple of things that I didn't address because I plan to refactor them later

  • UserService, UserDataProvider, and ActionDispatcher.
  • The set new password and the delete user flows.

@crazytonyli crazytonyli requested a review from kean November 6, 2024 22:43
@crazytonyli crazytonyli added this pull request to the merge queue Nov 7, 2024
Merged via the queue into trunk with commit 2d76a25 Nov 7, 2024
24 checks passed
@crazytonyli crazytonyli deleted the add/self-hosted-site-users-list-view branch November 7, 2024 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants