Skip to content

Commit

Permalink
Add context menus and previews for sites in Reader (#23964)
Browse files Browse the repository at this point in the history
* Fix l10n typo

* Add Unsubscribe context menu to Reader sidebar sites

* Extract ReaderSiteFavoriteButton

* Move actions to ReaderSidebarSubscriptionCell

* Extract ReaderSubscriptionContextMenu and add Share

* Add Notification Settings and Copy Link buttons

* Add context menu for sites in Subscriptions view

* Add previews

* Fix notification settings sometimes being clipped on iPad

* Fix layout in ReaderSubscriptionCel actions
  • Loading branch information
kean authored Jan 9, 2025
1 parent 39fa791 commit 3a4c671
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 54 deletions.
5 changes: 5 additions & 0 deletions WordPress/Classes/Utility/SharedStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ enum SharedStrings {
/// - warning: This is the legacy value. It's not compliant with the new format but has the correct translation for different languages.
static let title = NSLocalizedString("Reader", comment: "The accessibility value of the Reader tab.")
static let unfollow = NSLocalizedString("reader.button.unfollow", value: "Unfollow", comment: "Reader sidebar button title")
static let subscribe = NSLocalizedString("reader.button.subscribe", value: "Subscribe", comment: "A shared button title for Reader")
static let unsubscribe = NSLocalizedString("reader.button.unsubscribe", value: "Unsubscribe", comment: "A shared button title for Reader")
static let addToFavorites = NSLocalizedString("reader.button.addToFavorites", value: "Add to Favorites", comment: "A shared button title for Reader")
static let notificationSettings = NSLocalizedString("reader.button.notificationSettings", value: "Notification Settings", comment: "A shared button title for Reader")
static let removeFromFavorites = NSLocalizedString("reader.button.removeFromFavorites", value: "Remove from Favorites", comment: "A shared button title for Reader")
static let recent = NSLocalizedString("reader.recent.title", value: "Recent", comment: "Used in multiple contexts, usually as a screen title")
static let discover = NSLocalizedString("reader.discover.title", value: "Discover", comment: "Used in multiple contexts, usually as a screen title")
static let saved = NSLocalizedString("reader.saved.title", value: "Saved", comment: "Used in multiple contexts, usually as a screen title")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ struct ReaderPostMenu {
}

private var subscribe: UIAction {
UIAction(Strings.subscribe, systemImage: "plus.circle") {
UIAction(SharedStrings.Reader.subscribe, systemImage: "plus.circle") {
ReaderSubscriptionHelper().toggleSiteSubscription(forPost: post)
track(.subscribe)
}
Expand All @@ -102,7 +102,7 @@ struct ReaderPostMenu {
}

private var ubsubscribe: UIAction {
UIAction(Strings.unsubscribe, systemImage: "minus.circle", attributes: [.destructive]) {
UIAction(SharedStrings.Reader.unsubscribe, systemImage: "minus.circle", attributes: [.destructive]) {
ReaderSubscriptionHelper().toggleSiteSubscription(forPost: post)
track(.unsubscribe)
}
Expand Down Expand Up @@ -214,8 +214,6 @@ private enum Strings {
static let viewInBrowser = NSLocalizedString("reader.postContextMenu.viewInBrowser", value: "View in Browser", comment: "Context menu action")
static let blockOrReport = NSLocalizedString("reader.postContextMenu.blockOrReportMenu", value: "Block or Report", comment: "Context menu action")
static let goToBlog = NSLocalizedString("reader.postContextMenu.showBlog", value: "Go to Blog", comment: "Context menu action")
static let subscribe = NSLocalizedString("reader.postContextMenu.subscribeT", value: "Subscribe", comment: "Context menu action")
static let unsubscribe = NSLocalizedString("reader.postContextMenu.unsubscribe", value: "Unsubscribe", comment: "Context menu action")
static let manageNotifications = NSLocalizedString("reader.postContextMenu.manageNotifications", value: "Manage Notifications", comment: "Context menu action")
static let blogDetails = NSLocalizedString("reader.postContextMenu.blogDetails", value: "Blog Details", comment: "Context menu action (placeholder value when blog name not available – should never happen)")
static let blockSite = NSLocalizedString("reader.postContextMenu.blockSite", value: "Block Site", comment: "Context menu action")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct ReaderSidebarSubscriptionsSection: View {
struct ReaderSidebarSubscriptionCell: View {
@ObservedObject var site: ReaderSiteTopic
@Environment(\.editMode) var editMode
@State private var isShowingSettings = false

var body: some View {
HStack {
Expand All @@ -40,28 +41,92 @@ struct ReaderSidebarSubscriptionCell: View {
}
if editMode?.wrappedValue.isEditing == true {
Spacer()
Button {
if !site.showInMenu {
WPAnalytics.track(.readerAddSiteToFavoritesTapped)
}

let siteObjectID = TaggedManagedObjectID(site)
ContextManager.shared.performAndSave({ managedObjectContext in
let site = try managedObjectContext.existingObject(with: siteObjectID)
site.showInMenu.toggle()
}, completion: nil, on: DispatchQueue.main)
} label: {
Image(systemName: site.showInMenu ? "star.fill" : "star")
.foregroundStyle(site.showInMenu ? .pink : .secondary)
}.buttonStyle(.plain)
ReaderSiteToggleFavoriteButton(site: site, source: "edit_mode")
.labelStyle(.iconOnly)
}
}
.lineLimit(1)
.tag(ReaderSidebarItem.subscription(TaggedManagedObjectID(site)))
.swipeActions(edge: .leading) {
if let siteURL = URL(string: site.siteURL) {
ShareLink(item: siteURL).tint(.blue)
}
}
.swipeActions(edge: .trailing) {
Button(SharedStrings.Reader.unfollow, role: .destructive) {
ReaderSubscriptionHelper().unfollow(site)
}.tint(.red)
}
.contextMenu(menuItems: {
ReaderSubscriptionContextMenu(site: site, isShowingSettings: $isShowingSettings)
}, preview: {
ReaderTopicPreviewView(topic: site)
})
.sheet(isPresented: $isShowingSettings) {
ReaderSubscriptionNotificationSettingsView(siteID: site.siteID.intValue)
.presentationDetents([.medium, .large])
.edgesIgnoringSafeArea(.bottom)
}
}
}

struct ReaderSubscriptionContextMenu: View {
let site: ReaderSiteTopic

@Binding var isShowingSettings: Bool

var body: some View {
if let siteURL = URL(string: site.siteURL) {
ShareLink(item: siteURL)
Button(SharedStrings.Button.copyLink, systemImage: "doc.on.doc") {
UIPasteboard.general.string = siteURL.absoluteString
}
}
if site.following {
ReaderSiteToggleFavoriteButton(site: site, source: "context_menu")
Button(SharedStrings.Reader.notificationSettings, systemImage: "bell") {
isShowingSettings = true
}
Button(SharedStrings.Reader.unsubscribe, systemImage: "minus.circle", role: .destructive) {
ReaderSubscriptionHelper().unfollow(site)
}
} else {
Button(SharedStrings.Reader.subscribe, systemImage: "plus.circle") {
ReaderSubscriptionHelper().toggleFollowingForSite(site)
}
}
}
}

struct ReaderTopicPreviewView: UIViewControllerRepresentable {
let topic: ReaderAbstractTopic

func makeUIViewController(context: Context) -> ReaderStreamViewController {
ReaderStreamViewController.controllerWithTopic(topic)
}

func updateUIViewController(_ vc: ReaderStreamViewController, context: Context) {
// Do nothing
}
}

struct ReaderSiteToggleFavoriteButton: View {
let site: ReaderSiteTopic
let source: String

var body: some View {
Button {
if !site.showInMenu {
WPAnalytics.track(.readerAddSiteToFavoritesTapped, properties: ["via": source])
}
let siteObjectID = TaggedManagedObjectID(site)
ContextManager.shared.performAndSave({ managedObjectContext in
let site = try managedObjectContext.existingObject(with: siteObjectID)
site.showInMenu.toggle()
}, completion: nil, on: DispatchQueue.main)
} label: {
Label(site.showInMenu ? SharedStrings.Reader.removeFromFavorites : SharedStrings.Reader.addToFavorites, systemImage: site.showInMenu ? "star.fill" : "star")
.foregroundStyle(site.showInMenu ? .pink : .secondary)
}.buttonStyle(.plain)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,19 @@ struct ReaderSubscriptionCell: View {

Spacer()

if let status = ReaderSubscriptionNotificationsStatus(site: site) {
makeButtonNotificationSettings(with: status)
HStack(spacing: 0) {
if let status = ReaderSubscriptionNotificationsStatus(site: site) {
makeButtonNotificationSettings(with: status)
}
buttonMore
}
buttonMore
.padding(.trailing, -16)
}
.contextMenu(menuItems: {
ReaderSubscriptionContextMenu(site: site, isShowingSettings: $isShowingSettings)
}, preview: {
ReaderTopicPreviewView(topic: site)
})
}

private func makeButtonNotificationSettings(with status: ReaderSubscriptionNotificationsStatus) -> some View {
Expand All @@ -65,36 +73,24 @@ struct ReaderSubscriptionCell: View {
}
.font(.subheadline)
.frame(width: 34, alignment: .center)
.padding(.trailing, 6)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.popover(isPresented: $isShowingSettings) { settings }
}

@ViewBuilder
private var settings: some View {
if horizontalSizeClass == .compact {
ReaderSubscriptionNotificationSettingsView(siteID: site.siteID.intValue, isCompact: true)
.presentationDetents([.medium, .large])
.edgesIgnoringSafeArea(.all)
} else {
.sheet(isPresented: $isShowingSettings) {
ReaderSubscriptionNotificationSettingsView(siteID: site.siteID.intValue)
.presentationDetents([.medium, .large])
.edgesIgnoringSafeArea(.bottom)
}
}

private var buttonMore: some View {
Menu {
if let siteURL = URL(string: site.siteURL) {
ShareLink(item: siteURL)
}
Button(role: .destructive) {
onDelete(site)
} label: {
Label(SharedStrings.Reader.unfollow, systemImage: "trash")
}
ReaderSubscriptionContextMenu(site: site, isShowingSettings: $isShowingSettings)
} label: {
Image(systemName: "ellipsis")
.foregroundStyle(.secondary)
.frame(width: 40, height: 40)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,22 @@ import UIKit

struct ReaderSubscriptionNotificationSettingsView: UIViewControllerRepresentable {
let siteID: Int
var isCompact = false

@Environment(\.dismiss) var dismiss

func makeUIViewController(context: Context) -> UIViewController {
let vc = NotificationSiteSubscriptionViewController(siteId: siteID)
if isCompact {
vc.navigationItem.rightBarButtonItem = UIBarButtonItem(title: SharedStrings.Button.done, primaryAction: .init { _ in
dismiss()
})
// - warning: UIKit is used to prevent the modifiers from the
// containing list to affect this screen/
return UINavigationController(rootViewController: vc)
}
return vc
vc.navigationItem.rightBarButtonItem = UIBarButtonItem(title: SharedStrings.Button.done, primaryAction: .init { _ in
dismiss()
})
// - warning: UIKit is used to prevent the modifiers from the
// containing list to affect this screen/
return UINavigationController(rootViewController: vc)
}

func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
// Do nothing
}

func sizeThatFits(_ proposal: ProposedViewSize, uiViewController: UIViewController, context: Context) -> CGSize? {
isCompact ? nil : CGSize(width: 320, height: 434)
}
}

extension NotificationSiteSubscriptionViewController {
Expand Down

0 comments on commit 3a4c671

Please sign in to comment.