Fix snackbar issues

This commit is contained in:
Jackson Harper 2023-07-27 14:06:29 +08:00
parent 2fd841775d
commit 2d7fe30d6d
17 changed files with 94 additions and 85 deletions

View file

@ -173,15 +173,6 @@ import Utils
let highlights = annotations?.compactMap { $0 as? HighlightAnnotation }
let shortId = highlights.flatMap { coordinator.shortHighlightIds($0).first }
if let shortId = shortId, FeatureFlag.enableShareButton {
let share = MenuItem(title: "Share", block: {
if let shareURL = viewModel.highlightShareURL(dataService: dataService, shortId: shortId) {
shareLink = ShareLink(id: UUID(), url: shareURL)
}
})
result.append(share)
}
return result
})
.fullScreenCover(isPresented: $readerView, content: {
@ -270,7 +261,7 @@ import Utils
if let customHighlight = annotation.customData?["omnivoreHighlight"] as? [String: String] {
if customHighlight["id"]?.lowercased() == highlightId {
if !document.remove(annotations: [annotation]) {
Snackbar.show(message: "Error removing highlight")
viewModel.snackbar(message: "Error removing highlight")
}
}
}

View file

@ -2,17 +2,26 @@ import Foundation
import Models
import Services
import Utils
// import Views
final class PDFViewerViewModel: ObservableObject {
@Published var errorMessage: String?
@Published var readerView: Bool = false
@Published var showSnackbar: Bool = false
var snackbarMessage: String?
let pdfItem: PDFItem
init(pdfItem: PDFItem) {
self.pdfItem = pdfItem
}
func snackbar(message: String) {
snackbarMessage = message
showSnackbar = true
}
func loadHighlightPatches(completion onComplete: @escaping ([String]) -> Void) {
onComplete(pdfItem.highlights.map { $0.patch ?? "" })
}
@ -75,19 +84,6 @@ final class PDFViewerViewModel: ObservableObject {
)
}
func highlightShareURL(dataService: DataService, shortId: String) -> URL? {
let baseURL = dataService.appEnvironment.serverBaseURL
var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)
if let username = dataService.currentViewer?.username {
components?.path = "/\(username)/\(pdfItem.slug)/highlights/\(shortId)"
} else {
return nil
}
return components?.url
}
func downloadPDF(dataService: DataService) async -> URL? {
do {
if let localPdfURL = pdfItem.localPdfURL, FileManager.default.fileExists(atPath: localPdfURL.path) {

View file

@ -3,7 +3,7 @@ import Services
import Views
extension Snackbar {
static func show(message: String, undoAction: (() -> Void)? = nil) {
NSNotification.operationSuccess(message: message, undoAction: undoAction)
static func showInLibrary(message: String, undoAction: (() -> Void)? = nil) {
NSNotification.librarySnackBar(message: message, undoAction: undoAction)
}
}

View file

@ -35,7 +35,7 @@
pasteBoard.writeObjects([highlightParams.quote as NSString])
#endif
Snackbar.show(message: "Highlight copied")
// Snackbar.show(message: "Highlight copied")
},
label: { Label("Copy", systemImage: "doc.on.doc") }
)

View file

@ -242,9 +242,6 @@ struct AnimatingCellHeight: AnimatableModifier {
@Binding var prefersListLayout: Bool
@ObservedObject var viewModel: HomeFeedViewModel
@State var showSnackbar = false
@State var snackbarOperation: SnackbarOperation?
var body: some View {
VStack(spacing: 0) {
if prefersListLayout || !enableGrid {
@ -261,9 +258,9 @@ struct AnimatingCellHeight: AnimatableModifier {
self.viewModel.negatedLabels = $1
}
}
.popup(isPresented: $showSnackbar) {
if let operation = snackbarOperation {
Snackbar(isShowing: $showSnackbar, operation: operation)
.popup(isPresented: $viewModel.showSnackbar) {
if let operation = viewModel.snackbarOperation {
Snackbar(isShowing: $viewModel.showSnackbar, operation: operation)
} else {
EmptyView()
}
@ -275,17 +272,13 @@ struct AnimatingCellHeight: AnimatableModifier {
.animation(.spring())
.isOpaque(false)
}
.onReceive(NSNotification.operationSuccessPublisher) { notification in
if let message = notification.userInfo?["message"] as? String {
snackbarOperation = SnackbarOperation(message: message,
undoAction: notification.userInfo?["undoAction"] as? SnackbarUndoAction)
showSnackbar = true
}
}
.onReceive(NSNotification.operationFailedPublisher) { notification in
if let message = notification.userInfo?["message"] as? String {
showSnackbar = true
snackbarOperation = SnackbarOperation(message: message, undoAction: nil)
.onReceive(NSNotification.librarySnackBarPublisher) { notification in
if !viewModel.showSnackbar {
if let message = notification.userInfo?["message"] as? String {
viewModel.snackbarOperation = SnackbarOperation(message: message,
undoAction: notification.userInfo?["undoAction"] as? SnackbarUndoAction)
viewModel.showSnackbar = true
}
}
}
}

View file

@ -37,6 +37,9 @@ import Views
@Published var listConfig: LibraryListConfig
@Published var showSnackbar = false
@Published var snackbarOperation: SnackbarOperation?
var cursor: String?
// These are used to make sure we handle search result
@ -314,9 +317,14 @@ import Views
setItems(dataService.viewContext, fetchedResultsController.fetchedObjects ?? [])
}
func snackbar(_ message: String, undoAction: SnackbarUndoAction? = nil) {
snackbarOperation = SnackbarOperation(message: message, undoAction: undoAction)
showSnackbar = true
}
func setLinkArchived(dataService: DataService, objectID: NSManagedObjectID, archived: Bool) {
dataService.archiveLink(objectID: objectID, archived: archived)
Snackbar.show(message: archived ? "Link archived" : "Link moved to Inbox")
snackbar(archived ? "Link archived" : "Link moved to Inbox")
}
func removeLink(dataService: DataService, objectID: NSManagedObjectID) {
@ -326,9 +334,9 @@ import Views
func recoverItem(dataService: DataService, itemID: String) {
Task {
if await dataService.recoverItem(itemID: itemID) {
Snackbar.show(message: "Item recovered")
snackbar("Item recovered")
} else {
Snackbar.show(message: "Error. Check trash to recover.")
snackbar("Error. Check trash to recover.")
}
}
}
@ -398,7 +406,7 @@ import Views
)
if let message = successMessage {
Snackbar.show(message: message)
snackbar(message)
}
} catch {
NSNotification.operationFailed(message: "Failed to snooze")

View file

@ -5,8 +5,6 @@ import Views
@MainActor
struct HomeView: View {
@State private var viewModel: HomeFeedViewModel
@State var showSnackbar = false
@State var snackbarOperation: SnackbarOperation?
init(viewModel: HomeFeedViewModel) {
self.viewModel = viewModel

View file

@ -25,7 +25,7 @@ import Views
func handleArchiveAction(dataService: DataService) {
guard let objectID = item?.objectID ?? pdfItem?.objectID else { return }
dataService.archiveLink(objectID: objectID, archived: !isItemArchived)
showInSnackbar(!isItemArchived ? "Link archived" : "Link moved to Inbox")
showInLibrarySnackbar(!isItemArchived ? "Link archived" : "Link moved to Inbox")
}
func handleDeleteAction(dataService: DataService) {

View file

@ -18,6 +18,9 @@
@Published var realisticVoicesToggle: Bool = false
@Published var waitingForRealisticVoices: Bool = false
@Published var showSnackbar: Bool = false
var snackbarMessage: String?
func requestUltraRealisticFeatureAccess(
dataService: DataService,
audioController: AudioController
@ -50,7 +53,8 @@
realisticVoicesToggle = false
waitingForRealisticVoices = false
audioController.ultraRealisticFeatureRequested = false
Snackbar.show(message: "Error signing up for beta. Please try again.")
snackbarMessage = "Error signing up for beta. Please try again."
showSnackbar = true
}
}
}

View file

@ -27,7 +27,7 @@ func removeLibraryItemAction(dataService: DataService, objectID: NSManagedObject
print("checking if task is canceled: ", Task.isCancelled)
}
Snackbar.show(message: "Item removed", undoAction: {
Snackbar.showInLibrary(message: "Item removed", undoAction: {
print("canceling task", syncTask)
syncTask.cancel()
dataService.viewContext.performAndWait {

View file

@ -95,7 +95,7 @@
return AnyView(Button(action: {
Task {
if await viewModel.recommend(dataService: dataService) {
Snackbar.show(message: "Recommendation sent")
// Snackbar.show(message: "Recommendation sent")
dismiss()
}
}

View file

@ -7,6 +7,7 @@ import WebKit
@MainActor
struct WebReader: PlatformViewRepresentable {
let item: LinkedItem
let viewModel: WebReaderViewModel
let articleContent: ArticleContent
let openLinkAction: (URL) -> Void
let tapHandler: () -> Void
@ -100,7 +101,7 @@ struct WebReader: PlatformViewRepresentable {
do {
try (webView as? OmnivoreWebView)?.dispatchEvent(.saveAnnotation(annotation: annotation))
} catch {
showInSnackbar("Error saving note.")
showInLibrarySnackbar("Error saving note.")
}
}

View file

@ -377,6 +377,7 @@ struct WebReaderContainerView: View {
if let articleContent = viewModel.articleContent {
WebReader(
item: item,
viewModel: viewModel,
articleContent: articleContent,
openLinkAction: {
#if os(macOS)
@ -427,7 +428,7 @@ struct WebReaderContainerView: View {
#else
// Pasteboard.general.string = item.unwrappedPageURLString TODO: fix for mac
#endif
showInSnackbar("Link Copied")
showInLibrarySnackbar("Link Copied")
}, label: { Text(LocalText.readerCopyLink) })
Button(action: {
if let linkToOpen = linkToOpen {
@ -593,6 +594,13 @@ struct WebReaderContainerView: View {
.animation(.spring())
.closeOnTapOutside(true)
}
.onReceive(NSNotification.readerSnackBarPublisher) { notification in
if let message = notification.userInfo?["message"] as? String {
viewModel.snackbarOperation = SnackbarOperation(message: message,
undoAction: notification.userInfo?["undoAction"] as? SnackbarUndoAction)
viewModel.showSnackbar = true
}
}
}
func archive() {
@ -600,7 +608,6 @@ struct WebReaderContainerView: View {
#if os(iOS)
presentationMode.wrappedValue.dismiss()
#endif
viewModel.snackbar(message: !item.isArchived ? "Link archived" : "Link moved to Inbox")
}
func recommend() {
@ -620,9 +627,9 @@ struct WebReaderContainerView: View {
pasteBoard.clearContents()
pasteBoard.writeObjects([deepLink.absoluteString as NSString])
#endif
showInSnackbar("Deeplink Copied")
showInLibrarySnackbar("Deeplink Copied")
} else {
showInSnackbar("Error copying deeplink")
showInLibrarySnackbar("Error copying deeplink")
}
}

View file

@ -2,6 +2,7 @@ import CoreData
import Foundation
import Models
import SwiftGraphQL
import Utils
extension DataService {
public func archiveLink(objectID: NSManagedObjectID, archived: Bool) {
@ -13,6 +14,11 @@ extension DataService {
// Send update to server
self.syncLinkArchiveStatus(itemID: linkedItem.unwrappedID, archived: archived)
let message = archived ? "Link archived" : "Link moved to Inbox"
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
showInLibrarySnackbar(message)
}
}
}

View file

@ -5,7 +5,8 @@ import Models
public extension NSNotification {
static let PushJSONArticle = Notification.Name("PushJSONArticle")
static let PushReaderItem = Notification.Name("PushReaderItem")
static let OperationSuccess = Notification.Name("OperationSuccess")
static let LibrarySnackBar = Notification.Name("LibrarySnackBar")
static let ReaderSnackBar = Notification.Name("ReaderSnackBar")
static let OperationFailure = Notification.Name("OperationFailure")
static let ReaderSettingsChanged = Notification.Name("ReaderSettingsChanged")
static let SpeakingReaderItem = Notification.Name("SpeakingReaderItem")
@ -20,8 +21,12 @@ public extension NSNotification {
NotificationCenter.default.publisher(for: PushReaderItem)
}
static var operationSuccessPublisher: NotificationCenter.Publisher {
NotificationCenter.default.publisher(for: OperationSuccess)
static var readerSnackBarPublisher: NotificationCenter.Publisher {
NotificationCenter.default.publisher(for: ReaderSnackBar)
}
static var librarySnackBarPublisher: NotificationCenter.Publisher {
NotificationCenter.default.publisher(for: LibrarySnackBar)
}
static var operationFailedPublisher: NotificationCenter.Publisher {
@ -67,8 +72,8 @@ public extension NSNotification {
)
}
static func operationSuccess(message: String, undoAction: (() -> Void)?) {
NotificationCenter.default.post(name: NSNotification.OperationSuccess,
static func librarySnackBar(message: String, undoAction: (() -> Void)?) {
NotificationCenter.default.post(name: NSNotification.LibrarySnackBar,
object: nil,
userInfo: ["message": message, "undoAction": undoAction as Any])
}

View file

@ -7,12 +7,12 @@
import Foundation
public func showInSnackbar(_ message: String) {
let nname = Notification.Name("OperationSuccess")
public func showInLibrarySnackbar(_ message: String) {
let nname = Notification.Name("LibrarySnackBar")
NotificationCenter.default.post(name: nname, object: nil, userInfo: ["message": message])
}
public func showErrorInSnackbar(_ message: String) {
let nname = Notification.Name("OperationFailure")
public func showInReaderSnackbar(_ message: String) {
let nname = Notification.Name("ReaderSnackBar")
NotificationCenter.default.post(name: nname, object: nil, userInfo: ["message": message])
}

View file

@ -46,7 +46,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.updateTheme(themeName: ThemeManager.currentTheme.themeKey))
} catch {
showErrorInSnackbar("Error updating theme")
showInReaderSnackbar("Error updating theme")
}
}
@ -56,7 +56,7 @@ public final class OmnivoreWebView: WKWebView {
try dispatchEvent(.updateFontFamily(family: fontFamily))
}
} catch {
showErrorInSnackbar("Error updating font")
showInReaderSnackbar("Error updating font")
}
}
@ -66,7 +66,7 @@ public final class OmnivoreWebView: WKWebView {
try dispatchEvent(.updateFontSize(size: fontSize))
}
} catch {
showErrorInSnackbar("Error updating font")
showInReaderSnackbar("Error updating font")
}
}
@ -77,7 +77,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.updateMaxWidthPercentage(maxWidthPercentage: maxWidthPercentage))
} catch {
showErrorInSnackbar("Error updating max width")
showInReaderSnackbar("Error updating max width")
}
}
}
@ -87,7 +87,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.updateLineHeight(height: height))
} catch {
showErrorInSnackbar("Error updating line height")
showInReaderSnackbar("Error updating line height")
}
}
}
@ -101,7 +101,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.handleFontContrastChange(isHighContrast: isHighContrast))
} catch {
showErrorInSnackbar("Error updating text contrast")
showInReaderSnackbar("Error updating text contrast")
}
}
}
@ -115,7 +115,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.handleAutoHighlightModeChange(isEnabled: isEnabled))
} catch {
showErrorInSnackbar("Error updating text contrast")
showInReaderSnackbar("Error updating text contrast")
}
}
}
@ -126,7 +126,7 @@ public final class OmnivoreWebView: WKWebView {
try dispatchEvent(.updateJustifyText(justify: justify))
}
} catch {
showErrorInSnackbar("Error updating justify-text")
showInReaderSnackbar("Error updating justify-text")
}
}
@ -134,7 +134,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.updateTitle(title: title))
} catch {
showErrorInSnackbar("Error updating title")
showInReaderSnackbar("Error updating title")
}
}
@ -142,7 +142,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.updateLabels(labels: labelsJSON))
} catch {
showErrorInSnackbar("Error updating labels")
showInReaderSnackbar("Error updating labels")
}
}
@ -150,7 +150,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.share)
} catch {
showErrorInSnackbar("Error updating line height")
showInReaderSnackbar("Error updating line height")
}
}
@ -179,7 +179,7 @@ public final class OmnivoreWebView: WKWebView {
try dispatchEvent(.updateTheme(themeName: ThemeManager.currentTheme.themeKey))
}
} catch {
showErrorInSnackbar("Error updating theme due to colormode change")
showInReaderSnackbar("Error updating theme due to colormode change")
}
}
@ -305,7 +305,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.annotate)
} catch {
showErrorInSnackbar("Error creating highlight")
showInReaderSnackbar("Error creating highlight")
}
hideMenu()
}
@ -314,7 +314,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.highlight)
} catch {
showErrorInSnackbar("Error creating highlight")
showInReaderSnackbar("Error creating highlight")
}
hideMenu()
}
@ -323,7 +323,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.share)
} catch {
showErrorInSnackbar("Error sharing highlight")
showInReaderSnackbar("Error sharing highlight")
}
hideMenu()
}
@ -332,7 +332,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.remove)
} catch {
showErrorInSnackbar("Error deleting highlight")
showInReaderSnackbar("Error deleting highlight")
}
hideMenu()
}
@ -342,7 +342,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.copyHighlight)
} catch {
showErrorInSnackbar("Error copying highlight")
showInReaderSnackbar("Error copying highlight")
}
hideMenu()
}
@ -351,7 +351,7 @@ public final class OmnivoreWebView: WKWebView {
do {
try dispatchEvent(.setHighlightLabels)
} catch {
showErrorInSnackbar("Error setting labels for highlight")
showInReaderSnackbar("Error setting labels for highlight")
}
hideMenu()
}