Skip to content

Commit 6af4f4f

Browse files
authored
Feature: Post Settings (#24623)
* Add PostAuthorPicker * Remvoe preview * Add .postSettingsV2 FF * Add empty PostSettingsView * Add PostSettings * Remove localizable strings * Make status type-safe * Remove publicize options for now * Fix compilation * Fix cancel flow * Fix presenation from editor * Show Done button when shown from the editor * Cleanup PostSettings * Cleanup * Fix compilation with AnyView * Add PostFormatPicker * Remove unused blogervice * Add the General section * Finish author row changes * Add pending review row * Move analytics to ViewModel * Display time zone at the bottom of date picker making it clear that it is not editable * Implement Publish Row * Add visibility picker * Add visibility section * Cleanup * Add Featured Image section * Add animations * Add Taxonomy section * Update unit tests * Add Excerpt * Add Slug field * Move Pending Review also to More options * Add post format field * Add Parent Page * Cleanup * Minor fixes * Fix CMM-548: an issue with social sharing options in the prepublishing sheet * Remove Social Sharing * Make enablePublicizeConnectionWithKeyringID non-objc * Remove isMultiAuthorBlog * Remove PublishSettingsViewModel * Remove titleForVisibility * Remove FeaturedImageDelegate * Remove ategoriesText * Remove PostSettingsViewController * Remove PageSettingsViewController * Rename NewPostSettingsVC * Remove postSettingsV2 FF * Fix build * Add Sticky * Move analytics to save * Refresh only what changed * Add organization improvements * Add excerpt counter outside * Improve excerpt editing experience * Add Info section * Cleanup the Info section * Remove unused Slugfordisplay * Use switch for Post/Page * Add new section headers with WordPress fonst * Nicer PostSettingsFeaturedImageRow * Add improved excerpt editor * Increase featured image row * Simplify excerpt header * Add new design for categories and tags * Add a better way to display categories and tags * Revert drag-n-drop code * Cleanup * Move sections in code according to design * Upadte last edited to show relative date * Move slug to the General section * Update UI tests * Cleanup * Update UI tests * Cleanur Me menu * Simplify ParentPagePicker
1 parent 8160e41 commit 6af4f4f

72 files changed

Lines changed: 2529 additions & 2125 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ WordPress-iOS uses a modular architecture with the main app and separate Swift p
4242
- Follow Swift API Design Guidelines
4343
- Use strict access control modifiers where possible
4444
- Use four spaces (not tabs)
45+
- Lines should not have trailing whitespace
4546
- Follow the standard formatting practices enforced by SwiftLint
4647
- Don't create `body` for `View` that are too long
4748
- Use semantics text sizes like `.headline`

Modules/Sources/UITestsFoundation/Screens/Editor/EditorPostSettings.swift

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,39 @@ import XCTest
44
public class EditorPostSettings: ScreenObject {
55

66
private let settingsTableGetter: (XCUIApplication) -> XCUIElement = {
7-
$0.tables["SettingsTable"]
7+
$0.collectionViews["post_settings_form"].firstMatch
88
}
99

1010
private let categoriesSectionGetter: (XCUIApplication) -> XCUIElement = {
11-
$0.cells["Categories"]
11+
$0.buttons["post_settings_categories"].firstMatch
1212
}
1313

1414
private let tagsSectionGetter: (XCUIApplication) -> XCUIElement = {
15-
$0.cells["Tags"]
15+
$0.buttons["post_settings_tags"].firstMatch
1616
}
1717

1818
private let publishDateButtonGetter: (XCUIApplication) -> XCUIElement = {
19-
$0.staticTexts["Publish Date"]
19+
$0.staticTexts["Publish Date"].firstMatch
2020
}
2121

2222
private let dateSelectorGetter: (XCUIApplication) -> XCUIElement = {
23-
$0.staticTexts["Immediately"]
23+
$0.staticTexts["Immediately"].firstMatch
2424
}
2525

2626
private let nextMonthButtonGetter: (XCUIApplication) -> XCUIElement = {
27-
$0.buttons["Next Month"]
27+
$0.buttons["Next Month"].firstMatch
2828
}
2929

3030
private let monthLabelGetter: (XCUIApplication) -> XCUIElement = {
31-
$0.buttons["Month"]
31+
$0.buttons["Month"].firstMatch
3232
}
3333

3434
private let firstCalendarDayButtonGetter: (XCUIApplication) -> XCUIElement = {
3535
$0.buttons.containing(.staticText, identifier: "1").element
3636
}
3737

38-
private let closeButtonGetter: (XCUIApplication) -> XCUIElement = {
39-
$0.navigationBars.buttons["close"]
38+
private let saveButtonGetter: (XCUIApplication) -> XCUIElement = {
39+
$0.navigationBars.buttons["post_settings_save_button"].firstMatch
4040
}
4141

4242
private let backButtonGetter: (XCUIApplication) -> XCUIElement? = {
@@ -45,10 +45,9 @@ public class EditorPostSettings: ScreenObject {
4545

4646
var categoriesSection: XCUIElement { categoriesSectionGetter(app) }
4747
var chooseFromMediaButton: XCUIElement { app.buttons["Choose from Media"].firstMatch }
48-
var closeButton: XCUIElement { closeButtonGetter(app) }
48+
var saveButton: XCUIElement { saveButtonGetter(app) }
4949
var backButton: XCUIElement? { backButtonGetter(app) }
50-
var featuredImageCell: XCUIElement { app.cells["post_settings_featured_image_cell"].firstMatch }
51-
var selectedFeaturedImage: XCUIElement { app.otherElements["featured_image_current_image"].firstMatch }
50+
var selectedFeaturedImage: XCUIElement { app.images["featured_image_current_image_menu"].firstMatch }
5251
var firstCalendarDayButton: XCUIElement { firstCalendarDayButtonGetter(app) }
5352
var monthLabel: XCUIElement { monthLabelGetter(app) }
5453
var nextMonthButton: XCUIElement { nextMonthButtonGetter(app) }
@@ -88,13 +87,13 @@ public class EditorPostSettings: ScreenObject {
8887
}
8988

9089
public func removeFeatureImage() throws -> EditorPostSettings {
91-
featuredImageCell.tap()
90+
app.buttons["post_settings_featured_image_cell"].firstMatch.tap()
9291
app.buttons["featured_image_button_remove"].firstMatch.tap()
9392
return try EditorPostSettings()
9493
}
9594

9695
public func setFeaturedImage() throws -> EditorPostSettings {
97-
featuredImageCell.tap()
96+
app.buttons["post_settings_featured_image_cell"].firstMatch.tap()
9897
chooseFromMediaButton.tap()
9998
try MediaPickerAlbumScreen()
10099
.selectImage(atIndex: 0) // Select latest uploaded image
@@ -118,10 +117,13 @@ public class EditorPostSettings: ScreenObject {
118117
return try EditorPostSettings()
119118
}
120119

121-
@discardableResult
122-
public func closePostSettings() throws -> BlockEditorScreen {
123-
closeButton.tap()
120+
public func closePostSettings() {
121+
app.buttons["post_settings_cancel_button"].firstMatch.tap()
122+
}
124123

124+
@discardableResult
125+
public func savePostSettings() throws -> BlockEditorScreen {
126+
saveButton.tap()
125127
return try BlockEditorScreen()
126128
}
127129

Modules/Sources/WordPressUI/Extensions/SwiftUI+Extensions.swift

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,3 @@ import SwiftUI
44
public extension EdgeInsets {
55
static let zero = EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
66
}
7-
8-
private struct PresentingViewControllerKey: EnvironmentKey {
9-
static let defaultValue = WeakEnvironmentValueWrapper<UIViewController>()
10-
}
11-
12-
extension EnvironmentValues {
13-
public var presentingViewController: UIViewController? {
14-
get {
15-
self[PresentingViewControllerKey.self].value ?? UIViewController.topViewController
16-
}
17-
set {
18-
self[PresentingViewControllerKey.self].value = newValue
19-
}
20-
}
21-
}
22-
23-
private final class WeakEnvironmentValueWrapper<T: AnyObject> {
24-
weak var value: T?
25-
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import SwiftUI
2+
import DesignSystem
3+
4+
public struct SectionHeader: View {
5+
let title: String
6+
7+
public init(_ title: String) {
8+
self.title = title
9+
}
10+
11+
public var body: some View {
12+
Text(title.uppercased())
13+
.font(.caption2).fontWeight(.medium)
14+
.foregroundStyle(Color.secondary)
15+
}
16+
}

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
-----
77
* [**] Add new “Subscribers” screen that shows both your email and Reader subscribers [#24513]
88
* [*] Improve search in TimeZone Picker for more accurate results [#24612]
9+
* [*] Add new "Author Picker" for "Post Settings" with search and better design [#24621]
910
* [*] Fix an issue with “Stats / Subscribers” sometimes not showing the latest email subscribers [#24513]
1011
* [*] Fix an issue with "Stats" / "Subscribers" / "Emails" showing html encoded characters [#24513]
1112
* [*] Add search to “Jetpack Activity List” and display actors and dates [#24597]

Sources/Keystone/WordPress.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ FOUNDATION_EXPORT const unsigned char WordPressVersionString[];
2626

2727
#import <WordPress/NSObject+Helpers.h>
2828

29-
#import <WordPress/PageSettingsViewController.h>
3029
#import <WordPress/PostCategoryService.h>
31-
#import <WordPress/PostSettingsViewController.h>
3230
#import <WordPress/PostTagService.h>
3331

3432
#import <WordPress/ReaderPostService.h>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import Foundation
2+
import CoreData
3+
import WordPressShared
4+
5+
extension Blog {
6+
7+
// MARK: - Post Formats
8+
9+
/// Returns an array of post format keys sorted with "standard" first, then alphabetically
10+
@objc public var sortedPostFormats: [String] {
11+
guard let postFormats = postFormats as? [String: String], !postFormats.isEmpty else {
12+
return []
13+
}
14+
15+
var sortedFormats: [String] = []
16+
17+
// Add standard format first if it exists
18+
if postFormats[PostFormatStandard] != nil {
19+
sortedFormats.append(PostFormatStandard)
20+
}
21+
22+
// Add remaining formats sorted by their display names
23+
let nonStandardFormats = postFormats
24+
.filter { $0.key != PostFormatStandard }
25+
.sorted { $0.value.localizedCaseInsensitiveCompare($1.value) == .orderedAscending }
26+
.map { $0.key }
27+
28+
sortedFormats.append(contentsOf: nonStandardFormats)
29+
30+
return sortedFormats
31+
}
32+
33+
/// Returns an array of post format display names sorted with "Standard" first, then alphabetically
34+
@objc public var sortedPostFormatNames: [String] {
35+
guard let postFormats = postFormats as? [String: String] else {
36+
return []
37+
}
38+
return sortedPostFormats.compactMap { postFormats[$0] }
39+
}
40+
41+
/// Returns the default post format display text
42+
@objc public var defaultPostFormatText: String? {
43+
postFormatText(fromSlug: settings?.defaultPostFormat)
44+
}
45+
46+
// MARK: - Connections
47+
48+
/// Returns an array of PublicizeConnection objects sorted by service name, then by external name
49+
@objc public var sortedConnections: [PublicizeConnection] {
50+
guard let connections = Array(connections ?? []) as? [PublicizeConnection] else {
51+
return []
52+
}
53+
return connections.sorted { lhs, rhs in
54+
// First sort by service name (case insensitive, localized)
55+
let serviceComparison = lhs.service.localizedCaseInsensitiveCompare(rhs.service)
56+
if serviceComparison != .orderedSame {
57+
return serviceComparison == .orderedAscending
58+
}
59+
// Then sort by external name (case insensitive)
60+
return lhs.externalName.caseInsensitiveCompare(rhs.externalName) == .orderedAscending
61+
}
62+
}
63+
64+
// MARK: - Roles
65+
66+
/// Returns an array of roles sorted by order.
67+
public var sortedRoles: [Role] {
68+
guard let roles = Array(roles ?? []) as? [Role] else {
69+
return []
70+
}
71+
return roles.sorted { lhs, rhs in
72+
(lhs.order?.intValue ?? 0) < (rhs.order?.intValue ?? 0)
73+
}
74+
}
75+
}

Sources/WordPressData/Objective-C/AbstractPost.m

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,6 @@ - (BOOL)isPrivateAtWPCom
320320
return self.blog.isPrivateAtWPCom;
321321
}
322322

323-
- (BOOL)isMultiAuthorBlog
324-
{
325-
return self.blog.isMultiAuthor;
326-
}
327-
328323
- (BOOL)isUploading
329324
{
330325
return self.remoteStatus == AbstractPostRemoteStatusPushing;

Sources/WordPressData/Objective-C/BasePost.m

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,6 @@ - (NSDate *)dateForDisplay
7676
return [self dateCreated];
7777
}
7878

79-
- (NSString *)slugForDisplay
80-
{
81-
if (self.wp_slug.length > 0) {
82-
return self.wp_slug;
83-
}
84-
return self.suggested_slug;
85-
}
86-
8779
- (BOOL)hasContent
8880
{
8981
BOOL titleIsEmpty = self.postTitle ? self.postTitle.isEmpty : YES;

Sources/WordPressData/Objective-C/Blog.m

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -281,56 +281,6 @@ - (NSArray *)sortedCategories
281281
return [[self.categories allObjects] sortedArrayUsingDescriptors:sortDescriptors];
282282
}
283283

284-
- (NSArray *)sortedPostFormats
285-
{
286-
if ([self.postFormats count] == 0) {
287-
return @[];
288-
}
289-
290-
NSMutableArray *sortedFormats = [NSMutableArray arrayWithCapacity:[self.postFormats count]];
291-
292-
if (self.postFormats[PostFormatStandard]) {
293-
[sortedFormats addObject:PostFormatStandard];
294-
}
295-
296-
NSArray *sortedNonStandardFormats = [[self.postFormats keysSortedByValueUsingSelector:@selector(localizedCaseInsensitiveCompare:)] wp_filter:^BOOL(id obj) {
297-
return ![obj isEqual:PostFormatStandard];
298-
}];
299-
300-
[sortedFormats addObjectsFromArray:sortedNonStandardFormats];
301-
302-
return [NSArray arrayWithArray:sortedFormats];
303-
}
304-
305-
- (NSArray *)sortedPostFormatNames
306-
{
307-
return [[self sortedPostFormats] wp_map:^id(NSString *key) {
308-
return self.postFormats[key];
309-
}];
310-
}
311-
312-
- (NSArray *)sortedConnections
313-
{
314-
NSSortDescriptor *sortServiceDescriptor = [[NSSortDescriptor alloc] initWithKey:@"service"
315-
ascending:YES
316-
selector:@selector(localizedCaseInsensitiveCompare:)];
317-
NSSortDescriptor *sortExternalNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"externalName"
318-
ascending:YES
319-
selector:@selector(caseInsensitiveCompare:)];
320-
NSArray *sortDescriptors = @[sortServiceDescriptor, sortExternalNameDescriptor];
321-
return [[self.connections allObjects] sortedArrayUsingDescriptors:sortDescriptors];
322-
}
323-
324-
- (NSArray<Role *> *)sortedRoles
325-
{
326-
return [self.roles sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"order" ascending:YES]]];
327-
}
328-
329-
- (NSString *)defaultPostFormatText
330-
{
331-
return [self postFormatTextFromSlug:self.settings.defaultPostFormat];
332-
}
333-
334284
- (BOOL)hasMappedDomain {
335285
if (![self isHostedAtWPcom]) {
336286
return NO;

0 commit comments

Comments
 (0)