Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add context menus and previews for sites in Reader #23964

Merged
merged 10 commits into from
Jan 9, 2025
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
Loading