Skip to content

Commit

Permalink
Merge branch 'trunk' into task/23048-stats-total-subscribers-card
Browse files Browse the repository at this point in the history
  • Loading branch information
guarani committed Apr 26, 2024
2 parents 97af0e8 + ed5ed59 commit c307735
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 52 deletions.
33 changes: 0 additions & 33 deletions WordPress/Classes/Utility/ContextManager.swift
Expand Up @@ -337,36 +337,3 @@ private enum SaveContextOption {
case asynchronouslyWithCallback(completion: () -> Void, queue: DispatchQueue)
case alreadyInContextQueue
}

/// Use this temporary workaround to mitigate Core Data concurrency issues when accessing the given `object`.
///
/// When the app is launched from Xcode, some code may crash due to the effect of the "com.apple.CoreData.ConcurrencyDebug"
/// launch argument. The crash indicates the crash site violates [the following rule](https://developer.apple.com/documentation/coredata/using_core_data_in_the_background#overview)
///
/// > To use Core Data in a multithreaded environment, ensure that:
/// > - Managed objects retrieved from a context are bound to the same queue that the context is bound to.
///
/// This function can be used as a temporary workaround to mitigate aforementioned crashes during development.
///
/// - Warning: The workaround does not apply to release builds. In a release build, calling this function is exactly
/// the same as calling the given `closure` directly.
///
/// - Warning: This function is _not_ a solution for Core Data concurrency issues, and should only be used as a
/// temporary solution, to avoid the Core Data concurrency issue becoming a blocker to feature developlement.
@available(*, deprecated, message: "This workaround is meant as a temporary solution to mitigate Core Data concurrency issues when accessing the `object`. Please see this function's API doc for details.")
@inlinable
public func workaroundCoreDataConcurrencyIssue<Value>(accessing object: NSManagedObject, _ closure: () -> Value) -> Value {
#if DEBUG
guard let context = object.managedObjectContext else {
fatalError("The object must be bound to a context: \(object)")
}

var value: Value!
context.performAndWait {
value = closure()
}
return value
#else
return closure()
#endif /* DEBUG */
}
Expand Up @@ -582,6 +582,7 @@ extension GutenbergViewController {

// MARK: - GutenbergBridgeDelegate

/// - warning: the app can't make any assumption about the thread on which `GutenbergBridgeDelegate` gets invoked. In some scenarios, it gets called from the main thread, for example, if being invoked directly from [Gutenberg.swift](https://github.com/WordPress/gutenberg/blob/64f9d9d1ced7a5aa7f3874890306554c5b703ce6/packages/react-native-bridge/ios/Gutenberg.swift). And sometimes, it gets called on a dispatch queue created by the React Native runtime for a native module (see [React Native: Threading](https://reactnative.dev/docs/native-modules-ios#threading). It happens when the methods are invoked directly from JavaScript.
extension GutenbergViewController: GutenbergBridgeDelegate {
func gutenbergDidGetRequestFetch(path: String, completion: @escaping (Result<Any, NSError>) -> Void) {
guard let context = post.managedObjectContext else {
Expand Down Expand Up @@ -1139,14 +1140,14 @@ extension GutenbergViewController: GutenbergBridgeDataSource {
}

func gutenbergMediaSources() -> [Gutenberg.MediaSource] {
workaroundCoreDataConcurrencyIssue(accessing: post) {
post.managedObjectContext?.performAndWait {
[
post.blog.supports(.stockPhotos) ? .stockPhotos : nil,
post.blog.supports(.tenor) ? .tenor : nil,
.otherApps,
.allFiles,
].compactMap { $0 }
}
} ?? []
}

func gutenbergCapabilities() -> [Capabilities: Bool] {
Expand Down
39 changes: 30 additions & 9 deletions WordPress/Classes/ViewRelated/People/PeopleViewController.swift
Expand Up @@ -27,6 +27,10 @@ class PeopleViewController: UITableViewController {
}
}

/// Default Filter value when People loads
///
fileprivate var defaultFilter = Filter.users

/// NoResults Helper
///
private let noResultsViewController = NoResultsViewController.controller()
Expand Down Expand Up @@ -240,12 +244,9 @@ extension PeopleViewController: NetworkStatusDelegate {
}
}

// MARK: - Private behavior

private extension PeopleViewController {

// MARK: Enums
// MARK: - Enum

extension PeopleViewController {
enum Filter: String, CaseIterable, FilterTabBarItem {

case users = "users"
Expand All @@ -262,11 +263,11 @@ private extension PeopleViewController {
case .users:
return NSLocalizedString("Users", comment: "Blog Users")
case .followers:
return NSLocalizedString("Followers", comment: "Blog Followers")
return NSLocalizedString("users.list.title.subscribers", value: "Subscribers", comment: "Site Subscribers")
case .viewers:
return NSLocalizedString("Viewers", comment: "Blog Viewers")
case .email:
return NSLocalizedString("Email Followers", comment: "Blog Email Followers")
return NSLocalizedString("users.list.title.subscribers", value: "Email Subscribers", comment: "Site Email Subscribers")
}
}

Expand Down Expand Up @@ -296,6 +297,11 @@ private extension PeopleViewController {
}
}
}
}

// MARK: - Private behavior

private extension PeopleViewController {

enum Storyboard {
static let inviteSegueIdentifier = "invite"
Expand Down Expand Up @@ -497,8 +503,9 @@ private extension PeopleViewController {
filterBar.items = filtersAvailableForBlog(blog)
filterBar.addTarget(self, action: #selector(selectedFilterDidChange(_:)), for: .valueChanged)

// By default, let's display the Blog's Users
filter = .users
let indexToSet = Filter.allCases.firstIndex(where: { $0 == defaultFilter }) ?? 0
filterBar.setSelectedIndex(indexToSet)
filter = defaultFilter
}

func setupTableView() {
Expand Down Expand Up @@ -555,3 +562,17 @@ extension PeopleViewController {
WPAnalytics.track(.peopleFilterChanged, properties: [:], blog: blog)
}
}

extension PeopleViewController {
class func controllerWithBlog(_ blog: Blog, selectedFilter: Filter) -> PeopleViewController? {
let storyboard = UIStoryboard(name: "People", bundle: nil)
guard let viewController = storyboard.instantiateInitialViewController() as? PeopleViewController else {
return nil
}

viewController.defaultFilter = selectedFilter
viewController.blog = blog

return viewController
}
}
Expand Up @@ -28,13 +28,13 @@ final class PersonViewController: UITableViewController {
var title: String {
switch self {
case .User:
return NSLocalizedString("Blog's User", comment: "Blog's User Profile. Displayed when the name is empty!")
return NSLocalizedString("user.details.title.user", value: "Site's User", comment: "Sites's User Profile. Displayed when the name is empty!")
case .Follower:
return NSLocalizedString("Blog's Follower", comment: "Blog's Follower Profile. Displayed when the name is empty!")
return NSLocalizedString("user.details.title.subscriber", value: "Site's Subscriber", comment: "Site's Subscriber Profile. Displayed when the name is empty!")
case .Viewer:
return NSLocalizedString("Blog's Viewer", comment: "Blog's Viewer Profile. Displayed when the name is empty!")
return NSLocalizedString("user.details.title.viewer", value: "Site's Viewer", comment: "Site's Viewers Profile. Displayed when the name is empty!")
case .Email:
return NSLocalizedString("Blog's Email Follower", comment: "Blog's Email Follower Profile. Displayed when the name is empty!")
return NSLocalizedString("user.details.title.emailSubscriber", value: "Site's Email Subscriber", comment: "Site's Email Subscriber Profile. Displayed when the name is empty!")
}
}
}
Expand Down
Expand Up @@ -40,7 +40,10 @@ private extension ViewMoreRow {
backgroundColor = .listForeground
viewMoreLabel.text = NSLocalizedString("View more", comment: "Label for viewing more stats.")
viewMoreLabel.textColor = WPStyleGuide.Stats.actionTextColor
if statSection == .insightsFollowersWordPress || statSection == .insightsFollowersEmail {
if statSection == .insightsFollowersWordPress ||
statSection == .insightsFollowersEmail ||
statSection == .subscribersList ||
statSection == .subscribersEmailsSummary {
disclosureImageView.isHidden = true
}
}
Expand Down
Expand Up @@ -80,8 +80,18 @@ final class StatsSubscribersViewController: SiteStatsBaseTableViewController {

extension StatsSubscribersViewController: SiteStatsPeriodDelegate {
func viewMoreSelectedForStatSection(_ statSection: StatSection) {
let detailTableViewController = SiteStatsDetailTableViewController.loadFromStoryboard()
detailTableViewController.configure(statSection: statSection)
navigationController?.pushViewController(detailTableViewController, animated: true)
switch statSection {
case .subscribersList:
guard let blog = RootViewCoordinator.sharedPresenter.mySitesCoordinator.currentBlog,
let peopleViewController = PeopleViewController.controllerWithBlog(blog, selectedFilter: .followers) else { return }
navigationController?.pushViewController(peopleViewController, animated: true)
case .subscribersEmailsSummary:
let detailTableViewController = SiteStatsDetailTableViewController.loadFromStoryboard()
detailTableViewController.configure(statSection: statSection)
navigationController?.pushViewController(detailTableViewController, animated: true)
default:
// TODO
DDLogInfo("\(statSection) selected")
}
}
}

0 comments on commit c307735

Please sign in to comment.