diff --git a/WordPress/Classes/Utility/SharedStrings.swift b/WordPress/Classes/Utility/SharedStrings.swift index 628c684947b1..32c4c32adcc8 100644 --- a/WordPress/Classes/Utility/SharedStrings.swift +++ b/WordPress/Classes/Utility/SharedStrings.swift @@ -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") diff --git a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderPostMenu.swift b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderPostMenu.swift index 42fa44f212a2..6bd3e5d084fe 100644 --- a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderPostMenu.swift +++ b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderPostMenu.swift @@ -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) } @@ -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) } @@ -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") diff --git a/WordPress/Classes/ViewRelated/Reader/Sidebar/ReaderSidebarSubscriptionsSection.swift b/WordPress/Classes/ViewRelated/Reader/Sidebar/ReaderSidebarSubscriptionsSection.swift index 40a6100cd2ac..b435a270b9da 100644 --- a/WordPress/Classes/ViewRelated/Reader/Sidebar/ReaderSidebarSubscriptionsSection.swift +++ b/WordPress/Classes/ViewRelated/Reader/Sidebar/ReaderSidebarSubscriptionsSection.swift @@ -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 { @@ -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) } } diff --git a/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionCell.swift b/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionCell.swift index 49575ab2542f..c976a7d1298e 100644 --- a/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionCell.swift +++ b/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionCell.swift @@ -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 { @@ -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) } diff --git a/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionNotificationSettingsView.swift b/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionNotificationSettingsView.swift index 9242977fe0e1..350679a1d34f 100644 --- a/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionNotificationSettingsView.swift +++ b/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionNotificationSettingsView.swift @@ -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 {