From e41d7ca9ee19befc4897eb5ea79d262e334bebf2 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 29 Apr 2024 15:20:52 +0300 Subject: [PATCH 001/112] Remove emailFollowers from People Email followers are now aggregated together with Followers --- .../Classes/Services/PeopleService.swift | 25 ---------------- .../People/PeopleViewController.swift | 13 ++------- .../People/PersonViewController.swift | 29 ------------------- 3 files changed, 2 insertions(+), 65 deletions(-) diff --git a/WordPress/Classes/Services/PeopleService.swift b/WordPress/Classes/Services/PeopleService.swift index 04f6e6adaef8..dd8520c3ae42 100644 --- a/WordPress/Classes/Services/PeopleService.swift +++ b/WordPress/Classes/Services/PeopleService.swift @@ -76,31 +76,6 @@ struct PeopleService { }) } - /// Loads a page of Email Followers associated to the current blog, starting at the specified offset. - /// - /// - Parameters: - /// - offset: Number of records to skip. - /// - count: Number of records to retrieve. By default set to 20. - /// - success: Closure to be executed on success with the number of followers retrieved and a bool indicating if more are available. - /// - failure: Closure to be executed on failure. - /// - func loadEmailFollowersPage(_ offset: Int = 0, - count: Int = 20, - success: @escaping ((_ retrieved: Int, _ shouldLoadMore: Bool) -> Void), - failure: ((Error) -> Void)? = nil) { - let page = (offset / count) + 1 - remote.getEmailFollowers(siteID, page: page, max: count, success: { followers, hasMore in - self.coreDataStack.performAndSave({ context in - self.mergePeople(followers, in: context) - }, completion: { - success(followers.count, hasMore) - }, on: .main) - }, failure: { error in - DDLogError(String(describing: error)) - failure?(error) - }) - } - /// Loads a page of Viewers associated to the current blog, starting at the specified offset. /// /// - Parameters: diff --git a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift index 52cb9b0e885c..98b1facdb1f7 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift @@ -74,7 +74,7 @@ class PeopleViewController: UITableViewController { // Followers must be sorted out by creationDate! // switch filter { - case .followers, .email: + case .followers: return [NSSortDescriptor(key: "creationDate", ascending: true, selector: #selector(NSDate.compare(_:)))] default: return [NSSortDescriptor(key: "displayName", ascending: true, selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))] @@ -251,11 +251,10 @@ extension PeopleViewController { case users = "users" case followers = "followers" - case email = "email" case viewers = "viewers" static var defaultFilters: [Filter] { - return [.users, .followers, .email] + return [.users, .followers] } var title: String { @@ -266,8 +265,6 @@ extension PeopleViewController { return NSLocalizedString("users.list.title.subscribers", value: "Subscribers", comment: "Site Subscribers") case .viewers: return NSLocalizedString("Viewers", comment: "Blog Viewers") - case .email: - return NSLocalizedString("users.list.title.subscribers", value: "Email Subscribers", comment: "Site Email Subscribers") } } @@ -279,8 +276,6 @@ extension PeopleViewController { return .follower case .viewers: return .viewer - case .email: - return .emailFollower } } @@ -292,8 +287,6 @@ extension PeopleViewController { return .Follower case .viewers: return .Viewer - case .email: - return .Email } } } @@ -382,8 +375,6 @@ private extension PeopleViewController { loadUsersPage(offset, success: success) case .viewers: service.loadViewersPage(offset, success: success) - case .email: - service.loadEmailFollowersPage(offset, success: success) } } diff --git a/WordPress/Classes/ViewRelated/People/PersonViewController.swift b/WordPress/Classes/ViewRelated/People/PersonViewController.swift index 4617f0f635fd..c9b79bb3b3dd 100644 --- a/WordPress/Classes/ViewRelated/People/PersonViewController.swift +++ b/WordPress/Classes/ViewRelated/People/PersonViewController.swift @@ -23,7 +23,6 @@ final class PersonViewController: UITableViewController { case User = "user" case Follower = "follower" case Viewer = "viewer" - case Email = "email" var title: String { switch self { @@ -33,8 +32,6 @@ final class PersonViewController: UITableViewController { 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("user.details.title.viewer", value: "Site's Viewer", comment: "Site's Viewers Profile. Displayed when the name is empty!") - case .Email: - return NSLocalizedString("user.details.title.emailSubscriber", value: "Site's Email Subscriber", comment: "Site's Email Subscriber Profile. Displayed when the name is empty!") } } } @@ -230,8 +227,6 @@ private extension PersonViewController { case .Viewer: strongSelf.deleteViewer() return - case .Email: - strongSelf.deleteEmailFollower() } } @@ -251,9 +246,6 @@ private extension PersonViewController { case .Viewer: messageFirstLine = NSLocalizedString("If you remove this viewer, he or she will not be able to visit this site.", comment: "First line of remove viewer warning in confirmation dialog.") - case .Email: - messageFirstLine = NSLocalizedString("Removing followers makes them stop receiving updates from your site. If they choose to, they can still visit your site, and follow it again.", - comment: "First line of remove email follower warning in confirmation dialog.") } let messageSecondLineText = NSLocalizedString("Would you still like to remove this person?", @@ -298,23 +290,6 @@ private extension PersonViewController { _ = navigationController?.popViewController(animated: true) } - func deleteEmailFollower() { - guard let emailFollower = emailFollower, isEmailFollower else { - DDLogError("Error: Only email followers can be deleted here") - assertionFailure() - return - } - - service?.deleteEmailFollower(emailFollower, failure: { [weak self] error in - guard let strongSelf = self, let error = error as NSError? else { - return - } - - strongSelf.handleRemoveViewerOrFollowerError(error) - }) - _ = navigationController?.popViewController(animated: true) - } - func deleteViewer() { guard let viewer = viewer, isViewer else { DDLogError("Error: Only Viewers can be deleted here") @@ -542,8 +517,6 @@ private extension PersonViewController { return isFollower == true case .Viewer: return isViewer == true - case .Email: - return isEmailFollower } } @@ -587,8 +560,6 @@ private extension PersonViewController { return .viewer case .User: return try? Role.lookup(withBlogID: blog.objectID, slug: person.role, in: context)?.toUnmanaged() - case .Email: - return .follower } } } From c092c71c99fb6dcdcabd544f6e21f6ec387231a3 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 29 Apr 2024 16:23:11 +0300 Subject: [PATCH 002/112] Update PeopleCell UI --- .../ViewRelated/People/People.storyboard | 149 +++++++++--------- .../ViewRelated/People/PeopleCell.swift | 10 +- 2 files changed, 83 insertions(+), 76 deletions(-) diff --git a/WordPress/Classes/ViewRelated/People/People.storyboard b/WordPress/Classes/ViewRelated/People/People.storyboard index c6020e76d931..9f8b321db8d9 100644 --- a/WordPress/Classes/ViewRelated/People/People.storyboard +++ b/WordPress/Classes/ViewRelated/People/People.storyboard @@ -1,9 +1,9 @@ - + - + @@ -12,12 +12,12 @@ - + - + - - + + - + - + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - + + + + + @@ -170,7 +175,7 @@ - + @@ -200,7 +205,7 @@ - + @@ -214,16 +219,16 @@ - + @@ -198,9 +197,9 @@ - + - + @@ -221,13 +220,13 @@ - - + diff --git a/WordPress/Classes/ViewRelated/People/PersonViewController.swift b/WordPress/Classes/ViewRelated/People/PersonViewController.swift index c9b79bb3b3dd..14c3c242097f 100644 --- a/WordPress/Classes/ViewRelated/People/PersonViewController.swift +++ b/WordPress/Classes/ViewRelated/People/PersonViewController.swift @@ -70,6 +70,13 @@ final class PersonViewController: UITableViewController { super.init(coder: coder) } + class func controllerWithBlog(_ blog: Blog, context: NSManagedObjectContext, person: Person, screenMode: ScreenMode) -> PersonViewController? { + let storyboard = UIStoryboard(name: "People", bundle: nil) + return storyboard.instantiateViewController(identifier: "PersonViewController") { coder in + PersonViewController(coder: coder, blog: blog, context: context, person: person, screenMode: screenMode) + } + } + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } From 1bd642f54c8dbd07d1323a086565ebf7ff8569d1 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 29 Apr 2024 16:51:57 +0300 Subject: [PATCH 007/112] Open PersonViewController from code --- .../People/PeopleViewController.swift | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift index c070735e884d..8386b6f309c3 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift @@ -163,6 +163,28 @@ class PeopleViewController: UITableViewController { loadMorePeopleIfNeeded() } + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + + guard let blog = blog, let blogId = blog.dotComID?.intValue else { return } + + switch filter { + case .users, .viewers: + guard let viewController = PersonViewController.controllerWithBlog( + blog, + context: viewContext, + person: personAtIndexPath(indexPath), + screenMode: filter.screenMode + ) else { + return + } + navigationController?.pushViewController(viewController, animated: true) + case .followers: + // TBD + break + } + } + // MARK: UIViewController override func viewDidLoad() { @@ -189,16 +211,6 @@ class PeopleViewController: UITableViewController { tableView.reloadData() } - @IBSegueAction func createPersonViewController(_ coder: NSCoder) -> PersonViewController? { - guard let selectedIndexPath = tableView.indexPathForSelectedRow, let blog = blog else { return nil } - - return PersonViewController(coder: coder, - blog: blog, - context: viewContext, - person: personAtIndexPath(selectedIndexPath), - screenMode: filter.screenMode) - } - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let navController = segue.destination as? UINavigationController, let inviteViewController = navController.topViewController as? InvitePersonViewController { From 916bdb68232d122680cb2000f005d492b2b1eeee Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 29 Apr 2024 16:52:27 +0300 Subject: [PATCH 008/112] Open Subscribers web view after subscriber is selected in PeopleViewController --- .../ViewRelated/People/PeopleViewController.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift index 8386b6f309c3..983f7ce11a87 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift @@ -180,8 +180,13 @@ class PeopleViewController: UITableViewController { } navigationController?.pushViewController(viewController, animated: true) case .followers: - // TBD - break + let url = URL(string: "https://wordpress.com/subscribers/\(blogId)/\(personAtIndexPath(indexPath).ID)") + let configuration = WebViewControllerConfiguration(url: url) + configuration.authenticateWithDefaultAccount() + configuration.secureInteraction = true + let viewController = WebKitViewController(configuration: configuration) + let navWrapper = LightNavigationController(rootViewController: viewController) + navigationController?.present(navWrapper, animated: true) } } From a05469f7dd62ab12713736b89a4e78add7bf0f5f Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 29 Apr 2024 17:48:43 +0300 Subject: [PATCH 009/112] Update PeopleViewController.swift --- WordPress/Classes/ViewRelated/People/PeopleViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift index 983f7ce11a87..7f89048d9af4 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift @@ -537,6 +537,7 @@ private extension PeopleViewController { target: self, action: #selector(invitePersonWasPressed)) + WPStyleGuide.configureColors(view: view, tableView: tableView) WPStyleGuide.configureAutomaticHeightRows(for: tableView) tableView.separatorStyle = .none From d4dd1af6cd3018b2f6e4094812daf8709bd8a45f Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 29 Apr 2024 18:06:35 +0300 Subject: [PATCH 010/112] Show loading indicator when switching between People tabs --- WordPress/Classes/ViewRelated/People/PeopleViewController.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift index 7f89048d9af4..af3fab259f11 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift @@ -346,6 +346,8 @@ private extension PeopleViewController { // MARK: Sync Helpers func refreshPeople() { + self.isInitialLoad = true + self.refreshNoResultsView() loadPeoplePage() { [weak self] (retrieved, shouldLoadMore) in self?.isInitialLoad = false self?.refreshNoResultsView() From 3d9ced67774578697096b59bf23e1cdfbf40abef Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 29 Apr 2024 18:25:28 +0300 Subject: [PATCH 011/112] Adjust dynamic type for PeopleCell Enable dynamic type for username and id, and disable it for role badge --- WordPress/Classes/ViewRelated/People/People.storyboard | 4 ++-- .../Classes/ViewRelated/People/PeopleRoleBadgeLabel.swift | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/WordPress/Classes/ViewRelated/People/People.storyboard b/WordPress/Classes/ViewRelated/People/People.storyboard index f1b593d26d44..c3e0a39b2a3a 100644 --- a/WordPress/Classes/ViewRelated/People/People.storyboard +++ b/WordPress/Classes/ViewRelated/People/People.storyboard @@ -48,13 +48,13 @@ - + + - - - - - - + - - - - - - - - + + + + + + - - - - + + + From 69d3e805a2e7071864b8a5650fd891b79aac52e8 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 13:25:05 +0300 Subject: [PATCH 043/112] Create StatsGhostSingleValue cell for cells displaying Totals --- .../GhostViews/StatsGhostCells.swift | 2 + .../GhostViews/StatsGhostSingleValueCell.xib | 40 +++++++++++++++++++ .../GhostViews/StatsGhostTableViewRows.swift | 8 ++++ WordPress/WordPress.xcodeproj/project.pbxproj | 12 ++++++ 4 files changed, 62 insertions(+) create mode 100644 WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.xib diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift index 98f30e98d3ff..e90cf1e06092 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift @@ -114,3 +114,5 @@ fileprivate extension Date { return days } } + +class StatsGhostSingleValueCell: StatsGhostBaseCell, NibLoadable { } diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.xib b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.xib new file mode 100644 index 000000000000..2285ac8c545b --- /dev/null +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.xib @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift index 3a39501557f0..6d27cf368ab7 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift @@ -134,3 +134,11 @@ struct StatsGhostTitleRow: StatsRowGhostable { enum GhostCellStyle { static let muriel = GhostStyle(beatStartColor: .placeholderElement, beatEndColor: .placeholderElementFaded) } + +struct StatsGhostSingleValueRow: StatsRowGhostable { + let statSection: StatSection? + + static let cell: ImmuTableCell = { + return ImmuTableCell.nib(StatsGhostSingleValueCell.defaultNib, StatsGhostSingleValueCell.self) + }() +} diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 16ec4c868263..78bfcea06f8e 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -137,6 +137,8 @@ 011896A629D5B72500D34BA9 /* DomainsDashboardCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011896A429D5B72500D34BA9 /* DomainsDashboardCoordinator.swift */; }; 011896A829D5BBB400D34BA9 /* DomainsDashboardFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011896A729D5BBB400D34BA9 /* DomainsDashboardFactory.swift */; }; 011896A929D5BBB400D34BA9 /* DomainsDashboardFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011896A729D5BBB400D34BA9 /* DomainsDashboardFactory.swift */; }; + 011F156F2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 011F156E2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib */; }; + 011F15702BE4E8CE008029ED /* StatsGhostSingleValueCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 011F156E2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib */; }; 011F52BD2A15327700B04114 /* BaseDashboardDomainsCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011F52BC2A15327700B04114 /* BaseDashboardDomainsCardCell.swift */; }; 011F52BE2A15327700B04114 /* BaseDashboardDomainsCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011F52BC2A15327700B04114 /* BaseDashboardDomainsCardCell.swift */; }; 011F52C02A1538EC00B04114 /* FreeToPaidPlansDashboardCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011F52BF2A1538EC00B04114 /* FreeToPaidPlansDashboardCardCell.swift */; }; @@ -222,6 +224,8 @@ 01B7590B2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7590A2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift */; }; 01B7590C2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7590A2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift */; }; 01B7590E2B3EEEA400179AE6 /* SiteDomainsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7590D2B3EEEA400179AE6 /* SiteDomainsViewModelTests.swift */; }; + 01C591452BDBD63D0071515C /* StatsGhostTopCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C591442BDBD63D0071515C /* StatsGhostTopCell.swift */; }; + 01C591462BDBD63D0071515C /* StatsGhostTopCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C591442BDBD63D0071515C /* StatsGhostTopCell.swift */; }; 01CE5007290A889F00A9C2E0 /* TracksConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01CE5006290A889F00A9C2E0 /* TracksConfiguration.swift */; }; 01CE5008290A88BD00A9C2E0 /* TracksConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01CE5006290A889F00A9C2E0 /* TracksConfiguration.swift */; }; 01CE500E290A88C100A9C2E0 /* TracksConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01CE5006290A889F00A9C2E0 /* TracksConfiguration.swift */; }; @@ -5993,6 +5997,7 @@ 011896A429D5B72500D34BA9 /* DomainsDashboardCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainsDashboardCoordinator.swift; sourceTree = ""; }; 011896A729D5BBB400D34BA9 /* DomainsDashboardFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainsDashboardFactory.swift; sourceTree = ""; }; 011A2815DB0DE7E3973CBC0E /* Pods-Apps-Jetpack.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Apps-Jetpack.release.xcconfig"; path = "../Pods/Target Support Files/Pods-Apps-Jetpack/Pods-Apps-Jetpack.release.xcconfig"; sourceTree = ""; }; + 011F156E2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsGhostSingleValueCell.xib; sourceTree = ""; }; 011F52BC2A15327700B04114 /* BaseDashboardDomainsCardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDashboardDomainsCardCell.swift; sourceTree = ""; }; 011F52BF2A1538EC00B04114 /* FreeToPaidPlansDashboardCardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FreeToPaidPlansDashboardCardCell.swift; sourceTree = ""; }; 011F52C22A153A3400B04114 /* FreeToPaidPlansDashboardCardHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FreeToPaidPlansDashboardCardHelper.swift; sourceTree = ""; }; @@ -6051,6 +6056,7 @@ 01B759072B3ECAF300179AE6 /* DomainsStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainsStateView.swift; sourceTree = ""; }; 01B7590A2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainDetailsWebViewControllerWrapper.swift; sourceTree = ""; }; 01B7590D2B3EEEA400179AE6 /* SiteDomainsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteDomainsViewModelTests.swift; sourceTree = ""; }; + 01C591442BDBD63D0071515C /* StatsGhostTopCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsGhostTopCell.swift; sourceTree = ""; }; 01CE5006290A889F00A9C2E0 /* TracksConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracksConfiguration.swift; sourceTree = ""; }; 01CE5010290A890300A9C2E0 /* TracksConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracksConfiguration.swift; sourceTree = ""; }; 01D2FF5D2AA733690038E040 /* LockScreenFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenFieldView.swift; sourceTree = ""; }; @@ -14976,11 +14982,13 @@ children = ( 9A09F91D230C4C0200F42AB7 /* StatsGhostTableViewRows.swift */, 9A9E3FAB230E9DD000909BC4 /* StatsGhostCells.swift */, + 01C591442BDBD63D0071515C /* StatsGhostTopCell.swift */, 9A9E3FB3230EC4F700909BC4 /* StatsGhostPostingActivityCell.xib */, 9A9E3FB1230EB74300909BC4 /* StatsGhostTabbedCell.xib */, 9A9E3FAF230EA7A300909BC4 /* StatsGhostTopCell.xib */, 9A19D440236C7C7500D393E5 /* StatsGhostTopHeaderCell.xib */, 9A9E3FAC230E9DD000909BC4 /* StatsGhostTwoColumnCell.xib */, + 011F156E2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib */, 9AC3C69A231543C2007933CD /* StatsGhostChartCell.xib */, 9A73CB072350DE4C002EF20C /* StatsGhostSingleRowCell.xib */, 9AB36B83236B25D900FAD72A /* StatsGhostTitleCell.xib */, @@ -19641,6 +19649,7 @@ FA1A55FF25A6F07F0033967D /* RestoreStatusView.xib in Resources */, 4058F41A1FF40EE1000D5559 /* PluginDetailViewHeaderCell.xib in Resources */, 1761F17226209AEE000815EF /* open-source-dark-icon-app-60x60@3x.png in Resources */, + 011F156F2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib in Resources */, 406A0EF0224D39C50016AD6A /* Flags.xcassets in Resources */, 9A9E3FB0230EA7A300909BC4 /* StatsGhostTopCell.xib in Resources */, 985793C922F23D7000643DBF /* CustomizeInsightsCell.xib in Resources */, @@ -20188,6 +20197,7 @@ FABB1FCE2602FC2C00C8785C /* StatsChildRowsView.xib in Resources */, F41E4EEE28F247D3001880C6 /* white-on-green-icon-app-76.png in Resources */, FABB1FD02602FC2C00C8785C /* xhtml1-transitional.dtd in Resources */, + 011F15702BE4E8CE008029ED /* StatsGhostSingleValueCell.xib in Resources */, 8BF281FA27CE8E4100AF8CF3 /* DashboardGhostCardContent.xib in Resources */, F465979A28E65FC800D5F49A /* dark-green-icon-app-60@3x.png in Resources */, FABB1FD22602FC2C00C8785C /* RestoreStatusView.xib in Resources */, @@ -23367,6 +23377,7 @@ E6FACB1E1EC675E300284AC7 /* GravatarProfile.swift in Sources */, D8212CB720AA7703008E8AE8 /* ReaderShareAction.swift in Sources */, C7BB60192863AF9700748FD9 /* QRLoginProtocols.swift in Sources */, + 01C591452BDBD63D0071515C /* StatsGhostTopCell.swift in Sources */, 9826AE8A21B5CC7300C851FA /* PostingActivityMonth.swift in Sources */, 08F531FE2B7E94F20061BD0E /* CachedAsyncImage.swift in Sources */, 3F09CCAE24292EFD00D00A8C /* ReaderTabItem.swift in Sources */, @@ -24626,6 +24637,7 @@ FABB21762602FC2C00C8785C /* CameraHandler.swift in Sources */, F4C1FC642A44831300AD7CB0 /* PrivacySettingsAnalyticsTracker.swift in Sources */, FA73D7EA27987BA500DF24B3 /* SitePickerViewController+SiteIcon.swift in Sources */, + 01C591462BDBD63D0071515C /* StatsGhostTopCell.swift in Sources */, FABB21772602FC2C00C8785C /* JetpackConnectionViewController.swift in Sources */, 803BB97A2959543D00B3F6D6 /* RootViewCoordinator.swift in Sources */, FABB21782602FC2C00C8785C /* NotificationActionsService.swift in Sources */, From 00ad2946a1ed033dff2609451f7b197c24814c56 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 13:37:50 +0300 Subject: [PATCH 044/112] Update StatsGhostTopCell.swift --- .../Stats/Shared Views/GhostViews/StatsGhostTopCell.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.swift index 9dbb30374275..ef4d7838245d 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.swift @@ -141,6 +141,12 @@ private class StatsGhostTopCellColumn: UIView { topView.startGhostAnimation(style: GhostCellStyle.muriel) bottomView.startGhostAnimation(style: GhostCellStyle.muriel) } + + override func tintColorDidChange() { + super.tintColorDidChange() + topView.restartGhostAnimation(style: GhostCellStyle.muriel) + bottomView.restartGhostAnimation(style: GhostCellStyle.muriel) + } } fileprivate extension StatsGhostTopCell { From 1cc289468a9a9cd4a16740d1054c64606dc263ba Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 13:38:58 +0300 Subject: [PATCH 045/112] Update SubscribersVM to use new Ghost cells --- .../Stats/Subscribers/StatsSubscribersViewModel.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift index 0fabf6e09ca3..e328b6aa0189 100644 --- a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift @@ -50,8 +50,8 @@ private extension StatsSubscribersViewModel { tableViewSnapshot.send(snapshot) } - func loadingRows(_ section: StatSection) -> [any StatsHashableImmuTableRow] { - return [StatsGhostTopImmutableRow(statSection: section)] + func loadingRows(_ section: StatSection, numberOfColumns: Int) -> [any StatsHashableImmuTableRow] { + return [StatsGhostTopImmutableRow(numberOfColumns: numberOfColumns, statSection: section)] } func errorRows(_ section: StatSection) -> [any StatsHashableImmuTableRow] { @@ -65,7 +65,7 @@ private extension StatsSubscribersViewModel { func subscribersTotalsRows() -> [any StatsHashableImmuTableRow] { switch store.subscribersList.value { case .loading, .idle: - return loadingRows(.subscribersTotal) + return [StatsGhostSingleValueRow(statSection: .subscribersTotal)] case .success(let subscribersData): return [ TotalInsightStatsRow( @@ -110,7 +110,7 @@ private extension StatsSubscribersViewModel { func emailsSummaryRows() -> [any StatsHashableImmuTableRow] { switch store.emailsSummary.value { case .loading, .idle: - return loadingRows(.subscribersEmailsSummary) + return loadingRows(.subscribersEmailsSummary, numberOfColumns: 3) case .success(let emailsSummary): return [ TopTotalsPeriodStatsRow( @@ -146,7 +146,7 @@ private extension StatsSubscribersViewModel { func subscribersListRows() -> [any StatsHashableImmuTableRow] { switch store.subscribersList.value { case .loading, .idle: - return loadingRows(.subscribersList) + return loadingRows(.subscribersList, numberOfColumns: 2) case .success(let subscribersData): return [ TopTotalsPeriodStatsRow( From a18a5dbb4c739a2162ad1982349e6a28eff45f60 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 13:39:11 +0300 Subject: [PATCH 046/112] Register new Ghost cells in Subscribers View Controller --- .../Stats/Subscribers/StatsSubscribersViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift index 2df49148e7dd..e3d4a9cba9e3 100644 --- a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift @@ -71,8 +71,10 @@ final class StatsSubscribersViewController: SiteStatsBaseTableViewController { return [ SubscriberChartRow.self, TopTotalsPeriodStatsRow.self, - StatsGhostTopImmutableRow.self, TotalInsightStatsRow.self, + StatsGhostTopImmutableRow.self, + StatsGhostChartImmutableRow.self, + StatsGhostSingleValueRow.self, StatsErrorRow.self ] } From fcee2fb31f08054bd11cb865fbdc69729841544c Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 14:30:52 +0300 Subject: [PATCH 047/112] Create StatsGhostLineChart to display loading of a line chart --- .../GhostViews/StatsGhostLineChartCell.swift | 67 +++++++++++++++++++ .../GhostViews/StatsGhostLineChartCell.xib | 41 ++++++++++++ .../GhostViews/StatsGhostTableViewRows.swift | 8 +++ 3 files changed, 116 insertions(+) create mode 100644 WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift create mode 100644 WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.xib diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift new file mode 100644 index 000000000000..7dd502228cc5 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift @@ -0,0 +1,67 @@ +import UIKit +import WordPressShared + +class StatsGhostLineChartCell: StatsGhostBaseCell, NibLoadable { + @IBOutlet private weak var lineChart: StatsGhostLineChartView! +} + +final class StatsGhostLineChartView: UIView { + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + private func commonInit() { + backgroundColor = .clear + createMask() + } + + private func createMask() { + let maskLayer = CAShapeLayer() + maskLayer.frame = bounds + + let path = UIBezierPath() + path.move(to: CGPoint(x: 0, y: bounds.maxY)) + + let wavePoints = [ + CGPoint(x: bounds.width * 0.1, y: bounds.maxY * 0.8), + CGPoint(x: bounds.width * 0.3, y: bounds.maxY * 0.6), + CGPoint(x: bounds.width * 0.5, y: bounds.maxY * 0.4), + CGPoint(x: bounds.width * 0.7, y: bounds.maxY * 0.2), + CGPoint(x: bounds.width * 0.9, y: bounds.maxY * 0.5), + CGPoint(x: bounds.width, y: 0) + ] + + for (index, point) in wavePoints.enumerated() { + if index == 0 { + path.addLine(to: point) + } else { + let previousPoint = wavePoints[index - 1] + let midPointX = (previousPoint.x + point.x) / 2 + path.addCurve(to: point, controlPoint1: CGPoint(x: midPointX, y: previousPoint.y), controlPoint2: CGPoint(x: midPointX, y: point.y)) + } + } + + path.addLine(to: CGPoint(x: bounds.width, y: 0)) + path.addLine(to: CGPoint(x: bounds.width, y: bounds.maxY)) + path.addLine(to: CGPoint(x: 0, y: bounds.maxY)) + path.close() + + maskLayer.path = path.cgPath + maskLayer.fillColor = UIColor.white.cgColor + maskLayer.fillRule = .evenOdd + layer.mask = maskLayer + backgroundColor = .clear + } + + override func layoutSubviews() { + super.layoutSubviews() + layer.sublayers?.forEach { $0.frame = bounds } + createMask() + } +} diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.xib b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.xib new file mode 100644 index 000000000000..cfbd207a1d6a --- /dev/null +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.xib @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift index 6d27cf368ab7..fbf039f29e85 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift @@ -142,3 +142,11 @@ struct StatsGhostSingleValueRow: StatsRowGhostable { return ImmuTableCell.nib(StatsGhostSingleValueCell.defaultNib, StatsGhostSingleValueCell.self) }() } + +struct StatsGhostLineChartRow: StatsRowGhostable { + let statSection: StatSection? + + static let cell: ImmuTableCell = { + return ImmuTableCell.nib(StatsGhostLineChartCell.defaultNib, StatsGhostLineChartCell.self) + }() +} From 06ae50ac7a894964c1466f9e162d26b3b2e08703 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 14:31:12 +0300 Subject: [PATCH 048/112] Use StatsGhostLineChart for loading Subscribers line chart --- .../Subscribers/StatsSubscribersViewController.swift | 2 +- .../Subscribers/StatsSubscribersViewModel.swift | 2 +- WordPress/WordPress.xcodeproj/project.pbxproj | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift index e3d4a9cba9e3..48b189837d8f 100644 --- a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift @@ -73,7 +73,7 @@ final class StatsSubscribersViewController: SiteStatsBaseTableViewController { TopTotalsPeriodStatsRow.self, TotalInsightStatsRow.self, StatsGhostTopImmutableRow.self, - StatsGhostChartImmutableRow.self, + StatsGhostLineChartRow.self, StatsGhostSingleValueRow.self, StatsErrorRow.self ] diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift index e328b6aa0189..5e6405e72558 100644 --- a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift @@ -85,7 +85,7 @@ private extension StatsSubscribersViewModel { func chartRows() -> [any StatsHashableImmuTableRow] { switch store.chartSummary.value { case .loading, .idle: - return loadingRows(.subscribersChart) + return [StatsGhostLineChartRow(statSection: .subscribersChart)] case .success(let chartSummary): let xAxisDates = chartSummary.history.map { $0.date } let viewsChart = StatsSubscribersLineChart(counts: chartSummary.history.map { $0.count }) diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 78bfcea06f8e..76af2d5a4cb2 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -224,6 +224,10 @@ 01B7590B2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7590A2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift */; }; 01B7590C2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7590A2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift */; }; 01B7590E2B3EEEA400179AE6 /* SiteDomainsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7590D2B3EEEA400179AE6 /* SiteDomainsViewModelTests.swift */; }; + 01C447292BE4F8490006F787 /* StatsGhostLineChartCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01C447282BE4F8490006F787 /* StatsGhostLineChartCell.xib */; }; + 01C4472A2BE4F8490006F787 /* StatsGhostLineChartCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01C447282BE4F8490006F787 /* StatsGhostLineChartCell.xib */; }; + 01C4472C2BE4F8560006F787 /* StatsGhostLineChartCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C4472B2BE4F8560006F787 /* StatsGhostLineChartCell.swift */; }; + 01C4472D2BE4F8560006F787 /* StatsGhostLineChartCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C4472B2BE4F8560006F787 /* StatsGhostLineChartCell.swift */; }; 01C591452BDBD63D0071515C /* StatsGhostTopCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C591442BDBD63D0071515C /* StatsGhostTopCell.swift */; }; 01C591462BDBD63D0071515C /* StatsGhostTopCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C591442BDBD63D0071515C /* StatsGhostTopCell.swift */; }; 01CE5007290A889F00A9C2E0 /* TracksConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01CE5006290A889F00A9C2E0 /* TracksConfiguration.swift */; }; @@ -6056,6 +6060,8 @@ 01B759072B3ECAF300179AE6 /* DomainsStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainsStateView.swift; sourceTree = ""; }; 01B7590A2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainDetailsWebViewControllerWrapper.swift; sourceTree = ""; }; 01B7590D2B3EEEA400179AE6 /* SiteDomainsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteDomainsViewModelTests.swift; sourceTree = ""; }; + 01C447282BE4F8490006F787 /* StatsGhostLineChartCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsGhostLineChartCell.xib; sourceTree = ""; }; + 01C4472B2BE4F8560006F787 /* StatsGhostLineChartCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsGhostLineChartCell.swift; sourceTree = ""; }; 01C591442BDBD63D0071515C /* StatsGhostTopCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsGhostTopCell.swift; sourceTree = ""; }; 01CE5006290A889F00A9C2E0 /* TracksConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracksConfiguration.swift; sourceTree = ""; }; 01CE5010290A890300A9C2E0 /* TracksConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracksConfiguration.swift; sourceTree = ""; }; @@ -14993,6 +14999,8 @@ 9A73CB072350DE4C002EF20C /* StatsGhostSingleRowCell.xib */, 9AB36B83236B25D900FAD72A /* StatsGhostTitleCell.xib */, FA347AF126EB7A420096604B /* StatsGhostGrowAudienceCell.xib */, + 01C447282BE4F8490006F787 /* StatsGhostLineChartCell.xib */, + 01C4472B2BE4F8560006F787 /* StatsGhostLineChartCell.swift */, ); path = GhostViews; sourceTree = ""; @@ -19628,6 +19636,7 @@ 17222D9B261DDDF90047B163 /* black-icon-app-76x76.png in Resources */, DC772B0828201F5300664C02 /* ViewsVisitorsLineChartCell.xib in Resources */, 5D6C4AF61B603CA3005E3C43 /* WPTableViewActivityCell.xib in Resources */, + 01C447292BE4F8490006F787 /* StatsGhostLineChartCell.xib in Resources */, A01C55480E25E0D000D411F2 /* defaultPostTemplate.html in Resources */, 17222D89261DDDF90047B163 /* celadon-icon-app-60x60@3x.png in Resources */, 08BA4BC9298A9AD500015BD2 /* JetpackInstallPluginLogoAnimation_ltr.json in Resources */, @@ -20301,6 +20310,7 @@ FABB20302602FC2C00C8785C /* OverviewCell.xib in Resources */, 83A8A2972BDC557F001F9133 /* ReaderTagFooterView.xib in Resources */, F46597E928E6698D00D5F49A /* spectrum-on-black-icon-app-83.5@2x.png in Resources */, + 01C4472A2BE4F8490006F787 /* StatsGhostLineChartCell.xib in Resources */, FABB20322602FC2C00C8785C /* JetpackRemoteInstallStateView.xib in Resources */, FABB20352602FC2C00C8785C /* TemplatePreviewViewController.xib in Resources */, F46597BB28E6687800D5F49A /* neumorphic-dark-icon-app-76@2x.png in Resources */, @@ -22122,6 +22132,7 @@ E166FA1B1BB0656B00374B5B /* PeopleCellViewModel.swift in Sources */, 436D56292117312700CEAA33 /* RegisterDomainDetailsViewController.swift in Sources */, F4EDAA4C29A516EA00622D3D /* ReaderPostService.swift in Sources */, + 01C4472C2BE4F8560006F787 /* StatsGhostLineChartCell.swift in Sources */, F5E29036243E4F5F00C19CA5 /* FilterProvider.swift in Sources */, F4FE743429C3767300AC2729 /* AddressTableViewCell+ViewModel.swift in Sources */, 027AC51D227896540033E56E /* DomainCreditEligibilityChecker.swift in Sources */, @@ -25279,6 +25290,7 @@ 8B55FAAD2614FC87007D618E /* Text+BoldSubString.swift in Sources */, 01616C8C2B5AA6E50023C123 /* StatsTrafficBarChartTabs.swift in Sources */, FABB23382602FC2C00C8785C /* WordPress-22-23.xcmappingmodel in Sources */, + 01C4472D2BE4F8560006F787 /* StatsGhostLineChartCell.swift in Sources */, FED65D79293511E4008071BF /* SharedDataIssueSolver.swift in Sources */, FABB23392602FC2C00C8785C /* CountriesMap.swift in Sources */, F45EB50C2B883E6E004E9053 /* CommentNotification.swift in Sources */, From edd95a6c6ead4cbcafdd3cffb07211552d70e682 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 15:46:17 +0300 Subject: [PATCH 049/112] Update StatsSubscribersViewModelTests.swift --- WordPress/WordPressTest/StatsSubscribersViewModelTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/WordPressTest/StatsSubscribersViewModelTests.swift b/WordPress/WordPressTest/StatsSubscribersViewModelTests.swift index b803c52f8380..521b91bafa35 100644 --- a/WordPress/WordPressTest/StatsSubscribersViewModelTests.swift +++ b/WordPress/WordPressTest/StatsSubscribersViewModelTests.swift @@ -18,7 +18,7 @@ final class StatsSubscribersViewModelTests: XCTestCase { let expectation = expectation(description: "First section should be loading") sut.tableViewSnapshot .sink(receiveValue: { snapshot in - if let _ = snapshot.itemIdentifiers.first?.immuTableRow as? StatsGhostTopImmutableRow { + if let _ = snapshot.itemIdentifiers.first?.immuTableRow as? (any StatsRowGhostable) { expectation.fulfill() } }) From 72c41b245018fd1963a2429ba4735d41816c15d4 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 16:55:45 +0300 Subject: [PATCH 050/112] Remove WPStyleGuide+People --- WordPress/Classes/Models/Role.swift | 16 +++++----- .../People/PeopleCellViewModel.swift | 7 +++-- .../People/PeopleRoleBadgeLabel.swift | 10 +++--- .../People/WPStyleGuide+People.swift | 31 ------------------- WordPress/WordPress.xcodeproj/project.pbxproj | 26 ++++------------ 5 files changed, 23 insertions(+), 67 deletions(-) delete mode 100644 WordPress/Classes/ViewRelated/People/WPStyleGuide+People.swift diff --git a/WordPress/Classes/Models/Role.swift b/WordPress/Classes/Models/Role.swift index 64a8f96ce1cc..6c97f7b0ac0e 100644 --- a/WordPress/Classes/Models/Role.swift +++ b/WordPress/Classes/Models/Role.swift @@ -26,26 +26,26 @@ extension Role { @objc var backgroundColor: UIColor { switch slug { case .some("super-admin"): - return WPStyleGuide.People.Color.Admin.background + return .DS.Foreground.primary case .some("administrator"): - return WPStyleGuide.People.Color.Admin.background + return .DS.Foreground.primary case .some("editor"): - return WPStyleGuide.People.Color.Other.background + return .DS.Background.secondary default: - return WPStyleGuide.People.Color.Other.background + return .DS.Background.secondary } } @objc var textColor: UIColor { switch slug { case .some("super-admin"): - return WPStyleGuide.People.Color.Admin.text + return .DS.Background.primary case .some("administrator"): - return WPStyleGuide.People.Color.Admin.text + return .DS.Background.primary case .some("editor"): - return WPStyleGuide.People.Color.Other.text + return .DS.Foreground.primary default: - return WPStyleGuide.People.Color.Other.text + return .DS.Foreground.primary } } } diff --git a/WordPress/Classes/ViewRelated/People/PeopleCellViewModel.swift b/WordPress/Classes/ViewRelated/People/PeopleCellViewModel.swift index 122724e8c7be..994f5cc65295 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleCellViewModel.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleCellViewModel.swift @@ -1,5 +1,6 @@ import Foundation import WordPressShared +import DesignSystem struct PeopleCellViewModel { let displayName: String @@ -29,11 +30,11 @@ struct PeopleCellViewModel { } var roleBackgroundColor: UIColor { - return role?.backgroundColor ?? WPStyleGuide.People.Color.Other.background + return role?.backgroundColor ?? .DS.Background.secondary } var roleTextColor: UIColor { - return role?.textColor ?? WPStyleGuide.People.Color.Other.text + return role?.textColor ?? .DS.Foreground.primary } var roleText: String { @@ -49,7 +50,7 @@ struct PeopleCellViewModel { } var superAdminBackgroundColor: UIColor { - return WPStyleGuide.People.Color.Admin.background + return .DS.Foreground.primary } var superAdminHidden: Bool { diff --git a/WordPress/Classes/ViewRelated/People/PeopleRoleBadgeLabel.swift b/WordPress/Classes/ViewRelated/People/PeopleRoleBadgeLabel.swift index 37164bb94567..6d0d3289065c 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleRoleBadgeLabel.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleRoleBadgeLabel.swift @@ -1,5 +1,5 @@ import UIKit -import WordPressShared.WPStyleGuide +import DesignSystem class PeopleRoleBadgeLabel: BadgeLabel { override init(frame: CGRect) { @@ -14,9 +14,9 @@ class PeopleRoleBadgeLabel: BadgeLabel { private func setupView() { adjustsFontSizeToFitWidth = true - horizontalPadding = WPStyleGuide.People.RoleBadge.horizontalPadding - verticalPadding = WPStyleGuide.People.RoleBadge.verticalPadding - font = WPStyleGuide.People.RoleBadge.font - layer.cornerRadius = WPStyleGuide.People.RoleBadge.cornerRadius + horizontalPadding = CGFloat.DS.Padding.single + verticalPadding = .DS.Padding.half + font = .DS.font(.footnote) + layer.cornerRadius = .DS.Radius.small } } diff --git a/WordPress/Classes/ViewRelated/People/WPStyleGuide+People.swift b/WordPress/Classes/ViewRelated/People/WPStyleGuide+People.swift deleted file mode 100644 index 151eca4d0f73..000000000000 --- a/WordPress/Classes/ViewRelated/People/WPStyleGuide+People.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Foundation -import DesignSystem - -extension WPStyleGuide { - struct People { - struct RoleBadge { - // MARK: Metrics - static let horizontalPadding: CGFloat = .DS.Padding.single - static let verticalPadding: CGFloat = .DS.Padding.half - static let cornerRadius: CGFloat = .DS.Radius.small - - // MARK: Typography - static var font: UIFont { - return .DS.font(.footnote) - } - } - - // MARK: Colors - struct Color { - struct Admin { - static let background: UIColor = .DS.Foreground.primary - static let text: UIColor = .DS.Background.primary - } - - struct Other { - static let background: UIColor = .DS.Background.secondary - static let text: UIColor = .DS.Foreground.primary - } - } - } -} diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 50cec8696c9c..232504314a5c 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -3455,7 +3455,6 @@ E1B912811BB00EFD003C25B9 /* People.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E1B912801BB00EFD003C25B9 /* People.storyboard */; }; E1B912831BB01047003C25B9 /* PeopleRoleBadgeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B912821BB01047003C25B9 /* PeopleRoleBadgeLabel.swift */; }; E1B912891BB01288003C25B9 /* PeopleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B912881BB01288003C25B9 /* PeopleViewController.swift */; }; - E1B9128B1BB0129C003C25B9 /* WPStyleGuide+People.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B9128A1BB0129C003C25B9 /* WPStyleGuide+People.swift */; }; E1B9128F1BB05B1D003C25B9 /* PeopleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B912841BB01266003C25B9 /* PeopleCell.swift */; }; E1B921BC1C0ED5A3003EA3CB /* MediaSizeSliderCellTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B921BB1C0ED5A3003EA3CB /* MediaSizeSliderCellTest.swift */; }; E1BB92321FDAAFFA00F2D817 /* TextWithAccessoryButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1BB92311FDAAFFA00F2D817 /* TextWithAccessoryButtonCell.swift */; }; @@ -4824,7 +4823,6 @@ FABB23192602FC2C00C8785C /* Media+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 08C388691ED78EE70057BE49 /* Media+Extensions.m */; }; FABB231A2602FC2C00C8785C /* GutenbergImgUploadProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2EC3BF2209A144006176E1 /* GutenbergImgUploadProcessor.swift */; }; FABB231B2602FC2C00C8785C /* PluginListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E151C0C71F388A2000710A83 /* PluginListViewModel.swift */; }; - FABB231C2602FC2C00C8785C /* WPStyleGuide+People.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B9128A1BB0129C003C25B9 /* WPStyleGuide+People.swift */; }; FABB231D2602FC2C00C8785C /* PlanGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F2787B21BC1A48008B4DB5 /* PlanGroup.swift */; }; FABB231E2602FC2C00C8785C /* MenuItemPostsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 08216FB91CDBF96000304BA7 /* MenuItemPostsViewController.m */; }; FABB231F2602FC2C00C8785C /* SignupUsernameTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4322A20C203E1885004EA740 /* SignupUsernameTableViewController.swift */; }; @@ -8875,7 +8873,6 @@ E1B912821BB01047003C25B9 /* PeopleRoleBadgeLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeopleRoleBadgeLabel.swift; sourceTree = ""; }; E1B912841BB01266003C25B9 /* PeopleCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeopleCell.swift; sourceTree = ""; }; E1B912881BB01288003C25B9 /* PeopleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeopleViewController.swift; sourceTree = ""; }; - E1B9128A1BB0129C003C25B9 /* WPStyleGuide+People.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WPStyleGuide+People.swift"; sourceTree = ""; }; E1B921BB1C0ED5A3003EA3CB /* MediaSizeSliderCellTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaSizeSliderCellTest.swift; sourceTree = ""; }; E1BB92311FDAAFFA00F2D817 /* TextWithAccessoryButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextWithAccessoryButtonCell.swift; sourceTree = ""; }; E1BCFBC51C0626C5004BDADF /* WordPress 43.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 43.xcdatamodel"; sourceTree = ""; }; @@ -11073,7 +11070,7 @@ path = Classes; sourceTree = ""; }; - 29B97314FDCFA39411CA2CEA = { + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { isa = PBXGroup; children = ( 3F20FDF3276BF21000DA3CAD /* Packages */, @@ -15788,14 +15785,6 @@ name = Notifications; sourceTree = ""; }; - B59B18761CC7FC070055EB7C /* Style */ = { - isa = PBXGroup; - children = ( - E1B9128A1BB0129C003C25B9 /* WPStyleGuide+People.swift */, - ); - name = Style; - sourceTree = ""; - }; B59B18771CC7FC190055EB7C /* Controllers */ = { isa = PBXGroup; children = ( @@ -17219,7 +17208,6 @@ isa = PBXGroup; children = ( B59B18771CC7FC190055EB7C /* Controllers */, - B59B18761CC7FC070055EB7C /* Style */, B59B18781CC7FC230055EB7C /* Views */, B59B18791CC7FC330055EB7C /* ViewModels */, E1B912801BB00EFD003C25B9 /* People.storyboard */, @@ -19515,7 +19503,7 @@ bg, sk, ); - mainGroup = 29B97314FDCFA39411CA2CEA; + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; packageReferences = ( 3FF1442E266F3C2400138163 /* XCRemoteSwiftPackageReference "ScreenObject" */, 3FC2C33B26C4CF0A00C6D98F /* XCRemoteSwiftPackageReference "XCUITestHelpers" */, @@ -21134,11 +21122,11 @@ files = ( ); inputPaths = ( - "$SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.inputs.xcfilelist", + $SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.inputs.xcfilelist, ); name = "Copy Gutenberg JS"; outputFileListPaths = ( - "$SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.outputs.xcfilelist", + $SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.outputs.xcfilelist, ); outputPaths = ( "", @@ -21333,13 +21321,13 @@ files = ( ); inputFileListPaths = ( - "$SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.inputs.xcfilelist", + $SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.inputs.xcfilelist, ); inputPaths = ( ); name = "Copy Gutenberg JS"; outputFileListPaths = ( - "$SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.outputs.xcfilelist", + $SRCROOT/../Scripts/BuildPhases/CopyGutenbergJS.outputs.xcfilelist, ); outputPaths = ( ); @@ -22302,7 +22290,6 @@ 08C3886A1ED78EE70057BE49 /* Media+Extensions.m in Sources */, FF2EC3C02209A144006176E1 /* GutenbergImgUploadProcessor.swift in Sources */, E151C0C81F388A2000710A83 /* PluginListViewModel.swift in Sources */, - E1B9128B1BB0129C003C25B9 /* WPStyleGuide+People.swift in Sources */, E6F2788121BC1A4A008B4DB5 /* PlanGroup.swift in Sources */, 08216FCE1CDBF96000304BA7 /* MenuItemPostsViewController.m in Sources */, 4322A20D203E1885004EA740 /* SignupUsernameTableViewController.swift in Sources */, @@ -25187,7 +25174,6 @@ FABB23192602FC2C00C8785C /* Media+Extensions.m in Sources */, FABB231A2602FC2C00C8785C /* GutenbergImgUploadProcessor.swift in Sources */, FABB231B2602FC2C00C8785C /* PluginListViewModel.swift in Sources */, - FABB231C2602FC2C00C8785C /* WPStyleGuide+People.swift in Sources */, B09879772B572E1F0048256D /* StatsTrafficDatePickerViewModel.swift in Sources */, FA98B61729A3B76A0071AAE8 /* DashboardBlazeCardCell.swift in Sources */, FA3A281C2A39C8FF00206D74 /* BlazeCampaignSingleStatView.swift in Sources */, From c4e77bfc75d6afdcafd16264d1f553cac3494648 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 16:58:38 +0300 Subject: [PATCH 051/112] Avoid PeopleCell wrong avatar appearing when scrolling --- WordPress/Classes/ViewRelated/People/PeopleCell.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/WordPress/Classes/ViewRelated/People/PeopleCell.swift b/WordPress/Classes/ViewRelated/People/PeopleCell.swift index c5808429b843..de40c50be052 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleCell.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleCell.swift @@ -22,6 +22,12 @@ class PeopleCell: WPTableViewCell { usernameLabel.font = .DS.font(.bodyMedium(.regular)) } + override func prepareForReuse() { + super.prepareForReuse() + + avatarImageView.image = UIImage(named: "gravatar") + } + func bindViewModel(_ viewModel: PeopleCellViewModel) { setAvatarURL(viewModel.avatarURL as URL?) displayNameLabel.text = viewModel.displayName From 9ee884014d3c4e67ae28e8bb26a6c6e8d67af804 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 17:07:21 +0300 Subject: [PATCH 052/112] Remove background and textColor from role --- WordPress/Classes/Models/Role.swift | 28 ------------------- .../People/PeopleCellViewModel.swift | 14 ++++++++-- 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/WordPress/Classes/Models/Role.swift b/WordPress/Classes/Models/Role.swift index 6c97f7b0ac0e..c414a1b5c7f2 100644 --- a/WordPress/Classes/Models/Role.swift +++ b/WordPress/Classes/Models/Role.swift @@ -21,31 +21,3 @@ extension Role { return context.firstObject(ofType: Role.self, matching: predicate) } } - -extension Role { - @objc var backgroundColor: UIColor { - switch slug { - case .some("super-admin"): - return .DS.Foreground.primary - case .some("administrator"): - return .DS.Foreground.primary - case .some("editor"): - return .DS.Background.secondary - default: - return .DS.Background.secondary - } - } - - @objc var textColor: UIColor { - switch slug { - case .some("super-admin"): - return .DS.Background.primary - case .some("administrator"): - return .DS.Background.primary - case .some("editor"): - return .DS.Foreground.primary - default: - return .DS.Foreground.primary - } - } -} diff --git a/WordPress/Classes/ViewRelated/People/PeopleCellViewModel.swift b/WordPress/Classes/ViewRelated/People/PeopleCellViewModel.swift index 994f5cc65295..fa3f07d8c844 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleCellViewModel.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleCellViewModel.swift @@ -30,11 +30,21 @@ struct PeopleCellViewModel { } var roleBackgroundColor: UIColor { - return role?.backgroundColor ?? .DS.Background.secondary + switch role?.slug { + case .some("super-admin"), .some("administrator"): + return .DS.Foreground.primary + default: + return .DS.Background.secondary + } } var roleTextColor: UIColor { - return role?.textColor ?? .DS.Foreground.primary + switch role?.slug { + case .some("super-admin"), .some("administrator"): + return .DS.Background.primary + default: + return .DS.Foreground.primary + } } var roleText: String { From 649a28261c9ed5c4d4606f48ca99c57ff07a8bad Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 17:28:59 +0300 Subject: [PATCH 053/112] Refresh Subscribers view after closing Subscribers details to account for removed subscribers --- .../Classes/ViewRelated/People/PeopleViewController.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift index af3fab259f11..7880d4821e61 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift @@ -184,6 +184,10 @@ class PeopleViewController: UITableViewController { let configuration = WebViewControllerConfiguration(url: url) configuration.authenticateWithDefaultAccount() configuration.secureInteraction = true + configuration.onClose = { [weak self] in + self?.resetManagedPeople() + self?.refreshPeople() + } let viewController = WebKitViewController(configuration: configuration) let navWrapper = LightNavigationController(rootViewController: viewController) navigationController?.present(navWrapper, animated: true) From 1a74730e7904800f563ffd46a6606fef8708ecd9 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 18:13:13 +0300 Subject: [PATCH 054/112] Add Stats Subscriber related analytics events --- .../Utility/Analytics/WPAnalyticsEvent.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift index 6dc775a463d6..091202eaf25f 100644 --- a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift +++ b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift @@ -594,6 +594,11 @@ import Foundation case readingPreferencesSaved case readingPreferencesClosed + // Stats Subscribers + case statsSubscribersViewMoreTapped + case statsEmailsViewMoreTapped + case statsSubscribersChartTapped + /// A String that represents the event var value: String { switch self { @@ -1615,6 +1620,14 @@ import Foundation case .readingPreferencesClosed: return "reader_reading_preferences_closed" + // Stats Subscribers + case .statsSubscribersViewMoreTapped: + return "stats_subscribers_view_more_tapped" + case .statsEmailsViewMoreTapped: + return "stats_emails_view_more_tapped" + case .statsSubscribersChartTapped: + return "stats_subscribers_chart_tapped" + } // END OF SWITCH } From 20d9ac0696757b452d1ed7289ee3f97c8e750f75 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 18:13:37 +0300 Subject: [PATCH 055/112] Track subscribers list and emails summary view more events --- .../Stats/Subscribers/StatsSubscribersViewController.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift index 2df49148e7dd..34d8d2e28edc 100644 --- a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift @@ -85,13 +85,14 @@ extension StatsSubscribersViewController: SiteStatsPeriodDelegate { guard let blog = RootViewCoordinator.sharedPresenter.mySitesCoordinator.currentBlog, let peopleViewController = PeopleViewController.controllerWithBlog(blog, selectedFilter: .followers) else { return } navigationController?.pushViewController(peopleViewController, animated: true) + WPAnalytics.track(.statsSubscribersViewMoreTapped) case .subscribersEmailsSummary: let detailTableViewController = SiteStatsDetailTableViewController.loadFromStoryboard() detailTableViewController.configure(statSection: statSection) navigationController?.pushViewController(detailTableViewController, animated: true) + WPAnalytics.track(.statsEmailsViewMoreTapped) default: - // TODO - DDLogInfo("\(statSection) selected") + break } } } From 1ce2eb840e73f078e3d10220190c5bca427e2195 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Fri, 3 May 2024 18:16:55 +0300 Subject: [PATCH 056/112] Track statsSubscribersChartTapped event --- .../ViewRelated/Stats/Charts/StatsLineChartView.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Charts/StatsLineChartView.swift b/WordPress/Classes/ViewRelated/Stats/Charts/StatsLineChartView.swift index 9ba554bd8da0..10a8ddbeb5a8 100644 --- a/WordPress/Classes/ViewRelated/Stats/Charts/StatsLineChartView.swift +++ b/WordPress/Classes/ViewRelated/Stats/Charts/StatsLineChartView.swift @@ -115,9 +115,10 @@ private extension StatsLineChartView { if case let .viewsAndVisitors(statsInsightsFilterDimension) = statType { properties[LineChartAnalyticsPropertyKey] = statsInsightsFilterDimension.analyticsProperty + WPAnalytics.track(.statsLineChartTapped, properties: properties) + } else if case .subscribers = statType { + WPAnalytics.track(.statsSubscribersChartTapped) } - - WPAnalytics.track(.statsLineChartTapped, properties: properties) } func configureAndPopulateData() { From 81fcafb1a18b9555f53ac33f49bd60f7bc0c9df5 Mon Sep 17 00:00:00 2001 From: kean Date: Thu, 2 May 2024 11:06:17 -0400 Subject: [PATCH 057/112] Allow previewing the trashed posts and restoring them right from the editor --- WordPress/Classes/Models/AbstractPost.swift | 9 +++ .../Classes/Services/PostCoordinator.swift | 12 +++- .../AztecPostViewController.swift | 2 +- .../Gutenberg/GutenbergViewController.swift | 2 +- .../Pages/PageEditorPresenter.swift | 3 +- .../ViewRelated/Post/PostEditor+Publish.swift | 6 +- .../Classes/ViewRelated/Post/PostEditor.swift | 69 ++++++++++++++++++- .../Post/PostListViewController.swift | 2 +- .../Post/PostTrashedOverlayView.swift | 18 +++++ .../Search/PostSearchViewController.swift | 5 +- WordPress/WordPress.xcodeproj/project.pbxproj | 6 ++ 11 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 WordPress/Classes/ViewRelated/Post/PostTrashedOverlayView.swift diff --git a/WordPress/Classes/Models/AbstractPost.swift b/WordPress/Classes/Models/AbstractPost.swift index 569c5bdb1c7f..3e020ecf5943 100644 --- a/WordPress/Classes/Models/AbstractPost.swift +++ b/WordPress/Classes/Models/AbstractPost.swift @@ -132,6 +132,15 @@ extension AbstractPost { } } + var localizedPostType: String { + switch self { + case is Page: + return NSLocalizedString("postType.page", value: "page", comment: "Localized post type: `Page`") + default: + return NSLocalizedString("postType.post", value: "post", comment: "Localized post type: `Post`") + } + } + // MARK: - Misc /// Represent the supported properties used to sort posts. diff --git a/WordPress/Classes/Services/PostCoordinator.swift b/WordPress/Classes/Services/PostCoordinator.swift index dd5b0aa88980..de08dcd4b403 100644 --- a/WordPress/Classes/Services/PostCoordinator.swift +++ b/WordPress/Classes/Services/PostCoordinator.swift @@ -256,7 +256,7 @@ class PostCoordinator: NSObject { } } - private func handleError(_ error: Error, for post: AbstractPost) { + func handleError(_ error: Error, for post: AbstractPost) { guard let topViewController = UIApplication.shared.mainWindow?.topmostPresentedViewController else { wpAssertionFailure("Failed to show an error alert") return @@ -332,6 +332,16 @@ class PostCoordinator: NSObject { save(post) } + /// Restores a trashed post by moving it to draft. + @MainActor + func restore(_ post: AbstractPost) async throws { + wpAssert(post.isOriginal()) + + var changes = RemotePostUpdateParameters() + changes.status = Post.Status.draft.rawValue + try await _update(post, changes: changes) + } + /// Sets the post state to "updating" and performs the given changes. private func _performChanges(_ changes: RemotePostUpdateParameters, for post: AbstractPost) { Task { @MainActor in diff --git a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift index 0d87e45fa9f5..3d57500e61b3 100644 --- a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift +++ b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift @@ -740,7 +740,6 @@ class AztecPostViewController: UIViewController, PostEditor { navigationController?.navigationBar.isTranslucent = false navigationController?.navigationBar.accessibilityIdentifier = "Azctec Editor Navigation Bar" navigationItem.leftBarButtonItems = navigationBarManager.leftBarButtonItems - navigationItem.rightBarButtonItems = navigationBarManager.rightBarButtonItemsAztec navigationItem.titleView = navigationBarManager.blogTitleViewLabel } @@ -823,6 +822,7 @@ class AztecPostViewController: UIViewController, PostEditor { reloadEditorContents() reloadPublishButton() refreshTitleViewForMediaUploadIfNeeded() + navigationItem.rightBarButtonItems = post.status == .trash ? [] : navigationBarManager.rightBarButtonItemsAztec } func refreshTitleViewForMediaUploadIfNeeded() { diff --git a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift index d445bd0c665e..da8ac1fb1f7e 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift @@ -438,7 +438,6 @@ class GutenbergViewController: UIViewController, PostEditor, FeaturedImageDelega navigationController?.navigationBar.isTranslucent = false navigationController?.navigationBar.accessibilityIdentifier = "Gutenberg Editor Navigation Bar" navigationItem.leftBarButtonItems = navigationBarManager.leftBarButtonItems - navigationItem.rightBarButtonItems = navigationBarManager.rightBarButtonItems // Add bottom border line let screenScale = UIScreen.main.scale @@ -481,6 +480,7 @@ class GutenbergViewController: UIViewController, PostEditor, FeaturedImageDelega reloadBlogIconView() reloadEditorContents() reloadPublishButton() + navigationItem.rightBarButtonItems = post.status == .trash ? [] : navigationBarManager.rightBarButtonItems } func toggleEditingMode() { diff --git a/WordPress/Classes/ViewRelated/Pages/PageEditorPresenter.swift b/WordPress/Classes/ViewRelated/Pages/PageEditorPresenter.swift index 38a76380e71a..7bba5acabe54 100644 --- a/WordPress/Classes/ViewRelated/Pages/PageEditorPresenter.swift +++ b/WordPress/Classes/ViewRelated/Pages/PageEditorPresenter.swift @@ -9,7 +9,8 @@ struct PageEditorPresenter { return false } - guard page.status != .trash else { + if page.status == .trash && !FeatureFlag.syncPublishing.enabled { + // No editing posts that are trashed. return false } diff --git a/WordPress/Classes/ViewRelated/Post/PostEditor+Publish.swift b/WordPress/Classes/ViewRelated/Post/PostEditor+Publish.swift index 092852216ebf..baabd7344655 100644 --- a/WordPress/Classes/ViewRelated/Post/PostEditor+Publish.swift +++ b/WordPress/Classes/ViewRelated/Post/PostEditor+Publish.swift @@ -429,6 +429,10 @@ extension PublishingEditor { return _discardChanges() } + guard post.status != .trash else { + return true // No revision is created for trashed posts + } + guard let context = post.managedObjectContext else { wpAssertionFailure("Missing managedObjectContext") return true @@ -724,7 +728,7 @@ extension PublishingEditor { wpAssert(post.latest() == post, "Must be opened with the latest verison of the post") - if !post.isUnsavedRevision { + if !post.isUnsavedRevision && post.status != .trash { DDLogDebug("Creating new revision") post = post._createRevision() } diff --git a/WordPress/Classes/ViewRelated/Post/PostEditor.swift b/WordPress/Classes/ViewRelated/Post/PostEditor.swift index e5e1652fcae6..70b064c5adf9 100644 --- a/WordPress/Classes/ViewRelated/Post/PostEditor.swift +++ b/WordPress/Classes/ViewRelated/Post/PostEditor.swift @@ -1,5 +1,6 @@ import UIKit import Combine +import WordPressFlux enum EditMode { case richText @@ -141,7 +142,12 @@ extension PostEditor where Self: UIViewController { guard FeatureFlag.syncPublishing.enabled else { return } - showAutosaveAvailableAlertIfNeeded() + + if post.original().status == .trash { + showPostTrashedOverlay() + } else { + showAutosaveAvailableAlertIfNeeded() + } var cancellables: [AnyCancellable] = [] @@ -160,6 +166,8 @@ extension PostEditor where Self: UIViewController { objc_setAssociatedObject(self, &cancellablesKey, cancellables, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + // MARK: - Autosave + private func showAutosaveAvailableAlertIfNeeded() { // The revision has unsaved local changes, takes precedence over autosave guard post.changes.isEmpty else { @@ -182,6 +190,8 @@ extension PostEditor where Self: UIViewController { present(alert, animated: true) } + // MARK: - App Termination + private func appWillTerminate() { guard let context = post.managedObjectContext else { return @@ -206,6 +216,8 @@ extension PostEditor where Self: UIViewController { } } + // MARK: - Conflict Resolution + private func postConflictResolved(_ notification: Foundation.Notification) { guard let userInfo = notification.userInfo, @@ -213,8 +225,53 @@ extension PostEditor where Self: UIViewController { else { return } - self.post = post - createRevisionOfPost() + self.configureWithUpdatedPost(post) + } + + // MARK: - Restore Trashed Post + + private func showPostTrashedOverlay() { + let overlay = TrashedPostOverlayView() + view.addSubview(overlay) + overlay.translatesAutoresizingMaskIntoConstraints = false + overlay.onOverlayTapped = { [weak self] in self?.showRestorePostAlert(with: $0) } + view.pinSubviewToAllEdges(overlay) + } + + private func showRestorePostAlert(with overlay: TrashedPostOverlayView) { + overlay.isUserInteractionEnabled = false + + let postType = post.localizedPostType.lowercased() + let alert = UIAlertController(title: String(format: Strings.trashedPostSheetTitle, postType), message: String(format: Strings.trashedPostSheetMessage, postType), preferredStyle: .alert) + alert.addAction(UIAlertAction(title: Strings.trashedPostSheetCancel, style: .cancel) { _ in + overlay.isUserInteractionEnabled = true + }) + alert.addAction(UIAlertAction(title: Strings.trashedPostSheetRecover, style: .default) { [weak self] _ in + guard let self else { return } + Task { + await self.restorePostFromTrash() + } + }) + present(alert, animated: true) + } + + @MainActor + private func restorePostFromTrash() async { + SVProgressHUD.show() + defer { SVProgressHUD.dismiss() } + let coordinator = PostCoordinator.shared + do { + try await coordinator.restore(post) + ActionDispatcher.dispatch(NoticeAction.post(Notice(title: Strings.trashedPostRestored))) + self.configureWithUpdatedPost(post) + } catch { + coordinator.handleError(error, for: post) + } + } + + private func configureWithUpdatedPost(_ post: AbstractPost) { + self.post = post // Even if it's the same instance, it's how you currently refresh the editor + self.createRevisionOfPost() } } @@ -242,4 +299,10 @@ private enum Strings { static let autosaveAlertContinue = NSLocalizedString("autosaveAlert.viewChanges", value: "View Changes", comment: "An alert suggesting to load autosaved revision for a published post") static let autosaveAlertCancel = NSLocalizedString("autosaveAlert.cancel", value: "Cancel", comment: "An alert suggesting to load autosaved revision for a published post") + + static let trashedPostSheetTitle = NSLocalizedString("postEditor.recoverTrashedPostAlert.title", value: "Trashed %@", comment: "Editor, alert for recovering a trashed post") + static let trashedPostSheetMessage = NSLocalizedString("postEditor.recoverTrashedPostAlert.message", value: "Trashed %1$@ can't be edited. To edit this %1$@, you'll need to restore it by moving it back to a draft.", comment: "Editor, alert for recovering a trashed post") + static let trashedPostSheetCancel = NSLocalizedString("postEditor.recoverTrashedPostAlert.cancel", value: "Cancel", comment: "Editor, alert for recovering a trashed post") + static let trashedPostSheetRecover = NSLocalizedString("postEditor.recoverTrashedPostAlert.restore", value: "Restore", comment: "Editor, alert for recovering a trashed post") + static let trashedPostRestored = NSLocalizedString("postEditor.recoverTrashedPost.postRecoveredNoticeTitle", value: "Post restored as a draft", comment: "Editor, notice for successful recovery a trashed post") } diff --git a/WordPress/Classes/ViewRelated/Post/PostListViewController.swift b/WordPress/Classes/ViewRelated/Post/PostListViewController.swift index ffb01e458134..3fde3595718a 100644 --- a/WordPress/Classes/ViewRelated/Post/PostListViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/PostListViewController.swift @@ -161,7 +161,7 @@ final class PostListViewController: AbstractPostListViewController, InteractiveP let post = postAtIndexPath(indexPath) - guard post.status != .trash else { + if post.status == .trash && !FeatureFlag.syncPublishing.enabled { // No editing posts that are trashed. return } diff --git a/WordPress/Classes/ViewRelated/Post/PostTrashedOverlayView.swift b/WordPress/Classes/ViewRelated/Post/PostTrashedOverlayView.swift new file mode 100644 index 000000000000..3cc121ff3575 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Post/PostTrashedOverlayView.swift @@ -0,0 +1,18 @@ +import UIKit + +final class TrashedPostOverlayView: UIView { + var onOverlayTapped: ((TrashedPostOverlayView) -> Void)? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + super.touchesBegan(touches, with: event) + onOverlayTapped?(self) + } +} diff --git a/WordPress/Classes/ViewRelated/Post/Search/PostSearchViewController.swift b/WordPress/Classes/ViewRelated/Post/Search/PostSearchViewController.swift index 1e4b1aa8b95c..cce161070ad8 100644 --- a/WordPress/Classes/ViewRelated/Post/Search/PostSearchViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/Search/PostSearchViewController.swift @@ -164,7 +164,10 @@ final class PostSearchViewController: UIViewController, UITableViewDelegate, UIS switch viewModel.posts[indexPath.row].latest() { case let post as Post: - guard post.status != .trash else { return } + if post.status == .trash && !FeatureFlag.syncPublishing.enabled { + // No editing posts that are trashed. + return + } delegate?.edit(post) case let page as Page: guard page.status != .trash else { return } diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index e1cb4ba67699..f0598667d2f8 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -432,6 +432,8 @@ 0A9687BC28B40771009DCD2F /* FullScreenCommentReplyViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9687BB28B40771009DCD2F /* FullScreenCommentReplyViewModelMock.swift */; }; 0C01A6EA2AB37F0F009F7145 /* SiteMediaCollectionCellSelectionOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C01A6E92AB37F0F009F7145 /* SiteMediaCollectionCellSelectionOverlayView.swift */; }; 0C01A6EB2AB37F0F009F7145 /* SiteMediaCollectionCellSelectionOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C01A6E92AB37F0F009F7145 /* SiteMediaCollectionCellSelectionOverlayView.swift */; }; + 0C02E6C52BE3B6E30055F0F6 /* PostTrashedOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C02E6C42BE3B6E30055F0F6 /* PostTrashedOverlayView.swift */; }; + 0C02E6C62BE3B6E30055F0F6 /* PostTrashedOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C02E6C42BE3B6E30055F0F6 /* PostTrashedOverlayView.swift */; }; 0C03AECA2B7D995F00B64A25 /* PublishButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C03AEC92B7D995F00B64A25 /* PublishButton.swift */; }; 0C03AECB2B7D995F00B64A25 /* PublishButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C03AEC92B7D995F00B64A25 /* PublishButton.swift */; }; 0C0453282AC73343003079C8 /* SiteMediaVideoDurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0453272AC73343003079C8 /* SiteMediaVideoDurationView.swift */; }; @@ -6290,6 +6292,7 @@ 0A9610F828B2E56300076EBA /* UserSuggestion+Comparable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserSuggestion+Comparable.swift"; sourceTree = ""; }; 0A9687BB28B40771009DCD2F /* FullScreenCommentReplyViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenCommentReplyViewModelMock.swift; sourceTree = ""; }; 0C01A6E92AB37F0F009F7145 /* SiteMediaCollectionCellSelectionOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteMediaCollectionCellSelectionOverlayView.swift; sourceTree = ""; }; + 0C02E6C42BE3B6E30055F0F6 /* PostTrashedOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostTrashedOverlayView.swift; sourceTree = ""; }; 0C03AEC92B7D995F00B64A25 /* PublishButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublishButton.swift; sourceTree = ""; }; 0C0453272AC73343003079C8 /* SiteMediaVideoDurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteMediaVideoDurationView.swift; sourceTree = ""; }; 0C04532A2AC77245003079C8 /* SiteMediaDocumentInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteMediaDocumentInfoView.swift; sourceTree = ""; }; @@ -15270,6 +15273,7 @@ 8B260D7D2444FC9D0010F756 /* PostVisibilitySelectorViewController.swift */, 593F26601CAB00CA00F14073 /* PostSharingController.swift */, E155EC711E9B7DCE009D7F63 /* PostTagPickerViewController.swift */, + 0C02E6C42BE3B6E30055F0F6 /* PostTrashedOverlayView.swift */, ); path = Post; sourceTree = ""; @@ -22865,6 +22869,7 @@ FAB8004925AEDC2300D5D54A /* JetpackBackupCompleteViewController.swift in Sources */, 9A8ECE0C2254A3260043C8DA /* JetpackLoginViewController.swift in Sources */, 46F583A92624CE790010A723 /* BlockEditorSettings+CoreDataClass.swift in Sources */, + 0C02E6C52BE3B6E30055F0F6 /* PostTrashedOverlayView.swift in Sources */, B50C0C641EF42B3A00372C65 /* Header+WordPress.swift in Sources */, FAA5C90F2B7FC74C002E073B /* PostSyncStateViewModel.swift in Sources */, F504D2B125D60C5900A2764C /* StoryMediaLoader.swift in Sources */, @@ -25047,6 +25052,7 @@ FABB22A72602FC2C00C8785C /* BlogListViewController+Activity.swift in Sources */, 3F810A5B2616870C00ADDCC2 /* UnifiedPrologueIntroContentView.swift in Sources */, FABB22A82602FC2C00C8785C /* TableDataCoordinator.swift in Sources */, + 0C02E6C62BE3B6E30055F0F6 /* PostTrashedOverlayView.swift in Sources */, FABB22A92602FC2C00C8785C /* SettingsMultiTextViewController.m in Sources */, FABB22AA2602FC2C00C8785C /* FilterSheetViewController.swift in Sources */, FABB22AB2602FC2C00C8785C /* Plan.swift in Sources */, From 2dc98c1600c68a97de83ae88e08e586301bf2b55 Mon Sep 17 00:00:00 2001 From: kean Date: Thu, 2 May 2024 11:59:26 -0400 Subject: [PATCH 058/112] Update release notes --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 6d5d65bf54fd..b4f1ba7f6e44 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -3,6 +3,7 @@ * [*] Add "Parent Page" to "Page Settings" [#23136] * [*] Add "Slug" and "Excerpt" fields to "Page Settings" [#23135] * [*] Make it easier to "Share" and "Blaze" a published post with an updated success view [##23128] +* [*] Add support for viewing trashed posts and pages and restoring them from the editor [#23142] 24.8 ----- From a7e3810f546e26b67309ea119c3c14c7c983a134 Mon Sep 17 00:00:00 2001 From: Alex Grebenyuk Date: Sat, 4 May 2024 08:15:00 -0400 Subject: [PATCH 059/112] Update WordPress/Classes/ViewRelated/Post/PostEditor.swift Co-authored-by: Momo Ozawa --- WordPress/Classes/ViewRelated/Post/PostEditor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Post/PostEditor.swift b/WordPress/Classes/ViewRelated/Post/PostEditor.swift index 70b064c5adf9..cbd6ef12c0af 100644 --- a/WordPress/Classes/ViewRelated/Post/PostEditor.swift +++ b/WordPress/Classes/ViewRelated/Post/PostEditor.swift @@ -301,7 +301,7 @@ private enum Strings { static let autosaveAlertCancel = NSLocalizedString("autosaveAlert.cancel", value: "Cancel", comment: "An alert suggesting to load autosaved revision for a published post") static let trashedPostSheetTitle = NSLocalizedString("postEditor.recoverTrashedPostAlert.title", value: "Trashed %@", comment: "Editor, alert for recovering a trashed post") - static let trashedPostSheetMessage = NSLocalizedString("postEditor.recoverTrashedPostAlert.message", value: "Trashed %1$@ can't be edited. To edit this %1$@, you'll need to restore it by moving it back to a draft.", comment: "Editor, alert for recovering a trashed post") + static let trashedPostSheetMessage = NSLocalizedString("postEditor.recoverTrashedPostAlert.message", value: "A trashed %1$@ can't be edited. To edit this %1$@, you'll need to restore it by moving it back to a draft.", comment: "Editor, alert for recovering a trashed post") static let trashedPostSheetCancel = NSLocalizedString("postEditor.recoverTrashedPostAlert.cancel", value: "Cancel", comment: "Editor, alert for recovering a trashed post") static let trashedPostSheetRecover = NSLocalizedString("postEditor.recoverTrashedPostAlert.restore", value: "Restore", comment: "Editor, alert for recovering a trashed post") static let trashedPostRestored = NSLocalizedString("postEditor.recoverTrashedPost.postRecoveredNoticeTitle", value: "Post restored as a draft", comment: "Editor, notice for successful recovery a trashed post") From bca05eb5109cb6c20393edd915f2f65f2316892a Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 08:16:51 -0400 Subject: [PATCH 060/112] Rename PostTrashedOverlayView --- WordPress/Classes/ViewRelated/Post/PostEditor.swift | 4 ++-- .../Classes/ViewRelated/Post/PostTrashedOverlayView.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Post/PostEditor.swift b/WordPress/Classes/ViewRelated/Post/PostEditor.swift index cbd6ef12c0af..0016190ac30e 100644 --- a/WordPress/Classes/ViewRelated/Post/PostEditor.swift +++ b/WordPress/Classes/ViewRelated/Post/PostEditor.swift @@ -231,14 +231,14 @@ extension PostEditor where Self: UIViewController { // MARK: - Restore Trashed Post private func showPostTrashedOverlay() { - let overlay = TrashedPostOverlayView() + let overlay = PostTrashedOverlayView() view.addSubview(overlay) overlay.translatesAutoresizingMaskIntoConstraints = false overlay.onOverlayTapped = { [weak self] in self?.showRestorePostAlert(with: $0) } view.pinSubviewToAllEdges(overlay) } - private func showRestorePostAlert(with overlay: TrashedPostOverlayView) { + private func showRestorePostAlert(with overlay: PostTrashedOverlayView) { overlay.isUserInteractionEnabled = false let postType = post.localizedPostType.lowercased() diff --git a/WordPress/Classes/ViewRelated/Post/PostTrashedOverlayView.swift b/WordPress/Classes/ViewRelated/Post/PostTrashedOverlayView.swift index 3cc121ff3575..968cff7d0e78 100644 --- a/WordPress/Classes/ViewRelated/Post/PostTrashedOverlayView.swift +++ b/WordPress/Classes/ViewRelated/Post/PostTrashedOverlayView.swift @@ -1,7 +1,7 @@ import UIKit -final class TrashedPostOverlayView: UIView { - var onOverlayTapped: ((TrashedPostOverlayView) -> Void)? +final class PostTrashedOverlayView: UIView { + var onOverlayTapped: ((PostTrashedOverlayView) -> Void)? override init(frame: CGRect) { super.init(frame: frame) From d9fdb34dd89b800e29872f82b9739d665ab3fd05 Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 12:32:05 -0400 Subject: [PATCH 061/112] Disable flaky part of the test --- WordPress/UITests/Tests/PagesTests.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/WordPress/UITests/Tests/PagesTests.swift b/WordPress/UITests/Tests/PagesTests.swift index f001b05dc546..30151f691033 100644 --- a/WordPress/UITests/Tests/PagesTests.swift +++ b/WordPress/UITests/Tests/PagesTests.swift @@ -25,8 +25,9 @@ class PageTests: XCTestCase { .enterTextInTitle(text: postTitle, postType: .page) .post(action: .publish, postType: .page) - try MySiteScreen() - .scrollToPagesCard() - .verifyPagePublished(title: postTitle) + // TODO: reimplement this part of the test (flaky) +// try MySiteScreen() +// .scrollToPagesCard() +// .verifyPagePublished(title: postTitle) } } From 0990236c15fe1b1c507f0cf3f6f68e0a098b2ce0 Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 13:03:08 -0400 Subject: [PATCH 062/112] Add defensive code to viewStatsForPost --- .../ViewRelated/Post/AbstractPostListViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift b/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift index aaa942a40f23..17dd6717a655 100644 --- a/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift @@ -758,7 +758,9 @@ class AbstractPostListViewController: UIViewController, SiteStatsInformation.sharedInstance.oauth2Token = blog.authToken SiteStatsInformation.sharedInstance.siteID = blog.dotComID - let postURL = URL(string: post.permaLink! as String) + guard let postURL = post.permaLink.flatMap(URL.init) else { + return wpAssertionFailure("permalink missing or invalid") + } let postStatsTableViewController = PostStatsTableViewController.withJPBannerForBlog(postID: postID, postTitle: post.titleForDisplay(), postURL: postURL) From a0e4531353355ccc08bfd17400afb3de14d39ca9 Mon Sep 17 00:00:00 2001 From: kean Date: Thu, 2 May 2024 17:47:02 -0400 Subject: [PATCH 063/112] Update Post Editor context menu --- .../Utility/Analytics/WPAnalyticsEvent.swift | 3 + .../GutenbergViewController+MoreActions.swift | 73 +++++++++++++++++++ .../Gutenberg/GutenbergViewController.swift | 9 +++ .../Post/PostEditor+MoreOptions.swift | 2 +- .../Post/PostEditorNavigationBarManager.swift | 2 +- 5 files changed, 87 insertions(+), 2 deletions(-) diff --git a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift index 6dc775a463d6..49a3c74f93c1 100644 --- a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift +++ b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift @@ -41,6 +41,7 @@ import Foundation case editorPostSlugChanged case editorPostExcerptChanged case editorPostSiteChanged + case editorPostLegacyMoreMenuShown // Resolve post version conflict case resolveConflictScreenShown @@ -661,6 +662,8 @@ import Foundation return "editor_post_excerpt_changed" case .editorPostSiteChanged: return "editor_post_site_changed" + case .editorPostLegacyMoreMenuShown: + return "editor_post_legacy_more_menu_shown" case .resolveConflictScreenShown: return "resolve_conflict_screen_shown" case .resolveConflictSaveTapped: diff --git a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift index c9f61cbbbfac..8e0a5727dde3 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift @@ -11,7 +11,11 @@ extension GutenbergViewController { case managedObjectContextMissing = 2 } + // - warning: deprecated (kahu-offline-mode) + // TODO: Remove when/if confirmed that this is never invoked by Gutenberg. func displayMoreSheet() { + WPAnalytics.track(.editorPostLegacyMoreMenuShown) + // Dismisses and locks the Notices Store from displaying any new notices. ActionDispatcher.dispatch(NoticeAction.lock) let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) @@ -86,11 +90,68 @@ extension GutenbergViewController { present(alert, animated: true) } + + func makeMoreMenu() -> UIMenu { + UIMenu(title: "", image: nil, identifier: nil, options: [], children: [ + UIDeferredMenuElement.uncached { [weak self] in + $0(self?.makeMoreMenuSections() ?? []) + } + ]) + } + + private func makeMoreMenuSections() -> [UIMenuElement] { + var sections: [UIMenuElement] = [ + UIMenu(title: "", subtitle: "", options: .displayInline, children: makeMoreMenuActions()) + ] + if let string = makeContextStructureString() { + sections.append(UIAction(subtitle: string, attributes: [.disabled], handler: { _ in })) + } + return sections + } + + private func makeMoreMenuActions() -> [UIAction] { + var actions: [UIAction] = [] + + let toggleModeTitle = mode == .richText ? Strings.codeEditor : Strings.visualEditor + let toggleModeIconName = mode == .richText ? "ellipsis.curlybraces" : "doc.richtext" + actions.append(UIAction(title: toggleModeTitle, image: UIImage(systemName: toggleModeIconName)) { [weak self] _ in + self?.toggleEditingMode() + }) + + actions.append(UIAction(title: Strings.preview, image: UIImage(systemName: "safari")) { [weak self] _ in + self?.displayPreview() + }) + + let revisionCount = (post.revisions ?? []).count + if revisionCount > 0 { + actions.append(UIAction(title: Strings.revisions + " (\(revisionCount))", image: UIImage(systemName: "clock.arrow.circlepath")) { [weak self] _ in + self?.displayHistory() + }) + } + + let settingsTitle = self.post is Page ? Strings.pageSettings : Strings.postSettings + actions.append(UIAction(title: settingsTitle, image: UIImage(systemName: "gearshape")) { [weak self] _ in + self?.displayPostSettings() + }) + let helpTitle = JetpackFeaturesRemovalCoordinator.jetpackFeaturesEnabled() ? Strings.helpAndSupport : Strings.help + actions.append(UIAction(title: helpTitle, image: UIImage(systemName: "questionmark.circle")) { [weak self] _ in + self?.showEditorHelp() + }) + return actions + } + + private func makeContextStructureString() -> String? { + guard mode == .richText, let contentInfo = contentInfo else { + return nil + } + return String(format: Strings.contentStructure, contentInfo.blockCount, contentInfo.wordCount, contentInfo.characterCount) + } } // MARK: - Constants extension GutenbergViewController { + // - warning: deprecated (kahu-offline-mode) struct MoreSheetAlert { static let htmlTitle = NSLocalizedString("Switch to HTML Mode", comment: "Switches the Editor to HTML Mode") static let richTitle = NSLocalizedString("Switch to Visual Mode", comment: "Switches the Editor to Rich Text Mode") @@ -104,3 +165,15 @@ extension GutenbergViewController { static let editorHelpTitle = NSLocalizedString("Help", comment: "Open editor help options") } } + +private enum Strings { + static let codeEditor = NSLocalizedString("postEditor.moreMenu.codeEditor", value: "Code Editor", comment: "Post Editor / Button in the 'More' menu") + static let visualEditor = NSLocalizedString("postEditor.moreMenu.visualEditor", value: "Visual Editor", comment: "Post Editor / Button in the 'More' menu") + static let preview = NSLocalizedString("postEditor.moreMenu.preview", value: "Preview", comment: "Post Editor / Button in the 'More' menu") + static let revisions = NSLocalizedString("postEditor.moreMenu.revisions", value: "Revisions", comment: "Post Editor / Button in the 'More' menu") + static let pageSettings = NSLocalizedString("postEditor.moreMenu.pageSettings", value: "Page Settings", comment: "Post Editor / Button in the 'More' menu") + static let postSettings = NSLocalizedString("postEditor.moreMenu.postSettings", value: "Post Settings", comment: "Post Editor / Button in the 'More' menu") + static let helpAndSupport = NSLocalizedString("postEditor.moreMenu.helpAndSupport", value: "Help & Support", comment: "Post Editor / Button in the 'More' menu") + static let help = NSLocalizedString("postEditor.moreMenu.help", value: "Help", comment: "Post Editor / Button in the 'More' menu") + static let contentStructure = NSLocalizedString("postEditor.moreMenu.contentStructure", value: "Blocks: %li, Words: %li, Characters: %li", comment: "Post Editor / 'More' menu details labels with 'Blocks', 'Words' and 'Characters' counts as parameters (in that order)") +} diff --git a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift index da8ac1fb1f7e..16eb9741a50a 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift @@ -449,6 +449,15 @@ class GutenbergViewController: UIViewController, PostEditor, FeaturedImageDelega borderBottom.frame = CGRect(x: 0, y: navigationController?.navigationBar.frame.size.height ?? 0 - borderWidth, width: navigationController?.navigationBar.frame.size.width ?? 0, height: borderWidth) borderBottom.autoresizingMask = [.flexibleWidth, .flexibleTopMargin] navigationController?.navigationBar.addSubview(borderBottom) + + if FeatureFlag.syncPublishing.enabled { + navigationBarManager.moreButton.menu = makeMoreMenu() + navigationBarManager.moreButton.showsMenuAsPrimaryAction = true + } + } + + @objc private func buttonMoreTapped() { + displayMoreSheet() } private func reloadBlogIconView() { diff --git a/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift b/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift index d4a93768833a..f08d52699fd9 100644 --- a/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift +++ b/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift @@ -174,7 +174,7 @@ extension PostEditor { self.post.mt_excerpt = revision.postExcerpt // It's important to clear the pending uploads associated with the -     // post. The assumption is that if the revision on the remote, + // post. The assumption is that if the revision on the remote, // its associated media has to be also uploaded. MediaCoordinator.shared.cancelUploadOfAllMedia(for: self.post) self.post.media = [] diff --git a/WordPress/Classes/ViewRelated/Post/PostEditorNavigationBarManager.swift b/WordPress/Classes/ViewRelated/Post/PostEditorNavigationBarManager.swift index db9fe23d2a50..b3be9854585d 100644 --- a/WordPress/Classes/ViewRelated/Post/PostEditorNavigationBarManager.swift +++ b/WordPress/Classes/ViewRelated/Post/PostEditorNavigationBarManager.swift @@ -117,7 +117,7 @@ class PostEditorNavigationBarManager { return button }() - private lazy var moreButton: UIButton = { + lazy var moreButton: UIButton = { let image = UIImage(named: "editor-more") let button = UIButton(type: .system) button.setImage(image, for: .normal) From e36ef840aedbc74fba7ff8dd8d03d66842ce56ee Mon Sep 17 00:00:00 2001 From: kean Date: Thu, 2 May 2024 17:50:21 -0400 Subject: [PATCH 064/112] Update table view style in revisions list --- .../Aztec/ViewControllers/AztecPostViewController.swift | 2 +- .../Gutenberg/GutenbergViewController+MoreActions.swift | 4 ++-- .../Classes/ViewRelated/Post/PostEditor+MoreOptions.swift | 2 +- .../Post/Revisions/RevisionsTableViewController.swift | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift index 3d57500e61b3..86de50e099df 100644 --- a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift +++ b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift @@ -1197,7 +1197,7 @@ private extension AztecPostViewController { if (post.revisions ?? []).count > 0 { alert.addDefaultActionWithTitle(MoreSheetAlert.historyTitle) { [unowned self] _ in - self.displayHistory() + self.displayRevisionsList() } } diff --git a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift index 8e0a5727dde3..2c761c22de5c 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift @@ -56,7 +56,7 @@ extension GutenbergViewController { if (post.revisions ?? []).count > 0 { alert.addDefaultActionWithTitle(MoreSheetAlert.historyTitle) { [weak self] _ in - self?.displayHistory() + self?.displayRevisionsList() ActionDispatcher.dispatch(NoticeAction.unlock) } } @@ -125,7 +125,7 @@ extension GutenbergViewController { let revisionCount = (post.revisions ?? []).count if revisionCount > 0 { actions.append(UIAction(title: Strings.revisions + " (\(revisionCount))", image: UIImage(systemName: "clock.arrow.circlepath")) { [weak self] _ in - self?.displayHistory() + self?.displayRevisionsList() }) } diff --git a/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift b/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift index f08d52699fd9..e0e83ebfeaa4 100644 --- a/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift +++ b/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift @@ -157,7 +157,7 @@ extension PostEditor { } } - func displayHistory() { + func displayRevisionsList() { guard FeatureFlag.syncPublishing.enabled else { _displayHistory() return diff --git a/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift b/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift index c83410bf0356..f3e1d9a6bd0d 100644 --- a/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift @@ -36,7 +36,7 @@ class RevisionsTableViewController: UITableViewController { required init(post: AbstractPost, onRevisionLoaded: @escaping RevisionLoadedBlock) { self.post = post self.onRevisionLoaded = onRevisionLoaded - super.init(nibName: nil, bundle: nil) + super.init(style: .insetGrouped) } required init?(coder aDecoder: NSCoder) { From 7a5576f41207edecfd2f20bae68b7fddf6a1fd04 Mon Sep 17 00:00:00 2001 From: kean Date: Thu, 2 May 2024 18:01:47 -0400 Subject: [PATCH 065/112] Update copy on the Revisions screen --- .../RevisionsTableViewController.swift | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift b/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift index f3e1d9a6bd0d..0b51c6f44c0c 100644 --- a/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift @@ -65,7 +65,7 @@ class RevisionsTableViewController: UITableViewController { private extension RevisionsTableViewController { private func setupUI() { - navigationItem.title = NSLocalizedString("History", comment: "Title of the post history screen") + navigationItem.title = Strings.title let cellNib = UINib(nibName: RevisionsTableViewCell.classNameWithoutNamespaces(), bundle: Bundle(for: RevisionsTableViewCell.self)) @@ -113,7 +113,7 @@ private extension RevisionsTableViewController { @objc private func refreshRevisions() { if sectionCount == 0 { - configureAndDisplayNoResults(title: NoResultsText.loadingTitle, + configureAndDisplayNoResults(title: Strings.loading, accessoryView: NoResultsViewController.loadingAccessoryView()) } @@ -157,7 +157,7 @@ private extension RevisionsTableViewController { return } - SVProgressHUD.show(withStatus: NSLocalizedString("Loading...", comment: "Text displayed in HUD while a revision post is loading.")) + SVProgressHUD.show(withStatus: Strings.loading) let coreDataStack = ContextManager.shared let postRepository = PostRepository(coreDataStack: coreDataStack) @@ -287,8 +287,8 @@ extension RevisionsTableViewController: RevisionsView { case (true, let count) where count == 0: // When the API call successed but there are no revisions loaded // This is an edge cas. It shouldn't happen since we open the revisions list only if the post revisions array is not empty. - configureAndDisplayNoResults(title: NoResultsText.noResultsTitle, - subtitle: NoResultsText.noResultsSubtitle) + configureAndDisplayNoResults(title: Strings.noResultsTitle, + subtitle: Strings.noResultsSubtitle) default: hideNoResults() } @@ -326,10 +326,14 @@ private extension Date { } struct NoResultsText { - static let loadingTitle = NSLocalizedString("Loading history...", comment: "Displayed while a call is loading the history.") static let reloadButtonTitle = NSLocalizedString("Try again", comment: "Re-load the history again. It appears if the loading call fails.") - static let noResultsTitle = NSLocalizedString("No history yet", comment: "Displayed when a call is made to load the revisions but there's no result or an error.") - static let noResultsSubtitle = NSLocalizedString("When you make changes in the editor you'll be able to see the history here", comment: "Displayed when a call is made to load the history but there's no result or an error.") static let errorTitle = NSLocalizedString("Oops", comment: "Title for the view when there's an error loading the history") static let errorSubtitle = NSLocalizedString("There was an error loading the history", comment: "Text displayed when there is a failure loading the history.") } + +private enum Strings { + static let title = NSLocalizedString("revisions.title", value: "Revisions", comment: "Post revisions list screen title") + static let loading = NSLocalizedString("revisions.loadingTitle", value: "Loading…", comment: "Post revisions list screen / loading view title") + static let noResultsTitle = NSLocalizedString("revisions.emptyStateTitle", value: "No revisions yet", comment: "Displayed when a call is made to load the revisions but there's no result or an error.") + static let noResultsSubtitle = NSLocalizedString("revisions.emptyStateSubtitle", value: "When you make changes in the editor you'll be able to see the revision history here", comment: "Displayed when a call is made to load the history but there's no result or an error.") +} From fa8edb9436f06055585b33a495ee083aa81da275 Mon Sep 17 00:00:00 2001 From: kean Date: Thu, 2 May 2024 18:22:01 -0400 Subject: [PATCH 066/112] Update release notes --- RELEASE-NOTES.txt | 1 + .../Gutenberg/GutenbergViewController+MoreActions.swift | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index b4f1ba7f6e44..8da42bb3cd69 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -4,6 +4,7 @@ * [*] Add "Slug" and "Excerpt" fields to "Page Settings" [#23135] * [*] Make it easier to "Share" and "Blaze" a published post with an updated success view [##23128] * [*] Add support for viewing trashed posts and pages and restoring them from the editor [#23142] +* [*] Update the "More" menu in the Editor to use modern iOS design and update copy to match Gutenberg [#23145] 24.8 ----- diff --git a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift index 2c761c22de5c..e8cf27fa7d1e 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift @@ -113,7 +113,7 @@ extension GutenbergViewController { var actions: [UIAction] = [] let toggleModeTitle = mode == .richText ? Strings.codeEditor : Strings.visualEditor - let toggleModeIconName = mode == .richText ? "ellipsis.curlybraces" : "doc.richtext" + let toggleModeIconName = mode == .richText ? "curlybraces" : "doc.richtext" actions.append(UIAction(title: toggleModeTitle, image: UIImage(systemName: toggleModeIconName)) { [weak self] _ in self?.toggleEditingMode() }) From 838cc2b51d58f03962f58201e970261df02ad8a5 Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 11:26:47 -0400 Subject: [PATCH 067/112] Fix header separator in Revisions list --- .../Pages/PageListSectionHeaderView.swift | 19 ------ .../Pages/PageListSectionHeaderView.xib | 67 ------------------- .../RevisionsTableViewController.swift | 12 +--- WordPress/WordPress.xcodeproj/project.pbxproj | 12 ---- 4 files changed, 2 insertions(+), 108 deletions(-) delete mode 100644 WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.swift delete mode 100644 WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.xib diff --git a/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.swift b/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.swift deleted file mode 100644 index 688e5a2d43ea..000000000000 --- a/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.swift +++ /dev/null @@ -1,19 +0,0 @@ -import UIKit - -class PageListSectionHeaderView: UIView { - - @IBOutlet weak var titleLabel: UILabel! - @IBOutlet weak var separator: UIView! - - func setTitle(_ title: String) { - titleLabel.text = title.uppercased(with: .current) - } - - override func awakeFromNib() { - super.awakeFromNib() - - backgroundColor = .listBackground - titleLabel.backgroundColor = .listBackground - WPStyleGuide.applyBorderStyle(separator) - } -} diff --git a/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.xib b/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.xib deleted file mode 100644 index 2e5cbbcac673..000000000000 --- a/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.xib +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift b/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift index 0b51c6f44c0c..4b087cfc5a8f 100644 --- a/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift @@ -229,16 +229,8 @@ extension RevisionsTableViewController: WPTableViewHandlerDelegate { return Sizes.cellEstimatedRowHeight } - override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - guard let sectionInfo = tableViewHandler.resultsController?.sections?[section], - let headerView = Bundle.main.loadNibNamed(PageListSectionHeaderView.classNameWithoutNamespaces(), - owner: nil, - options: nil)?.first as? PageListSectionHeaderView else { - return UIView() - } - - headerView.setTitle(sectionInfo.name) - return headerView + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + tableViewHandler.resultsController?.sections?[section].name } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index f0598667d2f8..54c80e9be845 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -1490,7 +1490,6 @@ 59ECF87B1CB7061D00E68F25 /* PostSharingControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59ECF87A1CB7061D00E68F25 /* PostSharingControllerTests.swift */; }; 59FBD5621B5684F300734466 /* ThemeServiceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 59FBD5611B5684F300734466 /* ThemeServiceTests.m */; }; 5D1181E71B4D6DEB003F3084 /* WPStyleGuide+Reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1181E61B4D6DEB003F3084 /* WPStyleGuide+Reader.swift */; }; - 5D13FA571AF99C2100F06492 /* PageListSectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */; }; 5D146EBB189857ED0068FDC6 /* FeaturedImageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D146EBA189857ED0068FDC6 /* FeaturedImageViewController.m */; }; 5D1D04751B7A50B100CDE646 /* Reader.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5D1D04731B7A50B100CDE646 /* Reader.storyboard */; }; 5D1D04761B7A50B100CDE646 /* ReaderStreamViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1D04741B7A50B100CDE646 /* ReaderStreamViewController.swift */; }; @@ -2391,7 +2390,6 @@ 8B92D69627CD51FA001F5371 /* DashboardGhostCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B92D69527CD51FA001F5371 /* DashboardGhostCardCell.swift */; }; 8B92D69727CD51FA001F5371 /* DashboardGhostCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B92D69527CD51FA001F5371 /* DashboardGhostCardCell.swift */; }; 8B93412F257029F60097D0AC /* FilterChipButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B93412E257029F50097D0AC /* FilterChipButton.swift */; }; - 8B93856E22DC08060010BF02 /* PageListSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B93856D22DC08060010BF02 /* PageListSectionHeaderView.swift */; }; 8BA125EB27D8F5E4008B779F /* UIView+PinSubviewPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA125EA27D8F5E4008B779F /* UIView+PinSubviewPriority.swift */; }; 8BA125EC27D8F5E4008B779F /* UIView+PinSubviewPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA125EA27D8F5E4008B779F /* UIView+PinSubviewPriority.swift */; }; 8BA77BCB2482C52A00E1EBBF /* ReaderCardDiscoverAttributionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8BA77BCA2482C52A00E1EBBF /* ReaderCardDiscoverAttributionView.xib */; }; @@ -4220,7 +4218,6 @@ FABB201D2602FC2C00C8785C /* PostStatsTitleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 98FCFC222231DF43006ECDD4 /* PostStatsTitleCell.xib */; }; FABB20212602FC2C00C8785C /* ReaderDetailToolbar.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8BA77BCC248340CE00E1EBBF /* ReaderDetailToolbar.xib */; }; FABB20222602FC2C00C8785C /* RewindStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4070D75B20E5F55A007CEBDA /* RewindStatusTableViewCell.xib */; }; - FABB20232602FC2C00C8785C /* PageListSectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */; }; FABB20242602FC2C00C8785C /* JetpackScanThreatDetailsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = FA7AA4A625BFE0A9005E7200 /* JetpackScanThreatDetailsViewController.xib */; }; FABB20252602FC2C00C8785C /* CollabsableHeaderFilterCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 469EB16424D8B12700C764CB /* CollabsableHeaderFilterCollectionViewCell.xib */; }; FABB20262602FC2C00C8785C /* ReplyTextView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B54E1DEF1A0A7BAA00807537 /* ReplyTextView.xib */; }; @@ -4502,7 +4499,6 @@ FABB21982602FC2C00C8785C /* UIViewController+Notice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F3EFCA2208E308900268758 /* UIViewController+Notice.swift */; }; FABB21992602FC2C00C8785C /* GutenbergFileUploadProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E0462152566938300EB98EF /* GutenbergFileUploadProcessor.swift */; }; FABB219A2602FC2C00C8785C /* EventLoggingDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F913BB0F24B3C5CE00C19032 /* EventLoggingDataProvider.swift */; }; - FABB219B2602FC2C00C8785C /* PageListSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B93856D22DC08060010BF02 /* PageListSectionHeaderView.swift */; }; FABB219C2602FC2C00C8785C /* InviteLinks+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E690F6ED25E05D170015A777 /* InviteLinks+CoreDataClass.swift */; }; FABB219D2602FC2C00C8785C /* Page+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59E1D46D1CEF77B500126697 /* Page+CoreDataProperties.swift */; }; FABB219F2602FC2C00C8785C /* RegisterDomainSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D74AD520FB5AD5004AD934 /* RegisterDomainSectionHeaderView.swift */; }; @@ -7197,7 +7193,6 @@ 59FBD5611B5684F300734466 /* ThemeServiceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThemeServiceTests.m; sourceTree = ""; }; 5C1CEB34870A8BA1ED1E502B /* Pods-WordPressUITests.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressUITests.release-alpha.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressUITests/Pods-WordPressUITests.release-alpha.xcconfig"; sourceTree = ""; }; 5D1181E61B4D6DEB003F3084 /* WPStyleGuide+Reader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WPStyleGuide+Reader.swift"; sourceTree = ""; }; - 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PageListSectionHeaderView.xib; sourceTree = ""; }; 5D146EB9189857ED0068FDC6 /* FeaturedImageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FeaturedImageViewController.h; sourceTree = ""; usesTabs = 0; }; 5D146EBA189857ED0068FDC6 /* FeaturedImageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FeaturedImageViewController.m; sourceTree = ""; usesTabs = 0; }; 5D1D04731B7A50B100CDE646 /* Reader.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Reader.storyboard; sourceTree = ""; }; @@ -7796,7 +7791,6 @@ 8B8FE8562343952B00F9AD2E /* PostAutoUploadMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostAutoUploadMessages.swift; sourceTree = ""; }; 8B92D69527CD51FA001F5371 /* DashboardGhostCardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardGhostCardCell.swift; sourceTree = ""; }; 8B93412E257029F50097D0AC /* FilterChipButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterChipButton.swift; sourceTree = ""; }; - 8B93856D22DC08060010BF02 /* PageListSectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageListSectionHeaderView.swift; sourceTree = ""; }; 8B9E15DAF3E1A369E9BE3407 /* Pods-WordPressUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressUITests.release.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressUITests/Pods-WordPressUITests.release.xcconfig"; sourceTree = ""; }; 8BA125EA27D8F5E4008B779F /* UIView+PinSubviewPriority.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+PinSubviewPriority.swift"; sourceTree = ""; }; 8BA77BCA2482C52A00E1EBBF /* ReaderCardDiscoverAttributionView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReaderCardDiscoverAttributionView.xib; sourceTree = ""; }; @@ -12984,8 +12978,6 @@ children = ( 0C700B852AE1E1300085C2EE /* PageListCell.swift */, 0C700B882AE1E1940085C2EE /* PageListItemViewModel.swift */, - 8B93856D22DC08060010BF02 /* PageListSectionHeaderView.swift */, - 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */, 834A49D12A0C23A90042ED3D /* TemplatePageTableViewCell.swift */, ); name = Views; @@ -19729,7 +19721,6 @@ FE015BB12ADA002400F50D7F /* ReaderTopicsNewCardCell.xib in Resources */, 8BA77BCD248340CE00E1EBBF /* ReaderDetailToolbar.xib in Resources */, 4070D75C20E5F55A007CEBDA /* RewindStatusTableViewCell.xib in Resources */, - 5D13FA571AF99C2100F06492 /* PageListSectionHeaderView.xib in Resources */, 1761F18D26209AEE000815EF /* hot-pink-icon-app-76x76.png in Resources */, FA7AA4A725BFE0A9005E7200 /* JetpackScanThreatDetailsViewController.xib in Resources */, 469EB16624D8B12700C764CB /* CollabsableHeaderFilterCollectionViewCell.xib in Resources */, @@ -20267,7 +20258,6 @@ F46597FD28E66A1100D5F49A /* white-on-black-icon-app-60@2x.png in Resources */, FABB20212602FC2C00C8785C /* ReaderDetailToolbar.xib in Resources */, FABB20222602FC2C00C8785C /* RewindStatusTableViewCell.xib in Resources */, - FABB20232602FC2C00C8785C /* PageListSectionHeaderView.xib in Resources */, 8091019629078CFE00FCB4EA /* JetpackFullscreenOverlayViewController.xib in Resources */, 98DCF4A8275945E00008630F /* ReaderDetailNoCommentCell.xib in Resources */, FABB20242602FC2C00C8785C /* JetpackScanThreatDetailsViewController.xib in Resources */, @@ -21785,7 +21775,6 @@ C3B554512965C32A00A04753 /* MenusViewController+JetpackBannerViewController.swift in Sources */, 1E0462162566938300EB98EF /* GutenbergFileUploadProcessor.swift in Sources */, F913BB1024B3C5CE00C19032 /* EventLoggingDataProvider.swift in Sources */, - 8B93856E22DC08060010BF02 /* PageListSectionHeaderView.swift in Sources */, E690F6EF25E05D180015A777 /* InviteLinks+CoreDataClass.swift in Sources */, 59E1D46F1CEF77B500126697 /* Page+CoreDataProperties.swift in Sources */, C373D6E728045281008F8C26 /* SiteIntentData.swift in Sources */, @@ -24691,7 +24680,6 @@ F4CBE3D7292597E3004FFBB6 /* SupportTableViewControllerConfiguration.swift in Sources */, FABB21992602FC2C00C8785C /* GutenbergFileUploadProcessor.swift in Sources */, FABB219A2602FC2C00C8785C /* EventLoggingDataProvider.swift in Sources */, - FABB219B2602FC2C00C8785C /* PageListSectionHeaderView.swift in Sources */, FABB219C2602FC2C00C8785C /* InviteLinks+CoreDataClass.swift in Sources */, 809101992908DE8500FCB4EA /* JetpackFullscreenOverlayViewModel.swift in Sources */, FABB219D2602FC2C00C8785C /* Page+CoreDataProperties.swift in Sources */, From 54b6b934028efbaad27a97af04b097fcf2cf42b0 Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 11:36:59 -0400 Subject: [PATCH 068/112] Update footer --- RELEASE-NOTES.txt | 1 + .../Revisions/RevisionsTableViewController.swift | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 8da42bb3cd69..d138674d290c 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -5,6 +5,7 @@ * [*] Make it easier to "Share" and "Blaze" a published post with an updated success view [##23128] * [*] Add support for viewing trashed posts and pages and restoring them from the editor [#23142] * [*] Update the "More" menu in the Editor to use modern iOS design and update copy to match Gutenberg [#23145] +* [*] Update the "Revisions" list design and fix an issue with the footer displaying incorrect "Date Created" for drafts [#23145] 24.8 ----- diff --git a/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift b/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift index 4b087cfc5a8f..c5e40da78f4c 100644 --- a/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift @@ -1,3 +1,5 @@ +import UIKit + class RevisionsTableViewController: UITableViewController { typealias RevisionLoadedBlock = (AbstractPost) -> Void @@ -76,7 +78,9 @@ private extension RevisionsTableViewController { refreshControl.addTarget(self, action: #selector(refreshRevisions), for: .valueChanged) self.refreshControl = refreshControl - tableView.tableFooterView = tableViewFooter + if post?.original().isStatus(in: [.draft, .pending]) == false { + tableView.tableFooterView = tableViewFooter + } tableView.separatorColor = .divider WPStyleGuide.configureColors(view: view, tableView: tableView) @@ -230,7 +234,11 @@ extension RevisionsTableViewController: WPTableViewHandlerDelegate { } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - tableViewHandler.resultsController?.sections?[section].name + guard let sections = tableViewHandler.resultsController?.sections, + sections.indices.contains(section) else { + return nil + } + return sections[section].name } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { @@ -303,7 +311,7 @@ private extension Date { private static let shortDateFormatter: DateFormatter = { let formatter = DateFormatter() - formatter.dateStyle = .short + formatter.dateStyle = .medium formatter.timeStyle = .none return formatter }() From 6bbb9c9cfac708e7e99082090ce49fe2281db60e Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 13:20:42 -0400 Subject: [PATCH 069/112] Show terminal upload failures when opening a draft --- .../Classes/Services/PostCoordinator.swift | 19 ++++++---- .../Classes/ViewRelated/Post/PostEditor.swift | 38 +++++++++++++++++++ .../Post/PostMediaUploadsView.swift | 13 +++++++ 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/WordPress/Classes/Services/PostCoordinator.swift b/WordPress/Classes/Services/PostCoordinator.swift index de08dcd4b403..fdfd16100e30 100644 --- a/WordPress/Classes/Services/PostCoordinator.swift +++ b/WordPress/Classes/Services/PostCoordinator.swift @@ -452,6 +452,9 @@ class PostCoordinator: NSObject { retryDelay = min(32, retryDelay * 1.5) return retryDelay } + func setLongerDelay() { + retryDelay = max(retryDelay, 20) + } var retryDelay: TimeInterval weak var retryTimer: Timer? @@ -610,14 +613,16 @@ class PostCoordinator: NSObject { worker.error = error postDidUpdateNotification(for: operation.post) - if !PostCoordinator.isTerminalError(error) { - let delay = worker.nextRetryDelay - worker.retryTimer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { [weak self, weak worker] _ in - guard let self, let worker else { return } - self.didRetryTimerFire(for: worker) - } - worker.log("scheduled retry with delay: \(delay)s.") + if PostCoordinator.isTerminalError(error) { + worker.setLongerDelay() + } + + let delay = worker.nextRetryDelay + worker.retryTimer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { [weak self, weak worker] _ in + guard let self, let worker else { return } + self.didRetryTimerFire(for: worker) } + worker.log("scheduled retry with delay: \(delay)s.") if let error = error as? PostRepository.PostSaveError, case .deleted = error { operation.log("post was permanently deleted") diff --git a/WordPress/Classes/ViewRelated/Post/PostEditor.swift b/WordPress/Classes/ViewRelated/Post/PostEditor.swift index 0016190ac30e..f86694d5d20d 100644 --- a/WordPress/Classes/ViewRelated/Post/PostEditor.swift +++ b/WordPress/Classes/ViewRelated/Post/PostEditor.swift @@ -147,6 +147,7 @@ extension PostEditor where Self: UIViewController { showPostTrashedOverlay() } else { showAutosaveAvailableAlertIfNeeded() + showTerminalUploadErrorAlertIfNeeded() } var cancellables: [AnyCancellable] = [] @@ -273,6 +274,39 @@ extension PostEditor where Self: UIViewController { self.post = post // Even if it's the same instance, it's how you currently refresh the editor self.createRevisionOfPost() } + + // MARK: - Failed Media Uploads + + private func showTerminalUploadErrorAlertIfNeeded() { + let hasTerminalError = post.media.contains { + guard let error = $0.error else { return false } + return MediaCoordinator.isTerminalError(error) + } + if hasTerminalError { + let notice = Notice(title: Strings.failingMediaUploadsMessage, feedbackType: .error, actionTitle: Strings.failingMediaUploadsViewAction, actionHandler: { [weak self] _ in + self?.showMediaUploadDetails() + }) + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(700)) { + ActionDispatcherFacade().dispatch(NoticeAction.post(notice)) + } // Delay to let the editor show first + } + } + + private func showMediaUploadDetails() { + let viewController = PostMediaUploadsViewController(post: post) + let nav = UINavigationController(rootViewController: viewController) + nav.navigationBar.isTranslucent = true // Reset to default + viewController.navigationItem.leftBarButtonItem = UIBarButtonItem(systemItem: .close, primaryAction: UIAction { [weak self] _ in + self?.dismiss(animated: true) + }) + if let sheetController = nav.sheetPresentationController { + sheetController.detents = [.medium(), .large()] + sheetController.prefersGrabberVisible = true + sheetController.preferredCornerRadius = 16 + nav.additionalSafeAreaInsets = UIEdgeInsets(top: 8, left: 0, bottom: 0, right: 0) + } + self.present(nav, animated: true) + } } private var cancellablesKey: UInt8 = 0 @@ -305,4 +339,8 @@ private enum Strings { static let trashedPostSheetCancel = NSLocalizedString("postEditor.recoverTrashedPostAlert.cancel", value: "Cancel", comment: "Editor, alert for recovering a trashed post") static let trashedPostSheetRecover = NSLocalizedString("postEditor.recoverTrashedPostAlert.restore", value: "Restore", comment: "Editor, alert for recovering a trashed post") static let trashedPostRestored = NSLocalizedString("postEditor.recoverTrashedPost.postRecoveredNoticeTitle", value: "Post restored as a draft", comment: "Editor, notice for successful recovery a trashed post") + + static let failingMediaUploadsMessage = NSLocalizedString("postEditor.postHasFailingMediaUploadsSnackbar.message", value: "Some media items failed to upload", comment: "A message for a snackbar informing the user that some media files requires their attention") + + static let failingMediaUploadsViewAction = NSLocalizedString("postEditor.postHasFailingMediaUploadsSnackbar.actionView", value: "View", comment: "A 'View' action for a snackbar informing the user that some media files requires their attention") } diff --git a/WordPress/Classes/ViewRelated/Post/PostMediaUploadsView.swift b/WordPress/Classes/ViewRelated/Post/PostMediaUploadsView.swift index a9e4f4a306e3..22818819b471 100644 --- a/WordPress/Classes/ViewRelated/Post/PostMediaUploadsView.swift +++ b/WordPress/Classes/ViewRelated/Post/PostMediaUploadsView.swift @@ -1,6 +1,19 @@ import Foundation import SwiftUI +final class PostMediaUploadsViewController: UIHostingController { + private let viewModel: PostMediaUploadsViewModel + + init(post: AbstractPost) { + self.viewModel = PostMediaUploadsViewModel(post: post) // Manange lifecycle + super.init(rootView: PostMediaUploadsView(viewModel: viewModel)) + } + + required dynamic init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + /// Displays upload progress for the media for the given post. struct PostMediaUploadsView: View { @ObservedObject var viewModel: PostMediaUploadsViewModel From d70876ea4bc99683bd204baaaf4a4450a98f5b4f Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 13:31:17 -0400 Subject: [PATCH 070/112] Show spinner on every retry --- WordPress/Classes/Services/PostCoordinator.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/WordPress/Classes/Services/PostCoordinator.swift b/WordPress/Classes/Services/PostCoordinator.swift index fdfd16100e30..2ea04cc4724e 100644 --- a/WordPress/Classes/Services/PostCoordinator.swift +++ b/WordPress/Classes/Services/PostCoordinator.swift @@ -512,10 +512,11 @@ class PostCoordinator: NSObject { } private func startSync(for post: AbstractPost) { - guard let revision = post.getLatestRevisionNeedingSync() else { - let worker = getWorker(for: post) + if let worker = workers[post.objectID], worker.error != nil { worker.error = nil postDidUpdateNotification(for: post) + } + guard let revision = post.getLatestRevisionNeedingSync() else { return DDLogInfo("sync: \(post.objectID.shortDescription) is already up to date") } startSync(for: post, revision: revision) From c8b389c3cdaee1447976432bef2281dca4376b3e Mon Sep 17 00:00:00 2001 From: kean Date: Fri, 3 May 2024 09:29:33 -0400 Subject: [PATCH 071/112] Update WordPressKit (fix an issue with removing password from posts) --- Podfile | 4 ++-- Podfile.lock | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Podfile b/Podfile index ba7a23f97908..2ed7dfd7f328 100644 --- a/Podfile +++ b/Podfile @@ -55,8 +55,8 @@ def gravatar end def wordpress_kit - pod 'WordPressKit', '~> 17.1.0' - # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '' + # pod 'WordPressKit', '~> 17.1.0' + pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '549fbee3059e71bd1471e35090ac942a3b23516a' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', branch: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', tag: '' # pod 'WordPressKit', path: '../WordPressKit-iOS' diff --git a/Podfile.lock b/Podfile.lock index f4440e678747..d720a8fee5cb 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -121,7 +121,7 @@ DEPENDENCIES: - SwiftLint (= 0.54.0) - WordPress-Editor-iOS (~> 1.19.11) - WordPressAuthenticator (>= 9.0.8, ~> 9.0) - - WordPressKit (~> 17.1.0) + - WordPressKit (from `https://github.com/wordpress-mobile/WordPressKit-iOS.git`, commit `549fbee3059e71bd1471e35090ac942a3b23516a`) - WordPressShared (>= 2.3.1, ~> 2.3) - WordPressUI (~> 1.16) - ZendeskSupportSDK (= 5.3.0) @@ -160,7 +160,6 @@ SPEC REPOS: - SVProgressHUD - SwiftLint - UIDeviceIdentifier - - WordPressKit - WordPressUI - wpxmlrpc - ZendeskCommonUISDK @@ -178,11 +177,17 @@ EXTERNAL SOURCES: :tag: 0.2.0 Gutenberg: :podspec: https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-v1.118.0.podspec + WordPressKit: + :commit: 549fbee3059e71bd1471e35090ac942a3b23516a + :git: https://github.com/wordpress-mobile/WordPressKit-iOS.git CHECKOUT OPTIONS: FSInteractiveMap: :git: https://github.com/wordpress-mobile/FSInteractiveMap.git :tag: 0.2.0 + WordPressKit: + :commit: 549fbee3059e71bd1471e35090ac942a3b23516a + :git: https://github.com/wordpress-mobile/WordPressKit-iOS.git SPEC CHECKSUMS: Alamofire: 02b772c9910e8eba1a079227c32fbd9e46c90a24 @@ -229,6 +234,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: d170fa8e270b2a32bef9dcdcabff5b8f1a5deced -PODFILE CHECKSUM: fe8df3e873baa4219a3e9b59632c5d2c7e3d774e +PODFILE CHECKSUM: e350c05f42484b1312d9cd1044b92551406d5411 COCOAPODS: 1.15.2 From e13fcbab4438325b102e98e8b7d1ce33143d9d7c Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 13:40:58 -0400 Subject: [PATCH 072/112] Revert "Update WordPressKit (fix an issue with removing password from posts)" This reverts commit c8b389c3cdaee1447976432bef2281dca4376b3e. --- Podfile | 4 ++-- Podfile.lock | 11 +++-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Podfile b/Podfile index 2ed7dfd7f328..ba7a23f97908 100644 --- a/Podfile +++ b/Podfile @@ -55,8 +55,8 @@ def gravatar end def wordpress_kit - # pod 'WordPressKit', '~> 17.1.0' - pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '549fbee3059e71bd1471e35090ac942a3b23516a' + pod 'WordPressKit', '~> 17.1.0' + # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', branch: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', tag: '' # pod 'WordPressKit', path: '../WordPressKit-iOS' diff --git a/Podfile.lock b/Podfile.lock index d720a8fee5cb..f4440e678747 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -121,7 +121,7 @@ DEPENDENCIES: - SwiftLint (= 0.54.0) - WordPress-Editor-iOS (~> 1.19.11) - WordPressAuthenticator (>= 9.0.8, ~> 9.0) - - WordPressKit (from `https://github.com/wordpress-mobile/WordPressKit-iOS.git`, commit `549fbee3059e71bd1471e35090ac942a3b23516a`) + - WordPressKit (~> 17.1.0) - WordPressShared (>= 2.3.1, ~> 2.3) - WordPressUI (~> 1.16) - ZendeskSupportSDK (= 5.3.0) @@ -160,6 +160,7 @@ SPEC REPOS: - SVProgressHUD - SwiftLint - UIDeviceIdentifier + - WordPressKit - WordPressUI - wpxmlrpc - ZendeskCommonUISDK @@ -177,17 +178,11 @@ EXTERNAL SOURCES: :tag: 0.2.0 Gutenberg: :podspec: https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-v1.118.0.podspec - WordPressKit: - :commit: 549fbee3059e71bd1471e35090ac942a3b23516a - :git: https://github.com/wordpress-mobile/WordPressKit-iOS.git CHECKOUT OPTIONS: FSInteractiveMap: :git: https://github.com/wordpress-mobile/FSInteractiveMap.git :tag: 0.2.0 - WordPressKit: - :commit: 549fbee3059e71bd1471e35090ac942a3b23516a - :git: https://github.com/wordpress-mobile/WordPressKit-iOS.git SPEC CHECKSUMS: Alamofire: 02b772c9910e8eba1a079227c32fbd9e46c90a24 @@ -234,6 +229,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: d170fa8e270b2a32bef9dcdcabff5b8f1a5deced -PODFILE CHECKSUM: e350c05f42484b1312d9cd1044b92551406d5411 +PODFILE CHECKSUM: fe8df3e873baa4219a3e9b59632c5d2c7e3d774e COCOAPODS: 1.15.2 From ff8802c8ddd3cea718c935488662a2fe98cc0727 Mon Sep 17 00:00:00 2001 From: Salim Braksa Date: Sun, 5 May 2024 19:17:31 +0100 Subject: [PATCH 073/112] Improve comment moderation sheet transition smoothness --- .../CommentModerationState.swift | 6 +- .../CommentModerationView.swift | 391 ++++++++++-------- .../CommentModerationViewModel.swift | 14 +- 3 files changed, 229 insertions(+), 182 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationState.swift b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationState.swift index 94322a80e308..a462e4970a72 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationState.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationState.swift @@ -1,6 +1,6 @@ -enum CommentModerationState: CaseIterable { +enum CommentModerationState: Equatable { case pending - case approved - case liked + case approved(liked: Bool) + case spam case trash } diff --git a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift index efda84b08ed5..6c9196533ce3 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift @@ -13,106 +13,182 @@ struct CommentModerationView: View { Divider() .foregroundStyle(Color.DS.Background.secondary) VStack(spacing: .DS.Padding.double) { - titleHStack - mainActionView - secondaryActionView + switch viewModel.state { + case .pending: + Pending(viewModel: viewModel) + case .approved(let liked): + Approved(viewModel: viewModel, liked: liked) + case .trash, .spam: + TrashSpam(viewModel: viewModel) + } } .padding(.horizontal, .DS.Padding.double) } + .animation(.smooth, value: viewModel.state) } +} + +// MARK: - Subviews + +private struct Container: View { - private var titleHStack: some View { + let title: String + let icon: V + let content: T + + private let animationDuration = 0.25 + + private var animation: Animation { + return .smooth(duration: animationDuration) + } + + init(title: String, @ViewBuilder icon: () -> V = { EmptyView() }, @ViewBuilder content: () -> T) { + self.content = content() + self.icon = icon() + self.title = title + } + + var body: some View { + VStack(spacing: .DS.Padding.double) { + titleHStack + content + } + .padding(.horizontal, .DS.Padding.double) + .transition( + .asymmetric( + insertion: .opacity.animation(animation.delay(animationDuration * 1)), + removal: .opacity.animation(animation) + ) + ) + } + + var titleHStack: some View { HStack(spacing: 0) { - switch viewModel.state { - case .pending: - Image.DS.icon(named: .clock) - .resizable() - .renderingMode(.template) - .frame(width: .DS.Padding.double, height: .DS.Padding.double) - .foregroundStyle(Color.DS.Foreground.secondary) - .padding(.trailing, .DS.Padding.half) - case .approved, .liked: - Image.DS.icon(named: .checkmark) - .resizable() - .frame(width: 20, height: 20) - .foregroundStyle(Color.DS.Foreground.secondary) - .padding(.trailing, 2) - case .trash: - EmptyView() - } - Text(viewModel.state.title) + icon + Text(title) .style(.caption) .foregroundStyle(Color.DS.Foreground.secondary) } } +} - @ViewBuilder - private var mainActionView: some View { - switch viewModel.state.mainAction { - case let .cta(title, iconName): - DSButton( - title: title, - iconName: iconName, - style: DSButtonStyle( - emphasis: .primary, - size: .large - )) { - withAnimation(.smooth) { +private struct Pending: View { + + let viewModel: CommentModerationViewModel + + var body: some View { + Container(title: Strings.title, icon: { icon }) { + VStack { + DSButton( + title: Strings.approveComment, + iconName: .checkmark, + style: DSButtonStyle( + emphasis: .primary, + size: .large + )) { viewModel.didTapPrimaryCTA() } - } - case .reply: - ContentPreview( - image: .init(url: viewModel.imageURL), - text: viewModel.userName - ) { - viewModel.didTapReply() + DSButton( + title: Strings.moreOptions, + style: .init(emphasis: .tertiary, size: .large)) { + viewModel.didTapMore() + } } } } @ViewBuilder - private var secondaryActionView: some View { - if case .more = viewModel.state.secondaryAction { - DSButton( - title: Strings.moreOptionsButtonTitle, - style: .init(emphasis: .tertiary, size: .large)) { - viewModel.didTapMore() + var icon: some View { + Image.DS.icon(named: .clock) + .resizable() + .renderingMode(.template) + .frame(width: .DS.Padding.double, height: .DS.Padding.double) + .foregroundStyle(Color.DS.Foreground.secondary) + .padding(.trailing, .DS.Padding.half) + } + + enum Strings { + static let title = NSLocalizedString( + "notifications.comment.moderation.pending.title", + value: "Comment pending moderation", + comment: "Title for Comment Moderation Pending State" + ) + static let approveComment = NSLocalizedString( + "notifications.comment.approval.cta.title", + value: "Approve Comment", + comment: "Title for Comment Approval CTA" + ) + static let moreOptions = NSLocalizedString( + "notifications.comment.moderation.more.cta.title", + value: "More options", + comment: "More button title for comment moderation options sheet." + ) + } +} + +private struct Approved: View { + + let viewModel: CommentModerationViewModel + let liked: Bool + + private var likeButtonTitle: String { + liked ? Strings.commentLikedTitle : String(format: Strings.commentLikeTitle, viewModel.userName) + } + + var body: some View { + Container(title: Strings.title, icon: { icon }) { + VStack { + ContentPreview( + image: .init(url: viewModel.imageURL, placeholder: Image("gravatar")), + text: viewModel.userName + ) { } - } else if case .like = viewModel.state.secondaryAction { - DSButton( - title: viewModel.state == .approved ? String( - format: Strings.commentLikeTitle, - viewModel.userName - ) : Strings.commentLikedTitle, - iconName: viewModel.state == .approved ? .starOutline : .starFill, - style: .init( - emphasis: .tertiary, - size: .large - ) - ) { - withAnimation(.interactiveSpring) { - viewModel.didTapLike() + DSButton( + title: likeButtonTitle, + iconName: liked ? .starFill : .starOutline, + style: .init( + emphasis: .tertiary, + size: .large + ) + ) { + withAnimation(.interactiveSpring) { + viewModel.didTapLike() + } } } } } -} -private extension CommentModerationView { + @ViewBuilder + var icon: some View { + Image.DS.icon(named: .checkmark) + .resizable() + .frame(width: 20, height: 20) + .foregroundStyle(Color.DS.Foreground.secondary) + .padding(.trailing, 2) + } + enum Strings { - static let moreOptionsButtonTitle = NSLocalizedString( + static let title = NSLocalizedString( + "notifications.comment.moderation.approved.title", + value: "Comment approved", + comment: "Title for Comment Moderation Approved State" + ) + static let approveComment = NSLocalizedString( + "notifications.comment.approval.cta.title", + value: "Approve Comment", + comment: "Title for Comment Approval CTA" + ) + static let moreOptions = NSLocalizedString( "notifications.comment.moderation.more.cta.title", value: "More options", comment: "More button title for comment moderation options sheet." ) - static let commentLikedTitle = NSLocalizedString( "notifications.comment.liked.title", value: "Comment liked", comment: "Liked state title for comment like button." ) - static let commentLikeTitle = NSLocalizedString( "notifications.comment.like.title", value: "Like %@'s comment", @@ -121,126 +197,91 @@ private extension CommentModerationView { } } -private extension CommentModerationState { - enum MainAction { - case cta(title: String, iconName: IconName) - case reply - } - - enum SecondaryAction { - case more - case like - case none - } - - var title: String { - switch self { - case .pending: - return NSLocalizedString( - "notifications.comment.moderation.pending.title", - value: "Comment pending moderation", - comment: "Title for Comment Moderation Pending State") - case .approved, .liked: - return NSLocalizedString( - "notifications.comment.moderation.approved.title", - value: "Comment approved", - comment: "Title for Comment Moderation Approved State") - case .trash: - return NSLocalizedString( - "notifications.comment.moderation.trash.title", - value: "Comment in trash", - comment: "Title for Comment Moderation Trash State") - } +private struct TrashSpam: View { + + @ObservedObject var viewModel: CommentModerationViewModel + + @State var title: String + + init(viewModel: CommentModerationViewModel) { + self.viewModel = viewModel + self.title = Self.title(for: viewModel.state) ?? "" } - var mainAction: MainAction { - switch self { - case .pending: - return .cta( - title: NSLocalizedString( - "notifications.comment.approval.cta.title", - value: "Approve Comment", - comment: "Title for Comment Approval CTA" - ), - iconName: .checkmark - ) - case .approved: - return .reply - case .liked: - return .reply - case .trash: - return .cta( - title: NSLocalizedString( - "notifications.comment.delete.cta.title", - value: "Delete Permanently", - comment: "Title for Comment Deletion CTA" - ), - iconName: .trash - ) + var body: some View { + Container(title: title) { + DSButton( + title: Strings.delete, + iconName: .trash, + style: .init( + emphasis: .primary, + size: .large + ) + ) { + } + }.onChange(of: viewModel.state) { state in + if let title = Self.title(for: state) { + self.title = title + } } } - var secondaryAction: SecondaryAction { - switch self { - case .pending: - return .more - case .approved, .liked: - return .like - case .trash: - return .none + static func title(for state: CommentModerationState) -> String? { + switch state { + case .spam: return Strings.spamTitle + case .trash: return Strings.trashTitle + default: return nil } } + + enum Strings { + static let trashTitle = NSLocalizedString( + "notifications.comment.moderation.trash.title", + value: "Comment in trash", + comment: "Title for Comment Moderation Trash State" + ) + static let spamTitle = NSLocalizedString( + "notifications.comment.moderation.spam.title", + value: "Comment in spam", + comment: "Title for Comment Moderation Spam State" + ) + static let delete = NSLocalizedString( + "notifications.comment.delete.cta.title", + value: "Delete Permanently", + comment: "Title for Comment Deletion CTA" + ) + } } -#Preview { - GeometryReader { proxy in - if #available(iOS 17.0, *) { - ScrollView(.horizontal) { - LazyHStack(spacing: 0) { - CommentModerationView( - viewModel: CommentModerationViewModel( - state: .pending, - imageURL: URL(string: "https://i.pravatar.cc/300"), - userName: "John Smith" - ) - ) - .frame( - width: proxy.size.width - ) - CommentModerationView( - viewModel: CommentModerationViewModel( - state: .approved, - imageURL: URL(string: "https://i.pravatar.cc/300"), - userName: "Jane Smith" - ) - ) - .frame( - width: proxy.size.width - ) - CommentModerationView( - viewModel: CommentModerationViewModel( - state: .liked, - imageURL: URL(string: "https://i.pravatar.cc/300"), - userName: "John Smith" - ) - ) - .frame( - width: proxy.size.width - ) - CommentModerationView( - viewModel: CommentModerationViewModel( - state: .trash, - imageURL: URL(string: "https://i.pravatar.cc/300"), - userName: "Jane Smith" - ) - ) - .frame( - width: proxy.size.width - ) +// MARK: - Preview + +struct CommentModerationView_Previews: PreviewProvider { + static let viewModel = CommentModerationViewModel( + state: .pending, + imageURL: URL(string: "https://i.pravatar.cc/300"), + userName: "John Smith" + ) + + static var previews: some View { + ZStack { + VStack(spacing: .DS.Padding.double) { + Button("Pending") { + viewModel.state = .pending + } + Button("Approve") { + viewModel.state = .approved(liked: false) } + Button("Spam") { + viewModel.state = .spam + } + Button("Trash") { + viewModel.state = .trash + } + } + VStack { + Spacer() + CommentModerationView(viewModel: viewModel) } - .scrollTargetBehavior(.paging) - .scrollIndicators(.hidden) } } } diff --git a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationViewModel.swift b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationViewModel.swift index fa29e45d19ac..c62cf9a81cf1 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationViewModel.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationViewModel.swift @@ -1,5 +1,6 @@ final class CommentModerationViewModel: ObservableObject { @Published var state: CommentModerationState + let imageURL: URL? let userName: String @@ -21,10 +22,10 @@ final class CommentModerationViewModel: ObservableObject { func didTapPrimaryCTA() { switch state { case .pending: - state = .approved - case .trash: + state = .approved(liked: false) + case .trash, .spam: () // Delete comment - case .approved, .liked: + case .approved: break } } @@ -34,6 +35,11 @@ final class CommentModerationViewModel: ObservableObject { } func didTapLike() { - state = state == .approved ? .liked : .approved + switch state { + case .approved(let liked): + state = .approved(liked: !liked) + default: + break + } } } From d1ed21d2f44145614632154f1148aea502006c08 Mon Sep 17 00:00:00 2001 From: Salim Braksa Date: Sun, 5 May 2024 20:20:42 +0100 Subject: [PATCH 074/112] Decrease insertion animation delay --- .../Comment Moderation/CommentModerationView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift index 6c9196533ce3..98e147d58e6d 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift @@ -56,7 +56,7 @@ private struct Container: View { .padding(.horizontal, .DS.Padding.double) .transition( .asymmetric( - insertion: .opacity.animation(animation.delay(animationDuration * 1)), + insertion: .opacity.animation(animation.delay(animationDuration * 0.8)), removal: .opacity.animation(animation) ) ) From eccb0167a4dc14a4fc297917775088bc75caf427 Mon Sep 17 00:00:00 2001 From: Salim Braksa Date: Sun, 5 May 2024 20:22:36 +0100 Subject: [PATCH 075/112] Fix a minor typo --- .../Comment Moderation/CommentModerationView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift index 98e147d58e6d..5d0b82f95ab6 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift @@ -268,7 +268,7 @@ struct CommentModerationView_Previews: PreviewProvider { Button("Pending") { viewModel.state = .pending } - Button("Approve") { + Button("Approved") { viewModel.state = .approved(liked: false) } Button("Spam") { From 8213bfdef38571310589634c238ae7fd91ea9d59 Mon Sep 17 00:00:00 2001 From: Salim Braksa Date: Sun, 5 May 2024 20:31:56 +0100 Subject: [PATCH 076/112] Fix lint issue --- .../Comment Moderation/CommentModerationViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationViewModel.swift b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationViewModel.swift index c62cf9a81cf1..326ef09876cd 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationViewModel.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationViewModel.swift @@ -1,6 +1,6 @@ final class CommentModerationViewModel: ObservableObject { @Published var state: CommentModerationState - + let imageURL: URL? let userName: String From be40a7018954ef93ef67f96b881c694790eddf1e Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 6 May 2024 10:50:28 +0300 Subject: [PATCH 077/112] Implement StatsGhostLineChartCell in the code --- .../GhostViews/StatsGhostCells.swift | 2 - .../GhostViews/StatsGhostLineChartCell.swift | 24 ++++++++++- .../GhostViews/StatsGhostLineChartCell.xib | 41 ------------------- .../GhostViews/StatsGhostTableViewRows.swift | 2 +- WordPress/WordPress.xcodeproj/project.pbxproj | 18 +++----- 5 files changed, 29 insertions(+), 58 deletions(-) delete mode 100644 WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.xib diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift index e90cf1e06092..98f30e98d3ff 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift @@ -114,5 +114,3 @@ fileprivate extension Date { return days } } - -class StatsGhostSingleValueCell: StatsGhostBaseCell, NibLoadable { } diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift index 7dd502228cc5..043bf18f430d 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift @@ -1,8 +1,28 @@ import UIKit import WordPressShared +import DesignSystem -class StatsGhostLineChartCell: StatsGhostBaseCell, NibLoadable { - @IBOutlet private weak var lineChart: StatsGhostLineChartView! +final class StatsGhostLineChartCell: StatsGhostBaseCell { + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + let lineChart = StatsGhostLineChartView() + headingLabel.isGhostableDisabled = true + lineChart.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(lineChart) + topConstraint = lineChart.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0) + topConstraint?.isActive = true + NSLayoutConstraint.activate([ + lineChart.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: .DS.Padding.double), + contentView.trailingAnchor.constraint(equalTo: lineChart.trailingAnchor, constant: .DS.Padding.double), + contentView.bottomAnchor.constraint(equalTo: lineChart.bottomAnchor, constant: .DS.Padding.single), + lineChart.heightAnchor.constraint(equalToConstant: 190), + ]) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } } final class StatsGhostLineChartView: UIView { diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.xib b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.xib deleted file mode 100644 index cfbd207a1d6a..000000000000 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.xib +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift index fbf039f29e85..ce04ad21807f 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift @@ -147,6 +147,6 @@ struct StatsGhostLineChartRow: StatsRowGhostable { let statSection: StatSection? static let cell: ImmuTableCell = { - return ImmuTableCell.nib(StatsGhostLineChartCell.defaultNib, StatsGhostLineChartCell.self) + return ImmuTableCell.class(StatsGhostLineChartCell.self) }() } diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 5fd43a2fc0b0..2e230462ae7e 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -137,8 +137,6 @@ 011896A629D5B72500D34BA9 /* DomainsDashboardCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011896A429D5B72500D34BA9 /* DomainsDashboardCoordinator.swift */; }; 011896A829D5BBB400D34BA9 /* DomainsDashboardFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011896A729D5BBB400D34BA9 /* DomainsDashboardFactory.swift */; }; 011896A929D5BBB400D34BA9 /* DomainsDashboardFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011896A729D5BBB400D34BA9 /* DomainsDashboardFactory.swift */; }; - 011F156F2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 011F156E2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib */; }; - 011F15702BE4E8CE008029ED /* StatsGhostSingleValueCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 011F156E2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib */; }; 011F52BD2A15327700B04114 /* BaseDashboardDomainsCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011F52BC2A15327700B04114 /* BaseDashboardDomainsCardCell.swift */; }; 011F52BE2A15327700B04114 /* BaseDashboardDomainsCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011F52BC2A15327700B04114 /* BaseDashboardDomainsCardCell.swift */; }; 011F52C02A1538EC00B04114 /* FreeToPaidPlansDashboardCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011F52BF2A1538EC00B04114 /* FreeToPaidPlansDashboardCardCell.swift */; }; @@ -204,6 +202,8 @@ 0189AF062ACAD89700F63393 /* ShoppingCartService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0189AF042ACAD89700F63393 /* ShoppingCartService.swift */; }; 018FF1352AE6771A00F301C3 /* LockScreenVerticalCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018FF1342AE6771A00F301C3 /* LockScreenVerticalCard.swift */; }; 018FF1372AE67C2600F301C3 /* LockScreenFlexibleCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018FF1362AE67C2600F301C3 /* LockScreenFlexibleCard.swift */; }; + 019105862BE8BD6000CDFB16 /* StatsGhostSingleValueCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 019105852BE8BD6000CDFB16 /* StatsGhostSingleValueCell.swift */; }; + 019105872BE8BD6000CDFB16 /* StatsGhostSingleValueCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 019105852BE8BD6000CDFB16 /* StatsGhostSingleValueCell.swift */; }; 019C5B8D2BD6570D00A69DB0 /* StatsSubscribersStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 019C5B8C2BD6570D00A69DB0 /* StatsSubscribersStore.swift */; }; 019C5B8E2BD6570D00A69DB0 /* StatsSubscribersStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 019C5B8C2BD6570D00A69DB0 /* StatsSubscribersStore.swift */; }; 019C5B942BD6917600A69DB0 /* StatsSubscribersCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 019C5B932BD6917600A69DB0 /* StatsSubscribersCache.swift */; }; @@ -224,8 +224,6 @@ 01B7590B2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7590A2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift */; }; 01B7590C2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7590A2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift */; }; 01B7590E2B3EEEA400179AE6 /* SiteDomainsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7590D2B3EEEA400179AE6 /* SiteDomainsViewModelTests.swift */; }; - 01C447292BE4F8490006F787 /* StatsGhostLineChartCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01C447282BE4F8490006F787 /* StatsGhostLineChartCell.xib */; }; - 01C4472A2BE4F8490006F787 /* StatsGhostLineChartCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01C447282BE4F8490006F787 /* StatsGhostLineChartCell.xib */; }; 01C4472C2BE4F8560006F787 /* StatsGhostLineChartCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C4472B2BE4F8560006F787 /* StatsGhostLineChartCell.swift */; }; 01C4472D2BE4F8560006F787 /* StatsGhostLineChartCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C4472B2BE4F8560006F787 /* StatsGhostLineChartCell.swift */; }; 01C591452BDBD63D0071515C /* StatsGhostTopCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C591442BDBD63D0071515C /* StatsGhostTopCell.swift */; }; @@ -6005,7 +6003,6 @@ 011896A429D5B72500D34BA9 /* DomainsDashboardCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainsDashboardCoordinator.swift; sourceTree = ""; }; 011896A729D5BBB400D34BA9 /* DomainsDashboardFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainsDashboardFactory.swift; sourceTree = ""; }; 011A2815DB0DE7E3973CBC0E /* Pods-Apps-Jetpack.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Apps-Jetpack.release.xcconfig"; path = "../Pods/Target Support Files/Pods-Apps-Jetpack/Pods-Apps-Jetpack.release.xcconfig"; sourceTree = ""; }; - 011F156E2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsGhostSingleValueCell.xib; sourceTree = ""; }; 011F52BC2A15327700B04114 /* BaseDashboardDomainsCardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDashboardDomainsCardCell.swift; sourceTree = ""; }; 011F52BF2A1538EC00B04114 /* FreeToPaidPlansDashboardCardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FreeToPaidPlansDashboardCardCell.swift; sourceTree = ""; }; 011F52C22A153A3400B04114 /* FreeToPaidPlansDashboardCardHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FreeToPaidPlansDashboardCardHelper.swift; sourceTree = ""; }; @@ -6053,6 +6050,7 @@ 0189AF042ACAD89700F63393 /* ShoppingCartService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShoppingCartService.swift; sourceTree = ""; }; 018FF1342AE6771A00F301C3 /* LockScreenVerticalCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenVerticalCard.swift; sourceTree = ""; }; 018FF1362AE67C2600F301C3 /* LockScreenFlexibleCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenFlexibleCard.swift; sourceTree = ""; }; + 019105852BE8BD6000CDFB16 /* StatsGhostSingleValueCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsGhostSingleValueCell.swift; sourceTree = ""; }; 019C5B8C2BD6570D00A69DB0 /* StatsSubscribersStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsSubscribersStore.swift; sourceTree = ""; }; 019C5B932BD6917600A69DB0 /* StatsSubscribersCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsSubscribersCache.swift; sourceTree = ""; }; 019C5B962BD6A49900A69DB0 /* StatsSubscribersViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsSubscribersViewModelTests.swift; sourceTree = ""; }; @@ -6064,7 +6062,6 @@ 01B759072B3ECAF300179AE6 /* DomainsStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainsStateView.swift; sourceTree = ""; }; 01B7590A2B3ED63B00179AE6 /* DomainDetailsWebViewControllerWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainDetailsWebViewControllerWrapper.swift; sourceTree = ""; }; 01B7590D2B3EEEA400179AE6 /* SiteDomainsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteDomainsViewModelTests.swift; sourceTree = ""; }; - 01C447282BE4F8490006F787 /* StatsGhostLineChartCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsGhostLineChartCell.xib; sourceTree = ""; }; 01C4472B2BE4F8560006F787 /* StatsGhostLineChartCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsGhostLineChartCell.swift; sourceTree = ""; }; 01C591442BDBD63D0071515C /* StatsGhostTopCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsGhostTopCell.swift; sourceTree = ""; }; 01CE5006290A889F00A9C2E0 /* TracksConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracksConfiguration.swift; sourceTree = ""; }; @@ -15003,13 +15000,12 @@ 9A9E3FAF230EA7A300909BC4 /* StatsGhostTopCell.xib */, 9A19D440236C7C7500D393E5 /* StatsGhostTopHeaderCell.xib */, 9A9E3FAC230E9DD000909BC4 /* StatsGhostTwoColumnCell.xib */, - 011F156E2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib */, 9AC3C69A231543C2007933CD /* StatsGhostChartCell.xib */, 9A73CB072350DE4C002EF20C /* StatsGhostSingleRowCell.xib */, 9AB36B83236B25D900FAD72A /* StatsGhostTitleCell.xib */, FA347AF126EB7A420096604B /* StatsGhostGrowAudienceCell.xib */, - 01C447282BE4F8490006F787 /* StatsGhostLineChartCell.xib */, 01C4472B2BE4F8560006F787 /* StatsGhostLineChartCell.swift */, + 019105852BE8BD6000CDFB16 /* StatsGhostSingleValueCell.swift */, ); path = GhostViews; sourceTree = ""; @@ -19644,7 +19640,6 @@ 17222D9B261DDDF90047B163 /* black-icon-app-76x76.png in Resources */, DC772B0828201F5300664C02 /* ViewsVisitorsLineChartCell.xib in Resources */, 5D6C4AF61B603CA3005E3C43 /* WPTableViewActivityCell.xib in Resources */, - 01C447292BE4F8490006F787 /* StatsGhostLineChartCell.xib in Resources */, A01C55480E25E0D000D411F2 /* defaultPostTemplate.html in Resources */, 17222D89261DDDF90047B163 /* celadon-icon-app-60x60@3x.png in Resources */, 08BA4BC9298A9AD500015BD2 /* JetpackInstallPluginLogoAnimation_ltr.json in Resources */, @@ -19666,7 +19661,6 @@ FA1A55FF25A6F07F0033967D /* RestoreStatusView.xib in Resources */, 4058F41A1FF40EE1000D5559 /* PluginDetailViewHeaderCell.xib in Resources */, 1761F17226209AEE000815EF /* open-source-dark-icon-app-60x60@3x.png in Resources */, - 011F156F2BE4E8CE008029ED /* StatsGhostSingleValueCell.xib in Resources */, 406A0EF0224D39C50016AD6A /* Flags.xcassets in Resources */, 9A9E3FB0230EA7A300909BC4 /* StatsGhostTopCell.xib in Resources */, 985793C922F23D7000643DBF /* CustomizeInsightsCell.xib in Resources */, @@ -20213,7 +20207,6 @@ FABB1FCE2602FC2C00C8785C /* StatsChildRowsView.xib in Resources */, F41E4EEE28F247D3001880C6 /* white-on-green-icon-app-76.png in Resources */, FABB1FD02602FC2C00C8785C /* xhtml1-transitional.dtd in Resources */, - 011F15702BE4E8CE008029ED /* StatsGhostSingleValueCell.xib in Resources */, 8BF281FA27CE8E4100AF8CF3 /* DashboardGhostCardContent.xib in Resources */, F465979A28E65FC800D5F49A /* dark-green-icon-app-60@3x.png in Resources */, FABB1FD22602FC2C00C8785C /* RestoreStatusView.xib in Resources */, @@ -20316,7 +20309,6 @@ FABB20302602FC2C00C8785C /* OverviewCell.xib in Resources */, 83A8A2972BDC557F001F9133 /* ReaderTagFooterView.xib in Resources */, F46597E928E6698D00D5F49A /* spectrum-on-black-icon-app-83.5@2x.png in Resources */, - 01C4472A2BE4F8490006F787 /* StatsGhostLineChartCell.xib in Resources */, FABB20322602FC2C00C8785C /* JetpackRemoteInstallStateView.xib in Resources */, FABB20352602FC2C00C8785C /* TemplatePreviewViewController.xib in Resources */, F46597BB28E6687800D5F49A /* neumorphic-dark-icon-app-76@2x.png in Resources */, @@ -22141,6 +22133,7 @@ 436D56292117312700CEAA33 /* RegisterDomainDetailsViewController.swift in Sources */, F4EDAA4C29A516EA00622D3D /* ReaderPostService.swift in Sources */, 01C4472C2BE4F8560006F787 /* StatsGhostLineChartCell.swift in Sources */, + 019105862BE8BD6000CDFB16 /* StatsGhostSingleValueCell.swift in Sources */, F5E29036243E4F5F00C19CA5 /* FilterProvider.swift in Sources */, F4FE743429C3767300AC2729 /* AddressTableViewCell+ViewModel.swift in Sources */, 027AC51D227896540033E56E /* DomainCreditEligibilityChecker.swift in Sources */, @@ -25422,6 +25415,7 @@ F41E32FF287B47A500F89082 /* SuggestionsListViewModel.swift in Sources */, F4EDAA5129A795C600622D3D /* BlockedAuthor.swift in Sources */, FABB238E2602FC2C00C8785C /* NotificationSettingsService.swift in Sources */, + 019105872BE8BD6000CDFB16 /* StatsGhostSingleValueCell.swift in Sources */, 8091019429078CFE00FCB4EA /* JetpackFullscreenOverlayViewController.swift in Sources */, FA347AEE26EB6E300096604B /* GrowAudienceCell.swift in Sources */, FABB23922602FC2C00C8785C /* UIImage+Assets.swift in Sources */, From 39e4394ffbb8819c90c5fe6b7346b797f9801b74 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 6 May 2024 10:52:52 +0300 Subject: [PATCH 078/112] Implement StatsGhostSingleValueCell in the code --- .../StatsGhostSingleValueCell.swift | 26 ++++++++++++ .../GhostViews/StatsGhostSingleValueCell.xib | 40 ------------------- .../GhostViews/StatsGhostTableViewRows.swift | 2 +- 3 files changed, 27 insertions(+), 41 deletions(-) create mode 100644 WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.swift delete mode 100644 WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.xib diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.swift new file mode 100644 index 000000000000..61fb4ebc45e3 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.swift @@ -0,0 +1,26 @@ +import UIKit +import WordPressShared +import DesignSystem + +final class StatsGhostSingleValueCell: StatsGhostBaseCell { + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + headingLabel.isGhostableDisabled = true + let ghostView = UIView() + ghostView.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(ghostView) + topConstraint = ghostView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: .DS.Padding.single) + topConstraint?.isActive = true + NSLayoutConstraint.activate([ + ghostView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: .DS.Padding.double), + ghostView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -.DS.Padding.double), + ghostView.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.35), + ghostView.heightAnchor.constraint(equalToConstant: 36) + ]) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.xib b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.xib deleted file mode 100644 index 2285ac8c545b..000000000000 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.xib +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift index ce04ad21807f..82d297c9ea57 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift @@ -139,7 +139,7 @@ struct StatsGhostSingleValueRow: StatsRowGhostable { let statSection: StatSection? static let cell: ImmuTableCell = { - return ImmuTableCell.nib(StatsGhostSingleValueCell.defaultNib, StatsGhostSingleValueCell.self) + return ImmuTableCell.class(StatsGhostSingleValueCell.self) }() } From 0f443e40feac64b5b1ce42840512e61121bbd6b8 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 6 May 2024 11:28:27 +0300 Subject: [PATCH 079/112] Implement StatsGhostTopCell in the code --- .../GhostViews/StatsGhostTableViewRows.swift | 2 +- .../GhostViews/StatsGhostTopCell.swift | 58 ++++++++++++++++-- .../GhostViews/StatsGhostTopCell.xib | 59 ------------------- WordPress/WordPress.xcodeproj/project.pbxproj | 6 -- 4 files changed, 53 insertions(+), 72 deletions(-) delete mode 100644 WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.xib diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift index 82d297c9ea57..0050af891b61 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift @@ -42,7 +42,7 @@ struct StatsGhostTwoColumnImmutableRow: StatsRowGhostable { struct StatsGhostTopImmutableRow: StatsRowGhostable { static let cell: ImmuTableCell = { - return ImmuTableCell.nib(StatsGhostTopCell.defaultNib, StatsGhostTopCell.self) + return ImmuTableCell.class(StatsGhostTopCell.self) }() var hideTopBorder = false diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.swift index ef4d7838245d..bcbbbeb53f9d 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.swift @@ -3,8 +3,27 @@ import DesignSystem import WordPressUI final class StatsGhostTopCell: StatsGhostBaseCell, NibLoadable { - @IBOutlet private weak var topCellRow: StatsGhostTopCellRow! - @IBOutlet private weak var topCellHeaders: UIStackView! + private let titleHeader: UIView = { + let header = UIView() + header.translatesAutoresizingMaskIntoConstraints = false + return header + }() + + private let valueHeaders: UIStackView = { + let headers = UIStackView() + headers.translatesAutoresizingMaskIntoConstraints = false + headers.spacing = .DS.Padding.double + headers.distribution = .fill + headers.alignment = .fill + headers.axis = .horizontal + return headers + }() + + private let contentRows: StatsGhostTopCellRow = { + let contentRows = StatsGhostTopCellRow() + contentRows.translatesAutoresizingMaskIntoConstraints = false + return contentRows + }() var numberOfColumns: Int = 2 { didSet { @@ -12,17 +31,44 @@ final class StatsGhostTopCell: StatsGhostBaseCell, NibLoadable { } } + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubviews([titleHeader, valueHeaders, contentRows]) + + topConstraint = valueHeaders.topAnchor.constraint(equalTo: contentView.topAnchor, constant: .DS.Padding.single) + topConstraint?.isActive = true + NSLayoutConstraint.activate([ + titleHeader.topAnchor.constraint(equalTo: valueHeaders.topAnchor), + titleHeader.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: .DS.Padding.double), + titleHeader.widthAnchor.constraint(equalToConstant: 50), + titleHeader.heightAnchor.constraint(equalToConstant: .DS.Padding.double), + + valueHeaders.heightAnchor.constraint(equalToConstant: .DS.Padding.double), + valueHeaders.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -.DS.Padding.double), + + contentRows.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0), + contentRows.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0), + contentRows.topAnchor.constraint(equalTo: valueHeaders.bottomAnchor, constant: .DS.Padding.half), + contentRows.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -.DS.Padding.single) + ]) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + private func configureCell(with count: Int) { updateHeaders(count: count) - topCellRow.updateColumns(count: count) + contentRows.updateColumns(count: count) } private func updateHeaders(count: Int) { - topCellHeaders.removeAllSubviews() + valueHeaders.removeAllSubviews() let headers = Array(repeating: UIView(), count: count-1) headers.forEach { header in configureHeader(header) - topCellHeaders.addArrangedSubview(header) + valueHeaders.addArrangedSubview(header) } } @@ -32,7 +78,7 @@ final class StatsGhostTopCell: StatsGhostBaseCell, NibLoadable { } } -class StatsGhostTopCellRow: UIView { +final class StatsGhostTopCellRow: UIView { private let avatarView = UIView() private let columnsStackView = createStackView() private let mainColumn = StatsGhostTopCellColumn() diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.xib b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.xib deleted file mode 100644 index d005ec3a1558..000000000000 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTopCell.xib +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 2e230462ae7e..1eed07272e63 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -2784,7 +2784,6 @@ 9A9E3FA3230D5F0A00909BC4 /* StatsStackViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9E3FA2230D5F0A00909BC4 /* StatsStackViewCell.swift */; }; 9A9E3FAD230E9DD000909BC4 /* StatsGhostCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9E3FAB230E9DD000909BC4 /* StatsGhostCells.swift */; }; 9A9E3FAE230E9DD000909BC4 /* StatsGhostTwoColumnCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9A9E3FAC230E9DD000909BC4 /* StatsGhostTwoColumnCell.xib */; }; - 9A9E3FB0230EA7A300909BC4 /* StatsGhostTopCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9A9E3FAF230EA7A300909BC4 /* StatsGhostTopCell.xib */; }; 9A9E3FB2230EB74300909BC4 /* StatsGhostTabbedCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9A9E3FB1230EB74300909BC4 /* StatsGhostTabbedCell.xib */; }; 9A9E3FB4230EC4F700909BC4 /* StatsGhostPostingActivityCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9A9E3FB3230EC4F700909BC4 /* StatsGhostPostingActivityCell.xib */; }; 9AA0ADB1235F11700027AB5D /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0ADB0235F116F0027AB5D /* AsyncOperation.swift */; }; @@ -4186,7 +4185,6 @@ FABB1FD22602FC2C00C8785C /* RestoreStatusView.xib in Resources */ = {isa = PBXBuildFile; fileRef = FA1A55FE25A6F07F0033967D /* RestoreStatusView.xib */; }; FABB1FD32602FC2C00C8785C /* PluginDetailViewHeaderCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4058F4191FF40EE1000D5559 /* PluginDetailViewHeaderCell.xib */; }; FABB1FD42602FC2C00C8785C /* Flags.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 406A0EEF224D39C50016AD6A /* Flags.xcassets */; }; - FABB1FD52602FC2C00C8785C /* StatsGhostTopCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9A9E3FAF230EA7A300909BC4 /* StatsGhostTopCell.xib */; }; FABB1FD62602FC2C00C8785C /* CustomizeInsightsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 985793C722F23D7000643DBF /* CustomizeInsightsCell.xib */; }; FABB1FD92602FC2C00C8785C /* oswald_upper.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F5A34D0625DF2F7700C9654B /* oswald_upper.ttf */; }; FABB1FDB2602FC2C00C8785C /* xhtmlValidatorTemplate.xhtml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAE97080E33B21600CA8540 /* xhtmlValidatorTemplate.xhtml */; }; @@ -8179,7 +8177,6 @@ 9A9E3FA2230D5F0A00909BC4 /* StatsStackViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsStackViewCell.swift; sourceTree = ""; }; 9A9E3FAB230E9DD000909BC4 /* StatsGhostCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsGhostCells.swift; sourceTree = ""; }; 9A9E3FAC230E9DD000909BC4 /* StatsGhostTwoColumnCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsGhostTwoColumnCell.xib; sourceTree = ""; }; - 9A9E3FAF230EA7A300909BC4 /* StatsGhostTopCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsGhostTopCell.xib; sourceTree = ""; }; 9A9E3FB1230EB74300909BC4 /* StatsGhostTabbedCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsGhostTabbedCell.xib; sourceTree = ""; }; 9A9E3FB3230EC4F700909BC4 /* StatsGhostPostingActivityCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsGhostPostingActivityCell.xib; sourceTree = ""; }; 9AA0ADB0235F116F0027AB5D /* AsyncOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncOperation.swift; sourceTree = ""; }; @@ -14997,7 +14994,6 @@ 01C591442BDBD63D0071515C /* StatsGhostTopCell.swift */, 9A9E3FB3230EC4F700909BC4 /* StatsGhostPostingActivityCell.xib */, 9A9E3FB1230EB74300909BC4 /* StatsGhostTabbedCell.xib */, - 9A9E3FAF230EA7A300909BC4 /* StatsGhostTopCell.xib */, 9A19D440236C7C7500D393E5 /* StatsGhostTopHeaderCell.xib */, 9A9E3FAC230E9DD000909BC4 /* StatsGhostTwoColumnCell.xib */, 9AC3C69A231543C2007933CD /* StatsGhostChartCell.xib */, @@ -19662,7 +19658,6 @@ 4058F41A1FF40EE1000D5559 /* PluginDetailViewHeaderCell.xib in Resources */, 1761F17226209AEE000815EF /* open-source-dark-icon-app-60x60@3x.png in Resources */, 406A0EF0224D39C50016AD6A /* Flags.xcassets in Resources */, - 9A9E3FB0230EA7A300909BC4 /* StatsGhostTopCell.xib in Resources */, 985793C922F23D7000643DBF /* CustomizeInsightsCell.xib in Resources */, 1761F17926209AEE000815EF /* wordpress-dark-icon-app-60x60@2x.png in Resources */, 83A8A2962BDC557F001F9133 /* ReaderTagFooterView.xib in Resources */, @@ -20213,7 +20208,6 @@ FABB1FD32602FC2C00C8785C /* PluginDetailViewHeaderCell.xib in Resources */, FABB1FD42602FC2C00C8785C /* Flags.xcassets in Resources */, FE3E427826A868E300C596CE /* ListTableViewCell.xib in Resources */, - FABB1FD52602FC2C00C8785C /* StatsGhostTopCell.xib in Resources */, C7124E4E2638528F00929318 /* JetpackPrologueViewController.xib in Resources */, F465978628E65E1800D5F49A /* blue-on-white-icon-app-83.5@2x.png in Resources */, FABB1FD62602FC2C00C8785C /* CustomizeInsightsCell.xib in Resources */, From 42e742c2a79bc1cedf55561ffee5232ca20633b1 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 6 May 2024 11:29:38 +0300 Subject: [PATCH 080/112] Update StatsGhostBaseCell to have common init for both nib and code paths --- .../Shared Views/GhostViews/StatsGhostCells.swift | 14 ++++++++++++++ .../GhostViews/StatsGhostLineChartCell.swift | 1 - .../GhostViews/StatsGhostSingleValueCell.swift | 1 - 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift index 98f30e98d3ff..61764277b2e4 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostCells.swift @@ -6,6 +6,20 @@ class StatsGhostBaseCell: StatsBaseCell { override func awakeFromNib() { super.awakeFromNib() + commonInit() + } + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + private func commonInit() { headingLabel.isGhostableDisabled = true Style.configureCell(self) } diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift index 043bf18f430d..5aaef8923418 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostLineChartCell.swift @@ -7,7 +7,6 @@ final class StatsGhostLineChartCell: StatsGhostBaseCell { super.init(style: style, reuseIdentifier: reuseIdentifier) let lineChart = StatsGhostLineChartView() - headingLabel.isGhostableDisabled = true lineChart.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(lineChart) topConstraint = lineChart.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0) diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.swift index 61fb4ebc45e3..c11bf06e95c4 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostSingleValueCell.swift @@ -6,7 +6,6 @@ final class StatsGhostSingleValueCell: StatsGhostBaseCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) - headingLabel.isGhostableDisabled = true let ghostView = UIView() ghostView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(ghostView) From 1c366ba0a41cf6821e532c1eafc3c2b70eb3e456 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 6 May 2024 11:42:30 +0300 Subject: [PATCH 081/112] Update People table view color --- WordPress/Classes/ViewRelated/People/PeopleViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift index 7880d4821e61..982c14a03bbb 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift @@ -546,6 +546,7 @@ private extension PeopleViewController { WPStyleGuide.configureColors(view: view, tableView: tableView) WPStyleGuide.configureAutomaticHeightRows(for: tableView) tableView.separatorStyle = .none + tableView.backgroundColor = .DS.Background.primary setupFilterBar() setupTableView() From f301d05e5309cc8273e994891c7fdf329778d67c Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 6 May 2024 11:54:37 +0300 Subject: [PATCH 082/112] Set top padding for People table --- WordPress/Classes/ViewRelated/People/PeopleViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift index 982c14a03bbb..539b0302a20a 100644 --- a/WordPress/Classes/ViewRelated/People/PeopleViewController.swift +++ b/WordPress/Classes/ViewRelated/People/PeopleViewController.swift @@ -150,7 +150,7 @@ class PeopleViewController: UITableViewController { // MARK: UITableViewDelegate override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - return hasHorizontallyCompactView() ? CGFloat.leastNormalMagnitude : 0 + return .DS.Padding.single } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { From 72bc12534cdbb851bfeef64606825e951d14eaed Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Mon, 6 May 2024 16:03:41 +0300 Subject: [PATCH 083/112] Update RELEASE-NOTES for Stats Subscribers feature --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index b4f1ba7f6e44..a13a9e9911c5 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -4,6 +4,7 @@ * [*] Add "Slug" and "Excerpt" fields to "Page Settings" [#23135] * [*] Make it easier to "Share" and "Blaze" a published post with an updated success view [##23128] * [*] Add support for viewing trashed posts and pages and restoring them from the editor [#23142] +* [***] [Jetpack-only] Reorganized Stats to include updated Traffic and Insights tabs, along with a newly added Subscribers tab to improve subscriber metrics analysis [#23159] 24.8 ----- From 0f14a2716cece89b06c7f0e13d9cfab70c9ff1fe Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Tue, 7 May 2024 10:48:54 +0300 Subject: [PATCH 084/112] Make the first Stats tab a default one if there's none selected --- WordPress/Classes/Stores/UserPersistentRepository.swift | 1 + .../Stats/SiteStatsDashboardViewController.swift | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/WordPress/Classes/Stores/UserPersistentRepository.swift b/WordPress/Classes/Stores/UserPersistentRepository.swift index 40437d5a4b6b..cff0dad25007 100644 --- a/WordPress/Classes/Stores/UserPersistentRepository.swift +++ b/WordPress/Classes/Stores/UserPersistentRepository.swift @@ -7,6 +7,7 @@ protocol UserPersistentRepositoryReader { func array(forKey key: String) -> [Any]? func dictionary(forKey key: String) -> [String: Any]? func url(forKey key: String) -> URL? + func value(forKey key: String) -> Any? func dictionaryRepresentation() -> [String: Any] } diff --git a/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift b/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift index 047c95a5668b..d0605481268e 100644 --- a/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift @@ -364,7 +364,12 @@ struct SiteStatsDashboardPreferences { guard let siteID = SiteStatsInformation.sharedInstance.siteID?.intValue else { return nil } let key = Self.lastSelectedStatsTabTypeKey(forSiteID: siteID) - return StatsTabType(rawValue: UserPersistentStoreFactory.instance().integer(forKey: key)) + + guard let tabRawValue = UserPersistentStoreFactory.instance().value(forKey: key) as? Int else { + return nil + } + + return StatsTabType(rawValue: tabRawValue) } static func getSelectedPeriodUnit() -> StatsPeriodUnit? { From 2c0d8bcf238452e5c8c651b7662ce54605a0e839 Mon Sep 17 00:00:00 2001 From: kean Date: Tue, 7 May 2024 07:01:41 -0400 Subject: [PATCH 085/112] Fix an issue with passwod removal --- Podfile | 4 ++-- Podfile.lock | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Podfile b/Podfile index ba7a23f97908..2ed7dfd7f328 100644 --- a/Podfile +++ b/Podfile @@ -55,8 +55,8 @@ def gravatar end def wordpress_kit - pod 'WordPressKit', '~> 17.1.0' - # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '' + # pod 'WordPressKit', '~> 17.1.0' + pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '549fbee3059e71bd1471e35090ac942a3b23516a' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', branch: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', tag: '' # pod 'WordPressKit', path: '../WordPressKit-iOS' diff --git a/Podfile.lock b/Podfile.lock index f4440e678747..d720a8fee5cb 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -121,7 +121,7 @@ DEPENDENCIES: - SwiftLint (= 0.54.0) - WordPress-Editor-iOS (~> 1.19.11) - WordPressAuthenticator (>= 9.0.8, ~> 9.0) - - WordPressKit (~> 17.1.0) + - WordPressKit (from `https://github.com/wordpress-mobile/WordPressKit-iOS.git`, commit `549fbee3059e71bd1471e35090ac942a3b23516a`) - WordPressShared (>= 2.3.1, ~> 2.3) - WordPressUI (~> 1.16) - ZendeskSupportSDK (= 5.3.0) @@ -160,7 +160,6 @@ SPEC REPOS: - SVProgressHUD - SwiftLint - UIDeviceIdentifier - - WordPressKit - WordPressUI - wpxmlrpc - ZendeskCommonUISDK @@ -178,11 +177,17 @@ EXTERNAL SOURCES: :tag: 0.2.0 Gutenberg: :podspec: https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-v1.118.0.podspec + WordPressKit: + :commit: 549fbee3059e71bd1471e35090ac942a3b23516a + :git: https://github.com/wordpress-mobile/WordPressKit-iOS.git CHECKOUT OPTIONS: FSInteractiveMap: :git: https://github.com/wordpress-mobile/FSInteractiveMap.git :tag: 0.2.0 + WordPressKit: + :commit: 549fbee3059e71bd1471e35090ac942a3b23516a + :git: https://github.com/wordpress-mobile/WordPressKit-iOS.git SPEC CHECKSUMS: Alamofire: 02b772c9910e8eba1a079227c32fbd9e46c90a24 @@ -229,6 +234,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: d170fa8e270b2a32bef9dcdcabff5b8f1a5deced -PODFILE CHECKSUM: fe8df3e873baa4219a3e9b59632c5d2c7e3d774e +PODFILE CHECKSUM: e350c05f42484b1312d9cd1044b92551406d5411 COCOAPODS: 1.15.2 From 62a9700e2148dfaf5a0188d34437a8ee28eab31c Mon Sep 17 00:00:00 2001 From: kean Date: Thu, 2 May 2024 19:12:18 -0400 Subject: [PATCH 086/112] Integrate new visibility picker in Post Settings --- .../PostSettingsViewController+Swift.swift | 27 +++++++++++++++++++ .../Post/PostSettingsViewController.m | 12 +++------ .../Post/PostVisibilityPicker.swift | 5 +++- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift index 93af5cf6eb91..9156ccf6fba7 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift @@ -2,6 +2,7 @@ import UIKit import CoreData import Combine import WordPressKit +import SwiftUI extension PostSettingsViewController { static func make(for post: AbstractPost) -> PostSettingsViewController { @@ -178,6 +179,32 @@ extension PostSettingsViewController: UIAdaptivePresentationControllerDelegate { } } +// MARK: - PostSettingsViewController (Visibility) + +extension PostSettingsViewController { + @objc func showUpdatedPostVisibilityPicker() { + let view = PostVisibilityPicker(visibility: PostVisibility(post: apost)) { [weak self] selection in + guard let self else { return } + + switch selection.visibility { + case .public: + self.apost.status = .publish + case .private: + self.apost.status = .publishPrivate + case .protected: + self.apost.status = .publish + } + self.apost.password = selection.password + self.navigationController?.popViewController(animated: true) + self.reloadData() + } + let viewController = UIHostingController(rootView: view) + viewController.title = PostVisibilityPicker.title + viewController.configureDefaultNavigationBarAppearance() + navigationController?.pushViewController(viewController, animated: true) + } +} + // MARK: - PostSettingsViewController (Page Attributes) extension PostSettingsViewController { diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m index 3b25081a3f3f..b8f0b687276a 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m @@ -67,7 +67,6 @@ @interface PostSettingsViewController () Date: Fri, 3 May 2024 09:27:50 -0400 Subject: [PATCH 087/112] Fix reuse of table cells in PostSettingsViewController.m --- .../Post/PostSettingsViewController+Swift.swift | 2 ++ .../ViewRelated/Post/PostSettingsViewController.m | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift index 9156ccf6fba7..b27a42b173d6 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift @@ -186,6 +186,8 @@ extension PostSettingsViewController { let view = PostVisibilityPicker(visibility: PostVisibility(post: apost)) { [weak self] selection in guard let self else { return } + WPAnalytics.track(.editorPostVisibilityChanged, properties: ["via": "settings"]) + switch selection.visibility { case .public: self.apost.status = .publish diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m index b8f0b687276a..efcf319e846f 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m @@ -711,7 +711,7 @@ - (UITableViewCell *)configureMetaPostMetaCellForIndexPath:(NSIndexPath *)indexP cell.tag = PostSettingsRowAuthor; } else if (row == PostSettingsRowPublishDate) { // Publish date - cell = [self getWPTableViewDisclosureCell]; + cell = [self getWPTableViewDisclosureCellWithIdentifier:@"PostSettingsRowPublishDate"]; cell.textLabel.text = NSLocalizedString(@"Publish Date", @"Label for the publish date button."); // Note: it's safe to remove `shouldPublishImmediately` when // `RemoteFeatureFlagSyncPublishing` is enabled because this cell is not displayed. @@ -746,7 +746,7 @@ - (UITableViewCell *)configureMetaPostMetaCellForIndexPath:(NSIndexPath *)indexP } else if (row == PostSettingsRowVisibility) { // Visibility - cell = [self getWPTableViewDisclosureCell]; + cell = [self getWPTableViewDisclosureCellWithIdentifier:@"PostSettingsRowVisibility"]; cell.textLabel.text = NSLocalizedString(@"Visibility", @"The visibility settings of the post. Should be the same as in core WP."); cell.detailTextLabel.text = [self.apost titleForVisibility]; cell.tag = PostSettingsRowVisibility; @@ -1016,12 +1016,15 @@ - (UITableViewCell *)configureExcerptCellForIndexPath:(NSIndexPath *)indexPath return cell; } -- (WPTableViewCell *)getWPTableViewDisclosureCell +- (WPTableViewCell *)getWPTableViewDisclosureCell { + return [self getWPTableViewDisclosureCellWithIdentifier:@"WPTableViewDisclosureCellIdentifier"]; +} + +- (WPTableViewCell *)getWPTableViewDisclosureCellWithIdentifier:(NSString *)identifier { - static NSString *WPTableViewDisclosureCellIdentifier = @"WPTableViewDisclosureCellIdentifier"; - WPTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:WPTableViewDisclosureCellIdentifier]; + WPTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:identifier]; if (!cell) { - cell = [[WPTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:WPTableViewDisclosureCellIdentifier]; + cell = [[WPTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; [WPStyleGuide configureTableViewCell:cell]; } From 39c2583e563ecf37176ce111e3ed5fb87f5a7a65 Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 09:45:10 -0400 Subject: [PATCH 088/112] Pass password to PostVisibilityPicker in the inital selection --- .../PostSettingsViewController+Swift.swift | 4 +- .../Post/PostVisibilityPicker.swift | 38 +++++++++---------- .../PrepublishingViewController.swift | 21 +++++----- .../Site Creation/Wizard/SiteCreator.swift | 2 +- 4 files changed, 31 insertions(+), 34 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift index b27a42b173d6..e7a525ae3ec2 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift @@ -183,12 +183,12 @@ extension PostSettingsViewController: UIAdaptivePresentationControllerDelegate { extension PostSettingsViewController { @objc func showUpdatedPostVisibilityPicker() { - let view = PostVisibilityPicker(visibility: PostVisibility(post: apost)) { [weak self] selection in + let view = PostVisibilityPicker(selection: .init(post: apost)) { [weak self] selection in guard let self else { return } WPAnalytics.track(.editorPostVisibilityChanged, properties: ["via": "settings"]) - switch selection.visibility { + switch selection.type { case .public: self.apost.status = .publish case .private: diff --git a/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift b/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift index 6d5376de3387..eed8ce54fba2 100644 --- a/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift +++ b/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift @@ -1,22 +1,26 @@ import SwiftUI struct PostVisibilityPicker: View { - @State private var selection: PostVisibility = .public - @State private var password = "" + @State private var selection: Selection @State private var isEnteringPassword = false @State private var isDismissing = false struct Selection { - var visibility: PostVisibility - var password: String? + var type: PostVisibility + var password = "" + + init(post: AbstractPost) { + self.type = PostVisibility(status: post.status ?? .draft, password: post.password) + self.password = post.password ?? "" + } } private let onSubmit: (Selection) -> Void static var title: String { Strings.title } - init(visibility: PostVisibility, onSubmit: @escaping (Selection) -> Void) { - self._selection = State(initialValue: visibility) + init(selection: Selection, onSubmit: @escaping (Selection) -> Void) { + self._selection = State(initialValue: selection) self.onSubmit = onSubmit } @@ -36,8 +40,8 @@ struct PostVisibilityPicker: View { if visibility == .protected { isEnteringPassword = true } else { - selection = visibility - onSubmit(Selection(visibility: visibility, password: nil)) + selection.type = visibility + onSubmit(selection) } } }, label: { @@ -52,7 +56,7 @@ struct PostVisibilityPicker: View { Spacer() Image(systemName: "checkmark") .tint(Color(uiColor: .primary)) - .opacity((selection == visibility && !isEnteringPassword) ? 1 : 0) + .opacity((selection.type == visibility && !isEnteringPassword) ? 1 : 0) } }) .tint(.primary) @@ -65,13 +69,13 @@ struct PostVisibilityPicker: View { @ViewBuilder private var enterPasswordRows: some View { - PasswordField(password: $password) + PasswordField(password: $selection.password) .onSubmit(savePassword) HStack { Button(Strings.cancel) { withAnimation { - password = "" + selection.password = "" isEnteringPassword = false } } @@ -79,7 +83,7 @@ struct PostVisibilityPicker: View { Spacer() Button(Strings.save, action: savePassword) .font(.body.weight(.medium)) - .disabled(password.isEmpty) + .disabled(selection.password.isEmpty) } .buttonStyle(.plain) .foregroundStyle(Color(uiColor: .brand)) @@ -87,12 +91,12 @@ struct PostVisibilityPicker: View { private func savePassword() { withAnimation { - selection = .protected + selection.type = .protected isEnteringPassword = false isDismissing = true // Let the keyboard dismiss first to avoid janky animation DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(550)) { - onSubmit(Selection(visibility: .protected, password: password)) + onSubmit(selection) } } } @@ -137,7 +141,7 @@ enum PostVisibility: Identifiable, CaseIterable { case protected init(status: AbstractPost.Status, password: String?) { - if password != nil { + if let password, !password.isEmpty { self = .protected } else if status == .publishPrivate { self = .private @@ -146,10 +150,6 @@ enum PostVisibility: Identifiable, CaseIterable { } } - init(post: AbstractPost) { - self.init(status: post.status ?? .draft, password: post.password) - } - var id: PostVisibility { self } var localizedTitle: String { diff --git a/WordPress/Classes/ViewRelated/Post/Prepublishing/PrepublishingViewController.swift b/WordPress/Classes/ViewRelated/Post/Prepublishing/PrepublishingViewController.swift index 4f4a1a37a0f9..93269aaebe3f 100644 --- a/WordPress/Classes/ViewRelated/Post/Prepublishing/PrepublishingViewController.swift +++ b/WordPress/Classes/ViewRelated/Post/Prepublishing/PrepublishingViewController.swift @@ -370,18 +370,17 @@ final class PrepublishingViewController: UIViewController, UITableViewDataSource // MARK: - Visibility private func configureVisibilityCell(_ cell: WPTableViewCell) { - cell.detailTextLabel?.text = viewModel.visibility.localizedTitle + cell.detailTextLabel?.text = viewModel.visibility.type.localizedTitle } private func didTapVisibilityCell() { - let view = PostVisibilityPicker(visibility: viewModel.visibility) { [weak self] selection in + let view = PostVisibilityPicker(selection: viewModel.visibility) { [weak self] selection in guard let self else { return } - self.viewModel.visibility = selection.visibility - if selection.visibility == .private { + self.viewModel.visibility = selection + if selection.type == .private { self.viewModel.publishDate = nil self.updatePublishButtonLabel() } - self.viewModel.password = selection.password self.reloadData() self.navigationController?.popViewController(animated: true) } @@ -401,7 +400,7 @@ final class PrepublishingViewController: UIViewController, UITableViewDataSource } else { cell.detailTextLabel?.text = Strings.immediately } - viewModel.visibility == .private ? cell.disable() : cell.enable() + viewModel.visibility.type == .private ? cell.disable() : cell.enable() } func didTapSchedule(_ indexPath: IndexPath) { @@ -543,8 +542,7 @@ extension PrepublishingOption { private final class PrepublishingViewModel { private let post: AbstractPost - var visibility: PostVisibility - var password: String? + var visibility: PostVisibilityPicker.Selection var publishDate: Date? var publishButtonTitle: String { @@ -557,8 +555,7 @@ private final class PrepublishingViewModel { init(post: AbstractPost) { self.post = post - self.visibility = PostVisibility(status: post.status ?? .draft, password: post.password) - self.password = post.password + self.visibility = .init(post: post) // Ask the user to provide the date every time (ignore the obscure WP dateCreated/dateModified logic) self.publishDate = nil } @@ -568,8 +565,8 @@ private final class PrepublishingViewModel { wpAssert(post.isRevision()) try await coordinator._publish(post.original(), options: .init( - visibility: visibility, - password: password, + visibility: visibility.type, + password: visibility.password, publishDate: publishDate )) } diff --git a/WordPress/Classes/ViewRelated/Site Creation/Wizard/SiteCreator.swift b/WordPress/Classes/ViewRelated/Site Creation/Wizard/SiteCreator.swift index 677e7ebf0e9c..430916f4ae63 100644 --- a/WordPress/Classes/ViewRelated/Site Creation/Wizard/SiteCreator.swift +++ b/WordPress/Classes/ViewRelated/Site Creation/Wizard/SiteCreator.swift @@ -71,7 +71,7 @@ final class SiteCreator { // MARK: - Helper Extensions -extension String { +private extension String { var subdomain: String { return components(separatedBy: ".").first ?? "" } From f1395e037a468237e7be99c64736a9573df043cb Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 10:37:50 -0400 Subject: [PATCH 089/112] Update PostVisibilityPicker to display current password --- .../AbstractPost+TitleForVisibility.swift | 10 +++- .../PostSettingsViewController+Swift.swift | 3 +- .../Post/PostVisibilityPicker.swift | 60 ++++++++++--------- 3 files changed, 42 insertions(+), 31 deletions(-) diff --git a/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift b/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift index d2308bef2a56..c787debfce40 100644 --- a/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift +++ b/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift @@ -6,9 +6,15 @@ extension AbstractPost { static let publicLabel = NSLocalizedString("Public", comment: "Privacy setting for posts set to 'Public' (default). Should be the same as in core WP.") /// A title describing the status. Ie.: "Public" or "Private" or "Password protected" - /// - /// - warning: deprecated (kahu-offline-mode) (use ``PostVisibility``) @objc var titleForVisibility: String { + guard FeatureFlag.syncPublishing.enabled else { + return _titleForVisibility + } + return PostVisibility(status: status ?? .draft, password: password).localizedTitle + } + + /// - warning: deprecated (kahu-offline-mode) (use ``PostVisibility``) + @objc private var _titleForVisibility: String { if password != nil { return AbstractPost.passwordProtectedLabel } else if status == .publishPrivate { diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift index e7a525ae3ec2..973a69c95062 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift @@ -187,6 +187,7 @@ extension PostSettingsViewController { guard let self else { return } WPAnalytics.track(.editorPostVisibilityChanged, properties: ["via": "settings"]) + NSLog("set-visibility: \(selection)") switch selection.type { case .public: @@ -196,7 +197,7 @@ extension PostSettingsViewController { case .protected: self.apost.status = .publish } - self.apost.password = selection.password + self.apost.password = selection.password.isEmpty ? nil : selection.password self.navigationController?.popViewController(animated: true) self.reloadData() } diff --git a/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift b/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift index eed8ce54fba2..8e485c29acb6 100644 --- a/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift +++ b/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift @@ -2,8 +2,8 @@ import SwiftUI struct PostVisibilityPicker: View { @State private var selection: Selection - @State private var isEnteringPassword = false @State private var isDismissing = false + @FocusState private var isPasswordFieldFocused: Bool struct Selection { var type: PostVisibility @@ -37,10 +37,12 @@ struct PostVisibilityPicker: View { private func makeRow(for visibility: PostVisibility) -> some View { Button(action: { withAnimation { + selection.type = visibility + selection.password = "" + if visibility == .protected { - isEnteringPassword = true + isPasswordFieldFocused = true } else { - selection.type = visibility onSubmit(selection) } } @@ -51,48 +53,51 @@ struct PostVisibilityPicker: View { Text(visibility.localizedDetails) .font(.footnote) .foregroundStyle(.secondary) - .opacity(isEnteringPassword ? 0.5 : 1) + .opacity(visibility != .protected && isPasswordFieldFocused ? 0.4 : 1) } Spacer() Image(systemName: "checkmark") .tint(Color(uiColor: .primary)) - .opacity((selection.type == visibility && !isEnteringPassword) ? 1 : 0) + .opacity((selection.type == visibility && !isPasswordFieldFocused) ? 1 : 0) } }) .tint(.primary) - .disabled(isEnteringPassword && visibility != .protected) + .disabled(isPasswordFieldFocused && visibility != .protected) - if visibility == .protected, isEnteringPassword { + if visibility == .protected, selection.type == .protected { enterPasswordRows } } @ViewBuilder private var enterPasswordRows: some View { - PasswordField(password: $selection.password) - .onSubmit(savePassword) + PasswordField(password: $selection.password, isFocused: isPasswordFieldFocused) + .focused($isPasswordFieldFocused) + .onSubmit(buttonSavePasswordTapped) - HStack { - Button(Strings.cancel) { - withAnimation { - selection.password = "" - isEnteringPassword = false + if isPasswordFieldFocused { + HStack { + Button(Strings.cancel) { + withAnimation { + selection.type = .public + selection.password = "" + } } + .keyboardShortcut(.cancelAction) + Spacer() + Button(Strings.save, action: buttonSavePasswordTapped) + .font(.body.weight(.medium)) + .disabled(selection.password.trimmingCharacters(in: .whitespaces).isEmpty) } - .keyboardShortcut(.cancelAction) - Spacer() - Button(Strings.save, action: savePassword) - .font(.body.weight(.medium)) - .disabled(selection.password.isEmpty) + .buttonStyle(.plain) + .foregroundStyle(Color(uiColor: .brand)) } - .buttonStyle(.plain) - .foregroundStyle(Color(uiColor: .brand)) } - private func savePassword() { + private func buttonSavePasswordTapped() { withAnimation { - selection.type = .protected - isEnteringPassword = false + isPasswordFieldFocused = false + selection.password = selection.password.trimmingCharacters(in: .whitespaces) isDismissing = true // Let the keyboard dismiss first to avoid janky animation DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(550)) { @@ -105,13 +110,12 @@ struct PostVisibilityPicker: View { private struct PasswordField: View { @Binding var password: String @State var isSecure = true - @FocusState private var isFocused: Bool + let isFocused: Bool var body: some View { HStack { textField - .focused($isFocused) - if !password.isEmpty { + if isFocused && !password.isEmpty { Button(action: { password = "" }) { Image(systemName: "xmark.circle") .foregroundStyle(.secondary) @@ -123,8 +127,8 @@ private struct PasswordField: View { } } .buttonStyle(.plain) - .onAppear { isFocused = true } } + @ViewBuilder private var textField: some View { if isSecure { From 93d98f60bbf196daa562743d429b1be30041aa4e Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 10:45:11 -0400 Subject: [PATCH 090/112] Fix an issue with setting a passowrd on a scheduled post publishing it immediatelly --- .../Models/AbstractPost+TitleForVisibility.swift | 2 +- .../Post/PostSettingsViewController+Swift.swift | 11 ++++++----- .../ViewRelated/Post/PostVisibilityPicker.swift | 6 +++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift b/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift index c787debfce40..e90449a4563a 100644 --- a/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift +++ b/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift @@ -10,7 +10,7 @@ extension AbstractPost { guard FeatureFlag.syncPublishing.enabled else { return _titleForVisibility } - return PostVisibility(status: status ?? .draft, password: password).localizedTitle + return PostVisibility(post: self).localizedTitle } /// - warning: deprecated (kahu-offline-mode) (use ``PostVisibility``) diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift index 973a69c95062..df37d98f0c53 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift @@ -187,15 +187,16 @@ extension PostSettingsViewController { guard let self else { return } WPAnalytics.track(.editorPostVisibilityChanged, properties: ["via": "settings"]) - NSLog("set-visibility: \(selection)") switch selection.type { - case .public: - self.apost.status = .publish + case .public, .protected: + if self.apost.status == .scheduled { + // Keep it scheduled + } else { + self.apost.status = .publish + } case .private: self.apost.status = .publishPrivate - case .protected: - self.apost.status = .publish } self.apost.password = selection.password.isEmpty ? nil : selection.password self.navigationController?.popViewController(animated: true) diff --git a/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift b/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift index 8e485c29acb6..50f1c308f509 100644 --- a/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift +++ b/WordPress/Classes/ViewRelated/Post/PostVisibilityPicker.swift @@ -10,7 +10,7 @@ struct PostVisibilityPicker: View { var password = "" init(post: AbstractPost) { - self.type = PostVisibility(status: post.status ?? .draft, password: post.password) + self.type = PostVisibility(post: post) self.password = post.password ?? "" } } @@ -144,6 +144,10 @@ enum PostVisibility: Identifiable, CaseIterable { case `private` case protected + init(post: AbstractPost) { + self.init(status: post.status ?? .draft, password: post.password) + } + init(status: AbstractPost.Status, password: String?) { if let password, !password.isEmpty { self = .protected From bb7b13d364d805fbd926a19e3c0d26acfb813c3b Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 10:59:05 -0400 Subject: [PATCH 091/112] Update badge color --- .../Post/PostCardStatusViewModel.swift | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Post/PostCardStatusViewModel.swift b/WordPress/Classes/ViewRelated/Post/PostCardStatusViewModel.swift index ab5efecccb9a..08c88342ff85 100644 --- a/WordPress/Classes/ViewRelated/Post/PostCardStatusViewModel.swift +++ b/WordPress/Classes/ViewRelated/Post/PostCardStatusViewModel.swift @@ -97,14 +97,10 @@ class PostCardStatusViewModel: NSObject, AbstractPostMenuViewModel { return .warning } switch post.status ?? .draft { - case .pending: - return .success - case .scheduled: - return .primary(.shade40) case .trash: return .error default: - return .neutral(.shade70) + return .secondaryLabel } } @@ -277,9 +273,18 @@ class PostCardStatusViewModel: NSObject, AbstractPostMenuViewModel { func statusAndBadges(separatedBy separator: String) -> String { let sticky = post.isStickyPost ? Constants.stickyLabel : "" let pending = (post.status == .pending && isSyncPublishingEnabled) ? Constants.pendingReview : "" + let visibility: String = { + let visibility = PostVisibility(post: post) + switch visibility { + case .public: + return "" + case .private, .protected: + return visibility.localizedTitle + } + }() let status = self.status ?? "" - return [status, pending, sticky].filter { !$0.isEmpty }.joined(separator: separator) + return [status, visibility, pending, sticky].filter { !$0.isEmpty }.joined(separator: separator) } /// Determine what the failed status message should be and return it. From 229faf99f962f157376172152f3d16b6a5cb4ee2 Mon Sep 17 00:00:00 2001 From: kean Date: Sat, 4 May 2024 11:07:05 -0400 Subject: [PATCH 092/112] Add a warning about setting visibility to private --- .../Post/PostSettingsViewController+Swift.swift | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift index df37d98f0c53..92c9becf39db 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift @@ -190,12 +190,17 @@ extension PostSettingsViewController { switch selection.type { case .public, .protected: - if self.apost.status == .scheduled { + if self.apost.original().status == .scheduled { // Keep it scheduled } else { self.apost.status = .publish } case .private: + if self.apost.original().status == .scheduled { + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) { + self.showWarningPostWillBePublishedAlert() + } + } self.apost.status = .publishPrivate } self.apost.password = selection.password.isEmpty ? nil : selection.password @@ -207,6 +212,12 @@ extension PostSettingsViewController { viewController.configureDefaultNavigationBarAppearance() navigationController?.pushViewController(viewController, animated: true) } + + private func showWarningPostWillBePublishedAlert() { + let alert = UIAlertController(title: nil, message: Strings.warningPostWillBePublishedAlertMessage, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: "Ok"), style: .default)) + present(alert, animated: true) + } } // MARK: - PostSettingsViewController (Page Attributes) @@ -270,4 +281,6 @@ extension PostSettingsViewController { private enum Strings { static let errorMessage = NSLocalizedString("postSettings.updateFailedMessage", value: "Failed to update the post settings", comment: "Error message on post/page settings screen") + + static let warningPostWillBePublishedAlertMessage = NSLocalizedString("postSettings.warningPostWillBePublishedAlertMessage", value: "By changing the visibility to 'Private', the post will be published immediately", comment: "An alert message explaning that by changing the visibility to private, the post will be published immediately to your site") } From d43f54b041efef97cde41e990c8ac00ffbda406a Mon Sep 17 00:00:00 2001 From: Alex Grebenyuk Date: Tue, 7 May 2024 07:09:39 -0400 Subject: [PATCH 093/112] Update WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift Co-authored-by: Momo Ozawa --- .../ViewRelated/Post/PostSettingsViewController+Swift.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift index 92c9becf39db..0b4512331baa 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift @@ -215,7 +215,7 @@ extension PostSettingsViewController { private func showWarningPostWillBePublishedAlert() { let alert = UIAlertController(title: nil, message: Strings.warningPostWillBePublishedAlertMessage, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: "Ok"), style: .default)) + alert.addAction(UIAlertAction(title: NSLocalizedString("postSettings.ok", value: "OK", comment: "Button OK"), style: .default)) present(alert, animated: true) } } From 1ce5fcb1781dbd737c4dc78f04d3759107d69a33 Mon Sep 17 00:00:00 2001 From: kean Date: Tue, 7 May 2024 07:20:57 -0400 Subject: [PATCH 094/112] Fix SwiftLint warning --- .../ViewRelated/Post/PostSettingsViewController+Swift.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift index 0b4512331baa..336f8481866b 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift @@ -215,7 +215,7 @@ extension PostSettingsViewController { private func showWarningPostWillBePublishedAlert() { let alert = UIAlertController(title: nil, message: Strings.warningPostWillBePublishedAlertMessage, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("postSettings.ok", value: "OK", comment: "Button OK"), style: .default)) + alert.addAction(UIAlertAction(title: NSLocalizedString("postSettings.ok", value: "OK", comment: "Button OK"), style: .default)) present(alert, animated: true) } } From b4fb3a3a78f262c80f1734d208df4472308bcdd0 Mon Sep 17 00:00:00 2001 From: kean Date: Tue, 7 May 2024 08:14:33 -0400 Subject: [PATCH 095/112] Minor Post Settings improvements --- RELEASE-NOTES.txt | 1 + .../Post/PostSettingsViewController.m | 23 +++++++++---------- .../PostSettingsViewController_Internal.h | 3 +-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index d138674d290c..5e128f7bf766 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -4,6 +4,7 @@ * [*] Add "Slug" and "Excerpt" fields to "Page Settings" [#23135] * [*] Make it easier to "Share" and "Blaze" a published post with an updated success view [##23128] * [*] Add support for viewing trashed posts and pages and restoring them from the editor [#23142] +* [*] Impove the "Post Settings" screen groups/ordering to better align with Gutenberg [#23164] * [*] Update the "More" menu in the Editor to use modern iOS design and update copy to match Gutenberg [#23145] * [*] Update the "Revisions" list design and fix an issue with the footer displaying incorrect "Date Created" for drafts [#23145] diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m index efcf319e846f..fa9f509a6054 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m @@ -406,12 +406,12 @@ - (void)configureSections NSNumber *stickyPostSection = @(PostSettingsSectionStickyPost); NSNumber *disabledTwitterSection = @(PostSettingsSectionDisabledTwitter); NSNumber *remainingSharesSection = @(PostSettingsSectionSharesRemaining); - NSMutableArray *sections = [@[ @(PostSettingsSectionTaxonomy), - @(PostSettingsSectionMeta), - @(PostSettingsSectionFormat), + NSNumber *shareSection = @(PostSettingsSectionShare); + NSMutableArray *sections = [@[ @(PostSettingsSectionMeta), @(PostSettingsSectionFeaturedImage), + @(PostSettingsSectionTaxonomy), stickyPostSection, - @(PostSettingsSectionShare), + shareSection, disabledTwitterSection, remainingSharesSection, @(PostSettingsSectionMoreOptions) ] mutableCopy]; @@ -426,6 +426,10 @@ - (void)configureSections [sections removeObject:disabledTwitterSection]; } + if ([self numberOfRowsForShareSection] == 0) { + [sections removeObject:shareSection]; + } + if (![self showRemainingShares]) { [sections removeObject:remainingSharesSection]; } @@ -448,8 +452,6 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger return 2; } else if (sec == PostSettingsSectionMeta) { return [self.postMetaSectionRows count]; - } else if (sec == PostSettingsSectionFormat) { - return 1; } else if (sec == PostSettingsSectionFeaturedImage) { return 1; } else if (sec == PostSettingsSectionStickyPost) { @@ -461,7 +463,7 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger } else if (sec == PostSettingsSectionSharesRemaining) { return 1; } else if (sec == PostSettingsSectionMoreOptions) { - return 2; + return 3; } else if (sec == PostSettingsSectionPageAttributes) { return 1; } @@ -478,9 +480,6 @@ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInte } else if (sec == PostSettingsSectionMeta) { return NSLocalizedString(@"Publish", @"Label for the publish (verb) button. Tapping publishes a draft post."); - } else if (sec == PostSettingsSectionFormat) { - return NSLocalizedString(@"Post Format", @"For setting the format of a post."); - } else if (sec == PostSettingsSectionFeaturedImage) { return NSLocalizedString(@"Featured Image", @"Label for the Featured Image area in post settings."); @@ -572,8 +571,6 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell = [self configureTaxonomyCellForIndexPath:indexPath]; } else if (sec == PostSettingsSectionMeta) { cell = [self configureMetaPostMetaCellForIndexPath:indexPath]; - } else if (sec == PostSettingsSectionFormat) { - cell = [self configurePostFormatCellForIndexPath:indexPath]; } else if (sec == PostSettingsSectionFeaturedImage) { cell = [self configureFeaturedImageCellForIndexPath:indexPath]; } else if (sec == PostSettingsSectionStickyPost) { @@ -990,6 +987,8 @@ - (UITableViewCell *)configureShareCellForIndexPath:(NSIndexPath *)indexPath - (UITableViewCell *)configureMoreOptionsCellForIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) { + return [self configurePostFormatCellForIndexPath:indexPath]; + } else if (indexPath.row == 1) { return [self configureSlugCellForIndexPath:indexPath]; } else { return [self configureExcerptCellForIndexPath:indexPath]; diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController_Internal.h b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController_Internal.h index a4e6575b4eec..d6ed3545b9cc 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController_Internal.h +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController_Internal.h @@ -3,10 +3,9 @@ typedef enum { PostSettingsSectionTaxonomy = 0, PostSettingsSectionMeta, - PostSettingsSectionFormat, PostSettingsSectionFeaturedImage, - PostSettingsSectionStickyPost, PostSettingsSectionShare, + PostSettingsSectionStickyPost, PostSettingsSectionDisabledTwitter, // NOTE: Clean up when Twitter has been removed from Publicize services. PostSettingsSectionSharesRemaining, PostSettingsSectionGeolocation, From eeb08194f57c805a82955fa88a8d6b746bb90e3e Mon Sep 17 00:00:00 2001 From: David Christiandy <1299411+dvdchr@users.noreply.github.com> Date: Tue, 7 May 2024 22:27:41 +0700 Subject: [PATCH 096/112] Update WordPressKit dependency to stable version 17.1.1 for 24.8 Note: this is a direct commit to the release branch as part of the beta update process. --- Podfile | 4 ++-- Podfile.lock | 15 +++++---------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Podfile b/Podfile index 2ed7dfd7f328..ba7a23f97908 100644 --- a/Podfile +++ b/Podfile @@ -55,8 +55,8 @@ def gravatar end def wordpress_kit - # pod 'WordPressKit', '~> 17.1.0' - pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '549fbee3059e71bd1471e35090ac942a3b23516a' + pod 'WordPressKit', '~> 17.1.0' + # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', branch: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', tag: '' # pod 'WordPressKit', path: '../WordPressKit-iOS' diff --git a/Podfile.lock b/Podfile.lock index d720a8fee5cb..1054a42eb947 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -71,7 +71,7 @@ PODS: - WordPressKit (~> 17.0) - WordPressShared (~> 2.1-beta) - WordPressUI (~> 1.7-beta) - - WordPressKit (17.1.0): + - WordPressKit (17.1.1): - NSObject-SafeExpectations (~> 0.0.4) - UIDeviceIdentifier (~> 2.0) - WordPressShared (~> 2.0-beta) @@ -121,7 +121,7 @@ DEPENDENCIES: - SwiftLint (= 0.54.0) - WordPress-Editor-iOS (~> 1.19.11) - WordPressAuthenticator (>= 9.0.8, ~> 9.0) - - WordPressKit (from `https://github.com/wordpress-mobile/WordPressKit-iOS.git`, commit `549fbee3059e71bd1471e35090ac942a3b23516a`) + - WordPressKit (~> 17.1.0) - WordPressShared (>= 2.3.1, ~> 2.3) - WordPressUI (~> 1.16) - ZendeskSupportSDK (= 5.3.0) @@ -132,6 +132,7 @@ SPEC REPOS: - WordPress-Aztec-iOS - WordPress-Editor-iOS - WordPressAuthenticator + - WordPressKit - WordPressShared trunk: - Alamofire @@ -177,17 +178,11 @@ EXTERNAL SOURCES: :tag: 0.2.0 Gutenberg: :podspec: https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-v1.118.0.podspec - WordPressKit: - :commit: 549fbee3059e71bd1471e35090ac942a3b23516a - :git: https://github.com/wordpress-mobile/WordPressKit-iOS.git CHECKOUT OPTIONS: FSInteractiveMap: :git: https://github.com/wordpress-mobile/FSInteractiveMap.git :tag: 0.2.0 - WordPressKit: - :commit: 549fbee3059e71bd1471e35090ac942a3b23516a - :git: https://github.com/wordpress-mobile/WordPressKit-iOS.git SPEC CHECKSUMS: Alamofire: 02b772c9910e8eba1a079227c32fbd9e46c90a24 @@ -221,7 +216,7 @@ SPEC CHECKSUMS: WordPress-Aztec-iOS: 3732c6d865a5c9f35788377bdeda8a80ea10d0a1 WordPress-Editor-iOS: 453345420ced3d3ef20f0051b3df46ff10281e0c WordPressAuthenticator: 898acaac75c5ade9b900c02622a15b9aef8fde1a - WordPressKit: 4042625f32513a98fe1bc8552f1aa59ef236bcdc + WordPressKit: a4af3d5d071734aaec75ccecefc7850ece6068fc WordPressShared: 0160364ed24f4d67fed4e85003fefa837faad84f WordPressUI: ec5ebcf7e63e797ba51d07513e340c1b14cf45a4 wpxmlrpc: 68db063041e85d186db21f674adf08d9c70627fd @@ -234,6 +229,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: d170fa8e270b2a32bef9dcdcabff5b8f1a5deced -PODFILE CHECKSUM: e350c05f42484b1312d9cd1044b92551406d5411 +PODFILE CHECKSUM: fe8df3e873baa4219a3e9b59632c5d2c7e3d774e COCOAPODS: 1.15.2 From 04e1640c059aad8231fef227b8a532efa579c209 Mon Sep 17 00:00:00 2001 From: Automattic Release Bot Date: Tue, 7 May 2024 08:46:07 -0700 Subject: [PATCH 097/112] =?UTF-8?q?Update=20app=20translations=20=E2=80=93?= =?UTF-8?q?=20`Localizable.strings`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Resources/ar.lproj/Localizable.strings | 275 +++++++++++++++- .../Resources/de.lproj/Localizable.strings | 28 +- .../Resources/en-CA.lproj/Localizable.strings | 3 - .../Resources/en-GB.lproj/Localizable.strings | 28 +- .../Resources/es.lproj/Localizable.strings | 28 +- .../Resources/fr.lproj/Localizable.strings | 299 ++++++++++++++++- .../Resources/he.lproj/Localizable.strings | 302 ++++++++++++++++- .../Resources/id.lproj/Localizable.strings | 299 ++++++++++++++++- .../Resources/it.lproj/Localizable.strings | 170 +++++++++- .../Resources/ja.lproj/Localizable.strings | 299 ++++++++++++++++- .../Resources/ko.lproj/Localizable.strings | 25 +- .../Resources/nl.lproj/Localizable.strings | 19 +- .../Resources/pt-BR.lproj/Localizable.strings | 3 - .../Resources/ro.lproj/Localizable.strings | 32 +- .../Resources/ru.lproj/Localizable.strings | 296 ++++++++++++++++- .../Resources/sq.lproj/Localizable.strings | 3 - .../Resources/sv.lproj/Localizable.strings | 121 ++++++- .../Resources/tr.lproj/Localizable.strings | 310 +++++++++++++++++- .../zh-Hans.lproj/Localizable.strings | 302 ++++++++++++++++- .../zh-Hant.lproj/Localizable.strings | 302 ++++++++++++++++- 20 files changed, 3053 insertions(+), 91 deletions(-) diff --git a/WordPress/Resources/ar.lproj/Localizable.strings b/WordPress/Resources/ar.lproj/Localizable.strings index 672661919755..dfd420a68af6 100644 --- a/WordPress/Resources/ar.lproj/Localizable.strings +++ b/WordPress/Resources/ar.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-24 12:15:26+0000 */ +/* Translation-Revision-Date: 2024-05-05 16:02:21+0000 */ /* Plural-Forms: nplurals=6; plural=(n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((n % 100 >= 3 && n % 100 <= 10) ? 3 : ((n % 100 >= 11 && n % 100 <= 99) ? 4 : 5)))); */ /* Generator: GlotPress/4.0.1 */ /* Language: ar */ @@ -8853,6 +8853,18 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ /* translators: displays audio file extension. e.g. MP3 audio file */ "audio file" = "ملف صوت"; +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.cancel" = "إلغاء"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.message" = "لقد أجريت تغييرات غير محفوظة على هذه التدوينة من جهاز مختلف. المحرَّر: %@."; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.title" = "الحفظ التلقائي متاح"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.viewChanges" = "عرض التغييرات"; + /* Alert message when something goes wrong with the selected image. */ "avatarMenu.failedToSetAvatarAlertMessage" = "يتعذر تحميل الصورة. يرجى اختيار صورة مختلفة أو المحاولة مرة أخرى لاحقًا."; @@ -10305,6 +10317,27 @@ Example: Reply to Pamela Nguyen */ /* Status mesasge for post cells */ "post.movingToTrashStatusMessage" = "جارٍ نقل التدوينة إلى سلة المهملات..."; +/* Button in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.discardChanges" = "تجاهل التغييرات"; + +/* Button in an alert confirming discaring a new draft */ +"postEditor.closeConfirmationAlert.discardDraft" = "تجاهل المسودة"; + +/* Button to keep the changes in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.keepEditing" = "متابعة التحرير"; + +/* Button in an alert confirming saving a new draft */ +"postEditor.closeConfirmationAlert.saveDraft" = "حفظ المسودة"; + +/* Creating autosave to generate a preview (status message */ +"postEditor.creatingAutosaveForPreview" = "جار إنشاء الحفظ التلقائي..."; + +/* Title for a snackbar */ +"postEditor.revisionLoaded" = "تم تحميل المراجعة"; + +/* Saving draft to generate a preview (status message */ +"postEditor.savingDraftForPreview" = "جار حفظ المسودة..."; + /* Accessibility label for the post author in the post list. The parameter is the author name. For example, \"By Elsa.\" */ "postList.a11y.authorChunkFormat" = "بواسطة %@."; @@ -10317,6 +10350,24 @@ Example: Reply to Pamela Nguyen */ /* Accessibility label for a post in the post list. The first placeholder is the post title. The second placeholder is the date. */ "postList.a11y.titleAndDateChunkFormat" = "%1$@، %2$@."; +/* Badge for post cells */ +"postList.badgePendingReview" = "في انتظار المراجعة"; + +/* Cancels an Action */ +"postList.cancel" = "إلغاء"; + +/* Delete option in the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.actionTitle" = "الحذف نهائيًا"; + +/* Message of the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.alertMessage" = "هل تريد بالتأكيد حذف \"%@s\" نهائيًا؟"; + +/* An error message */ +"postList.errorUnsyncedChangesMessage" = "يرفع التطبيق التغييرات التي سبق إجراؤها على الخادم. يرجى المحاولة مرة أخرى لاحقًا."; + +/* A generic error message title */ +"postList.genericErrorMessage" = "حدث خطأ ما"; + /* Label for a post in the post list. Indicates that the post has offline changes. */ "postList.offlineChanges" = "تغييرات من دون اتصال بالإنترنت"; @@ -10332,6 +10383,39 @@ Example: Reply to Pamela Nguyen */ /* Title for the 'View' post list row swipe action */ "postList.swipeActionView" = "عرض"; +/* Trash option in the trash post or page confirmation alert. */ +"postList.trash.actionTitle" = "نقل إلى سلة المهملات"; + +/* Message of the trash post or page confirmation alert. */ +"postList.trash.alertMessage" = "هل تريد بالتأكيد وضع \"%@\" في سلة المهملات؟ ستُفقد أي تغييرات لم يسبق إرسالها إلى الخادم."; + +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "إلغاء الرفع"; + +/* Close button in postMediaUploadStatusView */ +"postMediaUploadStatusView.close" = "إغلاق"; + +/* Placeholder text in postMediaUploadStatusView when no uploads remain */ +"postMediaUploadStatusView.noPendingUploads" = "لا توجد عمليات رفع معلَّقة"; + +/* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ +"postMediaUploadStatusView.progress" = "%1$@ من %2$@"; + +/* Retry upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUploads" = "إعادة محاولة عمليات الرفع"; + +/* Title for post media upload status view */ +"postMediaUploadStatusView.title" = "عمليات رفع الوسائط"; + +/* A generic error message title */ +"postNotice.errorTitle" = "حدث خطأ ما"; + +/* An error message */ +"postNotice.errorUnsyncedChangesMessage" = "يرفع التطبيق التغييرات التي سبق إجراؤها على الخادم. يرجى المحاولة مرة أخرى لاحقًا."; + +/* Button OK */ +"postNotice.ok" = "موافق"; + /* Title of notification displayed when a page has been successfully saved as a draft. */ "postNotice.pageDraftCreated" = "تم رفع مسودة الصفحة"; @@ -10365,6 +10449,15 @@ Example: Reply to Pamela Nguyen */ /* Button title. Displays a summary / sharing screen for a specific post. */ "postNotice.view" = "عرض"; +/* Error message: content was modified on another device */ +"postSaveErrorMessage.conflict" = "تم تعديل المحتوى على جهاز آخر"; + +/* Error message: item permanently deleted */ +"postSaveErrorMessage.deleted" = "تم حذف \"%@\" بشكل دائم، ويتعذر تحديثه بعد الآن"; + +/* A default value for an post without a title */ +"postSaveErrorMessage.postUntitled" = "من دون عنوان"; + /* User action to dismiss featured media options. */ "postSettings.featuredImageUploadActionSheet.dismiss" = "تجاهل"; @@ -10377,6 +10470,9 @@ Example: Reply to Pamela Nguyen */ /* Title for action sheet with featured media options. */ "postSettings.featuredImageUploadActionSheet.title" = "خيارات الصور المتميزة"; +/* The 'Pending Review' setting of the post */ +"postSettings.pendingReview" = "في انتظار المراجعة"; + /* Section title for the disabled Twitter service in the Post Settings screen */ "postSettings.section.disabledTwitter.header" = "لا تتوافر ميزة المشاركة التلقائية في تويتر بعد الآن"; @@ -10386,6 +10482,36 @@ Example: Reply to Pamela Nguyen */ /* Error message on post/page settings screen */ "postSettings.updateFailedMessage" = "فشل تحديث إعدادات التدوينة"; +/* Details for a 'Private' privacy setting */ +"postVisibility.private.details" = "غير مرئية إلا لمسؤولي المواقع والمحررين"; + +/* Title for a 'Private' privacy setting */ +"postVisibility.private.title" = "خاص"; + +/* Details for a 'Password Protected' privacy setting */ +"postVisibility.protected.details" = "مرئية للجميع لكن يلزم كلمة مرور"; + +/* Title for a 'Password Protected' privacy setting */ +"postVisibility.protected.title" = "محمية بكلمة مرور"; + +/* Details for a 'Public' (default) privacy setting */ +"postVisibility.public.details" = "مرئية للجميع."; + +/* Title for a 'Public' (default) privacy setting */ +"postVisibility.public.title" = "عام"; + +/* Button cancel */ +"postVisibilityPicker.cancel" = "إلغاء"; + +/* Navigation bar title for the Post Visibility picker */ +"postVisibilityPicker.navigationTitle" = "إمكانية الرؤية"; + +/* Password placeholder text */ +"postVisibilityPicker.password" = "كلمة المرور"; + +/* Button save */ +"postVisibilityPicker.save" = "حفظ"; + /* Promote the post with Blaze. */ "posts.blaze.actionTitle" = "الترويج مع الإبراز"; @@ -10410,6 +10536,9 @@ Example: Reply to Pamela Nguyen */ /* Label for the preview post button. Tapping displays the post as it appears on the web. */ "posts.preview.actionTitle" = "معاينة"; +/* Label for the publish post button. */ +"posts.publish.actionTitle" = "نشر"; + /* Retry uploading the post. */ "posts.retry.actionTitle" = "إعادة المحاولة"; @@ -10476,6 +10605,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.jetpackSocial" = "وسائل التواصل الاجتماعي الخاصة بJetpack"; +/* Details for a publish button state in the pre-publishing sheet; count as a parameter */ +"prepublishing.mediaUploadFailedDetails" = "فشل رفع %@ من العناصر"; + /* Placeholder for a cell in the pre-publishing sheet */ "prepublishing.postTitle" = "العنوان"; @@ -10485,6 +10617,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.publishDate" = "تاريخ النشر"; +/* Placeholder value for a publishing date in the prepublishing sheet when the date is not selected */ +"prepublishing.publishDateImmediately" = "فورًا"; + /* Label in the header in the pre-publishing sheet */ "prepublishing.publishingTo" = "نشر إلى"; @@ -10549,6 +10684,12 @@ Tapping on this row allows the user to edit the sharing message. */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.tags" = "الوسوم"; +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaManyItemsRemaining" = "يتبقى %@ من العناصر"; + +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaOneItemRemaining" = "يتبقى %@ من العناصر"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.uploadingMedia" = "رفع الوسائط"; @@ -10558,6 +10699,24 @@ Tapping on this row allows the user to edit the sharing message. */ /* Title for a tappable string that opens the reader with a prompts tag */ "prompts.card.viewprompts.title" = "عرض كل الردود"; +/* Post publish date picker title for date cell */ +"publishDatePicker.date" = "تاريخ النشر"; + +/* Post publish date picker footer view when the selected date time zone is different from your current time zone; followed by the time in the current time zone. */ +"publishDatePicker.footerCurrentTimezone" = "التاريخ في منطقتك الزمنية الحالية:"; + +/* Post publish date picker: selected value placeholder when no date is selected and the post will be published immediately */ +"publishDatePicker.immediately" = "فورًا"; + +/* Title for button in publish date picker */ +"publishDatePicker.removePublishDate" = "إزالة تاريخ النشر"; + +/* Title for button in publish date picker */ +"publishDatePicker.selectPublishDate" = "تحديد تاريخ النشر"; + +/* Post publish time zone cell title */ +"publishDatePicker.timeZone" = "المنطقة الزمنية"; + /* Post publish date picker */ "publishDatePicker.title" = "تاريخ النشر"; @@ -10759,6 +10918,9 @@ with the filter chip button. */ /* Reader settings button accessibility label. */ "reader.navigation.settings.button.label" = "إعدادات القارئ"; +/* No Tags View Button Label */ +"reader.no.blog.title" = "إضافة مدونة"; + /* Title for button on the no followed blogs result screen */ "reader.no.blogs.button" = "اكتشاف المدونات"; @@ -10783,9 +10945,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "الانتقال إلى الاشتراكات"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "إضافة مدونة"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "يتعذر حظر المدونة"; @@ -11031,6 +11190,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title of a feature to add a new tag to the tags subscribed by the user. */ "reader.tags.add.tag.title" = "إضافة وسم"; +/* Text for the 'Like' button on the reader tag cell. */ +"reader.tags.button.like" = "إعجاب"; + +/* Text for the 'Liked' button on the reader tag cell. */ +"reader.tags.button.liked" = "أعجبني"; + /* Button title. Tapping shows the Subscribe to Tags screen. */ "reader.tags.discover.more.tags" = "اكتشاف مزيد من الوسوم"; @@ -11133,6 +11298,18 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for screen that allows configuration of your blog/site related posts settings. */ "relatedPostsSettings.title" = "التدوينات ذات الصلة"; +/* A version of the post on another device. */ +"resolveConflict.anotherDevice" = "جهاز آخر"; + +/* A version of the post on the current device. */ +"resolveConflict.currentDevice" = "الجهاز الحالي"; + +/* Title for the cancel button on the Resolve Conflict screen. */ +"resolveConflict.navigation.cancel" = "إلغاء"; + +/* Title for the save button on the Resolve Conflict screen. */ +"resolveConflict.navigation.save" = "حفظ"; + /* Title for the \"Copy Link\" action in Share Sheet. */ "share.sheet.copy.link.title" = "نسخ الرابط"; @@ -11310,6 +11487,30 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Text for unknown privacy setting */ "siteVisibility.unknown.title" = "غير معروف"; +/* All sites section title for site switcher. */ +"site_switcher.all_sites_section.title" = "جميع المواقع"; + +/* Pinned section title for site switcher. */ +"site_switcher.pinned_section.title" = "المواقع المثبَّتة"; + +/* Recents section title for site switcher. */ +"site_switcher.recents_section.title" = "المواقع الأحدث"; + +/* CTA title for the site switcher screen. */ +"site_switcher_cta_title" = "إضافة موقع"; + +/* Dismiss button title above the search. */ +"site_switcher_dismiss_button_title" = "إلغاء"; + +/* Done button title above the search. */ +"site_switcher_done_button_title" = "تم"; + +/* Edit button title above the search. */ +"site_switcher_edit_button_title" = "تحرير"; + +/* Title for site switcher screen. */ +"site_switcher_title" = "اختيار موقع"; + /* Label for the blogging reminders setting */ "sitesettings.reminders.title" = "التذكيرات"; @@ -11346,6 +11547,9 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A smallprint that hints the reason behind why Twitter is deprecated. */ "social.twitterDeprecation.text" = "لا تتوافر ميزة المشاركة التلقائية على تويتر بعد الآن بسبب التغييرات التي أجرتها تويتر في الشروط والأسعار."; +/* Title of Subscribers stats tab. */ +"stats.dashboard.tab.subscribers" = "المشتركون"; + /* Title of Traffic stats tab. */ "stats.dashboard.tab.traffic" = "حركة المرور"; @@ -11442,18 +11646,45 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Hint displayed on the 'Most Popular Time' stats card when a user's site hasn't yet received enough traffic. */ "stats.insights.mostPopularTime.noData" = "لا يوجد نشاط كافٍ. تحقَّق من هذا مرة أخرى لاحقًا عندما يكون لدى موقعك مزيد من الزائرين!"; +/* Insights 'Subscribers' header */ +"stats.insights.subscribers.title" = "المشتركون"; + /* A hint shown to the user in stats informing the user how many likes one of their posts has received. The %1$@ placeholder will be replaced with the title of a post, the %2$@ with the number of likes. */ "stats.insights.totalLikes.guideText.plural" = "حصلت تدوينتك الأخيرة %1$@ على %2$@ من الإعجابات."; /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "حصلت تدوينتك الأخيرة %1$@ على %2$@ من الإعجابات."; +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "إجمالي المشتركين"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; /* Stats 'Today' header */ "stats.period.todayCard.title" = "اليوم"; +/* Table column title that shows the date since the user became a subscriber. */ +"stats.section.dataSubtitles.subscriberSince" = "مشترك منذ"; + +/* Table column title that shows the names of subscribers. */ +"stats.section.itemSubtitles.subscriber" = "الاسم"; + +/* Stats 'Subscribers' card header, contains chart */ +"stats.subscribers.chart.title" = "المشتركون"; + +/* A title for table's column that shows a number of times a post was opened from an email */ +"stats.subscribers.emailsSummary.column.clicks" = "نقرات"; + +/* Stats 'Emails' card header */ +"stats.subscribers.emailsSummaryCard.title" = "رسائل البريد الإلكتروني"; + +/* Stats 'Subscribers' card header */ +"stats.subscribers.subscribersListCard.title" = "المشتركون"; + +/* Accessibility of stats table. Placeholders will be populated with names of data shown in table. */ +"stats.topTotalsCell.voiceOverDescription" = "جدول يعرض %1$@ و%2$@ و%3$@"; + /* This description is used to set the accessibility label for the Stats Traffic chart, with Comments selected. */ "stats.traffic.accessibilityLabel.comments" = "رسم بياني شريطي يوضّح التعليقات للفترة المحددة."; @@ -11466,6 +11697,18 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* This description is used to set the accessibility label for the Stats Traffic chart, with Visitors selected. */ "stats.traffic.accessibilityLabel.visitors" = "رسم بياني شريطي يوضّح الزائرين للفترة المحددة."; +/* The label for the option to show Stats Traffic chart for Days. */ +"stats.traffic.days" = "الأيام"; + +/* The label for the option to show Stats Traffic chart for Months. */ +"stats.traffic.months" = "أشهر"; + +/* The label for the option to show Stats Traffic chart for Weeks. */ +"stats.traffic.weeks" = "الأسابيع"; + +/* The label for the option to show Stats Traffic chart for Years. */ +"stats.traffic.years" = "سنوات"; + /* Dismiss the AlertView */ "stockPhotos.strings.dismiss" = "تجاهُل"; @@ -11475,9 +11718,24 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for placeholder in Free Photos */ "stockPhotos.title" = "ابحث للعثور على صور مجانية لإضافتها إلى مكتبة الوسائط الخاصة بك!"; +/* Label we show on an email input field */ +"submit.feedback.alert.empty.email" = "لم يتم إدخال أي بريد إلكتروني"; + +/* Label we show on an name input field */ +"submit.feedback.alert.empty.username" = "لم يتم إدخال اسم المستخدم"; + +/* Alert submit option for users to accept sharing their email and name when submitting feedback. */ +"submit.feedback.alert.submit" = "تم"; + +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.title" = "شكرًا لملاحظاتك"; + /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "إرسال"; +/* Notice informing user that their feedback is being submitted. */ +"submit.feedback.submit.loading" = "جار الإرسال"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "ملاحظات"; @@ -11649,6 +11907,15 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Displayed in the confirmation alert when marking unread notifications as read. */ "unread" = "غير مقروء"; +/* Site's Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.subscriber" = "المشترك بالموقع"; + +/* Site Email Subscribers */ +"users.list.title.emailSubscribers" = "مشتركي البريد الإلكتروني"; + +/* Site Subscribers */ +"users.list.title.subscribers" = "المشتركون"; + /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ "visit our documentation page" = "زيارة صفحة الوثائق الخاصة بنا"; diff --git a/WordPress/Resources/de.lproj/Localizable.strings b/WordPress/Resources/de.lproj/Localizable.strings index 5afa7fa0afe2..712e26e118b8 100644 --- a/WordPress/Resources/de.lproj/Localizable.strings +++ b/WordPress/Resources/de.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-30 07:28:34+0000 */ +/* Translation-Revision-Date: 2024-05-02 17:48:29+0000 */ /* Plural-Forms: nplurals=2; plural=n != 1; */ /* Generator: GlotPress/4.0.1 */ /* Language: de */ @@ -10924,6 +10924,9 @@ with the filter chip button. */ /* Reader settings button accessibility label. */ "reader.navigation.settings.button.label" = "Reader-Einstellungen"; +/* No Tags View Button Label */ +"reader.no.blog.title" = "Einen Blog hinzufügen"; + /* Title for button on the no followed blogs result screen */ "reader.no.blogs.button" = "Blogs entdecken"; @@ -10949,7 +10952,7 @@ with the filter chip button. */ "reader.no.results.subscriptions.button" = "Gehe zu Abonnements"; /* No Tags View Button Label */ -"reader.no.tags.title" = "Einen Blog hinzufügen"; +"reader.no.tags.title" = "Ein Schlagwort hinzufügen"; /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Der Blog kann nicht blockiert werden"; @@ -11667,6 +11670,15 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "Dein neuester Beitrag %1$@ hat %2$@ Like erhalten."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "WordPress.com-Abonnenten insgesamt: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "E-Mail-Abonnenten insgesamt: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "Abonnenten insgesamt"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; @@ -11751,10 +11763,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "Übermitteln"; -/* Notice informing user that their feedback is being submitted anonymously. - Notice informing user that their feedback is being submitted. */ +/* Notice informing user that their feedback is being submitted. */ "submit.feedback.submit.loading" = "Wird gesendet"; +/* Notice informing user that their feedback is being submitted anonymously. */ +"submit.feedback.submitAnonymously.loading" = "Wird anonym gesendet"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "Feedback"; @@ -11938,8 +11952,10 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Site's Viewers Profile. Displayed when the name is empty! */ "user.details.title.viewer" = "Besucher der Website"; -/* Site Email Subscribers - Site Subscribers */ +/* Site Email Subscribers */ +"users.list.title.emailSubscribers" = "E-Mail-Abonnenten"; + +/* Site Subscribers */ "users.list.title.subscribers" = "Abonnenten"; /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ diff --git a/WordPress/Resources/en-CA.lproj/Localizable.strings b/WordPress/Resources/en-CA.lproj/Localizable.strings index 6287a0d94fee..c028a12fa635 100644 --- a/WordPress/Resources/en-CA.lproj/Localizable.strings +++ b/WordPress/Resources/en-CA.lproj/Localizable.strings @@ -10681,9 +10681,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "Go to Subscriptions"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "Add a blog"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Unable to block blog"; diff --git a/WordPress/Resources/en-GB.lproj/Localizable.strings b/WordPress/Resources/en-GB.lproj/Localizable.strings index 1293f91c2998..0a6cd649fdc6 100644 --- a/WordPress/Resources/en-GB.lproj/Localizable.strings +++ b/WordPress/Resources/en-GB.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-30 08:19:40+0000 */ +/* Translation-Revision-Date: 2024-05-01 20:22:33+0000 */ /* Plural-Forms: nplurals=2; plural=n != 1; */ /* Generator: GlotPress/4.0.1 */ /* Language: en_GB */ @@ -10924,6 +10924,9 @@ with the filter chip button. */ /* Reader settings button accessibility label. */ "reader.navigation.settings.button.label" = "Reader Settings"; +/* No Tags View Button Label */ +"reader.no.blog.title" = "Add a blog"; + /* Title for button on the no followed blogs result screen */ "reader.no.blogs.button" = "Discover Blogs"; @@ -10949,7 +10952,7 @@ with the filter chip button. */ "reader.no.results.subscriptions.button" = "Go to Subscriptions"; /* No Tags View Button Label */ -"reader.no.tags.title" = "Add a blog"; +"reader.no.tags.title" = "Add a tag"; /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Unable to block blog"; @@ -11667,6 +11670,15 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "Your latest post %1$@ has received %2$@ like."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "Total WordPress.com subscribers: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "Total email subscribers: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "Total Subscribers"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; @@ -11751,10 +11763,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "Submit"; -/* Notice informing user that their feedback is being submitted anonymously. - Notice informing user that their feedback is being submitted. */ +/* Notice informing user that their feedback is being submitted. */ "submit.feedback.submit.loading" = "Sending"; +/* Notice informing user that their feedback is being submitted anonymously. */ +"submit.feedback.submitAnonymously.loading" = "Sending anonymously"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "Feedback"; @@ -11938,8 +11952,10 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Site's Viewers Profile. Displayed when the name is empty! */ "user.details.title.viewer" = "Site's Viewer"; -/* Site Email Subscribers - Site Subscribers */ +/* Site Email Subscribers */ +"users.list.title.emailSubscribers" = "Email Subscribers"; + +/* Site Subscribers */ "users.list.title.subscribers" = "Subscribers"; /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ diff --git a/WordPress/Resources/es.lproj/Localizable.strings b/WordPress/Resources/es.lproj/Localizable.strings index 8027c364ec96..ef92676b4584 100644 --- a/WordPress/Resources/es.lproj/Localizable.strings +++ b/WordPress/Resources/es.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-30 07:03:04+0000 */ +/* Translation-Revision-Date: 2024-05-02 09:00:33+0000 */ /* Plural-Forms: nplurals=2; plural=n != 1; */ /* Generator: GlotPress/4.0.1 */ /* Language: es */ @@ -10924,6 +10924,9 @@ with the filter chip button. */ /* Reader settings button accessibility label. */ "reader.navigation.settings.button.label" = "Configuración del lector"; +/* No Tags View Button Label */ +"reader.no.blog.title" = "Añadir un blog"; + /* Title for button on the no followed blogs result screen */ "reader.no.blogs.button" = "Descubrir blogs"; @@ -10949,7 +10952,7 @@ with the filter chip button. */ "reader.no.results.subscriptions.button" = "Ir a suscripciones"; /* No Tags View Button Label */ -"reader.no.tags.title" = "Añadir un blog"; +"reader.no.tags.title" = "Añadir una etiqueta"; /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "No se puede bloquear el blog"; @@ -11667,6 +11670,15 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "Tu última entrada %1$@ ha recibido %2$@ me gusta."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "Suscriptores totales de WordPress.com: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "Suscriptores totales por correo electrónico: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "Suscriptores totales"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; @@ -11751,10 +11763,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "Enviar"; -/* Notice informing user that their feedback is being submitted anonymously. - Notice informing user that their feedback is being submitted. */ +/* Notice informing user that their feedback is being submitted. */ "submit.feedback.submit.loading" = "Enviando"; +/* Notice informing user that their feedback is being submitted anonymously. */ +"submit.feedback.submitAnonymously.loading" = "Enviando de manera anónima"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "Sugerencias"; @@ -11938,8 +11952,10 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Site's Viewers Profile. Displayed when the name is empty! */ "user.details.title.viewer" = "Visor del sitio"; -/* Site Email Subscribers - Site Subscribers */ +/* Site Email Subscribers */ +"users.list.title.emailSubscribers" = "Suscriptores por correo electrónico"; + +/* Site Subscribers */ "users.list.title.subscribers" = "Suscriptores"; /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ diff --git a/WordPress/Resources/fr.lproj/Localizable.strings b/WordPress/Resources/fr.lproj/Localizable.strings index edc171c60cfc..2a3f172fc806 100644 --- a/WordPress/Resources/fr.lproj/Localizable.strings +++ b/WordPress/Resources/fr.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-17 15:44:22+0000 */ +/* Translation-Revision-Date: 2024-05-02 09:54:07+0000 */ /* Plural-Forms: nplurals=2; plural=n > 1; */ /* Generator: GlotPress/4.0.1 */ /* Language: fr */ @@ -8826,6 +8826,18 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ /* translators: displays audio file extension. e.g. MP3 audio file */ "audio file" = "fichier audio"; +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.cancel" = "Annuler"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.message" = "Vous avez apporté des modifications non enregistrées à cet article depuis un autre appareil. Modifié : %@."; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.title" = "Enregistrement automatique disponible"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.viewChanges" = "Afficher les modifications"; + /* Alert message when something goes wrong with the selected image. */ "avatarMenu.failedToSetAvatarAlertMessage" = "Échec du chargement de l’image. Veuillez en choisir une autre ou réessayer."; @@ -10260,6 +10272,27 @@ Example: Reply to Pamela Nguyen */ /* Status mesasge for post cells */ "post.movingToTrashStatusMessage" = "Déplacement de l‘article dans la corbeille…"; +/* Button in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.discardChanges" = "Abandonner les modifications"; + +/* Button in an alert confirming discaring a new draft */ +"postEditor.closeConfirmationAlert.discardDraft" = "Abandonner le brouillon"; + +/* Button to keep the changes in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.keepEditing" = "Poursuivre l’édition"; + +/* Button in an alert confirming saving a new draft */ +"postEditor.closeConfirmationAlert.saveDraft" = "Enregistrer le brouillon"; + +/* Creating autosave to generate a preview (status message */ +"postEditor.creatingAutosaveForPreview" = "Création de la sauvegarde automatique…"; + +/* Title for a snackbar */ +"postEditor.revisionLoaded" = "Révision chargée"; + +/* Saving draft to generate a preview (status message */ +"postEditor.savingDraftForPreview" = "Enregistrement du brouillon…"; + /* Accessibility label for the post author in the post list. The parameter is the author name. For example, \"By Elsa.\" */ "postList.a11y.authorChunkFormat" = "Par %@."; @@ -10269,6 +10302,24 @@ Example: Reply to Pamela Nguyen */ /* Accessibility label for a sticky post in the post list. */ "postList.a11y.sticky" = "Épinglé."; +/* Badge for post cells */ +"postList.badgePendingReview" = "En attente de relecture"; + +/* Cancels an Action */ +"postList.cancel" = "Annuler"; + +/* Delete option in the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.actionTitle" = "Supprimer définitivement"; + +/* Message of the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.alertMessage" = "Voulez-vous vraiment supprimer définitivement « %@ » ?"; + +/* An error message */ +"postList.errorUnsyncedChangesMessage" = "L’application charge les modifications effectuées précédemment sur le serveur. Veuillez réessayer plus tard."; + +/* A generic error message title */ +"postList.genericErrorMessage" = "Un problème est survenu"; + /* Label for a post in the post list. Indicates that the post has offline changes. */ "postList.offlineChanges" = "Changements hors ligne"; @@ -10281,6 +10332,39 @@ Example: Reply to Pamela Nguyen */ /* Title for the 'View' post list row swipe action */ "postList.swipeActionView" = "Voir"; +/* Trash option in the trash post or page confirmation alert. */ +"postList.trash.actionTitle" = "Envoyer dans la corbeille"; + +/* Message of the trash post or page confirmation alert. */ +"postList.trash.alertMessage" = "Voulez-vous vraiment envoyer « %@ » dans la corbeille ? Les modifications qui n’ont pas été envoyées précédemment sur le serveur seront perdues."; + +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "Annuler le chargement"; + +/* Close button in postMediaUploadStatusView */ +"postMediaUploadStatusView.close" = "Fermer"; + +/* Placeholder text in postMediaUploadStatusView when no uploads remain */ +"postMediaUploadStatusView.noPendingUploads" = "Aucun chargement en attente"; + +/* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ +"postMediaUploadStatusView.progress" = "%1$@ sur %2$@"; + +/* Retry (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUpload" = "Retenter le chargement"; + +/* Retry upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUploads" = "Retenter les chargements"; + +/* Title for post media upload status view */ +"postMediaUploadStatusView.title" = "Chargements de contenu multimédia"; + +/* A generic error message title */ +"postNotice.errorTitle" = "Une erreur s’est produite"; + +/* An error message */ +"postNotice.errorUnsyncedChangesMessage" = "L’application charge les modifications effectuées précédemment sur le serveur. Veuillez réessayer plus tard."; + /* Title of notification displayed when a page has been successfully saved as a draft. */ "postNotice.pageDraftCreated" = "Brouillon de la page mis en ligne"; @@ -10314,6 +10398,15 @@ Example: Reply to Pamela Nguyen */ /* Button title. Displays a summary / sharing screen for a specific post. */ "postNotice.view" = "Voir"; +/* Error message: content was modified on another device */ +"postSaveErrorMessage.conflict" = "Le contenu a été modifié sur un autre appareil"; + +/* Error message: item permanently deleted */ +"postSaveErrorMessage.deleted" = "« %@ » a été supprimé de manière permanente et ne peut plus être mis à jour"; + +/* A default value for an post without a title */ +"postSaveErrorMessage.postUntitled" = "Sans titre"; + /* User action to dismiss featured media options. */ "postSettings.featuredImageUploadActionSheet.dismiss" = "Ignorer"; @@ -10326,6 +10419,9 @@ Example: Reply to Pamela Nguyen */ /* Title for action sheet with featured media options. */ "postSettings.featuredImageUploadActionSheet.title" = "Options de l’image mise en avant"; +/* The 'Pending Review' setting of the post */ +"postSettings.pendingReview" = "En attente de relecture"; + /* Section title for the disabled Twitter service in the Post Settings screen */ "postSettings.section.disabledTwitter.header" = "Le partage automatique sur Twitter n’est plus disponible"; @@ -10335,6 +10431,33 @@ Example: Reply to Pamela Nguyen */ /* Error message on post/page settings screen */ "postSettings.updateFailedMessage" = "Impossible de mettre à jour les réglages de l‘article"; +/* Details for a 'Private' privacy setting */ +"postVisibility.private.details" = "Uniquement visible pour les administrateurs et les éditeurs du site"; + +/* Title for a 'Private' privacy setting */ +"postVisibility.private.title" = "Privé"; + +/* Details for a 'Password Protected' privacy setting */ +"postVisibility.protected.details" = "Visible pour tous mais nécessite un mot de passe"; + +/* Title for a 'Password Protected' privacy setting */ +"postVisibility.protected.title" = "Protégé par un mot de passe"; + +/* Details for a 'Public' (default) privacy setting */ +"postVisibility.public.details" = "Visible pour tout le monde"; + +/* Button cancel */ +"postVisibilityPicker.cancel" = "Annuler"; + +/* Navigation bar title for the Post Visibility picker */ +"postVisibilityPicker.navigationTitle" = "Visibilité"; + +/* Password placeholder text */ +"postVisibilityPicker.password" = "Mot de passe"; + +/* Button save */ +"postVisibilityPicker.save" = "Enregistrer"; + /* Promote the post with Blaze. */ "posts.blaze.actionTitle" = "Promouvoir avec Blaze"; @@ -10359,6 +10482,9 @@ Example: Reply to Pamela Nguyen */ /* Label for the preview post button. Tapping displays the post as it appears on the web. */ "posts.preview.actionTitle" = "Aperçu"; +/* Label for the publish post button. */ +"posts.publish.actionTitle" = "Publier"; + /* Retry uploading the post. */ "posts.retry.actionTitle" = "Réessayer"; @@ -10422,6 +10548,9 @@ Example: Reply to Pamela Nguyen */ /* Voiceover accessibility label informing the user that this button dismiss the current view */ "prepublishing.close" = "Fermer"; +/* Details for a publish button state in the pre-publishing sheet; count as a parameter */ +"prepublishing.mediaUploadFailedDetails" = "Échec du chargement de %@ éléments"; + /* Placeholder for a cell in the pre-publishing sheet */ "prepublishing.postTitle" = "Titre"; @@ -10431,6 +10560,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.publishDate" = "Date de publication"; +/* Placeholder value for a publishing date in the prepublishing sheet when the date is not selected */ +"prepublishing.publishDateImmediately" = "Immédiatement"; + /* Label in the header in the pre-publishing sheet */ "prepublishing.publishingTo" = "Publication sur"; @@ -10495,6 +10627,12 @@ Tapping on this row allows the user to edit the sharing message. */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.tags" = "Étiquettes"; +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaManyItemsRemaining" = "%@ éléments restants"; + +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaOneItemRemaining" = "%@ élément restant"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.uploadingMedia" = "Média en cours de chargement"; @@ -10504,6 +10642,24 @@ Tapping on this row allows the user to edit the sharing message. */ /* Title for a tappable string that opens the reader with a prompts tag */ "prompts.card.viewprompts.title" = "Afficher toutes les réponses"; +/* Post publish date picker title for date cell */ +"publishDatePicker.date" = "Date de publication"; + +/* Post publish date picker footer view when the selected date time zone is different from your current time zone; followed by the time in the current time zone. */ +"publishDatePicker.footerCurrentTimezone" = "La date dans votre fuseau horaire actuel :"; + +/* Post publish date picker: selected value placeholder when no date is selected and the post will be published immediately */ +"publishDatePicker.immediately" = "Immédiatement"; + +/* Title for button in publish date picker */ +"publishDatePicker.removePublishDate" = "Supprimer la date de publication"; + +/* Title for button in publish date picker */ +"publishDatePicker.selectPublishDate" = "Sélectionner la date de publication"; + +/* Post publish time zone cell title */ +"publishDatePicker.timeZone" = "Fuseau horaire"; + /* Post publish date picker */ "publishDatePicker.title" = "Date de publication"; @@ -10729,9 +10885,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "Accéder aux abonnements"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "Ajouter un blog"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Impossible de bloquer le blog"; @@ -10968,6 +11121,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title of a feature to add a new tag to the tags subscribed by the user. */ "reader.tags.add.tag.title" = "Ajouter une étiquette"; +/* Text for the 'Like' button on the reader tag cell. */ +"reader.tags.button.like" = "J’aime"; + +/* Text for the 'Liked' button on the reader tag cell. */ +"reader.tags.button.liked" = "J’aime"; + /* Button title. Tapping shows the Subscribe to Tags screen. */ "reader.tags.discover.more.tags" = "Découvrir d’autres étiquettes"; @@ -11070,6 +11229,27 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for screen that allows configuration of your blog/site related posts settings. */ "relatedPostsSettings.title" = "Articles similaires"; +/* A version of the post on another device. */ +"resolveConflict.anotherDevice" = "Autre appareil"; + +/* A version of the post on the current device. */ +"resolveConflict.currentDevice" = "Appareil actuel"; + +/* Description for the Resolve Conflict screen. */ +"resolveConflict.description" = "L’article a été modifié sur un autre appareil. Veuillez indiquer quelle version de l’article conserver."; + +/* Title for the cancel button on the Resolve Conflict screen. */ +"resolveConflict.navigation.cancel" = "Annuler"; + +/* Title for the save button on the Resolve Conflict screen. */ +"resolveConflict.navigation.save" = "Enregistrer"; + +/* Title for the Resolve Conflict screen. */ +"resolveConflict.navigation.title" = "Résoudre le conflit"; + +/* Title for the \"Copy Link\" action in Share Sheet. */ +"share.sheet.copy.link.title" = "Copier le lien"; + /* User action to dismiss media options. */ "shareExtension.editor.attachmentActions.dismiss" = "Ignorer"; @@ -11226,6 +11406,30 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Text for unknown privacy setting */ "siteVisibility.unknown.title" = "Inconnu"; +/* All sites section title for site switcher. */ +"site_switcher.all_sites_section.title" = "Tous les sites"; + +/* Pinned section title for site switcher. */ +"site_switcher.pinned_section.title" = "Sites épinglés"; + +/* Recents section title for site switcher. */ +"site_switcher.recents_section.title" = "Sites récents"; + +/* CTA title for the site switcher screen. */ +"site_switcher_cta_title" = "Ajouter un site"; + +/* Dismiss button title above the search. */ +"site_switcher_dismiss_button_title" = "Annuler"; + +/* Done button title above the search. */ +"site_switcher_done_button_title" = "Terminé"; + +/* Edit button title above the search. */ +"site_switcher_edit_button_title" = "Modifier"; + +/* Title for site switcher screen. */ +"site_switcher_title" = "Choisir un site"; + /* Label for the blogging reminders setting */ "sitesettings.reminders.title" = "Rappels"; @@ -11262,6 +11466,9 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A smallprint that hints the reason behind why Twitter is deprecated. */ "social.twitterDeprecation.text" = "Le partage automatique sur Twitter n’est plus disponible en raison de modifications des conditions générales et tarifaires de Twitter."; +/* Title of Subscribers stats tab. */ +"stats.dashboard.tab.subscribers" = "Abonnés"; + /* Title of Traffic stats tab. */ "stats.dashboard.tab.traffic" = "Trafic"; @@ -11358,18 +11565,57 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Hint displayed on the 'Most Popular Time' stats card when a user's site hasn't yet received enough traffic. */ "stats.insights.mostPopularTime.noData" = "Pas assez d’activité. Revenez lorsque votre site aura plus de visiteurs !"; +/* Insights 'Subscribers' header */ +"stats.insights.subscribers.title" = "Abonnés"; + /* A hint shown to the user in stats informing the user how many likes one of their posts has received. The %1$@ placeholder will be replaced with the title of a post, the %2$@ with the number of likes. */ "stats.insights.totalLikes.guideText.plural" = "Votre dernier article %1$@ a reçu %2$@ mentions J’aime."; /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "Votre dernier article %1$@ a reçu %2$@ mentions J’aime."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "Total des abonnés WordPress.com : %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "Total des abonnés par e-mail : %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "Total des abonnés"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; /* Stats 'Today' header */ "stats.period.todayCard.title" = "Aujourd’hui"; +/* Table column title that shows the date since the user became a subscriber. */ +"stats.section.dataSubtitles.subscriberSince" = "Abonné depuis"; + +/* Table column title that shows the names of subscribers. */ +"stats.section.itemSubtitles.subscriber" = "Nom"; + +/* Stats 'Subscribers' card header, contains chart */ +"stats.subscribers.chart.title" = "Abonnés"; + +/* A title for table's column that shows a number of times a post was opened from an email */ +"stats.subscribers.emailsSummary.column.clicks" = "Clics"; + +/* A title for table's column that shows a number of email openings */ +"stats.subscribers.emailsSummary.column.opens" = "Ouvertures"; + +/* A title for table's column that shows a name of an email */ +"stats.subscribers.emailsSummary.column.title" = "Derniers e-mails"; + +/* Stats 'Emails' card header */ +"stats.subscribers.emailsSummaryCard.title" = "E-mails"; + +/* Stats 'Subscribers' card header */ +"stats.subscribers.subscribersListCard.title" = "Abonnés"; + +/* Accessibility of stats table. Placeholders will be populated with names of data shown in table. */ +"stats.topTotalsCell.voiceOverDescription" = "Le tableau affiche %1$@, %2$@ et %3$@"; + /* This description is used to set the accessibility label for the Stats Traffic chart, with Comments selected. */ "stats.traffic.accessibilityLabel.comments" = "Graphique à barres représentant les commentaires pour la période sélectionnée."; @@ -11382,6 +11628,18 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* This description is used to set the accessibility label for the Stats Traffic chart, with Visitors selected. */ "stats.traffic.accessibilityLabel.visitors" = "Graphique à barres représentant les visiteurs pour la période sélectionnée."; +/* The label for the option to show Stats Traffic chart for Days. */ +"stats.traffic.days" = "Jours"; + +/* The label for the option to show Stats Traffic chart for Months. */ +"stats.traffic.months" = "Mois"; + +/* The label for the option to show Stats Traffic chart for Weeks. */ +"stats.traffic.weeks" = "Semaines"; + +/* The label for the option to show Stats Traffic chart for Years. */ +"stats.traffic.years" = "Années"; + /* Dismiss the AlertView */ "stockPhotos.strings.dismiss" = "Ignorer"; @@ -11391,9 +11649,27 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for placeholder in Free Photos */ "stockPhotos.title" = "Recherchez des photos gratuites pour les ajouter à votre bibliothèque de médias !"; +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.description" = "Vous pouvez inclure votre adresse e-mail et votre identifiant pour nous aider à comprendre votre expérience (facultatif)."; + +/* Label we show on an email input field */ +"submit.feedback.alert.empty.email" = "aucune adresse e-mail saisie"; + +/* Label we show on an name input field */ +"submit.feedback.alert.empty.username" = "aucun identifiant saisi"; + +/* Alert submit option for users to accept sharing their email and name when submitting feedback. */ +"submit.feedback.alert.submit" = "Terminé"; + +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.title" = "Merci pour vos retours"; + /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "Envoyer"; +/* Notice informing user that their feedback is being submitted. */ +"submit.feedback.submit.loading" = "Envoi en cours"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "Commentaire"; @@ -11565,6 +11841,21 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Displayed in the confirmation alert when marking unread notifications as read. */ "unread" = "non lues"; +/* Site's Email Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.emailSubscriber" = "Abonné par e-mail du site"; + +/* Site's Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.subscriber" = "Abonné du site"; + +/* Sites's User Profile. Displayed when the name is empty! */ +"user.details.title.user" = "Utilisateur du site"; + +/* Site's Viewers Profile. Displayed when the name is empty! */ +"user.details.title.viewer" = "Lecteur du site"; + +/* Site Subscribers */ +"users.list.title.subscribers" = "Abonnés"; + /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ "visit our documentation page" = "visiter notre page de documentation"; diff --git a/WordPress/Resources/he.lproj/Localizable.strings b/WordPress/Resources/he.lproj/Localizable.strings index 4cb8ebfaeef1..7dac13101a0e 100644 --- a/WordPress/Resources/he.lproj/Localizable.strings +++ b/WordPress/Resources/he.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-24 14:54:09+0000 */ +/* Translation-Revision-Date: 2024-05-02 15:54:08+0000 */ /* Plural-Forms: nplurals=2; plural=n != 1; */ /* Generator: GlotPress/4.0.1 */ /* Language: he_IL */ @@ -8850,6 +8850,18 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ /* translators: displays audio file extension. e.g. MP3 audio file */ "audio file" = "קובץ אודיו"; +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.cancel" = "ביטול"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.message" = "ביצעת שינויים שלא נשמרו בפוסט הזה ממכשיר שונה. נערך: %@."; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.title" = "שמירה אוטומטית זמינה"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.viewChanges" = "להציג את השינויים"; + /* Alert message when something goes wrong with the selected image. */ "avatarMenu.failedToSetAvatarAlertMessage" = "לא ניתן לטעון את התמונה. יש לספק תמונה אחרת או לנסות שוב מאוחר יותר."; @@ -10299,6 +10311,27 @@ Example: Reply to Pamela Nguyen */ /* Status mesasge for post cells */ "post.movingToTrashStatusMessage" = "מעביר לפח..."; +/* Button in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.discardChanges" = "לבטל את השינויים"; + +/* Button in an alert confirming discaring a new draft */ +"postEditor.closeConfirmationAlert.discardDraft" = "לבטל את הטיוטה"; + +/* Button to keep the changes in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.keepEditing" = "להמשיך לערוך"; + +/* Button in an alert confirming saving a new draft */ +"postEditor.closeConfirmationAlert.saveDraft" = "לשמור טיוטה"; + +/* Creating autosave to generate a preview (status message */ +"postEditor.creatingAutosaveForPreview" = "יוצר עותק לשמירה אוטומטית..."; + +/* Title for a snackbar */ +"postEditor.revisionLoaded" = "הגרסה העדכנית הוטענה"; + +/* Saving draft to generate a preview (status message */ +"postEditor.savingDraftForPreview" = "שומר טיוטה..."; + /* Accessibility label for the post author in the post list. The parameter is the author name. For example, \"By Elsa.\" */ "postList.a11y.authorChunkFormat" = "מאת %@."; @@ -10311,6 +10344,24 @@ Example: Reply to Pamela Nguyen */ /* Accessibility label for a post in the post list. The first placeholder is the post title. The second placeholder is the date. */ "postList.a11y.titleAndDateChunkFormat" = "%1$@, %2$@."; +/* Badge for post cells */ +"postList.badgePendingReview" = "בהמתנה לבדיקה"; + +/* Cancels an Action */ +"postList.cancel" = "ביטול"; + +/* Delete option in the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.actionTitle" = "למחוק לצמיתות"; + +/* Message of the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.alertMessage" = "האם ברצונך למחוק לצמיתות את \"%@\"?"; + +/* An error message */ +"postList.errorUnsyncedChangesMessage" = "האפליקציה מעלה שינויים שבוצעו בעבר בשרת. יש לנסות שוב מאוחר יותר."; + +/* A generic error message title */ +"postList.genericErrorMessage" = "משהו השתבש"; + /* Label for a post in the post list. Indicates that the post has offline changes. */ "postList.offlineChanges" = "שינויים שבוצעו באופן לא מקוון"; @@ -10326,6 +10377,42 @@ Example: Reply to Pamela Nguyen */ /* Title for the 'View' post list row swipe action */ "postList.swipeActionView" = "להציג"; +/* Trash option in the trash post or page confirmation alert. */ +"postList.trash.actionTitle" = "להעביר לפח"; + +/* Message of the trash post or page confirmation alert. */ +"postList.trash.alertMessage" = "האם ברצונך להעביר את \"%@\" לפח? אם שינויים לא נשמרו בעבר בשרת, הם יאבדו."; + +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "לבטל את ההעלאה"; + +/* Close button in postMediaUploadStatusView */ +"postMediaUploadStatusView.close" = "לסגור"; + +/* Placeholder text in postMediaUploadStatusView when no uploads remain */ +"postMediaUploadStatusView.noPendingUploads" = "אין העלאות בהמתנה"; + +/* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ +"postMediaUploadStatusView.progress" = "%1$@ מתוך %2$@"; + +/* Retry (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUpload" = "לנסות להעלות שוב"; + +/* Retry upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUploads" = "לנסות להעלות שוב"; + +/* Title for post media upload status view */ +"postMediaUploadStatusView.title" = "העלאות מדיה"; + +/* A generic error message title */ +"postNotice.errorTitle" = "אירעה שגיאה"; + +/* An error message */ +"postNotice.errorUnsyncedChangesMessage" = "האפליקציה מעלה שינויים שבוצעו בעבר בשרת. יש לנסות שוב מאוחר יותר."; + +/* Button OK */ +"postNotice.ok" = "אישור"; + /* Title of notification displayed when a page has been successfully saved as a draft. */ "postNotice.pageDraftCreated" = "הטיוטה של העמוד הועלתה"; @@ -10359,6 +10446,15 @@ Example: Reply to Pamela Nguyen */ /* Button title. Displays a summary / sharing screen for a specific post. */ "postNotice.view" = "להציג"; +/* Error message: content was modified on another device */ +"postSaveErrorMessage.conflict" = "בוצעו בתוכן שינויים ממכשיר אחר"; + +/* Error message: item permanently deleted */ +"postSaveErrorMessage.deleted" = "הפריט \"%@\" נמחק לצמיתות ולא ניתן לעדכן אותו עוד"; + +/* A default value for an post without a title */ +"postSaveErrorMessage.postUntitled" = "ללא כותרת"; + /* User action to dismiss featured media options. */ "postSettings.featuredImageUploadActionSheet.dismiss" = "ביטול"; @@ -10371,6 +10467,9 @@ Example: Reply to Pamela Nguyen */ /* Title for action sheet with featured media options. */ "postSettings.featuredImageUploadActionSheet.title" = "אפשרויות לתמונה המרכזית"; +/* The 'Pending Review' setting of the post */ +"postSettings.pendingReview" = "בהמתנה לבדיקה"; + /* Section title for the disabled Twitter service in the Post Settings screen */ "postSettings.section.disabledTwitter.header" = "האפשרות לשיתוף אוטומטי בטוויטר כבר לא זמינה"; @@ -10380,6 +10479,36 @@ Example: Reply to Pamela Nguyen */ /* Error message on post/page settings screen */ "postSettings.updateFailedMessage" = "העדכון של הגדרות ההודעה נכשל."; +/* Details for a 'Private' privacy setting */ +"postVisibility.private.details" = "זמין רק למנהלי האתר ולעורכים"; + +/* Title for a 'Private' privacy setting */ +"postVisibility.private.title" = "פרטי"; + +/* Details for a 'Password Protected' privacy setting */ +"postVisibility.protected.details" = "זמין לכולם אבל נדרשת סיסמה"; + +/* Title for a 'Password Protected' privacy setting */ +"postVisibility.protected.title" = "מוגן בסיסמה"; + +/* Details for a 'Public' (default) privacy setting */ +"postVisibility.public.details" = "זמין לכולם"; + +/* Title for a 'Public' (default) privacy setting */ +"postVisibility.public.title" = "ציבורי"; + +/* Button cancel */ +"postVisibilityPicker.cancel" = "ביטול"; + +/* Navigation bar title for the Post Visibility picker */ +"postVisibilityPicker.navigationTitle" = "נראות"; + +/* Password placeholder text */ +"postVisibilityPicker.password" = "סיסמה"; + +/* Button save */ +"postVisibilityPicker.save" = "לשמור"; + /* Promote the post with Blaze. */ "posts.blaze.actionTitle" = "לקדם עם Blaze"; @@ -10404,6 +10533,9 @@ Example: Reply to Pamela Nguyen */ /* Label for the preview post button. Tapping displays the post as it appears on the web. */ "posts.preview.actionTitle" = "תצוגה מקדימה"; +/* Label for the publish post button. */ +"posts.publish.actionTitle" = "לפרסם"; + /* Retry uploading the post. */ "posts.retry.actionTitle" = "לנסות שוב"; @@ -10470,6 +10602,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.jetpackSocial" = "Jetpack Social"; +/* Details for a publish button state in the pre-publishing sheet; count as a parameter */ +"prepublishing.mediaUploadFailedDetails" = "ההעלאה של %@ פריטים נכשלה"; + /* Placeholder for a cell in the pre-publishing sheet */ "prepublishing.postTitle" = "כותרת"; @@ -10479,6 +10614,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.publishDate" = "תאריך פרסום"; +/* Placeholder value for a publishing date in the prepublishing sheet when the date is not selected */ +"prepublishing.publishDateImmediately" = "מייד"; + /* Label in the header in the pre-publishing sheet */ "prepublishing.publishingTo" = "מפרסם בעמוד"; @@ -10543,6 +10681,12 @@ Tapping on this row allows the user to edit the sharing message. */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.tags" = "תגיות"; +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaManyItemsRemaining" = "נותרו %@ פריטים"; + +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaOneItemRemaining" = "נותר פריט %@"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.uploadingMedia" = "מעלה מדיה"; @@ -10552,6 +10696,24 @@ Tapping on this row allows the user to edit the sharing message. */ /* Title for a tappable string that opens the reader with a prompts tag */ "prompts.card.viewprompts.title" = "להציג את כל התשובות"; +/* Post publish date picker title for date cell */ +"publishDatePicker.date" = "תאריך פרסום"; + +/* Post publish date picker footer view when the selected date time zone is different from your current time zone; followed by the time in the current time zone. */ +"publishDatePicker.footerCurrentTimezone" = "התאריך באזור הזמן הנוכחי שלך:"; + +/* Post publish date picker: selected value placeholder when no date is selected and the post will be published immediately */ +"publishDatePicker.immediately" = "מייד"; + +/* Title for button in publish date picker */ +"publishDatePicker.removePublishDate" = "להסיר את 'תאריך פרסום'"; + +/* Title for button in publish date picker */ +"publishDatePicker.selectPublishDate" = "לבחור 'תאריך פרסום'"; + +/* Post publish time zone cell title */ +"publishDatePicker.timeZone" = "אזור זמן"; + /* Post publish date picker */ "publishDatePicker.title" = "תאריך פרסום"; @@ -10777,9 +10939,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "לעבור למינויים"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "להוסיף בלוג"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "אין אפשרות לחסום את הבלוג"; @@ -11019,6 +11178,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title of a feature to add a new tag to the tags subscribed by the user. */ "reader.tags.add.tag.title" = "להוסיף תגית"; +/* Text for the 'Like' button on the reader tag cell. */ +"reader.tags.button.like" = "לייק"; + +/* Text for the 'Liked' button on the reader tag cell. */ +"reader.tags.button.liked" = "קיבל לייק"; + /* Button title. Tapping shows the Subscribe to Tags screen. */ "reader.tags.discover.more.tags" = "לגלות תגיות נוספות"; @@ -11121,6 +11286,24 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for screen that allows configuration of your blog/site related posts settings. */ "relatedPostsSettings.title" = "פוסטים קשורים"; +/* A version of the post on another device. */ +"resolveConflict.anotherDevice" = "מכשיר אחר"; + +/* A version of the post on the current device. */ +"resolveConflict.currentDevice" = "מכשיר נוכחי"; + +/* Description for the Resolve Conflict screen. */ +"resolveConflict.description" = "בוצעו בפוסט שינויים ממכשיר אחר. יש לבחור את הגרסה של הפוסט שברצונך לשמור."; + +/* Title for the cancel button on the Resolve Conflict screen. */ +"resolveConflict.navigation.cancel" = "ביטול"; + +/* Title for the save button on the Resolve Conflict screen. */ +"resolveConflict.navigation.save" = "לשמור"; + +/* Title for the Resolve Conflict screen. */ +"resolveConflict.navigation.title" = "לפתור התנגשות"; + /* Title for the \"Copy Link\" action in Share Sheet. */ "share.sheet.copy.link.title" = "להעתיק את הקישור"; @@ -11298,6 +11481,30 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Text for unknown privacy setting */ "siteVisibility.unknown.title" = "לא ידוע"; +/* All sites section title for site switcher. */ +"site_switcher.all_sites_section.title" = "כל האתרים"; + +/* Pinned section title for site switcher. */ +"site_switcher.pinned_section.title" = "אתרים נעוצים"; + +/* Recents section title for site switcher. */ +"site_switcher.recents_section.title" = "אתרים מהתקופה האחרונה"; + +/* CTA title for the site switcher screen. */ +"site_switcher_cta_title" = "להוסיף אתר"; + +/* Dismiss button title above the search. */ +"site_switcher_dismiss_button_title" = "ביטול"; + +/* Done button title above the search. */ +"site_switcher_done_button_title" = "בוצע"; + +/* Edit button title above the search. */ +"site_switcher_edit_button_title" = "לערוך"; + +/* Title for site switcher screen. */ +"site_switcher_title" = "לבחור אתר"; + /* Label for the blogging reminders setting */ "sitesettings.reminders.title" = "תזכורות"; @@ -11334,6 +11541,9 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A smallprint that hints the reason behind why Twitter is deprecated. */ "social.twitterDeprecation.text" = "האפשרות לשיתוף אוטומטי בטוויטר כבר לא זמינה עקב השינויים בתנאים ובתמחור של טוויטר."; +/* Title of Subscribers stats tab. */ +"stats.dashboard.tab.subscribers" = "מנויים"; + /* Title of Traffic stats tab. */ "stats.dashboard.tab.traffic" = "תעבורה"; @@ -11430,18 +11640,57 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Hint displayed on the 'Most Popular Time' stats card when a user's site hasn't yet received enough traffic. */ "stats.insights.mostPopularTime.noData" = "אין מספיק פעילות. כדאי לחזור לכאן מאוחד יותר לאחר שעוד מבקרים נכנסו לאתר!"; +/* Insights 'Subscribers' header */ +"stats.insights.subscribers.title" = "מנויים"; + /* A hint shown to the user in stats informing the user how many likes one of their posts has received. The %1$@ placeholder will be replaced with the title of a post, the %2$@ with the number of likes. */ "stats.insights.totalLikes.guideText.plural" = "הפוסט האחרון שלך %1$@ קיבל %2$@ לייקים."; /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "הפוסט האחרון שלך %1$@ קיבל לייק %2$@."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "סך כל המנויים ב-WordPress.com: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "סך כל המנויים באימייל: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "סך כל המנויים"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; /* Stats 'Today' header */ "stats.period.todayCard.title" = "היום"; +/* Table column title that shows the date since the user became a subscriber. */ +"stats.section.dataSubtitles.subscriberSince" = "מנוי מאז"; + +/* Table column title that shows the names of subscribers. */ +"stats.section.itemSubtitles.subscriber" = "שם"; + +/* Stats 'Subscribers' card header, contains chart */ +"stats.subscribers.chart.title" = "מנויים"; + +/* A title for table's column that shows a number of times a post was opened from an email */ +"stats.subscribers.emailsSummary.column.clicks" = "קליקים"; + +/* A title for table's column that shows a number of email openings */ +"stats.subscribers.emailsSummary.column.opens" = "לחיצות לפתיחה"; + +/* A title for table's column that shows a name of an email */ +"stats.subscribers.emailsSummary.column.title" = "האימיילים האחרונים"; + +/* Stats 'Emails' card header */ +"stats.subscribers.emailsSummaryCard.title" = "הודעות אימייל"; + +/* Stats 'Subscribers' card header */ +"stats.subscribers.subscribersListCard.title" = "מנויים"; + +/* Accessibility of stats table. Placeholders will be populated with names of data shown in table. */ +"stats.topTotalsCell.voiceOverDescription" = "הטבלה מציגה את %1$@,‏ %2$@ ואת %3$@"; + /* This description is used to set the accessibility label for the Stats Traffic chart, with Comments selected. */ "stats.traffic.accessibilityLabel.comments" = "תרשים עמודות שמשקף את כמות התגובות בתקופה שבחרת."; @@ -11454,6 +11703,18 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* This description is used to set the accessibility label for the Stats Traffic chart, with Visitors selected. */ "stats.traffic.accessibilityLabel.visitors" = "תרשים עמודות שמשקף את כמות המבקרים בתקופה שבחרת."; +/* The label for the option to show Stats Traffic chart for Days. */ +"stats.traffic.days" = "ימים"; + +/* The label for the option to show Stats Traffic chart for Months. */ +"stats.traffic.months" = "חודשים"; + +/* The label for the option to show Stats Traffic chart for Weeks. */ +"stats.traffic.weeks" = "שבועות"; + +/* The label for the option to show Stats Traffic chart for Years. */ +"stats.traffic.years" = "שנים"; + /* Dismiss the AlertView */ "stockPhotos.strings.dismiss" = "ביטול"; @@ -11463,9 +11724,27 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for placeholder in Free Photos */ "stockPhotos.title" = "אפשר לחפש תמונות חינמיות ולהוסיף אותן לספריית המדיה שלך!"; +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.description" = "יש לך אפשרות לבחור אם להוסיף את האימייל ואת שם המשתמש כדי לעזור לנו להבין את החוויה שלך."; + +/* Label we show on an email input field */ +"submit.feedback.alert.empty.email" = "לא הזנת כתובת אימייל"; + +/* Label we show on an name input field */ +"submit.feedback.alert.empty.username" = "לא הזנת שם משתמש"; + +/* Alert submit option for users to accept sharing their email and name when submitting feedback. */ +"submit.feedback.alert.submit" = "בוצע"; + +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.title" = "תודה על המשוב"; + /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "לשלוח"; +/* Notice informing user that their feedback is being submitted. */ +"submit.feedback.submit.loading" = "שולח"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "משוב"; @@ -11637,6 +11916,21 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Displayed in the confirmation alert when marking unread notifications as read. */ "unread" = "לא נקרא"; +/* Site's Email Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.emailSubscriber" = "מנוי של האתר באימייל"; + +/* Site's Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.subscriber" = "מנוי של האתר"; + +/* Sites's User Profile. Displayed when the name is empty! */ +"user.details.title.user" = "משתמש של האתר"; + +/* Site's Viewers Profile. Displayed when the name is empty! */ +"user.details.title.viewer" = "צופה של האתר"; + +/* Site Subscribers */ +"users.list.title.subscribers" = "מנויים"; + /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ "visit our documentation page" = "היכנס לעמוד המסמכים שלנו"; diff --git a/WordPress/Resources/id.lproj/Localizable.strings b/WordPress/Resources/id.lproj/Localizable.strings index 3bce1b53c5d4..500321fb1379 100644 --- a/WordPress/Resources/id.lproj/Localizable.strings +++ b/WordPress/Resources/id.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-23 09:54:09+0000 */ +/* Translation-Revision-Date: 2024-05-02 09:54:08+0000 */ /* Plural-Forms: nplurals=2; plural=n > 1; */ /* Generator: GlotPress/4.0.1 */ /* Language: id */ @@ -8853,6 +8853,18 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ /* translators: displays audio file extension. e.g. MP3 audio file */ "audio file" = "file audio"; +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.cancel" = "Batal"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.message" = "Anda telah membuat perubahan yang tidak tersimpan pada pos ini dari perangkat lain. Diedit: %@."; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.title" = "Simpan Otomatis Tersedia"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.viewChanges" = "Lihat Perubahan"; + /* Alert message when something goes wrong with the selected image. */ "avatarMenu.failedToSetAvatarAlertMessage" = "Tidak dapat memuat gambar. Pilih yang lainnya atau coba lagi nanti."; @@ -10305,6 +10317,27 @@ Example: Reply to Pamela Nguyen */ /* Status mesasge for post cells */ "post.movingToTrashStatusMessage" = "Memindahkan ke tempat sampah..."; +/* Button in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.discardChanges" = "Buang Perubahan"; + +/* Button in an alert confirming discaring a new draft */ +"postEditor.closeConfirmationAlert.discardDraft" = "Buang Draf"; + +/* Button to keep the changes in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.keepEditing" = "Lanjutkan Mengedit"; + +/* Button in an alert confirming saving a new draft */ +"postEditor.closeConfirmationAlert.saveDraft" = "Simpan Draf"; + +/* Creating autosave to generate a preview (status message */ +"postEditor.creatingAutosaveForPreview" = "Membuat draf tersimpan otomatis..."; + +/* Title for a snackbar */ +"postEditor.revisionLoaded" = "Revisi dimuat"; + +/* Saving draft to generate a preview (status message */ +"postEditor.savingDraftForPreview" = "Menyimpan draf..."; + /* Accessibility label for the post author in the post list. The parameter is the author name. For example, \"By Elsa.\" */ "postList.a11y.authorChunkFormat" = "Oleh %@"; @@ -10317,6 +10350,24 @@ Example: Reply to Pamela Nguyen */ /* Accessibility label for a post in the post list. The first placeholder is the post title. The second placeholder is the date. */ "postList.a11y.titleAndDateChunkFormat" = "%1$@, %2$@."; +/* Badge for post cells */ +"postList.badgePendingReview" = "Menunggu peninjauan"; + +/* Cancels an Action */ +"postList.cancel" = "Batal"; + +/* Delete option in the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.actionTitle" = "Hapus Secara Permanen"; + +/* Message of the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.alertMessage" = "Anda yakin ingin menghapus \"%@\" secara permanen?"; + +/* An error message */ +"postList.errorUnsyncedChangesMessage" = "Aplikasi sedang mengunggah perubahan yang dibuat sebelumnya ke server. Silakan coba lagi nanti."; + +/* A generic error message title */ +"postList.genericErrorMessage" = "Terjadi kesalahan"; + /* Label for a post in the post list. Indicates that the post has offline changes. */ "postList.offlineChanges" = "Perubahan offline"; @@ -10332,6 +10383,42 @@ Example: Reply to Pamela Nguyen */ /* Title for the 'View' post list row swipe action */ "postList.swipeActionView" = "Lihat"; +/* Trash option in the trash post or page confirmation alert. */ +"postList.trash.actionTitle" = "Pindahkan ke Tempat Sampah"; + +/* Message of the trash post or page confirmation alert. */ +"postList.trash.alertMessage" = "Anda yakin ingin memindahkan \"%@\" ke tempat sampah? Perubahan yang tidak dikirimkan ke server akan hilang."; + +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "Batalkan Unggahan"; + +/* Close button in postMediaUploadStatusView */ +"postMediaUploadStatusView.close" = "Tutup"; + +/* Placeholder text in postMediaUploadStatusView when no uploads remain */ +"postMediaUploadStatusView.noPendingUploads" = "Tidak ada unggahan tertunda"; + +/* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ +"postMediaUploadStatusView.progress" = "%1$@ dari %2$@"; + +/* Retry (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUpload" = "Coba Unggah Lagi"; + +/* Retry upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUploads" = "Coba Unggah Lagi"; + +/* Title for post media upload status view */ +"postMediaUploadStatusView.title" = "Unggahan Media"; + +/* A generic error message title */ +"postNotice.errorTitle" = "Terjadi error"; + +/* An error message */ +"postNotice.errorUnsyncedChangesMessage" = "Aplikasi sedang mengunggah perubahan yang dibuat sebelumnya ke server. Silakan coba lagi nanti."; + +/* Button OK */ +"postNotice.ok" = "OKE"; + /* Title of notification displayed when a page has been successfully saved as a draft. */ "postNotice.pageDraftCreated" = "Konsep halaman diunggah"; @@ -10365,6 +10452,15 @@ Example: Reply to Pamela Nguyen */ /* Button title. Displays a summary / sharing screen for a specific post. */ "postNotice.view" = "Lihat"; +/* Error message: content was modified on another device */ +"postSaveErrorMessage.conflict" = "Konten diubah di perangkat lain"; + +/* Error message: item permanently deleted */ +"postSaveErrorMessage.deleted" = "\"%@\" dihapus permanen dan tidak dapat diperbarui lagi"; + +/* A default value for an post without a title */ +"postSaveErrorMessage.postUntitled" = "Tanpa Judul"; + /* User action to dismiss featured media options. */ "postSettings.featuredImageUploadActionSheet.dismiss" = "Tutup"; @@ -10377,6 +10473,9 @@ Example: Reply to Pamela Nguyen */ /* Title for action sheet with featured media options. */ "postSettings.featuredImageUploadActionSheet.title" = "Pilihan Gambar Andalan"; +/* The 'Pending Review' setting of the post */ +"postSettings.pendingReview" = "Menunggu peninjauan"; + /* Section title for the disabled Twitter service in the Post Settings screen */ "postSettings.section.disabledTwitter.header" = "Fitur Berbagi Otomatis ke Twitter Tidak Lagi Tersedia"; @@ -10386,6 +10485,36 @@ Example: Reply to Pamela Nguyen */ /* Error message on post/page settings screen */ "postSettings.updateFailedMessage" = "Gagal memperbarui pengaturan pos"; +/* Details for a 'Private' privacy setting */ +"postVisibility.private.details" = "Hanya dapat dilihat oleh admin dan editor situs."; + +/* Title for a 'Private' privacy setting */ +"postVisibility.private.title" = "Privat"; + +/* Details for a 'Password Protected' privacy setting */ +"postVisibility.protected.details" = "Dapat dilihat semua orang dengan memasukkan kata sandi."; + +/* Title for a 'Password Protected' privacy setting */ +"postVisibility.protected.title" = "Dilindungi kata sandi"; + +/* Details for a 'Public' (default) privacy setting */ +"postVisibility.public.details" = "Dapat dilihat oleh semua orang."; + +/* Title for a 'Public' (default) privacy setting */ +"postVisibility.public.title" = "Publik"; + +/* Button cancel */ +"postVisibilityPicker.cancel" = "Batal"; + +/* Navigation bar title for the Post Visibility picker */ +"postVisibilityPicker.navigationTitle" = "Visibilitas"; + +/* Password placeholder text */ +"postVisibilityPicker.password" = "Kata sandi"; + +/* Button save */ +"postVisibilityPicker.save" = "Simpan"; + /* Promote the post with Blaze. */ "posts.blaze.actionTitle" = "Promosikan dengan Blaze"; @@ -10410,6 +10539,9 @@ Example: Reply to Pamela Nguyen */ /* Label for the preview post button. Tapping displays the post as it appears on the web. */ "posts.preview.actionTitle" = "Pratinjau"; +/* Label for the publish post button. */ +"posts.publish.actionTitle" = "Publikasikan"; + /* Retry uploading the post. */ "posts.retry.actionTitle" = "Coba ulang"; @@ -10476,6 +10608,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.jetpackSocial" = "Jetpack Social"; +/* Details for a publish button state in the pre-publishing sheet; count as a parameter */ +"prepublishing.mediaUploadFailedDetails" = "%@ item gagal diunggah"; + /* Placeholder for a cell in the pre-publishing sheet */ "prepublishing.postTitle" = "Judul"; @@ -10485,6 +10620,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.publishDate" = "Tanggal Terbit"; +/* Placeholder value for a publishing date in the prepublishing sheet when the date is not selected */ +"prepublishing.publishDateImmediately" = "Segera"; + /* Label in the header in the pre-publishing sheet */ "prepublishing.publishingTo" = "Memublikasikan ke"; @@ -10549,6 +10687,12 @@ Tapping on this row allows the user to edit the sharing message. */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.tags" = "Tag"; +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaManyItemsRemaining" = "%@ item tersisa"; + +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaOneItemRemaining" = "%@ item tersisa"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.uploadingMedia" = "Mengunggah media"; @@ -10558,6 +10702,24 @@ Tapping on this row allows the user to edit the sharing message. */ /* Title for a tappable string that opens the reader with a prompts tag */ "prompts.card.viewprompts.title" = "Lihat semua tanggapan"; +/* Post publish date picker title for date cell */ +"publishDatePicker.date" = "Tanggal Publikasi"; + +/* Post publish date picker footer view when the selected date time zone is different from your current time zone; followed by the time in the current time zone. */ +"publishDatePicker.footerCurrentTimezone" = "Tanggal dalam zona waktu Anda saat ini:"; + +/* Post publish date picker: selected value placeholder when no date is selected and the post will be published immediately */ +"publishDatePicker.immediately" = "Segera"; + +/* Title for button in publish date picker */ +"publishDatePicker.removePublishDate" = "Hapus Tanggal Publikasi"; + +/* Title for button in publish date picker */ +"publishDatePicker.selectPublishDate" = "Pilih Tanggal Publikasi"; + +/* Post publish time zone cell title */ +"publishDatePicker.timeZone" = "Zona Waktu"; + /* Post publish date picker */ "publishDatePicker.title" = "Tanggal Terbit"; @@ -10783,9 +10945,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "Lihat Langganan"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "Tambahkan blog"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Tidak dapat memblokir blog"; @@ -11025,6 +11184,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title of a feature to add a new tag to the tags subscribed by the user. */ "reader.tags.add.tag.title" = "Tambahkan Tag"; +/* Text for the 'Like' button on the reader tag cell. */ +"reader.tags.button.like" = "Suka"; + +/* Text for the 'Liked' button on the reader tag cell. */ +"reader.tags.button.liked" = "Disukai"; + /* Button title. Tapping shows the Subscribe to Tags screen. */ "reader.tags.discover.more.tags" = "Temukan tag selengkapnya"; @@ -11127,6 +11292,24 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for screen that allows configuration of your blog/site related posts settings. */ "relatedPostsSettings.title" = "Pos Terkait"; +/* A version of the post on another device. */ +"resolveConflict.anotherDevice" = "Perangkat lain"; + +/* A version of the post on the current device. */ +"resolveConflict.currentDevice" = "Perangkat saat ini"; + +/* Description for the Resolve Conflict screen. */ +"resolveConflict.description" = "Pos ini diubah di perangkat lain. Pilih versi pos yang ingin disimpan."; + +/* Title for the cancel button on the Resolve Conflict screen. */ +"resolveConflict.navigation.cancel" = "Batal"; + +/* Title for the save button on the Resolve Conflict screen. */ +"resolveConflict.navigation.save" = "Simpan"; + +/* Title for the Resolve Conflict screen. */ +"resolveConflict.navigation.title" = "Selesaikan Konflik"; + /* Title for the \"Copy Link\" action in Share Sheet. */ "share.sheet.copy.link.title" = "Salin Tautan"; @@ -11304,6 +11487,27 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Text for unknown privacy setting */ "siteVisibility.unknown.title" = "Tidak diketahui"; +/* All sites section title for site switcher. */ +"site_switcher.all_sites_section.title" = "Semua situs"; + +/* Pinned section title for site switcher. */ +"site_switcher.pinned_section.title" = "Situs yang disematkan"; + +/* Recents section title for site switcher. */ +"site_switcher.recents_section.title" = "Situs terbaru"; + +/* CTA title for the site switcher screen. */ +"site_switcher_cta_title" = "Tambahkan situs"; + +/* Dismiss button title above the search. */ +"site_switcher_dismiss_button_title" = "Batal"; + +/* Done button title above the search. */ +"site_switcher_done_button_title" = "Selesai"; + +/* Title for site switcher screen. */ +"site_switcher_title" = "Pilih sebuah situs"; + /* Label for the blogging reminders setting */ "sitesettings.reminders.title" = "Pengingat"; @@ -11340,6 +11544,9 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A smallprint that hints the reason behind why Twitter is deprecated. */ "social.twitterDeprecation.text" = "Fitur berbagi otomatis ke Twitter tidak lagi tersedia karena adanya perubahan ketentuan dan harga dari Twitter."; +/* Title of Subscribers stats tab. */ +"stats.dashboard.tab.subscribers" = "Pengikut"; + /* Title of Traffic stats tab. */ "stats.dashboard.tab.traffic" = "Lalu lintas"; @@ -11436,18 +11643,57 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Hint displayed on the 'Most Popular Time' stats card when a user's site hasn't yet received enough traffic. */ "stats.insights.mostPopularTime.noData" = "Aktivitas tidak cukup. Periksa lagi nanti saat situs Anda lebih ramai pengunjung!"; +/* Insights 'Subscribers' header */ +"stats.insights.subscribers.title" = "Pengikut"; + /* A hint shown to the user in stats informing the user how many likes one of their posts has received. The %1$@ placeholder will be replaced with the title of a post, the %2$@ with the number of likes. */ "stats.insights.totalLikes.guideText.plural" = "Pos terbaru Anda %1$@ disukai %2$@ kali."; /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "Pos terbaru Anda %1$@ disukai %2$@ kali."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "Total Pengikut WordPress.com: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "Total Pengikut Email: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "Total Pengikut"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; /* Stats 'Today' header */ "stats.period.todayCard.title" = "Hari ini"; +/* Table column title that shows the date since the user became a subscriber. */ +"stats.section.dataSubtitles.subscriberSince" = "Pengikut sejak"; + +/* Table column title that shows the names of subscribers. */ +"stats.section.itemSubtitles.subscriber" = "Nama"; + +/* Stats 'Subscribers' card header, contains chart */ +"stats.subscribers.chart.title" = "Pengikut"; + +/* A title for table's column that shows a number of times a post was opened from an email */ +"stats.subscribers.emailsSummary.column.clicks" = "Klik"; + +/* A title for table's column that shows a number of email openings */ +"stats.subscribers.emailsSummary.column.opens" = "Buka"; + +/* A title for table's column that shows a name of an email */ +"stats.subscribers.emailsSummary.column.title" = "Email terbaru"; + +/* Stats 'Emails' card header */ +"stats.subscribers.emailsSummaryCard.title" = "Email"; + +/* Stats 'Subscribers' card header */ +"stats.subscribers.subscribersListCard.title" = "Pengikut"; + +/* Accessibility of stats table. Placeholders will be populated with names of data shown in table. */ +"stats.topTotalsCell.voiceOverDescription" = "Tabel menunjukkan %1$@, %2$@, dan %3$@"; + /* This description is used to set the accessibility label for the Stats Traffic chart, with Comments selected. */ "stats.traffic.accessibilityLabel.comments" = "Diagram Batang Jumlah Komentar untuk periode terpilih."; @@ -11460,6 +11706,18 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* This description is used to set the accessibility label for the Stats Traffic chart, with Visitors selected. */ "stats.traffic.accessibilityLabel.visitors" = "Diagram Batang Jumlah Pengunjung untuk periode terpilih."; +/* The label for the option to show Stats Traffic chart for Days. */ +"stats.traffic.days" = "Hari"; + +/* The label for the option to show Stats Traffic chart for Months. */ +"stats.traffic.months" = "Bulan"; + +/* The label for the option to show Stats Traffic chart for Weeks. */ +"stats.traffic.weeks" = "Minggu"; + +/* The label for the option to show Stats Traffic chart for Years. */ +"stats.traffic.years" = "Tahun"; + /* Dismiss the AlertView */ "stockPhotos.strings.dismiss" = "Tutup"; @@ -11469,9 +11727,27 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for placeholder in Free Photos */ "stockPhotos.title" = "Cari foto gratis untuk ditambahkan ke Pustaka Media Anda!"; +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.description" = "Anda dapat memilih untuk menyertakan email dan nama pengguna guna membantu kami memahami pengalaman Anda."; + +/* Label we show on an email input field */ +"submit.feedback.alert.empty.email" = "email tidak dimasukkan"; + +/* Label we show on an name input field */ +"submit.feedback.alert.empty.username" = "nama pengguna tidak dimasukkan"; + +/* Alert submit option for users to accept sharing their email and name when submitting feedback. */ +"submit.feedback.alert.submit" = "Selesai"; + +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.title" = "Terima kasih atas masukan Anda"; + /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "Kirim"; +/* Notice informing user that their feedback is being submitted. */ +"submit.feedback.submit.loading" = "Mengirim"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "Masukan"; @@ -11643,6 +11919,21 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Displayed in the confirmation alert when marking unread notifications as read. */ "unread" = "belum dibaca"; +/* Site's Email Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.emailSubscriber" = "Pengikut Email Situs"; + +/* Site's Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.subscriber" = "Pengikut Situs"; + +/* Sites's User Profile. Displayed when the name is empty! */ +"user.details.title.user" = "Pengguna Situs"; + +/* Site's Viewers Profile. Displayed when the name is empty! */ +"user.details.title.viewer" = "Pembaca Situs"; + +/* Site Subscribers */ +"users.list.title.subscribers" = "Pengikut"; + /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ "visit our documentation page" = "kunjungi halaman dokumentasi kami"; diff --git a/WordPress/Resources/it.lproj/Localizable.strings b/WordPress/Resources/it.lproj/Localizable.strings index 6fe724b64bb2..844ec16f0d33 100644 --- a/WordPress/Resources/it.lproj/Localizable.strings +++ b/WordPress/Resources/it.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-23 11:54:09+0000 */ +/* Translation-Revision-Date: 2024-05-03 12:54:13+0000 */ /* Plural-Forms: nplurals=2; plural=n != 1; */ /* Generator: GlotPress/4.0.1 */ /* Language: it */ @@ -8844,6 +8844,12 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ /* translators: displays audio file extension. e.g. MP3 audio file */ "audio file" = "file audio"; +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.message" = "Hai apportato modifiche non salvate a questo articolo da un dispositivo diverso. Modificato: %@."; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.viewChanges" = "Visualizza modifiche"; + /* Alert message when something goes wrong with the selected image. */ "avatarMenu.failedToSetAvatarAlertMessage" = "Impossibile caricare l'immagine. Scegline una differente o prova più tardi."; @@ -10290,6 +10296,18 @@ Example: Reply to Pamela Nguyen */ /* Status mesasge for post cells */ "post.movingToTrashStatusMessage" = "Spostamento dell'articolo nel cestino in corso..."; +/* Button in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.discardChanges" = "Annulla modifiche"; + +/* Button in an alert confirming discaring a new draft */ +"postEditor.closeConfirmationAlert.discardDraft" = "Annulla bozza"; + +/* Creating autosave to generate a preview (status message */ +"postEditor.creatingAutosaveForPreview" = "Creazione del salvataggio automatico in corso..."; + +/* Saving draft to generate a preview (status message */ +"postEditor.savingDraftForPreview" = "Salvataggio bozza in corso..."; + /* Accessibility label for the post author in the post list. The parameter is the author name. For example, \"By Elsa.\" */ "postList.a11y.authorChunkFormat" = "Di %@."; @@ -10299,6 +10317,15 @@ Example: Reply to Pamela Nguyen */ /* Accessibility label for a sticky post in the post list. */ "postList.a11y.sticky" = "In evidenza."; +/* Delete option in the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.actionTitle" = "Elimina in modo permanente"; + +/* Message of the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.alertMessage" = "Desideri eliminare in modo permanente \"%@\"?"; + +/* A generic error message title */ +"postList.genericErrorMessage" = "Si è verificato un problema"; + /* Label for a post in the post list. Indicates that the post has offline changes. */ "postList.offlineChanges" = "Modifiche offline"; @@ -10314,6 +10341,30 @@ Example: Reply to Pamela Nguyen */ /* Title for the 'View' post list row swipe action */ "postList.swipeActionView" = "Visualizza"; +/* Trash option in the trash post or page confirmation alert. */ +"postList.trash.actionTitle" = "Sposta nel cestino"; + +/* Message of the trash post or page confirmation alert. */ +"postList.trash.alertMessage" = "Desideri spostare \"%@\" nel cestino? Tutte le modifiche non inviate in precedenza al server andranno perse."; + +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "Annulla caricamento"; + +/* Placeholder text in postMediaUploadStatusView when no uploads remain */ +"postMediaUploadStatusView.noPendingUploads" = "Nessun caricamento in sospeso"; + +/* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ +"postMediaUploadStatusView.progress" = "%1$@ di %2$@"; + +/* Retry upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUploads" = "Riprova i caricamenti"; + +/* Title for post media upload status view */ +"postMediaUploadStatusView.title" = "Caricamenti di elementi multimediali"; + +/* Button OK */ +"postNotice.ok" = "OK"; + /* Title of notification displayed when a page has been successfully saved as a draft. */ "postNotice.pageDraftCreated" = "Bozza pagina caricata"; @@ -10347,6 +10398,9 @@ Example: Reply to Pamela Nguyen */ /* Button title. Displays a summary / sharing screen for a specific post. */ "postNotice.view" = "Visualizza"; +/* Error message: item permanently deleted */ +"postSaveErrorMessage.deleted" = "\"%@\" è stato eliminato in modo permanente e non può più essere aggiornato"; + /* User action to dismiss featured media options. */ "postSettings.featuredImageUploadActionSheet.dismiss" = "Ignora"; @@ -10368,6 +10422,15 @@ Example: Reply to Pamela Nguyen */ /* Error message on post/page settings screen */ "postSettings.updateFailedMessage" = "Impossibile aggiornare le impostazioni dell'articolo"; +/* Details for a 'Private' privacy setting */ +"postVisibility.private.details" = "Visibile solo agli amministratori e agli editori del sito"; + +/* Title for a 'Password Protected' privacy setting */ +"postVisibility.protected.title" = "Protetto da password"; + +/* Navigation bar title for the Post Visibility picker */ +"postVisibilityPicker.navigationTitle" = "Visibilità"; + /* Promote the post with Blaze. */ "posts.blaze.actionTitle" = "Promuovi con la Blaze"; @@ -10392,6 +10455,9 @@ Example: Reply to Pamela Nguyen */ /* Label for the preview post button. Tapping displays the post as it appears on the web. */ "posts.preview.actionTitle" = "Anteprima"; +/* Label for the publish post button. */ +"posts.publish.actionTitle" = "Pubblicazione"; + /* Retry uploading the post. */ "posts.retry.actionTitle" = "Riprova"; @@ -10452,6 +10518,9 @@ Example: Reply to Pamela Nguyen */ /* Voiceover accessibility label informing the user that this button dismiss the current view */ "prepublishing.close" = "Chiudi"; +/* Details for a publish button state in the pre-publishing sheet; count as a parameter */ +"prepublishing.mediaUploadFailedDetails" = "Impossibile caricare %@ articoli"; + /* Primary button label in the pre-publishing sheet */ "prepublishing.publish" = "Pubblicazione"; @@ -10516,12 +10585,24 @@ Tapping on this row allows the user to edit the sharing message. */ /* The navigation title for the pre-publishing social accounts screen. */ "prepublishing.socialAccounts.navigationTitle" = "Social"; +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaOneItemRemaining" = "%@ articolo rimanente"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.uploadingMedia" = "Caricamento del media"; /* Title for a tappable string that opens the reader with a prompts tag */ "prompts.card.viewprompts.title" = "Visualizza tutte le risposte"; +/* Post publish date picker footer view when the selected date time zone is different from your current time zone; followed by the time in the current time zone. */ +"publishDatePicker.footerCurrentTimezone" = "La data nel tuo fuso orario attuale:"; + +/* Title for button in publish date picker */ +"publishDatePicker.removePublishDate" = "Rimuovi data di pubblicazione"; + +/* Title for button in publish date picker */ +"publishDatePicker.selectPublishDate" = "Seleziona data di pubblicazione"; + /* Button label that dismisses the qr log in flow and returns the user back to the previous screen */ "qrLoginVerifyAuthorization.completedInstructions.dismiss" = "Ignora"; @@ -10744,9 +10825,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "Vai a Abbonamenti"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "Aggiungi un blog"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Non è possibile bloccare il blog"; @@ -10983,6 +11061,9 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title of a feature to add a new tag to the tags subscribed by the user. */ "reader.tags.add.tag.title" = "Aggiungi un tag"; +/* Text for the 'Liked' button on the reader tag cell. */ +"reader.tags.button.liked" = "Con Mi piace"; + /* Button title. Tapping shows the Subscribe to Tags screen. */ "reader.tags.discover.more.tags" = "Scopri di più sui tag"; @@ -11085,6 +11166,24 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for screen that allows configuration of your blog/site related posts settings. */ "relatedPostsSettings.title" = "Articoli correlati"; +/* A version of the post on another device. */ +"resolveConflict.anotherDevice" = "Altro dispositivo"; + +/* A version of the post on the current device. */ +"resolveConflict.currentDevice" = "Dispositivo corrente"; + +/* Description for the Resolve Conflict screen. */ +"resolveConflict.description" = "L'articolo è stato modificato su un altro dispositivo. Seleziona la versione dell'articolo da mantenere."; + +/* Title for the cancel button on the Resolve Conflict screen. */ +"resolveConflict.navigation.cancel" = "Annulla"; + +/* Title for the save button on the Resolve Conflict screen. */ +"resolveConflict.navigation.save" = "Salva"; + +/* Title for the Resolve Conflict screen. */ +"resolveConflict.navigation.title" = "Risolvi il conflitto"; + /* Title for the \"Copy Link\" action in Share Sheet. */ "share.sheet.copy.link.title" = "Copia link"; @@ -11259,6 +11358,15 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Text for unknown privacy setting */ "siteVisibility.unknown.title" = "Sconosciuto"; +/* All sites section title for site switcher. */ +"site_switcher.all_sites_section.title" = "Tutti i siti"; + +/* Pinned section title for site switcher. */ +"site_switcher.pinned_section.title" = "Siti fissati"; + +/* Done button title above the search. */ +"site_switcher_done_button_title" = "Fatto"; + /* Label for the blogging reminders setting */ "sitesettings.reminders.title" = "Promemoria"; @@ -11397,12 +11505,36 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "Il tuo ultimo articolo %1$@ ha ricevuto %2$@ mi piace."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "Abbonati totali di WordPress.com: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "Totale abbonati e-mail: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "Totale abbonati"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; /* Stats 'Today' header */ "stats.period.todayCard.title" = "Oggi"; +/* Table column title that shows the date since the user became a subscriber. */ +"stats.section.dataSubtitles.subscriberSince" = "Abbonato dal"; + +/* A title for table's column that shows a number of email openings */ +"stats.subscribers.emailsSummary.column.opens" = "Aperture"; + +/* A title for table's column that shows a name of an email */ +"stats.subscribers.emailsSummary.column.title" = "Ultime e-mail"; + +/* Stats 'Emails' card header */ +"stats.subscribers.emailsSummaryCard.title" = "E-mail"; + +/* Accessibility of stats table. Placeholders will be populated with names of data shown in table. */ +"stats.topTotalsCell.voiceOverDescription" = "La tabella mostra %1$@, %2$@ e %3$@"; + /* This description is used to set the accessibility label for the Stats Traffic chart, with Comments selected. */ "stats.traffic.accessibilityLabel.comments" = "Barra del grafico che indica i Commenti per il periodo selezionato."; @@ -11424,9 +11556,27 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for placeholder in Free Photos */ "stockPhotos.title" = "Cerca per trovare foto gratuite da aggiungere alla tua Libreria multimediale!"; +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.description" = "Opzionalmente, puoi includere l'e-mail e il nome utente per aiutarci a comprendere la tua esperienza."; + +/* Label we show on an email input field */ +"submit.feedback.alert.empty.email" = "nessuna e-mail inserita"; + +/* Label we show on an name input field */ +"submit.feedback.alert.empty.username" = "nessun nome utente inserito"; + +/* Alert submit option for users to accept sharing their email and name when submitting feedback. */ +"submit.feedback.alert.submit" = "Fatto"; + +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.title" = "Grazie per il feedback"; + /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "Invia"; +/* Notice informing user that their feedback is being submitted. */ +"submit.feedback.submit.loading" = "Invio in corso"; + /* Section title for prominent suggestions */ "suggestions.section.prominent" = "In questa conversazione"; @@ -11592,6 +11742,18 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Displayed in the confirmation alert when marking unread notifications as read. */ "unread" = "non lette"; +/* Site's Email Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.emailSubscriber" = "Abbonati e-mail del sito"; + +/* Site's Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.subscriber" = "Abbonati del sito"; + +/* Sites's User Profile. Displayed when the name is empty! */ +"user.details.title.user" = "Utenti del sito"; + +/* Site's Viewers Profile. Displayed when the name is empty! */ +"user.details.title.viewer" = "Visitatori del sito"; + /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ "visit our documentation page" = "visita la pagina della documentazione"; diff --git a/WordPress/Resources/ja.lproj/Localizable.strings b/WordPress/Resources/ja.lproj/Localizable.strings index 00d9fbf40d8f..91bd273fb89b 100644 --- a/WordPress/Resources/ja.lproj/Localizable.strings +++ b/WordPress/Resources/ja.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-23 10:54:08+0000 */ +/* Translation-Revision-Date: 2024-05-02 09:54:08+0000 */ /* Plural-Forms: nplurals=1; plural=0; */ /* Generator: GlotPress/4.0.1 */ /* Language: ja_JP */ @@ -8850,6 +8850,18 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ /* translators: displays audio file extension. e.g. MP3 audio file */ "audio file" = "音声ファイル"; +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.cancel" = "キャンセル"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.message" = "別の端末からこの投稿に加えられた未保存の変更があります。 編集済み: %@。"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.title" = "自動保存が利用可能"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.viewChanges" = "変更を表示"; + /* Alert message when something goes wrong with the selected image. */ "avatarMenu.failedToSetAvatarAlertMessage" = "画像をアップロードできません。 別の画像を選択するか、後でもう一度お試しください。"; @@ -10293,6 +10305,27 @@ Example: Reply to Pamela Nguyen */ /* Status mesasge for post cells */ "post.movingToTrashStatusMessage" = "投稿をゴミ箱に移動中..."; +/* Button in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.discardChanges" = "変更を破棄"; + +/* Button in an alert confirming discaring a new draft */ +"postEditor.closeConfirmationAlert.discardDraft" = "下書きを破棄"; + +/* Button to keep the changes in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.keepEditing" = "編集を続行"; + +/* Button in an alert confirming saving a new draft */ +"postEditor.closeConfirmationAlert.saveDraft" = "下書きを保存"; + +/* Creating autosave to generate a preview (status message */ +"postEditor.creatingAutosaveForPreview" = "自動保存を作成中..."; + +/* Title for a snackbar */ +"postEditor.revisionLoaded" = "変更履歴の読み取りが完了しました"; + +/* Saving draft to generate a preview (status message */ +"postEditor.savingDraftForPreview" = "下書きを保存中..."; + /* Accessibility label for the post author in the post list. The parameter is the author name. For example, \"By Elsa.\" */ "postList.a11y.authorChunkFormat" = "作成者: %@。"; @@ -10305,6 +10338,24 @@ Example: Reply to Pamela Nguyen */ /* Accessibility label for a post in the post list. The first placeholder is the post title. The second placeholder is the date. */ "postList.a11y.titleAndDateChunkFormat" = "%1$@、%2$@。"; +/* Badge for post cells */ +"postList.badgePendingReview" = "レビュー待ち"; + +/* Cancels an Action */ +"postList.cancel" = "キャンセル"; + +/* Delete option in the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.actionTitle" = "完全に削除"; + +/* Message of the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.alertMessage" = "「%@」を完全に削除してもよいですか ?"; + +/* An error message */ +"postList.errorUnsyncedChangesMessage" = "アプリが以前に行われた変更をサーバーにアップロードしています。 後でもう一度お試しください。"; + +/* A generic error message title */ +"postList.genericErrorMessage" = "エラーが発生しました"; + /* Label for a post in the post list. Indicates that the post has offline changes. */ "postList.offlineChanges" = "オフラインの変更"; @@ -10320,6 +10371,39 @@ Example: Reply to Pamela Nguyen */ /* Title for the 'View' post list row swipe action */ "postList.swipeActionView" = "表示"; +/* Trash option in the trash post or page confirmation alert. */ +"postList.trash.actionTitle" = "ゴミ箱に移動"; + +/* Message of the trash post or page confirmation alert. */ +"postList.trash.alertMessage" = "「%@」をゴミ箱に移動してもよいですか ? 以前サーバーに送信されなかった変更はすべて失われます。"; + +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "アップロードをキャンセル"; + +/* Close button in postMediaUploadStatusView */ +"postMediaUploadStatusView.close" = "閉じる"; + +/* Placeholder text in postMediaUploadStatusView when no uploads remain */ +"postMediaUploadStatusView.noPendingUploads" = "保留中のアップロードはありません"; + +/* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ +"postMediaUploadStatusView.progress" = "%1$@ \/ %2$@"; + +/* Retry (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUpload" = "アップロードを再試行"; + +/* Retry upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUploads" = "アップロードを再試行"; + +/* Title for post media upload status view */ +"postMediaUploadStatusView.title" = "メディアのアップロード"; + +/* A generic error message title */ +"postNotice.errorTitle" = "エラーが発生しました"; + +/* An error message */ +"postNotice.errorUnsyncedChangesMessage" = "アプリが以前に行われた変更をサーバーにアップロードしています。 後でもう一度お試しください。"; + /* Title of notification displayed when a page has been successfully saved as a draft. */ "postNotice.pageDraftCreated" = "ページの下書きをアップロードしました"; @@ -10353,6 +10437,15 @@ Example: Reply to Pamela Nguyen */ /* Button title. Displays a summary / sharing screen for a specific post. */ "postNotice.view" = "表示"; +/* Error message: content was modified on another device */ +"postSaveErrorMessage.conflict" = "コンテンツが別の端末で編集されました"; + +/* Error message: item permanently deleted */ +"postSaveErrorMessage.deleted" = "「%@」は完全に削除され、更新できなくなりました"; + +/* A default value for an post without a title */ +"postSaveErrorMessage.postUntitled" = "無題"; + /* User action to dismiss featured media options. */ "postSettings.featuredImageUploadActionSheet.dismiss" = "閉じる"; @@ -10365,6 +10458,9 @@ Example: Reply to Pamela Nguyen */ /* Title for action sheet with featured media options. */ "postSettings.featuredImageUploadActionSheet.title" = "アイキャッチ画像のオプション"; +/* The 'Pending Review' setting of the post */ +"postSettings.pendingReview" = "レビュー待ち"; + /* Section title for the disabled Twitter service in the Post Settings screen */ "postSettings.section.disabledTwitter.header" = "Twitter の自動共有機能は利用できなくなりました"; @@ -10374,6 +10470,36 @@ Example: Reply to Pamela Nguyen */ /* Error message on post/page settings screen */ "postSettings.updateFailedMessage" = "投稿設定を更新できませんでした"; +/* Details for a 'Private' privacy setting */ +"postVisibility.private.details" = "サイト管理者と編集者にだけ表示されます"; + +/* Title for a 'Private' privacy setting */ +"postVisibility.private.title" = "非公開"; + +/* Details for a 'Password Protected' privacy setting */ +"postVisibility.protected.details" = "誰でも見ることができますが、パスワードが必要です"; + +/* Title for a 'Password Protected' privacy setting */ +"postVisibility.protected.title" = "パスワード保護"; + +/* Details for a 'Public' (default) privacy setting */ +"postVisibility.public.details" = "すべての人に表示"; + +/* Title for a 'Public' (default) privacy setting */ +"postVisibility.public.title" = "公開"; + +/* Button cancel */ +"postVisibilityPicker.cancel" = "キャンセル"; + +/* Navigation bar title for the Post Visibility picker */ +"postVisibilityPicker.navigationTitle" = "表示設定"; + +/* Password placeholder text */ +"postVisibilityPicker.password" = "パスワード"; + +/* Button save */ +"postVisibilityPicker.save" = "保存"; + /* Promote the post with Blaze. */ "posts.blaze.actionTitle" = "Blaze を使って宣伝"; @@ -10398,6 +10524,9 @@ Example: Reply to Pamela Nguyen */ /* Label for the preview post button. Tapping displays the post as it appears on the web. */ "posts.preview.actionTitle" = "プレビュー"; +/* Label for the publish post button. */ +"posts.publish.actionTitle" = "公開"; + /* Retry uploading the post. */ "posts.retry.actionTitle" = "再試行"; @@ -10464,6 +10593,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.jetpackSocial" = "Jetpack ソーシャル"; +/* Details for a publish button state in the pre-publishing sheet; count as a parameter */ +"prepublishing.mediaUploadFailedDetails" = "%@件のアイテムのアップロードに失敗しました"; + /* Placeholder for a cell in the pre-publishing sheet */ "prepublishing.postTitle" = "タイトル"; @@ -10473,6 +10605,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.publishDate" = "公開日"; +/* Placeholder value for a publishing date in the prepublishing sheet when the date is not selected */ +"prepublishing.publishDateImmediately" = "すぐに"; + /* Label in the header in the pre-publishing sheet */ "prepublishing.publishingTo" = "公開先"; @@ -10537,6 +10672,12 @@ Tapping on this row allows the user to edit the sharing message. */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.tags" = "タグ"; +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaManyItemsRemaining" = "残り%@件のアイテム"; + +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaOneItemRemaining" = "残り%@件のアイテム"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.uploadingMedia" = "メディアをアップロード中"; @@ -10546,6 +10687,24 @@ Tapping on this row allows the user to edit the sharing message. */ /* Title for a tappable string that opens the reader with a prompts tag */ "prompts.card.viewprompts.title" = "すべての回答を表示"; +/* Post publish date picker title for date cell */ +"publishDatePicker.date" = "公開日"; + +/* Post publish date picker footer view when the selected date time zone is different from your current time zone; followed by the time in the current time zone. */ +"publishDatePicker.footerCurrentTimezone" = "現在お使いのタイムゾーンの日付:"; + +/* Post publish date picker: selected value placeholder when no date is selected and the post will be published immediately */ +"publishDatePicker.immediately" = "すぐに"; + +/* Title for button in publish date picker */ +"publishDatePicker.removePublishDate" = "公開日を削除"; + +/* Title for button in publish date picker */ +"publishDatePicker.selectPublishDate" = "公開日を選択"; + +/* Post publish time zone cell title */ +"publishDatePicker.timeZone" = "タイムゾーン"; + /* Post publish date picker */ "publishDatePicker.title" = "公開日"; @@ -10771,9 +10930,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "サブスクリプションに移動"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "ブログを追加"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "ブログをブロックできませんでした"; @@ -11010,6 +11166,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title of a feature to add a new tag to the tags subscribed by the user. */ "reader.tags.add.tag.title" = "タグを追加"; +/* Text for the 'Like' button on the reader tag cell. */ +"reader.tags.button.like" = "いいね"; + +/* Text for the 'Liked' button on the reader tag cell. */ +"reader.tags.button.liked" = "いいね済み"; + /* Button title. Tapping shows the Subscribe to Tags screen. */ "reader.tags.discover.more.tags" = "その他のタグを見る"; @@ -11109,6 +11271,24 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for screen that allows configuration of your blog/site related posts settings. */ "relatedPostsSettings.title" = "関連記事"; +/* A version of the post on another device. */ +"resolveConflict.anotherDevice" = "別の端末"; + +/* A version of the post on the current device. */ +"resolveConflict.currentDevice" = "現在の端末"; + +/* Description for the Resolve Conflict screen. */ +"resolveConflict.description" = "投稿が別の端末で編集されました。 保持する投稿のバージョンを選択してください。"; + +/* Title for the cancel button on the Resolve Conflict screen. */ +"resolveConflict.navigation.cancel" = "キャンセル"; + +/* Title for the save button on the Resolve Conflict screen. */ +"resolveConflict.navigation.save" = "保存"; + +/* Title for the Resolve Conflict screen. */ +"resolveConflict.navigation.title" = "競合を解決"; + /* Title for the \"Copy Link\" action in Share Sheet. */ "share.sheet.copy.link.title" = "リンクをコピー"; @@ -11286,6 +11466,30 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Text for unknown privacy setting */ "siteVisibility.unknown.title" = "不明"; +/* All sites section title for site switcher. */ +"site_switcher.all_sites_section.title" = "すべてのサイト"; + +/* Pinned section title for site switcher. */ +"site_switcher.pinned_section.title" = "固定済みサイト"; + +/* Recents section title for site switcher. */ +"site_switcher.recents_section.title" = "最近のサイト"; + +/* CTA title for the site switcher screen. */ +"site_switcher_cta_title" = "サイトを追加"; + +/* Dismiss button title above the search. */ +"site_switcher_dismiss_button_title" = "キャンセル"; + +/* Done button title above the search. */ +"site_switcher_done_button_title" = "完了"; + +/* Edit button title above the search. */ +"site_switcher_edit_button_title" = "編集"; + +/* Title for site switcher screen. */ +"site_switcher_title" = "サイトを選択"; + /* Label for the blogging reminders setting */ "sitesettings.reminders.title" = "リマインダー"; @@ -11322,6 +11526,9 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A smallprint that hints the reason behind why Twitter is deprecated. */ "social.twitterDeprecation.text" = "Twitter の自動共有機能は Twitter の規約と価格に変更があったため利用できなくなりました。"; +/* Title of Subscribers stats tab. */ +"stats.dashboard.tab.subscribers" = "購読者"; + /* Title of Traffic stats tab. */ "stats.dashboard.tab.traffic" = "トラフィック"; @@ -11418,15 +11625,54 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Hint displayed on the 'Most Popular Time' stats card when a user's site hasn't yet received enough traffic. */ "stats.insights.mostPopularTime.noData" = "アクティビティが足りません。 サイトの訪問者数が増えたら後でもう一度確認してください。"; +/* Insights 'Subscribers' header */ +"stats.insights.subscribers.title" = "購読者"; + /* A hint shown to the user in stats informing the user how many likes one of their posts has received. The %1$@ placeholder will be replaced with the title of a post, the %2$@ with the number of likes. */ "stats.insights.totalLikes.guideText.plural" = "最新の投稿 %1$@ が%2$@回「いいね」されました。"; /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "最新の投稿 %1$@ が%2$@回「いいね」されました。"; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "WordPress.com 購読者総数: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "メール購読者総数: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "購読者総数"; + /* Stats 'Today' header */ "stats.period.todayCard.title" = "今日"; +/* Table column title that shows the date since the user became a subscriber. */ +"stats.section.dataSubtitles.subscriberSince" = "購読者になった日時"; + +/* Table column title that shows the names of subscribers. */ +"stats.section.itemSubtitles.subscriber" = "名前"; + +/* Stats 'Subscribers' card header, contains chart */ +"stats.subscribers.chart.title" = "購読者"; + +/* A title for table's column that shows a number of times a post was opened from an email */ +"stats.subscribers.emailsSummary.column.clicks" = "クリック数"; + +/* A title for table's column that shows a number of email openings */ +"stats.subscribers.emailsSummary.column.opens" = "開いた数"; + +/* A title for table's column that shows a name of an email */ +"stats.subscribers.emailsSummary.column.title" = "最新のメール"; + +/* Stats 'Emails' card header */ +"stats.subscribers.emailsSummaryCard.title" = "メール"; + +/* Stats 'Subscribers' card header */ +"stats.subscribers.subscribersListCard.title" = "購読者"; + +/* Accessibility of stats table. Placeholders will be populated with names of data shown in table. */ +"stats.topTotalsCell.voiceOverDescription" = "%1$@、%2$@、%3$@ を示すテーブル"; + /* This description is used to set the accessibility label for the Stats Traffic chart, with Comments selected. */ "stats.traffic.accessibilityLabel.comments" = "選択した期間のコメントを表す棒グラフ。"; @@ -11439,6 +11685,18 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* This description is used to set the accessibility label for the Stats Traffic chart, with Visitors selected. */ "stats.traffic.accessibilityLabel.visitors" = "選択した期間の訪問者を表す棒グラフ。"; +/* The label for the option to show Stats Traffic chart for Days. */ +"stats.traffic.days" = "日"; + +/* The label for the option to show Stats Traffic chart for Months. */ +"stats.traffic.months" = "月"; + +/* The label for the option to show Stats Traffic chart for Weeks. */ +"stats.traffic.weeks" = "週"; + +/* The label for the option to show Stats Traffic chart for Years. */ +"stats.traffic.years" = "年"; + /* Dismiss the AlertView */ "stockPhotos.strings.dismiss" = "削除"; @@ -11448,9 +11706,27 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for placeholder in Free Photos */ "stockPhotos.title" = "無料の写真を検索して、メディアライブラリに追加してください。"; +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.description" = "お客様の状況をこちらで把握できるよう、メールアドレスとユーザー名を含めていただくことも可能です。"; + +/* Label we show on an email input field */ +"submit.feedback.alert.empty.email" = "入力されたメールアドレスがありません"; + +/* Label we show on an name input field */ +"submit.feedback.alert.empty.username" = "入力されたユーザー名がありません"; + +/* Alert submit option for users to accept sharing their email and name when submitting feedback. */ +"submit.feedback.alert.submit" = "完了"; + +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.title" = "フィードバックありがとうございます"; + /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "送信"; +/* Notice informing user that their feedback is being submitted. */ +"submit.feedback.submit.loading" = "送信中"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "フィードバック"; @@ -11622,6 +11898,21 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Displayed in the confirmation alert when marking unread notifications as read. */ "unread" = "未読"; +/* Site's Email Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.emailSubscriber" = "サイトのメール購読者"; + +/* Site's Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.subscriber" = "サイトの購読者"; + +/* Sites's User Profile. Displayed when the name is empty! */ +"user.details.title.user" = "サイトのユーザー"; + +/* Site's Viewers Profile. Displayed when the name is empty! */ +"user.details.title.viewer" = "サイトの読者"; + +/* Site Subscribers */ +"users.list.title.subscribers" = "購読者"; + /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ "visit our documentation page" = "ドキュメントページに移動する"; diff --git a/WordPress/Resources/ko.lproj/Localizable.strings b/WordPress/Resources/ko.lproj/Localizable.strings index 8aee55525f7b..e3ff03ef9c53 100644 --- a/WordPress/Resources/ko.lproj/Localizable.strings +++ b/WordPress/Resources/ko.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-30 02:15:15+0000 */ +/* Translation-Revision-Date: 2024-05-06 03:03:50+0000 */ /* Plural-Forms: nplurals=1; plural=0; */ /* Generator: GlotPress/4.0.1 */ /* Language: ko_KR */ @@ -10389,6 +10389,9 @@ Example: Reply to Pamela Nguyen */ /* Message of the trash post or page confirmation alert. */ "postList.trash.alertMessage" = "\"%@\"을 휴지통에 버리시겠습니까? 이전에 서버로 전송되지 않은 변경 내용은 모두 손실됩니다."; +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "업로드 취소"; + /* Close button in postMediaUploadStatusView */ "postMediaUploadStatusView.close" = "닫기"; @@ -10398,6 +10401,9 @@ Example: Reply to Pamela Nguyen */ /* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ "postMediaUploadStatusView.progress" = "%1$@\/%2$@"; +/* Retry (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUpload" = "업로드 재시도"; + /* Retry upload button in postMediaUploadStatusView */ "postMediaUploadStatusView.retryUploads" = "업로드 재시도"; @@ -10943,7 +10949,7 @@ with the filter chip button. */ "reader.no.results.subscriptions.button" = "구독으로 이동"; /* No Tags View Button Label */ -"reader.no.tags.title" = "블로그 추가"; +"reader.no.tags.title" = "태그 추가"; /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "블로그를 차단할 수 없음"; @@ -11661,6 +11667,15 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "최신 글 %1$@에 %2$@개의 좋아요가 있습니다."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "총 워드프레스닷컴 구독자: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "총 이메일 구독자: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "구독자 합계"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@(%3$@)"; @@ -11745,8 +11760,7 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "제출"; -/* Notice informing user that their feedback is being submitted anonymously. - Notice informing user that their feedback is being submitted. */ +/* Notice informing user that their feedback is being submitted. */ "submit.feedback.submit.loading" = "전송 중…"; /* The title for the the In-App Feedback screen */ @@ -11932,8 +11946,7 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Site's Viewers Profile. Displayed when the name is empty! */ "user.details.title.viewer" = "블로그 방문자"; -/* Site Email Subscribers - Site Subscribers */ +/* Site Subscribers */ "users.list.title.subscribers" = "구독자"; /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ diff --git a/WordPress/Resources/nl.lproj/Localizable.strings b/WordPress/Resources/nl.lproj/Localizable.strings index ba10d54dc72b..29d56eb96efc 100644 --- a/WordPress/Resources/nl.lproj/Localizable.strings +++ b/WordPress/Resources/nl.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-30 11:11:41+0000 */ +/* Translation-Revision-Date: 2024-05-01 19:21:12+0000 */ /* Plural-Forms: nplurals=2; plural=n != 1; */ /* Generator: GlotPress/4.0.1 */ /* Language: nl */ @@ -10924,6 +10924,9 @@ with the filter chip button. */ /* Reader settings button accessibility label. */ "reader.navigation.settings.button.label" = "Reader-instellingen"; +/* No Tags View Button Label */ +"reader.no.blog.title" = "Een blog toevoegen"; + /* Title for button on the no followed blogs result screen */ "reader.no.blogs.button" = "Ontdek blogs"; @@ -10949,7 +10952,7 @@ with the filter chip button. */ "reader.no.results.subscriptions.button" = "Ga naar Abonnementen"; /* No Tags View Button Label */ -"reader.no.tags.title" = "Een blog toevoegen"; +"reader.no.tags.title" = "Een tag toevoegen"; /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Kan blog niet blokkeren"; @@ -11760,10 +11763,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "Verzenden"; -/* Notice informing user that their feedback is being submitted anonymously. - Notice informing user that their feedback is being submitted. */ +/* Notice informing user that their feedback is being submitted. */ "submit.feedback.submit.loading" = "Aan het verzenden"; +/* Notice informing user that their feedback is being submitted anonymously. */ +"submit.feedback.submitAnonymously.loading" = "Anoniem aan het verzenden"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "Feedback"; @@ -11947,8 +11952,10 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Site's Viewers Profile. Displayed when the name is empty! */ "user.details.title.viewer" = "Site kijker"; -/* Site Email Subscribers - Site Subscribers */ +/* Site Email Subscribers */ +"users.list.title.emailSubscribers" = "E-mailabonnees"; + +/* Site Subscribers */ "users.list.title.subscribers" = "Abonnees"; /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ diff --git a/WordPress/Resources/pt-BR.lproj/Localizable.strings b/WordPress/Resources/pt-BR.lproj/Localizable.strings index 40161624dda4..778523868b4c 100644 --- a/WordPress/Resources/pt-BR.lproj/Localizable.strings +++ b/WordPress/Resources/pt-BR.lproj/Localizable.strings @@ -9521,9 +9521,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "Ir para Assinaturas"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "Adicionar um blog"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Não foi possível bloquear o blog"; diff --git a/WordPress/Resources/ro.lproj/Localizable.strings b/WordPress/Resources/ro.lproj/Localizable.strings index 632251085263..56b57fe7f58d 100644 --- a/WordPress/Resources/ro.lproj/Localizable.strings +++ b/WordPress/Resources/ro.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-30 06:00:52+0000 */ +/* Translation-Revision-Date: 2024-05-06 19:05:26+0000 */ /* Plural-Forms: nplurals=3; plural=(n == 1) ? 0 : ((n == 0 || n % 100 >= 2 && n % 100 <= 19) ? 1 : 2); */ /* Generator: GlotPress/4.0.1 */ /* Language: ro */ @@ -5405,7 +5405,7 @@ translators: %s: Select control button label e.g. \"Button width\" */ /* Link to privacy settings page Privacy Settings Title */ -"Privacy Settings" = "Setări confidențialitate"; +"Privacy Settings" = "Setări pentru confidențialitate"; /* No comment provided by engineer. */ "Privacy and Rating" = "Confidențialitate și evaluare"; @@ -7052,7 +7052,7 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ "The video could not be added to the Media Library." = "Videoul nu a putut fi adăugat în Biblioteca media."; /* Title of alert when theme activation succeeds */ -"Theme Activated" = "Tema a fost activată"; +"Theme Activated" = "Tema este activată"; /* Noun. Name of the Themes feature Themes option in the blog details @@ -10924,6 +10924,9 @@ with the filter chip button. */ /* Reader settings button accessibility label. */ "reader.navigation.settings.button.label" = "Setări Cititor"; +/* No Tags View Button Label */ +"reader.no.blog.title" = "Adaugă un blog"; + /* Title for button on the no followed blogs result screen */ "reader.no.blogs.button" = "Descoperă bloguri"; @@ -10949,7 +10952,7 @@ with the filter chip button. */ "reader.no.results.subscriptions.button" = "Mergi la Abonamente"; /* No Tags View Button Label */ -"reader.no.tags.title" = "Adaugă un blog"; +"reader.no.tags.title" = "Adaugă o etichetă"; /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Nu pot să blochez blogul"; @@ -11667,6 +11670,15 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "Ultimul tău articol %1$@ a avut %2$@ apreciere."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "Total abonați WordPress.com: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "Total abonați prin email: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "Total abonați"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; @@ -11751,10 +11763,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "Trimite"; -/* Notice informing user that their feedback is being submitted anonymously. - Notice informing user that their feedback is being submitted. */ +/* Notice informing user that their feedback is being submitted. */ "submit.feedback.submit.loading" = "Trimitere"; +/* Notice informing user that their feedback is being submitted anonymously. */ +"submit.feedback.submitAnonymously.loading" = "Trimitere anonimă"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "Impresii"; @@ -11938,8 +11952,10 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Site's Viewers Profile. Displayed when the name is empty! */ "user.details.title.viewer" = "Vizitator pe site"; -/* Site Email Subscribers - Site Subscribers */ +/* Site Email Subscribers */ +"users.list.title.emailSubscribers" = "Abonați prin email"; + +/* Site Subscribers */ "users.list.title.subscribers" = "Abonați"; /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ diff --git a/WordPress/Resources/ru.lproj/Localizable.strings b/WordPress/Resources/ru.lproj/Localizable.strings index f54bd8191637..5ed7b711f0ac 100644 --- a/WordPress/Resources/ru.lproj/Localizable.strings +++ b/WordPress/Resources/ru.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-29 21:08:36+0000 */ +/* Translation-Revision-Date: 2024-05-02 15:54:07+0000 */ /* Plural-Forms: nplurals=3; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : 2); */ /* Generator: GlotPress/4.0.1 */ /* Language: ru */ @@ -8853,6 +8853,18 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ /* translators: displays audio file extension. e.g. MP3 audio file */ "audio file" = "аудио файл"; +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.cancel" = "Отмена"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.message" = "Вы внесли изменения в эту запись с другого устройства, но не сохранили их. Изменено: %@."; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.title" = "Доступно автоматическое сохранение"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.viewChanges" = "Просмотреть изменения"; + /* Alert message when something goes wrong with the selected image. */ "avatarMenu.failedToSetAvatarAlertMessage" = "Не удалось загрузить изображение. Выберите другое или повторите попытку."; @@ -10305,6 +10317,27 @@ Example: Reply to Pamela Nguyen */ /* Status mesasge for post cells */ "post.movingToTrashStatusMessage" = "Удаление записи в корзину..."; +/* Button in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.discardChanges" = "Отменить изменения"; + +/* Button in an alert confirming discaring a new draft */ +"postEditor.closeConfirmationAlert.discardDraft" = "Удалить черновик"; + +/* Button to keep the changes in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.keepEditing" = "Продолжить редактирование"; + +/* Button in an alert confirming saving a new draft */ +"postEditor.closeConfirmationAlert.saveDraft" = "Сохранить черновик"; + +/* Creating autosave to generate a preview (status message */ +"postEditor.creatingAutosaveForPreview" = "Автосохранение..."; + +/* Title for a snackbar */ +"postEditor.revisionLoaded" = "Редакция загружена"; + +/* Saving draft to generate a preview (status message */ +"postEditor.savingDraftForPreview" = "Сохранение черновика..."; + /* Accessibility label for the post author in the post list. The parameter is the author name. For example, \"By Elsa.\" */ "postList.a11y.authorChunkFormat" = "Автор: %@."; @@ -10317,6 +10350,21 @@ Example: Reply to Pamela Nguyen */ /* Accessibility label for a post in the post list. The first placeholder is the post title. The second placeholder is the date. */ "postList.a11y.titleAndDateChunkFormat" = "%1$@, %2$@."; +/* Badge for post cells */ +"postList.badgePendingReview" = "На рассмотрении"; + +/* Cancels an Action */ +"postList.cancel" = "Отмена"; + +/* Delete option in the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.actionTitle" = "Удалить навсегда"; + +/* An error message */ +"postList.errorUnsyncedChangesMessage" = "Приложение загружает на сервер внесённые ранее изменения Повторите попытку позже."; + +/* A generic error message title */ +"postList.genericErrorMessage" = "Что-то пошло не так"; + /* Label for a post in the post list. Indicates that the post has offline changes. */ "postList.offlineChanges" = "Изменения внесены офлайн"; @@ -10332,6 +10380,42 @@ Example: Reply to Pamela Nguyen */ /* Title for the 'View' post list row swipe action */ "postList.swipeActionView" = "Просмотр"; +/* Trash option in the trash post or page confirmation alert. */ +"postList.trash.actionTitle" = "Отправить в корзину"; + +/* Message of the trash post or page confirmation alert. */ +"postList.trash.alertMessage" = "Вы действительно хотите отправить \"%@\" в корзину? Изменения, не загруженные на сервер, будут утрачены."; + +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "Отменить загрузку"; + +/* Close button in postMediaUploadStatusView */ +"postMediaUploadStatusView.close" = "Закрыть"; + +/* Placeholder text in postMediaUploadStatusView when no uploads remain */ +"postMediaUploadStatusView.noPendingUploads" = "Нет загрузок в очереди"; + +/* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ +"postMediaUploadStatusView.progress" = "%1$@ из %2$@"; + +/* Retry (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUpload" = "Повторить загрузку"; + +/* Retry upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUploads" = "Повторить загрузки"; + +/* Title for post media upload status view */ +"postMediaUploadStatusView.title" = "Загруженные медиафайлы"; + +/* A generic error message title */ +"postNotice.errorTitle" = "Произошла ошибка"; + +/* An error message */ +"postNotice.errorUnsyncedChangesMessage" = "Приложение загружает на сервер внесённые ранее изменения. Повторите попытку позже."; + +/* Button OK */ +"postNotice.ok" = "ОК"; + /* Title of notification displayed when a page has been successfully saved as a draft. */ "postNotice.pageDraftCreated" = "Черновик страницы загружен"; @@ -10365,6 +10449,15 @@ Example: Reply to Pamela Nguyen */ /* Button title. Displays a summary / sharing screen for a specific post. */ "postNotice.view" = "Просмотр"; +/* Error message: content was modified on another device */ +"postSaveErrorMessage.conflict" = "Запись была изменена на другом устройстве"; + +/* Error message: item permanently deleted */ +"postSaveErrorMessage.deleted" = "\"%@\" безвозвратно удалён и не может быть восстановлен."; + +/* A default value for an post without a title */ +"postSaveErrorMessage.postUntitled" = "Без названия"; + /* User action to dismiss featured media options. */ "postSettings.featuredImageUploadActionSheet.dismiss" = "Закрыть"; @@ -10377,6 +10470,9 @@ Example: Reply to Pamela Nguyen */ /* Title for action sheet with featured media options. */ "postSettings.featuredImageUploadActionSheet.title" = "Настройки изображения записи"; +/* The 'Pending Review' setting of the post */ +"postSettings.pendingReview" = "На рассмотрении"; + /* Section title for the disabled Twitter service in the Post Settings screen */ "postSettings.section.disabledTwitter.header" = "Автоматическая публикация в Twitter больше не доступна"; @@ -10386,6 +10482,36 @@ Example: Reply to Pamela Nguyen */ /* Error message on post/page settings screen */ "postSettings.updateFailedMessage" = "Не удалось обновить настройки записи"; +/* Details for a 'Private' privacy setting */ +"postVisibility.private.details" = "Видна только администраторам и редакторам"; + +/* Title for a 'Private' privacy setting */ +"postVisibility.private.title" = "Закрытая"; + +/* Details for a 'Password Protected' privacy setting */ +"postVisibility.protected.details" = "Общедоступная, но требует ввода пароля"; + +/* Title for a 'Password Protected' privacy setting */ +"postVisibility.protected.title" = "Защищено паролем"; + +/* Details for a 'Public' (default) privacy setting */ +"postVisibility.public.details" = "Видна всем"; + +/* Title for a 'Public' (default) privacy setting */ +"postVisibility.public.title" = "Публичная"; + +/* Button cancel */ +"postVisibilityPicker.cancel" = "Отмена"; + +/* Navigation bar title for the Post Visibility picker */ +"postVisibilityPicker.navigationTitle" = "Видимость"; + +/* Password placeholder text */ +"postVisibilityPicker.password" = "Пароль"; + +/* Button save */ +"postVisibilityPicker.save" = "Сохранить"; + /* Promote the post with Blaze. */ "posts.blaze.actionTitle" = "Продвигайте содержимое с помощью Blaze"; @@ -10479,6 +10605,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.jetpackSocial" = "Jetpack Social"; +/* Details for a publish button state in the pre-publishing sheet; count as a parameter */ +"prepublishing.mediaUploadFailedDetails" = "Не удалось загрузить элементы (%@)"; + /* Placeholder for a cell in the pre-publishing sheet */ "prepublishing.postTitle" = "Заголовок"; @@ -10488,6 +10617,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.publishDate" = "Дата публикации"; +/* Placeholder value for a publishing date in the prepublishing sheet when the date is not selected */ +"prepublishing.publishDateImmediately" = "Немедленно"; + /* Label in the header in the pre-publishing sheet */ "prepublishing.publishingTo" = "Публикация на"; @@ -10552,6 +10684,12 @@ Tapping on this row allows the user to edit the sharing message. */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.tags" = "Теги"; +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaManyItemsRemaining" = "Осталось элементов: %@"; + +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaOneItemRemaining" = "Осталось элементов: %@"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.uploadingMedia" = "Загрузка медиафайлов"; @@ -10561,6 +10699,24 @@ Tapping on this row allows the user to edit the sharing message. */ /* Title for a tappable string that opens the reader with a prompts tag */ "prompts.card.viewprompts.title" = "Посмотреть все ответы"; +/* Post publish date picker title for date cell */ +"publishDatePicker.date" = "Дата публикации"; + +/* Post publish date picker footer view when the selected date time zone is different from your current time zone; followed by the time in the current time zone. */ +"publishDatePicker.footerCurrentTimezone" = "Дата в вашем часовом поясе:"; + +/* Post publish date picker: selected value placeholder when no date is selected and the post will be published immediately */ +"publishDatePicker.immediately" = "Немедленно"; + +/* Title for button in publish date picker */ +"publishDatePicker.removePublishDate" = "Удалить дату публикации"; + +/* Title for button in publish date picker */ +"publishDatePicker.selectPublishDate" = "Выбрать дату публикации"; + +/* Post publish time zone cell title */ +"publishDatePicker.timeZone" = "Часовой пояс"; + /* Post publish date picker */ "publishDatePicker.title" = "Дата публикации"; @@ -10786,9 +10942,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "Перейти в подписки"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "Добавьте блог"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Не удалось заблокировать блог"; @@ -11028,6 +11181,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title of a feature to add a new tag to the tags subscribed by the user. */ "reader.tags.add.tag.title" = "Добавьте тег"; +/* Text for the 'Like' button on the reader tag cell. */ +"reader.tags.button.like" = "Оценка «Нравится»"; + +/* Text for the 'Liked' button on the reader tag cell. */ +"reader.tags.button.liked" = "Понравилось"; + /* Button title. Tapping shows the Subscribe to Tags screen. */ "reader.tags.discover.more.tags" = "Другие теги"; @@ -11130,6 +11289,24 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for screen that allows configuration of your blog/site related posts settings. */ "relatedPostsSettings.title" = "Похожие записи"; +/* A version of the post on another device. */ +"resolveConflict.anotherDevice" = "Другое устройство"; + +/* A version of the post on the current device. */ +"resolveConflict.currentDevice" = "Текущее устройство"; + +/* Description for the Resolve Conflict screen. */ +"resolveConflict.description" = "Запись была изменена на другом устройстве. Выберите, какую версию записи вы хотите сохранить."; + +/* Title for the cancel button on the Resolve Conflict screen. */ +"resolveConflict.navigation.cancel" = "Отмена"; + +/* Title for the save button on the Resolve Conflict screen. */ +"resolveConflict.navigation.save" = "Сохранить"; + +/* Title for the Resolve Conflict screen. */ +"resolveConflict.navigation.title" = "Устранить конфликт"; + /* Title for the \"Copy Link\" action in Share Sheet. */ "share.sheet.copy.link.title" = "Копировать ссылку"; @@ -11307,6 +11484,30 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Text for unknown privacy setting */ "siteVisibility.unknown.title" = "Неизвестно"; +/* All sites section title for site switcher. */ +"site_switcher.all_sites_section.title" = "Все сайты"; + +/* Pinned section title for site switcher. */ +"site_switcher.pinned_section.title" = "Закреплённые сайты"; + +/* Recents section title for site switcher. */ +"site_switcher.recents_section.title" = "Последние сайты"; + +/* CTA title for the site switcher screen. */ +"site_switcher_cta_title" = "Добавить сайт"; + +/* Dismiss button title above the search. */ +"site_switcher_dismiss_button_title" = "Отмена"; + +/* Done button title above the search. */ +"site_switcher_done_button_title" = "Готово"; + +/* Edit button title above the search. */ +"site_switcher_edit_button_title" = "Редактировать"; + +/* Title for site switcher screen. */ +"site_switcher_title" = "Выберите сайт"; + /* Label for the blogging reminders setting */ "sitesettings.reminders.title" = "Напоминания"; @@ -11343,6 +11544,9 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A smallprint that hints the reason behind why Twitter is deprecated. */ "social.twitterDeprecation.text" = "Автоматическая публикация в Twitter больше не доступна из-за изменения условий использования и цен Twitter."; +/* Title of Subscribers stats tab. */ +"stats.dashboard.tab.subscribers" = "Подписчики"; + /* Title of Traffic stats tab. */ "stats.dashboard.tab.traffic" = "Посещаемость"; @@ -11439,18 +11643,57 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Hint displayed on the 'Most Popular Time' stats card when a user's site hasn't yet received enough traffic. */ "stats.insights.mostPopularTime.noData" = "Недостаточно активности. Вернитесь позже, когда на сайте будет больше посетителей!"; +/* Insights 'Subscribers' header */ +"stats.insights.subscribers.title" = "Подписчики"; + /* A hint shown to the user in stats informing the user how many likes one of their posts has received. The %1$@ placeholder will be replaced with the title of a post, the %2$@ with the number of likes. */ "stats.insights.totalLikes.guideText.plural" = "Ваша последняя запись %1$@ получила %2$@ отметок \"нравится\"."; /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "Ваша последняя запись %1$@ получила %2$@ отметку \"нравится\"."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "Всего подписчиков WordPress.com: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "Всего подписчиков в рассылке: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "Всего подписчиков"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; /* Stats 'Today' header */ "stats.period.todayCard.title" = "Сегодня"; +/* Table column title that shows the date since the user became a subscriber. */ +"stats.section.dataSubtitles.subscriberSince" = "Подписчик с"; + +/* Table column title that shows the names of subscribers. */ +"stats.section.itemSubtitles.subscriber" = "Имя"; + +/* Stats 'Subscribers' card header, contains chart */ +"stats.subscribers.chart.title" = "Подписчики"; + +/* A title for table's column that shows a number of times a post was opened from an email */ +"stats.subscribers.emailsSummary.column.clicks" = "Переходы"; + +/* A title for table's column that shows a number of email openings */ +"stats.subscribers.emailsSummary.column.opens" = "Посещения"; + +/* A title for table's column that shows a name of an email */ +"stats.subscribers.emailsSummary.column.title" = "Недавние рассылки"; + +/* Stats 'Emails' card header */ +"stats.subscribers.emailsSummaryCard.title" = "Рассылки"; + +/* Stats 'Subscribers' card header */ +"stats.subscribers.subscribersListCard.title" = "Подписчики"; + +/* Accessibility of stats table. Placeholders will be populated with names of data shown in table. */ +"stats.topTotalsCell.voiceOverDescription" = "Таблица, где показаны %1$@, %2$@ и %3$@"; + /* This description is used to set the accessibility label for the Stats Traffic chart, with Comments selected. */ "stats.traffic.accessibilityLabel.comments" = "Столбчатая диаграмма, представляющая комментарии за выбранный период."; @@ -11463,6 +11706,18 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* This description is used to set the accessibility label for the Stats Traffic chart, with Visitors selected. */ "stats.traffic.accessibilityLabel.visitors" = "Столбчатая диаграмма, представляющая посетителей за выбранный период."; +/* The label for the option to show Stats Traffic chart for Days. */ +"stats.traffic.days" = "Дни"; + +/* The label for the option to show Stats Traffic chart for Months. */ +"stats.traffic.months" = "Месяцы"; + +/* The label for the option to show Stats Traffic chart for Weeks. */ +"stats.traffic.weeks" = "Недели"; + +/* The label for the option to show Stats Traffic chart for Years. */ +"stats.traffic.years" = "Годы"; + /* Dismiss the AlertView */ "stockPhotos.strings.dismiss" = "Закрыть"; @@ -11472,9 +11727,27 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for placeholder in Free Photos */ "stockPhotos.title" = "Найдите бесплатные фотографии и добавьте их в вашу Библиотеку файлов!"; +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.description" = "По желанию вы можете указать свой адрес электронной почты и имя пользователя, чтобы помочь нам понять ситуацию."; + +/* Label we show on an email input field */ +"submit.feedback.alert.empty.email" = "адрес эл. почты не указан"; + +/* Label we show on an name input field */ +"submit.feedback.alert.empty.username" = "имя пользователя не указано"; + +/* Alert submit option for users to accept sharing their email and name when submitting feedback. */ +"submit.feedback.alert.submit" = "Готово"; + +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.title" = "Спасибо за ваш отзыв!"; + /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "Отправить"; +/* Notice informing user that their feedback is being submitted. */ +"submit.feedback.submit.loading" = "Отправка"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "Обратная связь"; @@ -11646,6 +11919,21 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Displayed in the confirmation alert when marking unread notifications as read. */ "unread" = "непрочитано"; +/* Site's Email Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.emailSubscriber" = "Подписчик на рассылку сайта по эл. почте"; + +/* Site's Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.subscriber" = "Подписчик сайта"; + +/* Sites's User Profile. Displayed when the name is empty! */ +"user.details.title.user" = "Пользователь сайта"; + +/* Site's Viewers Profile. Displayed when the name is empty! */ +"user.details.title.viewer" = "Посетитель сайта"; + +/* Site Subscribers */ +"users.list.title.subscribers" = "Подписчики"; + /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ "visit our documentation page" = "посетите нашу страницу документации"; diff --git a/WordPress/Resources/sq.lproj/Localizable.strings b/WordPress/Resources/sq.lproj/Localizable.strings index edcfbba614fd..54dc21dcb8d9 100644 --- a/WordPress/Resources/sq.lproj/Localizable.strings +++ b/WordPress/Resources/sq.lproj/Localizable.strings @@ -10753,9 +10753,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "Kaloni te Pajtime"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "Shtoni një blog"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "S’arrihet të bllokohet blogu"; diff --git a/WordPress/Resources/sv.lproj/Localizable.strings b/WordPress/Resources/sv.lproj/Localizable.strings index 195895803aea..dd1c007ad120 100644 --- a/WordPress/Resources/sv.lproj/Localizable.strings +++ b/WordPress/Resources/sv.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-29 12:22:41+0000 */ +/* Translation-Revision-Date: 2024-05-04 14:07:56+0000 */ /* Plural-Forms: nplurals=2; plural=n != 1; */ /* Generator: GlotPress/4.0.1 */ /* Language: sv_SE */ @@ -8856,6 +8856,12 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ /* An alert suggesting to load autosaved revision for a published post */ "autosaveAlert.cancel" = "Avbryt"; +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.message" = "Du har ej sparade ändringar i det här inlägget som har gjorts med en annan enhet. Redigerade: %@."; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.title" = "Autosparning tillgänglig"; + /* An alert suggesting to load autosaved revision for a published post */ "autosaveAlert.viewChanges" = "Visa ändringar"; @@ -10323,6 +10329,12 @@ Example: Reply to Pamela Nguyen */ /* Button in an alert confirming saving a new draft */ "postEditor.closeConfirmationAlert.saveDraft" = "Spara utkast"; +/* Creating autosave to generate a preview (status message */ +"postEditor.creatingAutosaveForPreview" = "Skapar autosparning …"; + +/* Title for a snackbar */ +"postEditor.revisionLoaded" = "Versionen har lästs in"; + /* Saving draft to generate a preview (status message */ "postEditor.savingDraftForPreview" = "Sparar utkast …"; @@ -10338,12 +10350,21 @@ Example: Reply to Pamela Nguyen */ /* Accessibility label for a post in the post list. The first placeholder is the post title. The second placeholder is the date. */ "postList.a11y.titleAndDateChunkFormat" = "%1$@, %2$@."; +/* Badge for post cells */ +"postList.badgePendingReview" = "Inväntar granskning"; + /* Cancels an Action */ "postList.cancel" = "Avbryt"; /* Delete option in the confirmation alert when deleting a page from the trash. */ "postList.deletePermanently.actionTitle" = "Ta bort permanent"; +/* Message of the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.alertMessage" = "Är du säker på att du vill ta bort ”%@” permanent?"; + +/* An error message */ +"postList.errorUnsyncedChangesMessage" = "Appen laddar upp tidigare gjorda ändringar till servern. Försök igen senare."; + /* A generic error message title */ "postList.genericErrorMessage" = "Något gick fel"; @@ -10365,6 +10386,12 @@ Example: Reply to Pamela Nguyen */ /* Trash option in the trash post or page confirmation alert. */ "postList.trash.actionTitle" = "Flytta till papperskorgen"; +/* Message of the trash post or page confirmation alert. */ +"postList.trash.alertMessage" = "Är du säker på att du vill lägga %@ i papperskorgen? Ändringar som inte skickats till servern tidigare kommer att gå förlorade."; + +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "Avbryt uppladdning"; + /* Close button in postMediaUploadStatusView */ "postMediaUploadStatusView.close" = "Stäng"; @@ -10374,6 +10401,9 @@ Example: Reply to Pamela Nguyen */ /* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ "postMediaUploadStatusView.progress" = "%1$@ av %2$@"; +/* Retry (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUpload" = "Försök ladda upp igen"; + /* Retry upload button in postMediaUploadStatusView */ "postMediaUploadStatusView.retryUploads" = "Försök ladda upp igen"; @@ -10383,6 +10413,9 @@ Example: Reply to Pamela Nguyen */ /* A generic error message title */ "postNotice.errorTitle" = "Ett fel uppstod"; +/* An error message */ +"postNotice.errorUnsyncedChangesMessage" = "Appen laddar upp tidigare gjorda ändringar till servern. Försök igen senare."; + /* Button OK */ "postNotice.ok" = "OK"; @@ -10419,6 +10452,12 @@ Example: Reply to Pamela Nguyen */ /* Button title. Displays a summary / sharing screen for a specific post. */ "postNotice.view" = "Visa"; +/* Error message: content was modified on another device */ +"postSaveErrorMessage.conflict" = "Innehållet ändrades på annan enhet"; + +/* Error message: item permanently deleted */ +"postSaveErrorMessage.deleted" = "”%@” togs bort permanent och kan inte längre uppdateras"; + /* A default value for an post without a title */ "postSaveErrorMessage.postUntitled" = "Utan rubrik"; @@ -10434,6 +10473,9 @@ Example: Reply to Pamela Nguyen */ /* Title for action sheet with featured media options. */ "postSettings.featuredImageUploadActionSheet.title" = "Alternativ för utvald bild"; +/* The 'Pending Review' setting of the post */ +"postSettings.pendingReview" = "Inväntar granskning"; + /* Section title for the disabled Twitter service in the Post Settings screen */ "postSettings.section.disabledTwitter.header" = "Automatisk delning på Twitter är inte längre tillgängligt"; @@ -10443,9 +10485,15 @@ Example: Reply to Pamela Nguyen */ /* Error message on post/page settings screen */ "postSettings.updateFailedMessage" = "Det gick inte att uppdatera inläggsinställningarna"; +/* Details for a 'Private' privacy setting */ +"postVisibility.private.details" = "Endast synlig för webbplatsadministratörer och redigerare."; + /* Title for a 'Private' privacy setting */ "postVisibility.private.title" = "Privat"; +/* Details for a 'Password Protected' privacy setting */ +"postVisibility.protected.details" = "Synlig för alla men kräver ett lösenord"; + /* Title for a 'Password Protected' privacy setting */ "postVisibility.protected.title" = "Lösenordsskyddat"; @@ -10560,6 +10608,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.jetpackSocial" = "Jetpack Social"; +/* Details for a publish button state in the pre-publishing sheet; count as a parameter */ +"prepublishing.mediaUploadFailedDetails" = "%@ objekt kunde inte laddas upp"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.mediaUploadFailedTitle" = "Misslyckades att ladda upp media"; @@ -10639,6 +10690,12 @@ Tapping on this row allows the user to edit the sharing message. */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.tags" = "Etiketter"; +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaManyItemsRemaining" = "%@ objekt återstår"; + +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaOneItemRemaining" = "%@ objekt återstår"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.uploadingMedia" = "Laddar upp media"; @@ -10651,6 +10708,9 @@ Tapping on this row allows the user to edit the sharing message. */ /* Post publish date picker title for date cell */ "publishDatePicker.date" = "Publiceringsdatum"; +/* Post publish date picker footer view when the selected date time zone is different from your current time zone; followed by the time in the current time zone. */ +"publishDatePicker.footerCurrentTimezone" = "Datumet i din nuvarande tidszon:"; + /* Post publish date picker: selected value placeholder when no date is selected and the post will be published immediately */ "publishDatePicker.immediately" = "Omedelbart"; @@ -10864,6 +10924,9 @@ with the filter chip button. */ /* Reader settings button accessibility label. */ "reader.navigation.settings.button.label" = "Läsarinställningar"; +/* No Tags View Button Label */ +"reader.no.blog.title" = "Lägg till en blogg"; + /* Title for button on the no followed blogs result screen */ "reader.no.blogs.button" = "Upptäck bloggar"; @@ -10889,7 +10952,7 @@ with the filter chip button. */ "reader.no.results.subscriptions.button" = "Gå till prenumerationer"; /* No Tags View Button Label */ -"reader.no.tags.title" = "Lägg till en blogg"; +"reader.no.tags.title" = "Lägg till en etikett"; /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Kan inte blockera blogg"; @@ -11136,6 +11199,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title of a feature to add a new tag to the tags subscribed by the user. */ "reader.tags.add.tag.title" = "Lägg till en etikett"; +/* Text for the 'Like' button on the reader tag cell. */ +"reader.tags.button.like" = "Gilla"; + +/* Text for the 'Liked' button on the reader tag cell. */ +"reader.tags.button.liked" = "Gillat"; + /* Button title. Tapping shows the Subscribe to Tags screen. */ "reader.tags.discover.more.tags" = "Upptäck fler etiketter"; @@ -11436,6 +11505,9 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* All sites section title for site switcher. */ "site_switcher.all_sites_section.title" = "Alla webbplatser"; +/* Pinned section title for site switcher. */ +"site_switcher.pinned_section.title" = "Fastnålade webbplatser"; + /* Recents section title for site switcher. */ "site_switcher.recents_section.title" = "Senaste webbplatser"; @@ -11598,6 +11670,15 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "Ditt senaste inlägg %1$@ har fått %2$@ gillamarkering."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "Totalt antal WordPress.com-prenumeranter: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "Totalt antal e-postprenumeranter: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "Totalt antal prenumeranter"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; @@ -11616,6 +11697,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A title for table's column that shows a number of times a post was opened from an email */ "stats.subscribers.emailsSummary.column.clicks" = "Klick"; +/* A title for table's column that shows a number of email openings */ +"stats.subscribers.emailsSummary.column.opens" = "Öppnade"; + +/* A title for table's column that shows a name of an email */ +"stats.subscribers.emailsSummary.column.title" = "Senaste e-postmeddelanden"; + /* Stats 'Emails' card header */ "stats.subscribers.emailsSummaryCard.title" = "E-post"; @@ -11658,9 +11745,15 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for placeholder in Free Photos */ "stockPhotos.title" = "Sök efter gratis foton att lägga till i ditt mediabibliotek!"; +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.description" = "Du kan valfritt inkludera din e-post och ditt användarnamn för att hjälpa oss att förstå din upplevelse."; + /* Label we show on an email input field */ "submit.feedback.alert.empty.email" = "ingen e-post angiven"; +/* Label we show on an name input field */ +"submit.feedback.alert.empty.username" = "inget användarnamn angivet"; + /* Alert submit option for users to accept sharing their email and name when submitting feedback. */ "submit.feedback.alert.submit" = "Klar"; @@ -11670,10 +11763,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "Skicka"; -/* Notice informing user that their feedback is being submitted anonymously. - Notice informing user that their feedback is being submitted. */ +/* Notice informing user that their feedback is being submitted. */ "submit.feedback.submit.loading" = "Skickar"; +/* Notice informing user that their feedback is being submitted anonymously. */ +"submit.feedback.submitAnonymously.loading" = "Skickar anonymt"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "Feedback"; @@ -11845,8 +11940,22 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Displayed in the confirmation alert when marking unread notifications as read. */ "unread" = "ej läst"; -/* Site Email Subscribers - Site Subscribers */ +/* Site's Email Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.emailSubscriber" = "Webbplatsens e-postprenumerant"; + +/* Site's Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.subscriber" = "Webbplatsens prenumerant"; + +/* Sites's User Profile. Displayed when the name is empty! */ +"user.details.title.user" = "Webbplatsens användare"; + +/* Site's Viewers Profile. Displayed when the name is empty! */ +"user.details.title.viewer" = "Webbplatsens tittare"; + +/* Site Email Subscribers */ +"users.list.title.emailSubscribers" = "E-postprenumeranter"; + +/* Site Subscribers */ "users.list.title.subscribers" = "Prenumeranter"; /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ diff --git a/WordPress/Resources/tr.lproj/Localizable.strings b/WordPress/Resources/tr.lproj/Localizable.strings index 4e40025ad6a4..801fcf6d0cd4 100644 --- a/WordPress/Resources/tr.lproj/Localizable.strings +++ b/WordPress/Resources/tr.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-23 15:54:08+0000 */ +/* Translation-Revision-Date: 2024-05-07 13:54:08+0000 */ /* Plural-Forms: nplurals=2; plural=n > 1; */ /* Generator: GlotPress/4.0.1 */ /* Language: tr */ @@ -8853,6 +8853,18 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ /* translators: displays audio file extension. e.g. MP3 audio file */ "audio file" = "ses dosyası"; +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.cancel" = "İptal Et"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.message" = "Farklı bir cihazdan bu gönderide kaydedilmeyen değişiklikler yaptınız. Düzenlendi: %@."; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.title" = "Otomatik Kaydetme Kullanılabilir"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.viewChanges" = "Değişiklikleri Görüntüleyin"; + /* Alert message when something goes wrong with the selected image. */ "avatarMenu.failedToSetAvatarAlertMessage" = "Görsel yüklenemedi. Lütfen başka bir tane seçin ya da daha sonra yeniden deneyin."; @@ -10305,6 +10317,27 @@ Example: Reply to Pamela Nguyen */ /* Status mesasge for post cells */ "post.movingToTrashStatusMessage" = "Yazı çöpe atılıyor..."; +/* Button in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.discardChanges" = "Değişiklikleri İptal Et"; + +/* Button in an alert confirming discaring a new draft */ +"postEditor.closeConfirmationAlert.discardDraft" = "Taslaktan Vazgeç"; + +/* Button to keep the changes in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.keepEditing" = "Düzenlemeye Devam Et"; + +/* Button in an alert confirming saving a new draft */ +"postEditor.closeConfirmationAlert.saveDraft" = "Taslağı Kaydet"; + +/* Creating autosave to generate a preview (status message */ +"postEditor.creatingAutosaveForPreview" = "Otomatik kayıt oluşturuluyor..."; + +/* Title for a snackbar */ +"postEditor.revisionLoaded" = "Revizyon yüklendi"; + +/* Saving draft to generate a preview (status message */ +"postEditor.savingDraftForPreview" = "Taslak kaydediliyor..."; + /* Accessibility label for the post author in the post list. The parameter is the author name. For example, \"By Elsa.\" */ "postList.a11y.authorChunkFormat" = "%@ tarafından."; @@ -10317,6 +10350,24 @@ Example: Reply to Pamela Nguyen */ /* Accessibility label for a post in the post list. The first placeholder is the post title. The second placeholder is the date. */ "postList.a11y.titleAndDateChunkFormat" = "%1$@, %2$@."; +/* Badge for post cells */ +"postList.badgePendingReview" = "İnceleme bekliyor"; + +/* Cancels an Action */ +"postList.cancel" = "İptal Et"; + +/* Delete option in the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.actionTitle" = "Kalıcı Olarak Sil"; + +/* Message of the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.alertMessage" = "\"%@\" sayfasını kalıcı olarak silmek istediğinizden emin misiniz?"; + +/* An error message */ +"postList.errorUnsyncedChangesMessage" = "Uygulama, sunucuda önceden yapılan değişiklikleri yüklüyor. Lütfen daha sonra tekrar deneyin."; + +/* A generic error message title */ +"postList.genericErrorMessage" = "Bir sorun oluştu"; + /* Label for a post in the post list. Indicates that the post has offline changes. */ "postList.offlineChanges" = "Çevrimdışı değişiklikler"; @@ -10332,6 +10383,42 @@ Example: Reply to Pamela Nguyen */ /* Title for the 'View' post list row swipe action */ "postList.swipeActionView" = "Görüntüle"; +/* Trash option in the trash post or page confirmation alert. */ +"postList.trash.actionTitle" = "Çöp Kutusuna Taşı"; + +/* Message of the trash post or page confirmation alert. */ +"postList.trash.alertMessage" = "\"%@\" öğesini çöp kutusuna atmak istediğinizden emin misiniz? Daha önceden sunucuya gönderilmeyen tüm değişiklikler kaybolur."; + +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "Yüklemeyi İptal Et"; + +/* Close button in postMediaUploadStatusView */ +"postMediaUploadStatusView.close" = "Kapat"; + +/* Placeholder text in postMediaUploadStatusView when no uploads remain */ +"postMediaUploadStatusView.noPendingUploads" = "Bekleyen yükleme yok"; + +/* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ +"postMediaUploadStatusView.progress" = "%1$@\/%2$@"; + +/* Retry (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUpload" = "Yüklemeyi Yeniden Dene"; + +/* Retry upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUploads" = "Yüklemeleri Yeniden Dene"; + +/* Title for post media upload status view */ +"postMediaUploadStatusView.title" = "Ortam Yüklemeleri"; + +/* A generic error message title */ +"postNotice.errorTitle" = "Bir hata oluştu"; + +/* An error message */ +"postNotice.errorUnsyncedChangesMessage" = "Uygulama, sunucuda önceden yapılan değişiklikleri yüklüyor. Lütfen daha sonra tekrar deneyin."; + +/* Button OK */ +"postNotice.ok" = "Tamam"; + /* Title of notification displayed when a page has been successfully saved as a draft. */ "postNotice.pageDraftCreated" = "Sayfa taslağı karşıya yüklendi"; @@ -10365,6 +10452,15 @@ Example: Reply to Pamela Nguyen */ /* Button title. Displays a summary / sharing screen for a specific post. */ "postNotice.view" = "Görüntüle"; +/* Error message: content was modified on another device */ +"postSaveErrorMessage.conflict" = "Bu içerik başka bir cihazda değiştirilmiş"; + +/* Error message: item permanently deleted */ +"postSaveErrorMessage.deleted" = "\"%@\" kalıcı olarak silindi ve artık güncellenemez"; + +/* A default value for an post without a title */ +"postSaveErrorMessage.postUntitled" = "Başlıksız"; + /* User action to dismiss featured media options. */ "postSettings.featuredImageUploadActionSheet.dismiss" = "Kapat"; @@ -10377,6 +10473,9 @@ Example: Reply to Pamela Nguyen */ /* Title for action sheet with featured media options. */ "postSettings.featuredImageUploadActionSheet.title" = "Öne çıkarılmış görsel seçenekleri"; +/* The 'Pending Review' setting of the post */ +"postSettings.pendingReview" = "İnceleme bekliyor"; + /* Section title for the disabled Twitter service in the Post Settings screen */ "postSettings.section.disabledTwitter.header" = "X otomatik paylaşım özelliği artık kullanılamıyor"; @@ -10386,6 +10485,36 @@ Example: Reply to Pamela Nguyen */ /* Error message on post/page settings screen */ "postSettings.updateFailedMessage" = "Yazı ayarları güncellenemedi"; +/* Details for a 'Private' privacy setting */ +"postVisibility.private.details" = "Yalnızca site yöneticileri ve düzenleyicileri tarafından görülebilir"; + +/* Title for a 'Private' privacy setting */ +"postVisibility.private.title" = "Gizli"; + +/* Details for a 'Password Protected' privacy setting */ +"postVisibility.protected.details" = "Herkes tarafından görülebilir ancak şifre gerektirir"; + +/* Title for a 'Password Protected' privacy setting */ +"postVisibility.protected.title" = "Şifre korumalı"; + +/* Details for a 'Public' (default) privacy setting */ +"postVisibility.public.details" = "Herkes tarafından görülebilir"; + +/* Title for a 'Public' (default) privacy setting */ +"postVisibility.public.title" = "Herkese açık"; + +/* Button cancel */ +"postVisibilityPicker.cancel" = "İptal Et"; + +/* Navigation bar title for the Post Visibility picker */ +"postVisibilityPicker.navigationTitle" = "Görünürlük"; + +/* Password placeholder text */ +"postVisibilityPicker.password" = "Şifre"; + +/* Button save */ +"postVisibilityPicker.save" = "Kaydet"; + /* Promote the post with Blaze. */ "posts.blaze.actionTitle" = "Blaze ile tanıt"; @@ -10410,6 +10539,9 @@ Example: Reply to Pamela Nguyen */ /* Label for the preview post button. Tapping displays the post as it appears on the web. */ "posts.preview.actionTitle" = "Ön izleme"; +/* Label for the publish post button. */ +"posts.publish.actionTitle" = "Yayımla"; + /* Retry uploading the post. */ "posts.retry.actionTitle" = "Yeniden dene"; @@ -10476,6 +10608,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.jetpackSocial" = "Jetpack Social"; +/* Details for a publish button state in the pre-publishing sheet; count as a parameter */ +"prepublishing.mediaUploadFailedDetails" = "%@ öğe karşıya yüklenemedi"; + /* Placeholder for a cell in the pre-publishing sheet */ "prepublishing.postTitle" = "Başlık"; @@ -10485,6 +10620,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.publishDate" = "Yayınlanma tarihi"; +/* Placeholder value for a publishing date in the prepublishing sheet when the date is not selected */ +"prepublishing.publishDateImmediately" = "Hemen"; + /* Label in the header in the pre-publishing sheet */ "prepublishing.publishingTo" = "Şuraya yayınlanıyor"; @@ -10549,6 +10687,12 @@ Tapping on this row allows the user to edit the sharing message. */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.tags" = "Etiketler"; +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaManyItemsRemaining" = "%@ öğe kaldı"; + +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaOneItemRemaining" = "%@ öğe kaldı"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.uploadingMedia" = "Ortam dosyası yükleniyor"; @@ -10558,6 +10702,24 @@ Tapping on this row allows the user to edit the sharing message. */ /* Title for a tappable string that opens the reader with a prompts tag */ "prompts.card.viewprompts.title" = "Tüm yanıtları görüntüle"; +/* Post publish date picker title for date cell */ +"publishDatePicker.date" = "Yayımlama Tarihi"; + +/* Post publish date picker footer view when the selected date time zone is different from your current time zone; followed by the time in the current time zone. */ +"publishDatePicker.footerCurrentTimezone" = "Mevcut saat diliminizdeki tarih:"; + +/* Post publish date picker: selected value placeholder when no date is selected and the post will be published immediately */ +"publishDatePicker.immediately" = "Hemen"; + +/* Title for button in publish date picker */ +"publishDatePicker.removePublishDate" = "Yayımlama Tarihini Kaldır"; + +/* Title for button in publish date picker */ +"publishDatePicker.selectPublishDate" = "Yayımlama Tarihi Seç"; + +/* Post publish time zone cell title */ +"publishDatePicker.timeZone" = "Saat Dilimi"; + /* Post publish date picker */ "publishDatePicker.title" = "Yayınlanma tarihi"; @@ -10759,6 +10921,9 @@ with the filter chip button. */ /* Reader settings button accessibility label. */ "reader.navigation.settings.button.label" = "Okuyucu ayarları"; +/* No Tags View Button Label */ +"reader.no.blog.title" = "Blog ekle"; + /* Title for button on the no followed blogs result screen */ "reader.no.blogs.button" = "Blogları keşfedin"; @@ -10784,7 +10949,7 @@ with the filter chip button. */ "reader.no.results.subscriptions.button" = "Aboneliklere git"; /* No Tags View Button Label */ -"reader.no.tags.title" = "Blog ekle"; +"reader.no.tags.title" = "Etiket ekle"; /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "Blog engellenemedi"; @@ -11025,6 +11190,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title of a feature to add a new tag to the tags subscribed by the user. */ "reader.tags.add.tag.title" = "Etiket ekle"; +/* Text for the 'Like' button on the reader tag cell. */ +"reader.tags.button.like" = "Beğen"; + +/* Text for the 'Liked' button on the reader tag cell. */ +"reader.tags.button.liked" = "Beğenildi"; + /* Button title. Tapping shows the Subscribe to Tags screen. */ "reader.tags.discover.more.tags" = "Daha fazla etiket keşfet"; @@ -11127,6 +11298,24 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for screen that allows configuration of your blog/site related posts settings. */ "relatedPostsSettings.title" = "İlgili yazılar"; +/* A version of the post on another device. */ +"resolveConflict.anotherDevice" = "Başka cihaz"; + +/* A version of the post on the current device. */ +"resolveConflict.currentDevice" = "Mevcut cihaz"; + +/* Description for the Resolve Conflict screen. */ +"resolveConflict.description" = "Bu gönderi başka bir cihazda değiştirilmiş. Lütfen gönderinin hangi versiyonunu korumak istediğinizi seçin."; + +/* Title for the cancel button on the Resolve Conflict screen. */ +"resolveConflict.navigation.cancel" = "İptal Et"; + +/* Title for the save button on the Resolve Conflict screen. */ +"resolveConflict.navigation.save" = "Kaydet"; + +/* Title for the Resolve Conflict screen. */ +"resolveConflict.navigation.title" = "Çakışmayı Çözün"; + /* Title for the \"Copy Link\" action in Share Sheet. */ "share.sheet.copy.link.title" = "Bağlantıyı Kopyala"; @@ -11304,6 +11493,30 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Text for unknown privacy setting */ "siteVisibility.unknown.title" = "Bilinmiyor"; +/* All sites section title for site switcher. */ +"site_switcher.all_sites_section.title" = "Tüm siteler"; + +/* Pinned section title for site switcher. */ +"site_switcher.pinned_section.title" = "Sabitlenen siteler"; + +/* Recents section title for site switcher. */ +"site_switcher.recents_section.title" = "Son siteler"; + +/* CTA title for the site switcher screen. */ +"site_switcher_cta_title" = "Site ekleyin"; + +/* Dismiss button title above the search. */ +"site_switcher_dismiss_button_title" = "İptal Et"; + +/* Done button title above the search. */ +"site_switcher_done_button_title" = "Tamam"; + +/* Edit button title above the search. */ +"site_switcher_edit_button_title" = "Düzenle"; + +/* Title for site switcher screen. */ +"site_switcher_title" = "Bir site seçin"; + /* Label for the blogging reminders setting */ "sitesettings.reminders.title" = "Hatırlatıcılar"; @@ -11340,6 +11553,9 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A smallprint that hints the reason behind why Twitter is deprecated. */ "social.twitterDeprecation.text" = "X otomatik paylaşım özelliği, X koşullarındaki ve fiyatlarındaki değişiklikler nedeniyle artık kullanılmıyor."; +/* Title of Subscribers stats tab. */ +"stats.dashboard.tab.subscribers" = "Aboneler"; + /* Title of Traffic stats tab. */ "stats.dashboard.tab.traffic" = "Trafik"; @@ -11436,18 +11652,57 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Hint displayed on the 'Most Popular Time' stats card when a user's site hasn't yet received enough traffic. */ "stats.insights.mostPopularTime.noData" = "Yeterli etkinlik yok. Bir süre sonra sitenizin daha fazla ziyaretçisi olduğunda yeniden bakın!"; +/* Insights 'Subscribers' header */ +"stats.insights.subscribers.title" = "Aboneler"; + /* A hint shown to the user in stats informing the user how many likes one of their posts has received. The %1$@ placeholder will be replaced with the title of a post, the %2$@ with the number of likes. */ "stats.insights.totalLikes.guideText.plural" = "Son yazınız %1$@ %2$@ beğeni aldı."; /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "Son yazınız %1$@ %2$@ beğeni aldı."; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "Toplam WordPress.com Abonesi: %@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "Toplam E-posta Abonesi: %@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "Toplam Abone"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; /* Stats 'Today' header */ "stats.period.todayCard.title" = "Bugün"; +/* Table column title that shows the date since the user became a subscriber. */ +"stats.section.dataSubtitles.subscriberSince" = "Şu tarihten beri abone"; + +/* Table column title that shows the names of subscribers. */ +"stats.section.itemSubtitles.subscriber" = "Ad"; + +/* Stats 'Subscribers' card header, contains chart */ +"stats.subscribers.chart.title" = "Aboneler"; + +/* A title for table's column that shows a number of times a post was opened from an email */ +"stats.subscribers.emailsSummary.column.clicks" = "Tıklamalar"; + +/* A title for table's column that shows a number of email openings */ +"stats.subscribers.emailsSummary.column.opens" = "Açmalar"; + +/* A title for table's column that shows a name of an email */ +"stats.subscribers.emailsSummary.column.title" = "En son e-postalar"; + +/* Stats 'Emails' card header */ +"stats.subscribers.emailsSummaryCard.title" = "E-postalar"; + +/* Stats 'Subscribers' card header */ +"stats.subscribers.subscribersListCard.title" = "Aboneler"; + +/* Accessibility of stats table. Placeholders will be populated with names of data shown in table. */ +"stats.topTotalsCell.voiceOverDescription" = "%1$@, %2$@ ve %3$@ gösteren tablo"; + /* This description is used to set the accessibility label for the Stats Traffic chart, with Comments selected. */ "stats.traffic.accessibilityLabel.comments" = "Seçilmiş dönemdeki yorumları gösteren çubuk grafik."; @@ -11460,6 +11715,18 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* This description is used to set the accessibility label for the Stats Traffic chart, with Visitors selected. */ "stats.traffic.accessibilityLabel.visitors" = "Seçilmiş dönemdeki ziyaretçileri gösteren çubuk grafik."; +/* The label for the option to show Stats Traffic chart for Days. */ +"stats.traffic.days" = "Gün"; + +/* The label for the option to show Stats Traffic chart for Months. */ +"stats.traffic.months" = "Ay"; + +/* The label for the option to show Stats Traffic chart for Weeks. */ +"stats.traffic.weeks" = "Hafta"; + +/* The label for the option to show Stats Traffic chart for Years. */ +"stats.traffic.years" = "Yıl"; + /* Dismiss the AlertView */ "stockPhotos.strings.dismiss" = "Kapat"; @@ -11469,9 +11736,30 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for placeholder in Free Photos */ "stockPhotos.title" = "Ortam kitaplığınıza eklemek için ücretsiz fotoğraflar arayın!"; +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.description" = "Deneyiminizi anlamamıza yardımcı olmak için isteğe bağlı olarak e-posta adresinizi veya kullanıcı adınızı ekleyebilirsiniz."; + +/* Label we show on an email input field */ +"submit.feedback.alert.empty.email" = "e-posta adresi girilmedi"; + +/* Label we show on an name input field */ +"submit.feedback.alert.empty.username" = "kullanıcı adı girilmedi"; + +/* Alert submit option for users to accept sharing their email and name when submitting feedback. */ +"submit.feedback.alert.submit" = "Tamam"; + +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.title" = "Geri bildiriminiz için teşekkürler"; + /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "Gönder"; +/* Notice informing user that their feedback is being submitted. */ +"submit.feedback.submit.loading" = "Gönderiliyor"; + +/* Notice informing user that their feedback is being submitted anonymously. */ +"submit.feedback.submitAnonymously.loading" = "Anonim olarak gönderiliyor"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "Geri bildirim"; @@ -11643,6 +11931,24 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Displayed in the confirmation alert when marking unread notifications as read. */ "unread" = "okunmamış"; +/* Site's Email Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.emailSubscriber" = "Sitenin E-posta Abonesi"; + +/* Site's Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.subscriber" = "Sitenin Abonesi"; + +/* Sites's User Profile. Displayed when the name is empty! */ +"user.details.title.user" = "Sitenin Kullanıcısı"; + +/* Site's Viewers Profile. Displayed when the name is empty! */ +"user.details.title.viewer" = "Sitenin Görüntüleyicileri"; + +/* Site Email Subscribers */ +"users.list.title.emailSubscribers" = "E-posta Aboneleri"; + +/* Site Subscribers */ +"users.list.title.subscribers" = "Aboneler"; + /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ "visit our documentation page" = "belgeler sayfamıza bakın"; diff --git a/WordPress/Resources/zh-Hans.lproj/Localizable.strings b/WordPress/Resources/zh-Hans.lproj/Localizable.strings index 37328e38de77..53d7ed3b1d14 100644 --- a/WordPress/Resources/zh-Hans.lproj/Localizable.strings +++ b/WordPress/Resources/zh-Hans.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-23 09:54:08+0000 */ +/* Translation-Revision-Date: 2024-05-02 13:54:07+0000 */ /* Plural-Forms: nplurals=1; plural=0; */ /* Generator: GlotPress/4.0.1 */ /* Language: zh_CN */ @@ -8850,6 +8850,18 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ /* translators: displays audio file extension. e.g. MP3 audio file */ "audio file" = "音频文件"; +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.cancel" = "取消"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.message" = "您在其他设备上对该文章进行的更改未保存。 已编辑:%@。"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.title" = "支持自动保存"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.viewChanges" = "查看更改"; + /* Alert message when something goes wrong with the selected image. */ "avatarMenu.failedToSetAvatarAlertMessage" = "无法加载图片。 请选择其他图片或稍后重试。"; @@ -10287,6 +10299,27 @@ Example: Reply to Pamela Nguyen */ /* Status mesasge for post cells */ "post.movingToTrashStatusMessage" = "正在将文章移至回收站..."; +/* Button in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.discardChanges" = "放弃更改"; + +/* Button in an alert confirming discaring a new draft */ +"postEditor.closeConfirmationAlert.discardDraft" = "放弃草稿"; + +/* Button to keep the changes in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.keepEditing" = "继续编辑"; + +/* Button in an alert confirming saving a new draft */ +"postEditor.closeConfirmationAlert.saveDraft" = "保存草稿"; + +/* Creating autosave to generate a preview (status message */ +"postEditor.creatingAutosaveForPreview" = "正在创建自动保存……"; + +/* Title for a snackbar */ +"postEditor.revisionLoaded" = "修订内容已加载"; + +/* Saving draft to generate a preview (status message */ +"postEditor.savingDraftForPreview" = "正在保存草稿……"; + /* Accessibility label for the post author in the post list. The parameter is the author name. For example, \"By Elsa.\" */ "postList.a11y.authorChunkFormat" = "作者是 %@。"; @@ -10299,6 +10332,24 @@ Example: Reply to Pamela Nguyen */ /* Accessibility label for a post in the post list. The first placeholder is the post title. The second placeholder is the date. */ "postList.a11y.titleAndDateChunkFormat" = "%1$@,%2$@。"; +/* Badge for post cells */ +"postList.badgePendingReview" = "待审核"; + +/* Cancels an Action */ +"postList.cancel" = "取消"; + +/* Delete option in the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.actionTitle" = "永久删除"; + +/* Message of the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.alertMessage" = "是否确定要永久删除此 \"%@?"; + +/* An error message */ +"postList.errorUnsyncedChangesMessage" = "该应用程序正在将此前所做的更改上传至服务器。 请稍后重试。"; + +/* A generic error message title */ +"postList.genericErrorMessage" = "出错了"; + /* Label for a post in the post list. Indicates that the post has offline changes. */ "postList.offlineChanges" = "离线更改"; @@ -10314,6 +10365,42 @@ Example: Reply to Pamela Nguyen */ /* Title for the 'View' post list row swipe action */ "postList.swipeActionView" = "查看"; +/* Trash option in the trash post or page confirmation alert. */ +"postList.trash.actionTitle" = "移至回收站"; + +/* Message of the trash post or page confirmation alert. */ +"postList.trash.alertMessage" = "是否确定要将 \"%@\" 移至回收站? 此前未发送到服务器的任何更改都将丢失。"; + +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "取消上传"; + +/* Close button in postMediaUploadStatusView */ +"postMediaUploadStatusView.close" = "关闭"; + +/* Placeholder text in postMediaUploadStatusView when no uploads remain */ +"postMediaUploadStatusView.noPendingUploads" = "无待处理的上传"; + +/* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ +"postMediaUploadStatusView.progress" = "第 %1$@ 个,共 %2$@ 个"; + +/* Retry (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUpload" = "重试上传"; + +/* Retry upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUploads" = "重试上传"; + +/* Title for post media upload status view */ +"postMediaUploadStatusView.title" = "媒体上传"; + +/* A generic error message title */ +"postNotice.errorTitle" = "出错了"; + +/* An error message */ +"postNotice.errorUnsyncedChangesMessage" = "该应用程序正在将此前所做的更改上传至服务器。 请稍后重试。"; + +/* Button OK */ +"postNotice.ok" = "确定"; + /* Title of notification displayed when a page has been successfully saved as a draft. */ "postNotice.pageDraftCreated" = "页面草稿已上传"; @@ -10347,6 +10434,15 @@ Example: Reply to Pamela Nguyen */ /* Button title. Displays a summary / sharing screen for a specific post. */ "postNotice.view" = "查看"; +/* Error message: content was modified on another device */ +"postSaveErrorMessage.conflict" = "在其他设备上修改过此内容。"; + +/* Error message: item permanently deleted */ +"postSaveErrorMessage.deleted" = "\"%@\" 已被永久删除,无法再更新"; + +/* A default value for an post without a title */ +"postSaveErrorMessage.postUntitled" = "无标题"; + /* User action to dismiss featured media options. */ "postSettings.featuredImageUploadActionSheet.dismiss" = "忽略"; @@ -10359,6 +10455,9 @@ Example: Reply to Pamela Nguyen */ /* Title for action sheet with featured media options. */ "postSettings.featuredImageUploadActionSheet.title" = "特色图片选项"; +/* The 'Pending Review' setting of the post */ +"postSettings.pendingReview" = "待审核"; + /* Section title for the disabled Twitter service in the Post Settings screen */ "postSettings.section.disabledTwitter.header" = "Twitter 自动共享功能已无法使用"; @@ -10368,6 +10467,36 @@ Example: Reply to Pamela Nguyen */ /* Error message on post/page settings screen */ "postSettings.updateFailedMessage" = "更新文章设置失败"; +/* Details for a 'Private' privacy setting */ +"postVisibility.private.details" = "仅站点管理员和编辑可见"; + +/* Title for a 'Private' privacy setting */ +"postVisibility.private.title" = "私人"; + +/* Details for a 'Password Protected' privacy setting */ +"postVisibility.protected.details" = "所有人可见,但需要密码"; + +/* Title for a 'Password Protected' privacy setting */ +"postVisibility.protected.title" = "受密码保护"; + +/* Details for a 'Public' (default) privacy setting */ +"postVisibility.public.details" = "所有人可见"; + +/* Title for a 'Public' (default) privacy setting */ +"postVisibility.public.title" = "公开"; + +/* Button cancel */ +"postVisibilityPicker.cancel" = "取消"; + +/* Navigation bar title for the Post Visibility picker */ +"postVisibilityPicker.navigationTitle" = "可见性"; + +/* Password placeholder text */ +"postVisibilityPicker.password" = "密码"; + +/* Button save */ +"postVisibilityPicker.save" = "保存"; + /* Promote the post with Blaze. */ "posts.blaze.actionTitle" = "大力宣传并推广"; @@ -10392,6 +10521,9 @@ Example: Reply to Pamela Nguyen */ /* Label for the preview post button. Tapping displays the post as it appears on the web. */ "posts.preview.actionTitle" = "预览"; +/* Label for the publish post button. */ +"posts.publish.actionTitle" = "发布"; + /* Retry uploading the post. */ "posts.retry.actionTitle" = "重试"; @@ -10458,6 +10590,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.jetpackSocial" = "Jetpack Social"; +/* Details for a publish button state in the pre-publishing sheet; count as a parameter */ +"prepublishing.mediaUploadFailedDetails" = "%@ 个项目上传失败"; + /* Placeholder for a cell in the pre-publishing sheet */ "prepublishing.postTitle" = "标题"; @@ -10467,6 +10602,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.publishDate" = "发布日期"; +/* Placeholder value for a publishing date in the prepublishing sheet when the date is not selected */ +"prepublishing.publishDateImmediately" = "立即"; + /* Label in the header in the pre-publishing sheet */ "prepublishing.publishingTo" = "正在发布到"; @@ -10531,6 +10669,12 @@ Tapping on this row allows the user to edit the sharing message. */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.tags" = "标签"; +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaManyItemsRemaining" = "剩余 %@ 个项目"; + +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaOneItemRemaining" = "剩余 %@ 个项目"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.uploadingMedia" = "上传媒体"; @@ -10540,6 +10684,24 @@ Tapping on this row allows the user to edit the sharing message. */ /* Title for a tappable string that opens the reader with a prompts tag */ "prompts.card.viewprompts.title" = "查看所有回复"; +/* Post publish date picker title for date cell */ +"publishDatePicker.date" = "发布日期"; + +/* Post publish date picker footer view when the selected date time zone is different from your current time zone; followed by the time in the current time zone. */ +"publishDatePicker.footerCurrentTimezone" = "您当前时区的日期:"; + +/* Post publish date picker: selected value placeholder when no date is selected and the post will be published immediately */ +"publishDatePicker.immediately" = "立即"; + +/* Title for button in publish date picker */ +"publishDatePicker.removePublishDate" = "删除发布日期"; + +/* Title for button in publish date picker */ +"publishDatePicker.selectPublishDate" = "选择发布日期"; + +/* Post publish time zone cell title */ +"publishDatePicker.timeZone" = "时区"; + /* Post publish date picker */ "publishDatePicker.title" = "发布日期"; @@ -10765,9 +10927,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "前往“订阅”"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "添加博客"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "无法阻止博客"; @@ -11007,6 +11166,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title of a feature to add a new tag to the tags subscribed by the user. */ "reader.tags.add.tag.title" = "添加标签"; +/* Text for the 'Like' button on the reader tag cell. */ +"reader.tags.button.like" = "点赞"; + +/* Text for the 'Liked' button on the reader tag cell. */ +"reader.tags.button.liked" = "已点赞"; + /* Button title. Tapping shows the Subscribe to Tags screen. */ "reader.tags.discover.more.tags" = "发现更多标签"; @@ -11109,6 +11274,24 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for screen that allows configuration of your blog/site related posts settings. */ "relatedPostsSettings.title" = "相关文章"; +/* A version of the post on another device. */ +"resolveConflict.anotherDevice" = "其他设备"; + +/* A version of the post on the current device. */ +"resolveConflict.currentDevice" = "当前设备"; + +/* Description for the Resolve Conflict screen. */ +"resolveConflict.description" = "在其他设备上修改过此文章。 请选择要保留的文章版本。"; + +/* Title for the cancel button on the Resolve Conflict screen. */ +"resolveConflict.navigation.cancel" = "取消"; + +/* Title for the save button on the Resolve Conflict screen. */ +"resolveConflict.navigation.save" = "保存"; + +/* Title for the Resolve Conflict screen. */ +"resolveConflict.navigation.title" = "解决冲突"; + /* Title for the \"Copy Link\" action in Share Sheet. */ "share.sheet.copy.link.title" = "复制链接"; @@ -11286,6 +11469,30 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Text for unknown privacy setting */ "siteVisibility.unknown.title" = "未知"; +/* All sites section title for site switcher. */ +"site_switcher.all_sites_section.title" = "所有站点"; + +/* Pinned section title for site switcher. */ +"site_switcher.pinned_section.title" = "固定站点"; + +/* Recents section title for site switcher. */ +"site_switcher.recents_section.title" = "最近访问的站点"; + +/* CTA title for the site switcher screen. */ +"site_switcher_cta_title" = "添加站点"; + +/* Dismiss button title above the search. */ +"site_switcher_dismiss_button_title" = "取消"; + +/* Done button title above the search. */ +"site_switcher_done_button_title" = "完成"; + +/* Edit button title above the search. */ +"site_switcher_edit_button_title" = "编辑"; + +/* Title for site switcher screen. */ +"site_switcher_title" = "选择站点"; + /* Label for the blogging reminders setting */ "sitesettings.reminders.title" = "提醒"; @@ -11322,6 +11529,9 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A smallprint that hints the reason behind why Twitter is deprecated. */ "social.twitterDeprecation.text" = "由于 Twitter 更改了条款和定价,Twitter 自动共享功能已无法使用。"; +/* Title of Subscribers stats tab. */ +"stats.dashboard.tab.subscribers" = "订阅者"; + /* Title of Traffic stats tab. */ "stats.dashboard.tab.traffic" = "流量"; @@ -11418,15 +11628,54 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Hint displayed on the 'Most Popular Time' stats card when a user's site hasn't yet received enough traffic. */ "stats.insights.mostPopularTime.noData" = "动态数量不足。 请在您的站点拥有更多访客时回来查看!"; +/* Insights 'Subscribers' header */ +"stats.insights.subscribers.title" = "订阅者"; + /* A hint shown to the user in stats informing the user how many likes one of their posts has received. The %1$@ placeholder will be replaced with the title of a post, the %2$@ with the number of likes. */ "stats.insights.totalLikes.guideText.plural" = "您的最新文章 %1$@ 收到了 %2$@ 个赞。"; /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "您的最新文章 %1$@ 收到了 %2$@ 个赞。"; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "WordPress.com 订阅者总数:%@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "电子邮件订阅者总数:%@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "订阅者总数"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; +/* Table column title that shows the date since the user became a subscriber. */ +"stats.section.dataSubtitles.subscriberSince" = "成为订阅者的时间"; + +/* Table column title that shows the names of subscribers. */ +"stats.section.itemSubtitles.subscriber" = "名称"; + +/* Stats 'Subscribers' card header, contains chart */ +"stats.subscribers.chart.title" = "订阅者"; + +/* A title for table's column that shows a number of times a post was opened from an email */ +"stats.subscribers.emailsSummary.column.clicks" = "点击次数"; + +/* A title for table's column that shows a number of email openings */ +"stats.subscribers.emailsSummary.column.opens" = "打开次数"; + +/* A title for table's column that shows a name of an email */ +"stats.subscribers.emailsSummary.column.title" = "最近的电子邮件"; + +/* Stats 'Emails' card header */ +"stats.subscribers.emailsSummaryCard.title" = "电子邮件"; + +/* Stats 'Subscribers' card header */ +"stats.subscribers.subscribersListCard.title" = "订阅者"; + +/* Accessibility of stats table. Placeholders will be populated with names of data shown in table. */ +"stats.topTotalsCell.voiceOverDescription" = "显示 %1$@、%2$@ 和 %3$@ 的表格"; + /* This description is used to set the accessibility label for the Stats Traffic chart, with Comments selected. */ "stats.traffic.accessibilityLabel.comments" = "描绘所选时间段内评论数的条形图。"; @@ -11439,6 +11688,18 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* This description is used to set the accessibility label for the Stats Traffic chart, with Visitors selected. */ "stats.traffic.accessibilityLabel.visitors" = "描绘所选时间段内访客数的条形图。"; +/* The label for the option to show Stats Traffic chart for Days. */ +"stats.traffic.days" = "日"; + +/* The label for the option to show Stats Traffic chart for Months. */ +"stats.traffic.months" = "月"; + +/* The label for the option to show Stats Traffic chart for Weeks. */ +"stats.traffic.weeks" = "周"; + +/* The label for the option to show Stats Traffic chart for Years. */ +"stats.traffic.years" = "年"; + /* Dismiss the AlertView */ "stockPhotos.strings.dismiss" = "忽略"; @@ -11448,9 +11709,27 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for placeholder in Free Photos */ "stockPhotos.title" = "搜索查找要添加到您的媒体库中的免费照片!"; +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.description" = "您可以选填您的电子邮件地址和用户名,以帮助我们了解您的体验。"; + +/* Label we show on an email input field */ +"submit.feedback.alert.empty.email" = "未输入电子邮件地址"; + +/* Label we show on an name input field */ +"submit.feedback.alert.empty.username" = "未输入用户名"; + +/* Alert submit option for users to accept sharing their email and name when submitting feedback. */ +"submit.feedback.alert.submit" = "完成"; + +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.title" = "感谢您的反馈"; + /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "提交"; +/* Notice informing user that their feedback is being submitted. */ +"submit.feedback.submit.loading" = "正在发送"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "反馈"; @@ -11622,6 +11901,21 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Displayed in the confirmation alert when marking unread notifications as read. */ "unread" = "未读"; +/* Site's Email Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.emailSubscriber" = "站点的电子邮件订阅者"; + +/* Site's Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.subscriber" = "站点订阅者"; + +/* Sites's User Profile. Displayed when the name is empty! */ +"user.details.title.user" = "站点用户"; + +/* Site's Viewers Profile. Displayed when the name is empty! */ +"user.details.title.viewer" = "站点访客"; + +/* Site Subscribers */ +"users.list.title.subscribers" = "订阅者"; + /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ "visit our documentation page" = "访问我们的文档页面"; diff --git a/WordPress/Resources/zh-Hant.lproj/Localizable.strings b/WordPress/Resources/zh-Hant.lproj/Localizable.strings index 885f2efd73d1..4658089d6afc 100644 --- a/WordPress/Resources/zh-Hant.lproj/Localizable.strings +++ b/WordPress/Resources/zh-Hant.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* Translation-Revision-Date: 2024-04-23 09:54:08+0000 */ +/* Translation-Revision-Date: 2024-05-02 13:54:07+0000 */ /* Plural-Forms: nplurals=1; plural=0; */ /* Generator: GlotPress/4.0.1 */ /* Language: zh_TW */ @@ -8847,6 +8847,18 @@ translators: %s: Select control option value e.g: \"Auto, 25%\". */ /* translators: displays audio file extension. e.g. MP3 audio file */ "audio file" = "音訊檔案"; +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.cancel" = "取消"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.message" = "這篇文章在不同裝置有未儲存的變更。 已編輯:%@。"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.title" = "可使用自動儲存"; + +/* An alert suggesting to load autosaved revision for a published post */ +"autosaveAlert.viewChanges" = "檢視變更"; + /* Alert message when something goes wrong with the selected image. */ "avatarMenu.failedToSetAvatarAlertMessage" = "無法載入圖片。 請選擇其他圖片,或稍後再試。"; @@ -10296,6 +10308,27 @@ Example: Reply to Pamela Nguyen */ /* Status mesasge for post cells */ "post.movingToTrashStatusMessage" = "正在將文章移至垃圾桶..."; +/* Button in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.discardChanges" = "捨棄變更"; + +/* Button in an alert confirming discaring a new draft */ +"postEditor.closeConfirmationAlert.discardDraft" = "捨棄草稿"; + +/* Button to keep the changes in an alert confirming discaring changes */ +"postEditor.closeConfirmationAlert.keepEditing" = "繼續編輯"; + +/* Button in an alert confirming saving a new draft */ +"postEditor.closeConfirmationAlert.saveDraft" = "儲存草稿"; + +/* Creating autosave to generate a preview (status message */ +"postEditor.creatingAutosaveForPreview" = "正在建立自動儲存..."; + +/* Title for a snackbar */ +"postEditor.revisionLoaded" = "修訂版本已經載入"; + +/* Saving draft to generate a preview (status message */ +"postEditor.savingDraftForPreview" = "正在儲存草稿..."; + /* Accessibility label for the post author in the post list. The parameter is the author name. For example, \"By Elsa.\" */ "postList.a11y.authorChunkFormat" = "作者:%@。"; @@ -10308,6 +10341,24 @@ Example: Reply to Pamela Nguyen */ /* Accessibility label for a post in the post list. The first placeholder is the post title. The second placeholder is the date. */ "postList.a11y.titleAndDateChunkFormat" = "%1$@,%2$@。"; +/* Badge for post cells */ +"postList.badgePendingReview" = "待審中"; + +/* Cancels an Action */ +"postList.cancel" = "取消"; + +/* Delete option in the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.actionTitle" = "永久刪除"; + +/* Message of the confirmation alert when deleting a page from the trash. */ +"postList.deletePermanently.alertMessage" = "你確定要永久刪除「%@」?"; + +/* An error message */ +"postList.errorUnsyncedChangesMessage" = "應用程式正在將先前的變更上傳至伺服器。 請稍後再試。"; + +/* A generic error message title */ +"postList.genericErrorMessage" = "發生錯誤"; + /* Label for a post in the post list. Indicates that the post has offline changes. */ "postList.offlineChanges" = "離線變更"; @@ -10323,6 +10374,42 @@ Example: Reply to Pamela Nguyen */ /* Title for the 'View' post list row swipe action */ "postList.swipeActionView" = "檢視"; +/* Trash option in the trash post or page confirmation alert. */ +"postList.trash.actionTitle" = "移至垃圾桶"; + +/* Message of the trash post or page confirmation alert. */ +"postList.trash.alertMessage" = "你確定要將「%@」移至垃圾桶? 將遺失所有先前未傳送至伺服器的變更。"; + +/* Cancel (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.cancelUpload" = "取消上傳"; + +/* Close button in postMediaUploadStatusView */ +"postMediaUploadStatusView.close" = "關閉"; + +/* Placeholder text in postMediaUploadStatusView when no uploads remain */ +"postMediaUploadStatusView.noPendingUploads" = "沒有待上傳內容"; + +/* Shows the upload progress with two preformatted parameters: %1$@ is the placeholder for completed bytes, and %2$@ is the placeholder for total bytes */ +"postMediaUploadStatusView.progress" = "已完成 %1$@,共 %2$@"; + +/* Retry (single) upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUpload" = "重試上傳"; + +/* Retry upload button in postMediaUploadStatusView */ +"postMediaUploadStatusView.retryUploads" = "重試上傳"; + +/* Title for post media upload status view */ +"postMediaUploadStatusView.title" = "媒體上傳內容"; + +/* A generic error message title */ +"postNotice.errorTitle" = "發生錯誤"; + +/* An error message */ +"postNotice.errorUnsyncedChangesMessage" = "應用程式正在將先前的變更上傳至伺服器。 請稍後再試。"; + +/* Button OK */ +"postNotice.ok" = "確定"; + /* Title of notification displayed when a page has been successfully saved as a draft. */ "postNotice.pageDraftCreated" = "已上傳頁面草稿"; @@ -10356,6 +10443,15 @@ Example: Reply to Pamela Nguyen */ /* Button title. Displays a summary / sharing screen for a specific post. */ "postNotice.view" = "檢視"; +/* Error message: content was modified on another device */ +"postSaveErrorMessage.conflict" = "此內容已在其他裝置修改"; + +/* Error message: item permanently deleted */ +"postSaveErrorMessage.deleted" = "已永久刪除「%@」且無法更新"; + +/* A default value for an post without a title */ +"postSaveErrorMessage.postUntitled" = "無標題"; + /* User action to dismiss featured media options. */ "postSettings.featuredImageUploadActionSheet.dismiss" = "關閉"; @@ -10368,6 +10464,9 @@ Example: Reply to Pamela Nguyen */ /* Title for action sheet with featured media options. */ "postSettings.featuredImageUploadActionSheet.title" = "精選圖片選項"; +/* The 'Pending Review' setting of the post */ +"postSettings.pendingReview" = "待審中"; + /* Section title for the disabled Twitter service in the Post Settings screen */ "postSettings.section.disabledTwitter.header" = "Twitter 自動分享功能已無法使用"; @@ -10377,6 +10476,36 @@ Example: Reply to Pamela Nguyen */ /* Error message on post/page settings screen */ "postSettings.updateFailedMessage" = "無法更新文章設定"; +/* Details for a 'Private' privacy setting */ +"postVisibility.private.details" = "僅網站管理員及編輯者可檢視"; + +/* Title for a 'Private' privacy setting */ +"postVisibility.private.title" = "私人"; + +/* Details for a 'Password Protected' privacy setting */ +"postVisibility.protected.details" = "所有人都可檢視但需要密碼"; + +/* Title for a 'Password Protected' privacy setting */ +"postVisibility.protected.title" = "受密碼保護"; + +/* Details for a 'Public' (default) privacy setting */ +"postVisibility.public.details" = "所有人都能檢視"; + +/* Title for a 'Public' (default) privacy setting */ +"postVisibility.public.title" = "公開"; + +/* Button cancel */ +"postVisibilityPicker.cancel" = "取消"; + +/* Navigation bar title for the Post Visibility picker */ +"postVisibilityPicker.navigationTitle" = "可見度"; + +/* Password placeholder text */ +"postVisibilityPicker.password" = "密碼"; + +/* Button save */ +"postVisibilityPicker.save" = "儲存"; + /* Promote the post with Blaze. */ "posts.blaze.actionTitle" = "使用 Blaze 進行宣傳"; @@ -10401,6 +10530,9 @@ Example: Reply to Pamela Nguyen */ /* Label for the preview post button. Tapping displays the post as it appears on the web. */ "posts.preview.actionTitle" = "預覽"; +/* Label for the publish post button. */ +"posts.publish.actionTitle" = "發表"; + /* Retry uploading the post. */ "posts.retry.actionTitle" = "重試"; @@ -10464,6 +10596,9 @@ Example: Reply to Pamela Nguyen */ /* Voiceover accessibility label informing the user that this button dismiss the current view */ "prepublishing.close" = "關閉"; +/* Details for a publish button state in the pre-publishing sheet; count as a parameter */ +"prepublishing.mediaUploadFailedDetails" = "無法上傳 %@ 個項目"; + /* Placeholder for a cell in the pre-publishing sheet */ "prepublishing.postTitle" = "標題"; @@ -10473,6 +10608,9 @@ Example: Reply to Pamela Nguyen */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.publishDate" = "發表日期"; +/* Placeholder value for a publishing date in the prepublishing sheet when the date is not selected */ +"prepublishing.publishDateImmediately" = "立即"; + /* Label in the header in the pre-publishing sheet */ "prepublishing.publishingTo" = "發表至"; @@ -10537,6 +10675,12 @@ Tapping on this row allows the user to edit the sharing message. */ /* Label for a cell in the pre-publishing sheet */ "prepublishing.tags" = "標籤"; +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaManyItemsRemaining" = "剩下 %@ 個項目"; + +/* Details label for a publish button state in the pre-publishing sheet */ +"prepublishing.uploadMediaOneItemRemaining" = "剩下 %@ 個項目"; + /* Title for a publish button state in the pre-publishing sheet */ "prepublishing.uploadingMedia" = "正在上傳媒體"; @@ -10546,6 +10690,24 @@ Tapping on this row allows the user to edit the sharing message. */ /* Title for a tappable string that opens the reader with a prompts tag */ "prompts.card.viewprompts.title" = "檢視所有回應"; +/* Post publish date picker title for date cell */ +"publishDatePicker.date" = "發表日期"; + +/* Post publish date picker footer view when the selected date time zone is different from your current time zone; followed by the time in the current time zone. */ +"publishDatePicker.footerCurrentTimezone" = "你目前時區的日期:"; + +/* Post publish date picker: selected value placeholder when no date is selected and the post will be published immediately */ +"publishDatePicker.immediately" = "立即"; + +/* Title for button in publish date picker */ +"publishDatePicker.removePublishDate" = "移除發表日期"; + +/* Title for button in publish date picker */ +"publishDatePicker.selectPublishDate" = "選取發表日期"; + +/* Post publish time zone cell title */ +"publishDatePicker.timeZone" = "時區"; + /* Post publish date picker */ "publishDatePicker.title" = "發表日期"; @@ -10771,9 +10933,6 @@ with the filter chip button. */ /* Button title. Tapping lets the user view the blogs they're subscribed to. */ "reader.no.results.subscriptions.button" = "前往「訂閱」"; -/* No Tags View Button Label */ -"reader.no.tags.title" = "新增網誌"; - /* Notice title when blocking a blog fails. */ "reader.notice.blog.blocked.failure" = "無法封鎖網誌"; @@ -11010,6 +11169,12 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title of a feature to add a new tag to the tags subscribed by the user. */ "reader.tags.add.tag.title" = "新增標籤"; +/* Text for the 'Like' button on the reader tag cell. */ +"reader.tags.button.like" = "讚"; + +/* Text for the 'Liked' button on the reader tag cell. */ +"reader.tags.button.liked" = "已按讚"; + /* Button title. Tapping shows the Subscribe to Tags screen. */ "reader.tags.discover.more.tags" = "探索更多標籤"; @@ -11112,6 +11277,24 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for screen that allows configuration of your blog/site related posts settings. */ "relatedPostsSettings.title" = "相關文章"; +/* A version of the post on another device. */ +"resolveConflict.anotherDevice" = "其他裝置"; + +/* A version of the post on the current device. */ +"resolveConflict.currentDevice" = "目前裝置"; + +/* Description for the Resolve Conflict screen. */ +"resolveConflict.description" = "此文章已在其他裝置修改。 請選取要保存的文章版本。"; + +/* Title for the cancel button on the Resolve Conflict screen. */ +"resolveConflict.navigation.cancel" = "取消"; + +/* Title for the save button on the Resolve Conflict screen. */ +"resolveConflict.navigation.save" = "儲存"; + +/* Title for the Resolve Conflict screen. */ +"resolveConflict.navigation.title" = "解決衝突"; + /* Title for the \"Copy Link\" action in Share Sheet. */ "share.sheet.copy.link.title" = "複製連結"; @@ -11289,6 +11472,30 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Text for unknown privacy setting */ "siteVisibility.unknown.title" = "未知"; +/* All sites section title for site switcher. */ +"site_switcher.all_sites_section.title" = "所有網站"; + +/* Pinned section title for site switcher. */ +"site_switcher.pinned_section.title" = "已釘選的網站"; + +/* Recents section title for site switcher. */ +"site_switcher.recents_section.title" = "近期網站"; + +/* CTA title for the site switcher screen. */ +"site_switcher_cta_title" = "新增網站"; + +/* Dismiss button title above the search. */ +"site_switcher_dismiss_button_title" = "取消"; + +/* Done button title above the search. */ +"site_switcher_done_button_title" = "完成"; + +/* Edit button title above the search. */ +"site_switcher_edit_button_title" = "編輯"; + +/* Title for site switcher screen. */ +"site_switcher_title" = "選擇網站"; + /* Label for the blogging reminders setting */ "sitesettings.reminders.title" = "提醒"; @@ -11325,6 +11532,9 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* A smallprint that hints the reason behind why Twitter is deprecated. */ "social.twitterDeprecation.text" = "由於 Twitter 變更條款和定價,Twitter 自動分享功能已無法使用。"; +/* Title of Subscribers stats tab. */ +"stats.dashboard.tab.subscribers" = "訂閱者"; + /* Title of Traffic stats tab. */ "stats.dashboard.tab.traffic" = "流量"; @@ -11421,18 +11631,57 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Hint displayed on the 'Most Popular Time' stats card when a user's site hasn't yet received enough traffic. */ "stats.insights.mostPopularTime.noData" = "沒有足夠的活動。 有更多訪客造訪網站的時候,請再回來查看!"; +/* Insights 'Subscribers' header */ +"stats.insights.subscribers.title" = "訂閱者"; + /* A hint shown to the user in stats informing the user how many likes one of their posts has received. The %1$@ placeholder will be replaced with the title of a post, the %2$@ with the number of likes. */ "stats.insights.totalLikes.guideText.plural" = "你的最新文章 %1$@ 已獲得 %2$@ 次按讚。"; /* A hint shown to the user in stats informing the user that one of their posts has received a like. The %1$@ placeholder will be replaced with the title of a post, and the %2$@ will be replaced by the numeral one. */ "stats.insights.totalLikes.guideText.singular" = "你的最新文章 %1$@ 已獲得 %2$@ 次按讚。"; +/* Label displaying total number of WordPress.com subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.dotcomCount" = "WordPress.com 訂閱者總人數:%@"; + +/* Label displaying total number of email subscribers. %@ is the total. */ +"stats.insights.totalSubscribers.emailCount" = "電子郵件訂閱者總人數:%@"; + +/* Insights 'Total Subscribers' header */ +"stats.insights.totalSubscribers.title" = "訂閱者總人數"; + /* Text for the Stats Traffic Overview stat difference label. Shows the change from the previous period, including the percentage value. E.g.: +12.3K (5%). %1$@ is the placeholder for the change sign ('-', '+', or none). %2$@ is the placeholder for the change numerical value. %3$@ is the placeholder for the change percentage value. */ "stats.overview.differenceLabelWithNumber" = "%1$@%2$@ (%3$@)"; /* Stats 'Today' header */ "stats.period.todayCard.title" = "今天"; +/* Table column title that shows the date since the user became a subscriber. */ +"stats.section.dataSubtitles.subscriberSince" = "從以下時間開始訂閱:"; + +/* Table column title that shows the names of subscribers. */ +"stats.section.itemSubtitles.subscriber" = "名稱"; + +/* Stats 'Subscribers' card header, contains chart */ +"stats.subscribers.chart.title" = "訂閱者"; + +/* A title for table's column that shows a number of times a post was opened from an email */ +"stats.subscribers.emailsSummary.column.clicks" = "點擊數"; + +/* A title for table's column that shows a number of email openings */ +"stats.subscribers.emailsSummary.column.opens" = "開啟數"; + +/* A title for table's column that shows a name of an email */ +"stats.subscribers.emailsSummary.column.title" = "最新電子郵件"; + +/* Stats 'Emails' card header */ +"stats.subscribers.emailsSummaryCard.title" = "電子郵件"; + +/* Stats 'Subscribers' card header */ +"stats.subscribers.subscribersListCard.title" = "訂閱者"; + +/* Accessibility of stats table. Placeholders will be populated with names of data shown in table. */ +"stats.topTotalsCell.voiceOverDescription" = "顯示 %1$@、%2$@ 和 %3$@ 的表格"; + /* This description is used to set the accessibility label for the Stats Traffic chart, with Comments selected. */ "stats.traffic.accessibilityLabel.comments" = "顯示所選期間留言情況的長條圖。"; @@ -11445,6 +11694,18 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* This description is used to set the accessibility label for the Stats Traffic chart, with Visitors selected. */ "stats.traffic.accessibilityLabel.visitors" = "顯示所選期間訪客人數的長條圖。"; +/* The label for the option to show Stats Traffic chart for Days. */ +"stats.traffic.days" = "天"; + +/* The label for the option to show Stats Traffic chart for Months. */ +"stats.traffic.months" = "月"; + +/* The label for the option to show Stats Traffic chart for Weeks. */ +"stats.traffic.weeks" = "週"; + +/* The label for the option to show Stats Traffic chart for Years. */ +"stats.traffic.years" = "年"; + /* Dismiss the AlertView */ "stockPhotos.strings.dismiss" = "關閉"; @@ -11454,9 +11715,27 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Title for placeholder in Free Photos */ "stockPhotos.title" = "搜尋免費相片以新增至你的媒體庫!"; +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.description" = "你可以選擇納入電子郵件地址與使用者名稱,協助我們了解你的體驗。"; + +/* Label we show on an email input field */ +"submit.feedback.alert.empty.email" = "未輸入電子郵件地址"; + +/* Label we show on an name input field */ +"submit.feedback.alert.empty.username" = "未輸入使用者名稱"; + +/* Alert submit option for users to accept sharing their email and name when submitting feedback. */ +"submit.feedback.alert.submit" = "完成"; + +/* Alert users are shown when submtiting their feedback. */ +"submit.feedback.alert.title" = "感謝你的意見回饋"; + /* The button title for the Submit button in the In-App Feedback screen */ "submit.feedback.submit.button" = "提交"; +/* Notice informing user that their feedback is being submitted. */ +"submit.feedback.submit.loading" = "正在傳送"; + /* The title for the the In-App Feedback screen */ "submit.feedback.title" = "意見回饋"; @@ -11628,6 +11907,21 @@ Refer to: `reader.preferences.preview.body.feedback.format` */ /* Displayed in the confirmation alert when marking unread notifications as read. */ "unread" = "未讀"; +/* Site's Email Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.emailSubscriber" = "網站的電子郵件訂閱者"; + +/* Site's Subscriber Profile. Displayed when the name is empty! */ +"user.details.title.subscriber" = "網站的訂閱者"; + +/* Sites's User Profile. Displayed when the name is empty! */ +"user.details.title.user" = "網站的使用者"; + +/* Site's Viewers Profile. Displayed when the name is empty! */ +"user.details.title.viewer" = "網站的檢視者"; + +/* Site Subscribers */ +"users.list.title.subscribers" = "訂閱者"; + /* Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color. */ "visit our documentation page" = "請造訪我們的文件頁面"; From 2d02b4112363aa22d64ec72e6905d65dc8afb18f Mon Sep 17 00:00:00 2001 From: Automattic Release Bot Date: Tue, 7 May 2024 08:46:12 -0700 Subject: [PATCH 098/112] Update WordPress metadata translations --- fastlane/metadata/de-DE/release_notes.txt | 1 + fastlane/metadata/default/release_notes.txt | 4 +--- fastlane/metadata/en-GB/release_notes.txt | 1 + fastlane/metadata/es-ES/release_notes.txt | 1 + fastlane/metadata/nl-NL/release_notes.txt | 1 + fastlane/metadata/sv/release_notes.txt | 1 + fastlane/metadata/tr/release_notes.txt | 1 + 7 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 fastlane/metadata/de-DE/release_notes.txt create mode 100644 fastlane/metadata/en-GB/release_notes.txt create mode 100644 fastlane/metadata/es-ES/release_notes.txt create mode 100644 fastlane/metadata/nl-NL/release_notes.txt create mode 100644 fastlane/metadata/sv/release_notes.txt create mode 100644 fastlane/metadata/tr/release_notes.txt diff --git a/fastlane/metadata/de-DE/release_notes.txt b/fastlane/metadata/de-DE/release_notes.txt new file mode 100644 index 000000000000..4b579f7f3c19 --- /dev/null +++ b/fastlane/metadata/de-DE/release_notes.txt @@ -0,0 +1 @@ +Wir haben einige Aktualisierungen am Seitenmenü vorgenommen. Du findest die Einstellungen jetzt in einem eigenen Abschnitt, und sie sind sowohl für veröffentlichte als auch für unveröffentlichte Beiträge verfügbar. Das fetzt. diff --git a/fastlane/metadata/default/release_notes.txt b/fastlane/metadata/default/release_notes.txt index b947e8874d39..cbd493a03aa1 100644 --- a/fastlane/metadata/default/release_notes.txt +++ b/fastlane/metadata/default/release_notes.txt @@ -1,3 +1 @@ -* [*] [internal] Update Reachability. [#23030] -* [*] Move "Settings" context menu action in "Pages" from the submenu to a separate section to make it easily discoverable and make it available for unpublished posts [#23065] - +We made some updates to the Pages menu. You’ll now find Settings in its own section, and it’s available for both published and unpublished posts. Groovy. \ No newline at end of file diff --git a/fastlane/metadata/en-GB/release_notes.txt b/fastlane/metadata/en-GB/release_notes.txt new file mode 100644 index 000000000000..346d277faa34 --- /dev/null +++ b/fastlane/metadata/en-GB/release_notes.txt @@ -0,0 +1 @@ +We made some updates to the Pages menu. You’ll now find Settings in its own section, and it’s available for both published and unpublished posts. Groovy. diff --git a/fastlane/metadata/es-ES/release_notes.txt b/fastlane/metadata/es-ES/release_notes.txt new file mode 100644 index 000000000000..a2d6304d59d6 --- /dev/null +++ b/fastlane/metadata/es-ES/release_notes.txt @@ -0,0 +1 @@ +Hemos realizado algunas actualizaciones en el menú «Páginas». Ahora encontrarás lo ajustes en su propia sección, y está disponible tanto para las entradas publicadas como para las no publicadas. Genial. diff --git a/fastlane/metadata/nl-NL/release_notes.txt b/fastlane/metadata/nl-NL/release_notes.txt new file mode 100644 index 000000000000..c71937812a4d --- /dev/null +++ b/fastlane/metadata/nl-NL/release_notes.txt @@ -0,0 +1 @@ +We hebben enkele updates gemaakt aan het Pagina's menu. Je vindt nu instellingen in een eigen sectie, en het is beschikbaar voor zowel gepubliceerde als ongepubliceerde berichten. diff --git a/fastlane/metadata/sv/release_notes.txt b/fastlane/metadata/sv/release_notes.txt new file mode 100644 index 000000000000..acfc2ccdd025 --- /dev/null +++ b/fastlane/metadata/sv/release_notes.txt @@ -0,0 +1 @@ +Vi har gjort vissa uppdateringar i menyn för sidor. Nu finns alla inställningar i en egen sektion och de är alltid tillgängliga, oberoende av om ett inlägg är publicerat eller inte. Härligt. diff --git a/fastlane/metadata/tr/release_notes.txt b/fastlane/metadata/tr/release_notes.txt new file mode 100644 index 000000000000..a35959d9241b --- /dev/null +++ b/fastlane/metadata/tr/release_notes.txt @@ -0,0 +1 @@ +Sayfalar menüsünde bazı güncellemeler yaptık. Artık Ayarlar'ı kendi bölümünde bulabilirsiniz ve bu, hem yayımlanmış hem de yayımlanmamış gönderilerde mevcuttur. Havalı. From 02c65f528ad8a79644ba735d56c2431c1c5457a5 Mon Sep 17 00:00:00 2001 From: Automattic Release Bot Date: Tue, 7 May 2024 08:46:15 -0700 Subject: [PATCH 099/112] Update Jetpack metadata translations --- fastlane/jetpack_metadata/default/release_notes.txt | 5 +---- fastlane/jetpack_metadata/pt-BR/release_notes.txt | 1 + fastlane/jetpack_metadata/tr/release_notes.txt | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 fastlane/jetpack_metadata/pt-BR/release_notes.txt create mode 100644 fastlane/jetpack_metadata/tr/release_notes.txt diff --git a/fastlane/jetpack_metadata/default/release_notes.txt b/fastlane/jetpack_metadata/default/release_notes.txt index a051f43082d3..33f1301d9f19 100644 --- a/fastlane/jetpack_metadata/default/release_notes.txt +++ b/fastlane/jetpack_metadata/default/release_notes.txt @@ -1,4 +1 @@ -* [*] [internal] Update Reachability. [#23030] -* [*] Move "Settings" context menu action in "Pages" from the submenu to a separate section to make it easily discoverable and make it available for unpublished posts [#23065] -* [*] Add "Stats" context menu action to "Pages" [#23065] - +We made some updates to the Pages menu. You’ll now find Settings in its own section, right underneath Stats (which we also moved). It’s available for both published and unpublished posts. Groovy. \ No newline at end of file diff --git a/fastlane/jetpack_metadata/pt-BR/release_notes.txt b/fastlane/jetpack_metadata/pt-BR/release_notes.txt new file mode 100644 index 000000000000..588aafef9cdf --- /dev/null +++ b/fastlane/jetpack_metadata/pt-BR/release_notes.txt @@ -0,0 +1 @@ +Fizemos algumas atualizações no menu Páginas. Agora as configurações têm uma seção própria, logo abaixo de Estatísticas (que também mudou de lugar). Essa seção está disponível para posts publicados ou não. Legal, não é? diff --git a/fastlane/jetpack_metadata/tr/release_notes.txt b/fastlane/jetpack_metadata/tr/release_notes.txt new file mode 100644 index 000000000000..58fa1d96ae81 --- /dev/null +++ b/fastlane/jetpack_metadata/tr/release_notes.txt @@ -0,0 +1 @@ +Sayfalar menüsünde bazı güncellemeler yaptık. Artık Ayarlar'ı İstatistikler'in altında (bunu da taşıdık) kendi bölümünde bulabilirsiniz. Bu, hem yayımlanmış hem de yayımlanmamış gönderilerde mevcuttur. Havalı. From 746236e17980b02adcc6debffebcf8c427e6ee84 Mon Sep 17 00:00:00 2001 From: Automattic Release Bot Date: Tue, 7 May 2024 08:46:29 -0700 Subject: [PATCH 100/112] Bump version number --- config/Version.internal.xcconfig | 2 +- config/Version.public.xcconfig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/Version.internal.xcconfig b/config/Version.internal.xcconfig index 2b2b88fc0c89..8115973780ac 100644 --- a/config/Version.internal.xcconfig +++ b/config/Version.internal.xcconfig @@ -1,2 +1,2 @@ -VERSION_LONG = 24.8.0.20240430 +VERSION_LONG = 24.8.0.20240507 VERSION_SHORT = 24.8 diff --git a/config/Version.public.xcconfig b/config/Version.public.xcconfig index 510cabe182bf..13b1a450eee5 100644 --- a/config/Version.public.xcconfig +++ b/config/Version.public.xcconfig @@ -1,2 +1,2 @@ -VERSION_LONG = 24.8.0.2 +VERSION_LONG = 24.8.0.3 VERSION_SHORT = 24.8 From 9bfe09f9d4762a256d5f747f81d60f5bc37d73e3 Mon Sep 17 00:00:00 2001 From: Chris McGraw <2454408+wargcm@users.noreply.github.com> Date: Tue, 7 May 2024 16:13:17 -0400 Subject: [PATCH 101/112] Limit tag feed content length Setting a label to an entire post's content was causing animation hitches on posts which had a lot of data. This sets a limit to the content's length depending on the device it'll display on. --- .../Classes/ViewRelated/Reader/ReaderPost+Display.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Reader/ReaderPost+Display.swift b/WordPress/Classes/ViewRelated/Reader/ReaderPost+Display.swift index 5800dbda1994..fc4a935209f8 100644 --- a/WordPress/Classes/ViewRelated/Reader/ReaderPost+Display.swift +++ b/WordPress/Classes/ViewRelated/Reader/ReaderPost+Display.swift @@ -32,10 +32,12 @@ extension ReaderPost { .replacingOccurrences(of: "^\n+", with: "", options: .regularExpression) .replacingOccurrences(of: "\n{2,}", with: "\n\n", options: .regularExpression) .trim() - return content ?? contentPreviewForDisplay() - } else { - return contentPreviewForDisplay() + if let content { + let maxContentLength = isPad ? 4000 : 500 + return String(content.prefix(maxContentLength)) + } } + return contentPreviewForDisplay() } func countsForDisplay(isLoggedIn: Bool) -> String? { From 43973c062a304226ba9b09f13e7046774986b73d Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Wed, 8 May 2024 09:33:55 +0300 Subject: [PATCH 102/112] Use object(forKey) rather than value(forKey) Co-authored-by: Paul Von Schrottky --- WordPress/Classes/Stores/UserPersistentRepository.swift | 1 - .../ViewRelated/Stats/SiteStatsDashboardViewController.swift | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/WordPress/Classes/Stores/UserPersistentRepository.swift b/WordPress/Classes/Stores/UserPersistentRepository.swift index cff0dad25007..40437d5a4b6b 100644 --- a/WordPress/Classes/Stores/UserPersistentRepository.swift +++ b/WordPress/Classes/Stores/UserPersistentRepository.swift @@ -7,7 +7,6 @@ protocol UserPersistentRepositoryReader { func array(forKey key: String) -> [Any]? func dictionary(forKey key: String) -> [String: Any]? func url(forKey key: String) -> URL? - func value(forKey key: String) -> Any? func dictionaryRepresentation() -> [String: Any] } diff --git a/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift b/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift index d0605481268e..98c5f98fe51e 100644 --- a/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/SiteStatsDashboardViewController.swift @@ -365,7 +365,7 @@ struct SiteStatsDashboardPreferences { let key = Self.lastSelectedStatsTabTypeKey(forSiteID: siteID) - guard let tabRawValue = UserPersistentStoreFactory.instance().value(forKey: key) as? Int else { + guard let tabRawValue = UserPersistentStoreFactory.instance().object(forKey: key) as? Int else { return nil } From df858d6c23b3eebfbbc7be83e075ae0a9aa732ac Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Wed, 8 May 2024 11:35:53 +0300 Subject: [PATCH 103/112] Update Subscribers Chart card to Subscriber Growth --- WordPress/Classes/ViewRelated/Stats/Helpers/StatSection.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Helpers/StatSection.swift b/WordPress/Classes/ViewRelated/Stats/Helpers/StatSection.swift index a843e8f2e46b..3fa927ee6379 100644 --- a/WordPress/Classes/ViewRelated/Stats/Helpers/StatSection.swift +++ b/WordPress/Classes/ViewRelated/Stats/Helpers/StatSection.swift @@ -439,7 +439,7 @@ } struct SubscribersHeaders { - static let chart = NSLocalizedString("stats.subscribers.chart.title", value: "Subscribers", comment: "Stats 'Subscribers' card header, contains chart") + static let chart = NSLocalizedString("stats.subscribers.growthChart.title", value: "Subscriber Growth", comment: "Stats 'Subscriber Growth' card header, contains a chart showing the progression in the number of subscribers") static let emailsSummaryStats = NSLocalizedString("stats.subscribers.emailsSummaryCard.title", value: "Emails", comment: "Stats 'Emails' card header") static let subscribersList = NSLocalizedString("stats.subscribers.subscribersListCard.title", value: "Subscribers", comment: "Stats 'Subscribers' card header") } From f560543acaad12b3ae9941832d79f27667509d31 Mon Sep 17 00:00:00 2001 From: David Christiandy <1299411+dvdchr@users.noreply.github.com> Date: Wed, 8 May 2024 17:13:31 +0700 Subject: [PATCH 104/112] Register blank UICollectionViewCell as fallback cell --- .../ReaderSelectInterestsViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Reader/Select Interests/ReaderSelectInterestsViewController.swift b/WordPress/Classes/ViewRelated/Reader/Select Interests/ReaderSelectInterestsViewController.swift index d7574f346ac9..521f597aaa36 100644 --- a/WordPress/Classes/ViewRelated/Reader/Select Interests/ReaderSelectInterestsViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/Select Interests/ReaderSelectInterestsViewController.swift @@ -16,6 +16,7 @@ struct ReaderSelectInterestsConfiguration { class ReaderSelectInterestsViewController: UIViewController { private struct Constants { static let reuseIdentifier = ReaderInterestsCollectionViewCell.classNameWithoutNamespaces() + static let defaultCellIdentifier = "DefaultCell" static let interestsLabelMargin: CGFloat = 12 static let cellCornerRadius: CGFloat = 5 @@ -168,6 +169,7 @@ class ReaderSelectInterestsViewController: UIViewController { private func configureCollectionView() { let nib = UINib(nibName: String(describing: ReaderInterestsCollectionViewCell.self), bundle: nil) collectionView.register(nib, forCellWithReuseIdentifier: Constants.reuseIdentifier) + collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: Constants.defaultCellIdentifier) guard let layout = collectionView.collectionViewLayout as? ReaderInterestsCollectionViewFlowLayout else { return @@ -325,7 +327,7 @@ extension ReaderSelectInterestsViewController: UICollectionViewDataSource { guard let interest = dataSource.interest(for: indexPath.row) else { CrashLogging.main.logMessage("ReaderSelectInterestsViewController: Requested for data at invalid row", properties: ["row": indexPath.row], level: .warning) - return .init(frame: .zero) + return collectionView.dequeueReusableCell(withReuseIdentifier: Constants.defaultCellIdentifier, for: indexPath) } ReaderInterestsStyleGuide.applyCellLabelStyle(label: cell.label, From 6bc1dfd4ce001361220d121dda3745a9a964d036 Mon Sep 17 00:00:00 2001 From: Salim Braksa Date: Wed, 8 May 2024 18:19:48 +0100 Subject: [PATCH 105/112] Add a reusable component for the Bottom Sheet content view controller --- .../BottomSheetContentViewController.swift | 113 ++++++++++++++++++ WordPress/WordPress.xcodeproj/project.pbxproj | 16 +++ 2 files changed, 129 insertions(+) create mode 100644 WordPress/Classes/ViewRelated/System/Bottom Sheet/BottomSheetContentViewController.swift diff --git a/WordPress/Classes/ViewRelated/System/Bottom Sheet/BottomSheetContentViewController.swift b/WordPress/Classes/ViewRelated/System/Bottom Sheet/BottomSheetContentViewController.swift new file mode 100644 index 000000000000..816173da4da3 --- /dev/null +++ b/WordPress/Classes/ViewRelated/System/Bottom Sheet/BottomSheetContentViewController.swift @@ -0,0 +1,113 @@ +import UIKit +import SwiftUI +import WordPressUI + +class BottomSheetContentViewController: UIViewController { + + // MARK: - Views + + private let scrollView: UIScrollView = { + let view = UIScrollView() + view.showsVerticalScrollIndicator = false + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + private let contentView: UIView + + private var hostingController: UIViewController? + + // MARK: - Init + + init(contentView: UIView) { + self.contentView = contentView + super.init(nibName: nil, bundle: nil) + } + + init(contentView: T) { + let hostingController = UIHostingController(rootView: contentView) + self.contentView = hostingController.view + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - View Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + if let hostingController { + self.setupHostingController(hostingController) + } else { + self.setupContentView() + } + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + self.updateContentSize() + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + guard previousTraitCollection?.preferredContentSizeCategory != traitCollection.preferredContentSizeCategory else { + return + } + self.view.setNeedsLayout() + self.view.layoutIfNeeded() + self.updateContentSize() + if let presentationController = parent?.presentationController as? DrawerPresentationController { + presentationController.transition(to: presentationController.currentPosition) + } + } + + // MARK: - Setup Content View + + private func setupContentView() { + self.view.addSubview(scrollView) + self.view.pinSubviewToAllEdges(scrollView) + self.contentView.translatesAutoresizingMaskIntoConstraints = true + self.scrollView.addSubview(contentView) + } + + private func setupHostingController(_ hostingController: UIViewController) { + hostingController.willMove(toParent: self) + self.addChild(hostingController) + self.setupContentView() + hostingController.didMove(toParent: self) + } + + private func updateContentSize() { + // Calculate the size needed for the view to fit its content + let targetSize = CGSize(width: view.bounds.width, height: 0) + self.contentView.frame = CGRect(origin: .zero, size: targetSize) + let contentViewSize = contentView.systemLayoutSizeFitting(targetSize) + self.contentView.frame.size = contentViewSize + + // Set the scrollView's content size to match the contentView's size + // + // Scroll is enabled / disabled automatically depending on whether the `contentSize` is bigger than the its size. + self.scrollView.contentSize = contentViewSize + + // Set the preferred content size for the view controller to match the contentView's size + // + // This property should be updated when `DrawerPresentable.collapsedHeight` is `intrinsicHeight`. + // Because under the hood the `BottomSheetViewController` reads this property to layout its subviews. + self.preferredContentSize = contentViewSize + } +} + +extension BottomSheetContentViewController: DrawerPresentable { + + var collapsedHeight: DrawerHeight { + if traitCollection.verticalSizeClass == .compact { + return .maxHeight + } + return .intrinsicHeight + } + + var allowsUserTransition: Bool { + return false + } +} diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index f4d071bdd4d2..9068d3f0c0df 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -3784,6 +3784,9 @@ F46546312AF2F8D30017E3D1 /* DomainsStateViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46546302AF2F8D20017E3D1 /* DomainsStateViewModel.swift */; }; F46546332AF54DCD0017E3D1 /* AllDomainsListItemViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46546322AF54DCD0017E3D1 /* AllDomainsListItemViewModelTests.swift */; }; F46546352AF550A20017E3D1 /* AllDomainsListItem+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46546342AF550A20017E3D1 /* AllDomainsListItem+Helpers.swift */; }; + F4654FAA2BEBC75B005AB177 /* CommentModerationOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08169B9C2BDA68F600055454 /* CommentModerationOptionsView.swift */; }; + F4654FAD2BEBE994005AB177 /* BottomSheetContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4654FAC2BEBE994005AB177 /* BottomSheetContentViewController.swift */; }; + F4654FAE2BEBE994005AB177 /* BottomSheetContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4654FAC2BEBE994005AB177 /* BottomSheetContentViewController.swift */; }; F465976E28E4669200D5F49A /* cool-green-icon-app-76@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F465976928E4669200D5F49A /* cool-green-icon-app-76@2x.png */; }; F465976F28E4669200D5F49A /* cool-green-icon-app-76.png in Resources */ = {isa = PBXBuildFile; fileRef = F465976A28E4669200D5F49A /* cool-green-icon-app-76.png */; }; F465977028E4669200D5F49A /* cool-green-icon-app-60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F465976B28E4669200D5F49A /* cool-green-icon-app-60@3x.png */; }; @@ -9219,6 +9222,7 @@ F46546302AF2F8D20017E3D1 /* DomainsStateViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainsStateViewModel.swift; sourceTree = ""; }; F46546322AF54DCD0017E3D1 /* AllDomainsListItemViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllDomainsListItemViewModelTests.swift; sourceTree = ""; }; F46546342AF550A20017E3D1 /* AllDomainsListItem+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AllDomainsListItem+Helpers.swift"; sourceTree = ""; }; + F4654FAC2BEBE994005AB177 /* BottomSheetContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomSheetContentViewController.swift; sourceTree = ""; }; F465976928E4669200D5F49A /* cool-green-icon-app-76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cool-green-icon-app-76@2x.png"; sourceTree = ""; }; F465976A28E4669200D5F49A /* cool-green-icon-app-76.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cool-green-icon-app-76.png"; sourceTree = ""; }; F465976B28E4669200D5F49A /* cool-green-icon-app-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cool-green-icon-app-60@3x.png"; sourceTree = ""; }; @@ -14133,6 +14137,7 @@ 8584FDB619243AC40019C02E /* System */ = { isa = PBXGroup; children = ( + F4654FAB2BEBE978005AB177 /* Bottom Sheet */, 0CED1FFE2B6809C100E6DD52 /* FilterCompact */, F5E032E22408D537003AF350 /* Action Sheet */, F551E7F323F6EA1400751212 /* Floating Create Button */, @@ -17866,6 +17871,14 @@ path = Comment; sourceTree = ""; }; + F4654FAB2BEBE978005AB177 /* Bottom Sheet */ = { + isa = PBXGroup; + children = ( + F4654FAC2BEBE994005AB177 /* BottomSheetContentViewController.swift */, + ); + path = "Bottom Sheet"; + sourceTree = ""; + }; F465976528E464DE00D5F49A /* Icons */ = { isa = PBXGroup; children = ( @@ -22500,6 +22513,7 @@ 0CEA55522BC55940008D0FE5 /* WPInstrumentation.swift in Sources */, 80F8DAC1282B6546007434A0 /* WPAnalytics+QuickStart.swift in Sources */, FAFF7A1C2B693D8B006A7CB2 /* PHPLogsView.swift in Sources */, + F4654FAD2BEBE994005AB177 /* BottomSheetContentViewController.swift in Sources */, 59E1D46E1CEF77B500126697 /* Page.swift in Sources */, 321955BF24BE234C00E3F316 /* ReaderInterestsCoordinator.swift in Sources */, 4A5DE7382B0D511900363171 /* PageTree.swift in Sources */, @@ -24560,6 +24574,7 @@ 93F72150271831820021A09F /* SiteStatsPinnedItemStore.swift in Sources */, FABB212A2602FC2C00C8785C /* BadgeLabel.swift in Sources */, FABB212B2602FC2C00C8785C /* ReaderTagsTableViewController.swift in Sources */, + F4654FAA2BEBC75B005AB177 /* CommentModerationOptionsView.swift in Sources */, FE50965A2A17A69F00DDD071 /* TwitterDeprecationTableFooterView.swift in Sources */, FABB212C2602FC2C00C8785C /* NotificationsViewController+AppRatings.swift in Sources */, FABB212D2602FC2C00C8785C /* BlogService.m in Sources */, @@ -25420,6 +25435,7 @@ 83914BD52A2EA03A0017A588 /* PostSettingsViewController+JetpackSocial.swift in Sources */, F46546312AF2F8D30017E3D1 /* DomainsStateViewModel.swift in Sources */, FABB23852602FC2C00C8785C /* WPError.m in Sources */, + F4654FAE2BEBE994005AB177 /* BottomSheetContentViewController.swift in Sources */, FABB23862602FC2C00C8785C /* ContentRouter.swift in Sources */, FABB23872602FC2C00C8785C /* BlogToBlog32to33.swift in Sources */, FABB23882602FC2C00C8785C /* WPStyleGuide+ApplicationStyles.swift in Sources */, From 47229b491d5f3adfcbe346bd68da1fe37b0af031 Mon Sep 17 00:00:00 2001 From: Salim Braksa Date: Wed, 8 May 2024 18:20:22 +0100 Subject: [PATCH 106/112] Tweak Comment Moderation Options view spacing --- .../CommentModerationOptionsView.swift | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationOptionsView.swift b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationOptionsView.swift index 6fc883c2af86..01488bddc97e 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationOptionsView.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationOptionsView.swift @@ -10,15 +10,20 @@ struct CommentModerationOptionsView: View { .spam ] - var optionSelected: ((Option) -> Void)? + let onOptionSelected: (Option) -> Void + + init(onOptionSelected: @escaping (Option) -> Void) { + self.onOptionSelected = onOptionSelected + } var body: some View { - VStack(alignment: .leading, spacing: .DS.Padding.medium) { + VStack(alignment: .leading, spacing: .DS.Padding.double) { title optionsVStack } - .padding(.horizontal, .DS.Padding.double) + .padding(.DS.Padding.double) .background(Color.DS.Background.primary) + .fixedSize(horizontal: false, vertical: true) } private var title: some View { @@ -31,12 +36,13 @@ struct CommentModerationOptionsView: View { VStack(spacing: .DS.Padding.medium) { ForEach(options, id: \.title) { option in Button { - optionSelected?(option) + onOptionSelected(option) } label: { optionHStack(option: option) } } } + .padding(.vertical, .DS.Padding.double) } private func optionHStack(option: Option) -> some View { @@ -144,6 +150,20 @@ private extension CommentModerationOptionsView.Option { } } +final class CommentModerationOptionsViewController: BottomSheetContentViewController { + + typealias Option = CommentModerationOptionsView.Option + + init(onOptionSelected: @escaping (Option) -> Void) { + let content = CommentModerationOptionsView(onOptionSelected: onOptionSelected) + super.init(contentView: content) + } + + @MainActor required dynamic init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + #Preview { - CommentModerationOptionsView() + CommentModerationOptionsView(onOptionSelected: { _ in }) } From cf4b51c8a9576da65eea81469e47a5af01e879b5 Mon Sep 17 00:00:00 2001 From: Salim Braksa Date: Wed, 8 May 2024 18:20:43 +0100 Subject: [PATCH 107/112] Remove status options from the Comment Content Header menu --- .../Content/CommentContentHeaderView.swift | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Comments/Content/CommentContentHeaderView.swift b/WordPress/Classes/ViewRelated/Comments/Content/CommentContentHeaderView.swift index 83c9ca333f13..7e68e61020ca 100644 --- a/WordPress/Classes/ViewRelated/Comments/Content/CommentContentHeaderView.swift +++ b/WordPress/Classes/ViewRelated/Comments/Content/CommentContentHeaderView.swift @@ -54,11 +54,7 @@ struct CommentContentHeaderView: View { let onOptionSelected: (Option) -> Void enum Option { - case userInfo, share, editComment, changeStatus(Status) - } - - enum Status { - case approve, pending, spam, trash + case userInfo, share, editComment, changeStatus } } } @@ -94,12 +90,7 @@ private struct CommentContentHeaderMenu: View { button(title: Strings.editComment, icon: .edit) { config.onOptionSelected(.editComment) } } if config.changeStatus { - Menu(Strings.changeStatus) { - button(title: Strings.approve) { config.onOptionSelected(.changeStatus(.approve)) } - button(title: Strings.pending) { config.onOptionSelected(.changeStatus(.pending)) } - button(title: Strings.spam) { config.onOptionSelected(.changeStatus(.spam)) } - button(title: Strings.trash) { config.onOptionSelected(.changeStatus(.trash)) } - } + button(title: Strings.changeStatus) { config.onOptionSelected(.changeStatus) } } } label: { Image.DS.icon(named: .ellipsisHorizontal) @@ -147,26 +138,6 @@ private extension CommentContentHeaderMenu { value: "Change status", comment: "Change status option title for the comment moderation content menu." ) - static let approve = NSLocalizedString( - "comment.moderation.content.menu.approve.title", - value: "Approve", - comment: "Approve option title for the comment moderation content menu." - ) - static let pending = NSLocalizedString( - "comment.moderation.content.menu.pending.title", - value: "Pending", - comment: "Pending option title for the comment moderation content menu." - ) - static let trash = NSLocalizedString( - "comment.moderation.content.menu.trash.title", - value: "Trash", - comment: "Trash option title for the comment moderation content menu." - ) - static let spam = NSLocalizedString( - "comment.moderation.content.menu.spam.title", - value: "Spam", - comment: "Spam option title for the comment moderation content menu." - ) } } From 540aa4970d400d53c70cc0121ab8b919b5e4fcfd Mon Sep 17 00:00:00 2001 From: Salim Braksa Date: Wed, 8 May 2024 18:20:57 +0100 Subject: [PATCH 108/112] Present Comment Moderation Options view --- .../CommentDetailViewController.swift | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Comments/CommentDetailViewController.swift b/WordPress/Classes/ViewRelated/Comments/CommentDetailViewController.swift index 3b74bfe31258..80a8580daf49 100644 --- a/WordPress/Classes/ViewRelated/Comments/CommentDetailViewController.swift +++ b/WordPress/Classes/ViewRelated/Comments/CommentDetailViewController.swift @@ -140,6 +140,10 @@ class CommentDetailViewController: UIViewController, NoResultsViewHost { return cell }() + private weak var changeStatusViewController: UIViewController? + + // MARK: - + private lazy var commentService: CommentService = { return .init(coreDataStack: ContextManager.shared) }() @@ -449,8 +453,8 @@ private extension CommentDetailViewController { editButtonTapped() case .share: shareCommentURL(sourceView: cell) - case .changeStatus(let status): - print("Option \(status) tapped") + case .changeStatus: + presentChangeStatusSheet() } } let contentConfig = CommentDetailContentTableViewCell.ContentConfiguration(comment: comment) { [weak self] _ in @@ -711,6 +715,20 @@ private extension CommentDetailViewController { let bottomSheet = BottomSheetViewController(childViewController: viewController, customHeaderSpacing: 0) bottomSheet.show(from: self) } + + func presentChangeStatusSheet() { + self.changeStatusViewController?.dismiss(animated: false) + let controller = CommentModerationOptionsViewController { [weak self] status in + guard let self else { + return + } + self.updateCommentStatus(status) + self.changeStatusViewController?.dismiss(animated: true) + } + let bottomSheetViewController = BottomSheetViewController(childViewController: controller, customHeaderSpacing: 0) + bottomSheetViewController.show(from: self) + self.changeStatusViewController = bottomSheetViewController + } } // MARK: - Strings @@ -751,6 +769,15 @@ private extension CommentStatusType { // MARK: - Comment Moderation Actions private extension CommentDetailViewController { + func updateCommentStatus(_ status: CommentModerationOptionsView.Option) { + switch status { + case .pending: unapproveComment() + case .approve: approveComment() + case .spam: spamComment() + case .trash: trashComment() + } + } + func unapproveComment() { isNotificationComment ? WPAppAnalytics.track(.notificationsCommentUnapproved, withProperties: Constants.notificationDetailSource, From fc2c17644f4e5ac1406b0ccb6204b1f5f1a92aa4 Mon Sep 17 00:00:00 2001 From: Salim Braksa Date: Thu, 9 May 2024 01:12:24 +0100 Subject: [PATCH 109/112] Define transition and animation delay as constants --- .../Comment Moderation/CommentModerationView.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift index 5d0b82f95ab6..2742dafda005 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift @@ -36,9 +36,17 @@ private struct Container: View { let icon: V let content: T + private var transition: AnyTransition = .opacity + private let animationDuration = 0.25 - private var animation: Animation { + private let insertionAnimationDelay = 0.8 + + private var insertionAnimation: Animation { + return removalAnimation.delay(insertionAnimationDelay) + } + + private var removalAnimation: Animation { return .smooth(duration: animationDuration) } @@ -56,8 +64,8 @@ private struct Container: View { .padding(.horizontal, .DS.Padding.double) .transition( .asymmetric( - insertion: .opacity.animation(animation.delay(animationDuration * 0.8)), - removal: .opacity.animation(animation) + insertion: transition.animation(insertionAnimation), + removal: transition.animation(removalAnimation) ) ) } From 346b52c94bfc39b39125c576fb9e436b043ed52b Mon Sep 17 00:00:00 2001 From: Salim Braksa Date: Thu, 9 May 2024 11:18:02 +0100 Subject: [PATCH 110/112] Change changeStatusViewController type --- .../ViewRelated/Comments/CommentDetailViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Comments/CommentDetailViewController.swift b/WordPress/Classes/ViewRelated/Comments/CommentDetailViewController.swift index 80a8580daf49..c5a52ba98e6b 100644 --- a/WordPress/Classes/ViewRelated/Comments/CommentDetailViewController.swift +++ b/WordPress/Classes/ViewRelated/Comments/CommentDetailViewController.swift @@ -140,7 +140,7 @@ class CommentDetailViewController: UIViewController, NoResultsViewHost { return cell }() - private weak var changeStatusViewController: UIViewController? + private weak var changeStatusViewController: BottomSheetViewController? // MARK: - From c213fe8ab9b2582c5b91502f713e4fc6b1984596 Mon Sep 17 00:00:00 2001 From: kean Date: Tue, 7 May 2024 10:45:26 -0400 Subject: [PATCH 111/112] Update changelog --- RELEASE-NOTES.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 5e128f7bf766..9b529f776b98 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -7,6 +7,23 @@ * [*] Impove the "Post Settings" screen groups/ordering to better align with Gutenberg [#23164] * [*] Update the "More" menu in the Editor to use modern iOS design and update copy to match Gutenberg [#23145] * [*] Update the "Revisions" list design and fix an issue with the footer displaying incorrect "Date Created" for drafts [#23145] +* [***] Fix multiple post sync issues: [#21895], [#22037], [#8111], [#10168], [#17343], [#14221], [#12800], [#12073], [#14572], [#9319], [#21941], [#3070], [#3978], [#9449], [#13023], [#21472] +* [**] Fix [#4870], [#14798], an issue where the app sometimes overwrites the changes made on the remote. The app will now show a new "Conflict Resolution" screen if there is a conflict. It will also no longer try to auto-upload changes to an existing published post or publish a draft, eliminating multiple possible failure points. +* [**] Fix [#12121], [#13724], [#14251], [#18517], [#17086], [#15767], [#16514], [#13654] and other untracked issues with the "Publish Date" field in Post Settings. The app now has a new date picker that makes it easier to pick and remove the selected date. It also makes it clear if the blog is in a different time zones from your local timezone. The "Publish Date" field was also removed from the Post Settings screen for draft posts – the date needs to be selected right before publishing. +* [**] Add a new "Media Uploads" screen to the "Publishing Sheet" where you can see the status of the pending uploads. It also shows error messages for each individual upload (if any) and allows you to cancel the failing uploads. +* [*] Fix [#21940], [#13432], [#11435], issues where "Discard Changes" would sometimes delete the entire draft or appear when no changes were made +* [*] Fix [#12099] by removing the "Status" field from Post Settings that was leading to multiple unexpected and confusing scenarios. The app now manages the transitions between state the same way as Gutenberg, so it should feel more familiar to the users. +* [*] Fix [#10663], an issue with unclear error messages. The messages for "synchronous" operations will not appear as alerts with detailed information about the error. The error for drafts will also appear more clearly. +* [*] Update the "Autosave Available" dialog to work the same way as in Gutenberg and to better represent what this feature is – it can no longer be confused with a "Data Conflict" dialog (fixes [#13093]) +* [*] Fix [#22107], [#19540], [#13632] by removing the "Draft Uploaded" snackbar +* [*] Fix editor state restoration in the Jetpack app +* [*] Update the support for pending posts to have clearer flows for both admins and contributors +* [*] Fix [#19886], an issue with a missing error message when selecting a page author that's not eligible to be a page author +* [*] Fix [#22969] an issue where Post List content occasionally stops updating +* [*] Fix [#3862] by adding a "Password Protected" badge in [#23154] +* [*] Fix [#21093], a rare crash in "View Stats for Posts" – [#23155] +* [*] Fix [#22247], a rare crash in Post List +* [*] Update the app to use `wp.createPost` and `wp.editPost` instead of the respective deprecated `metaWeblog.*` methods (closes [#1385]) 24.8 ----- From e1b1031f7127a9c67d4a18ef1a825a8a4bda7b05 Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Thu, 9 May 2024 17:21:38 +0200 Subject: [PATCH 112/112] Fix regression on animation duration --- .../Comment Moderation/CommentModerationView.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift index 2742dafda005..7e3ef8bdac9e 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Comment Moderation/CommentModerationView.swift @@ -40,7 +40,9 @@ private struct Container: View { private let animationDuration = 0.25 - private let insertionAnimationDelay = 0.8 + private var insertionAnimationDelay: TimeInterval { + return animationDuration * 0.8 + } private var insertionAnimation: Animation { return removalAnimation.delay(insertionAnimationDelay)