Merge pull request #2528 from omnivore-app/fix/ios-sizing-issues
Fix iOS 15 navbar issues and add dynamic type support
This commit is contained in:
File diff suppressed because one or more lines are too long
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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") }
|
||||
)
|
||||
|
||||
@ -155,7 +155,7 @@
|
||||
Spacer(minLength: 120)
|
||||
.listRowSeparator(.hidden, edges: .all)
|
||||
}.sheet(item: $setLabelsHighlight) { highlight in
|
||||
ApplyLabelsView(mode: .highlight(highlight), isSearchFocused: false, onSave: { selectedLabels in
|
||||
ApplyLabelsView(mode: .highlight(highlight), onSave: { selectedLabels in
|
||||
hasHighlightMutations = true
|
||||
|
||||
viewModel.setLabelsForHighlight(highlightID: highlight.unwrappedID,
|
||||
|
||||
@ -81,7 +81,7 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
loadItems(isRefresh: true)
|
||||
}
|
||||
.sheet(item: $viewModel.itemUnderLabelEdit) { item in
|
||||
ApplyLabelsView(mode: .item(item), isSearchFocused: false, onSave: nil)
|
||||
ApplyLabelsView(mode: .item(item), onSave: nil)
|
||||
}
|
||||
.sheet(item: $viewModel.itemUnderTitleEdit) { item in
|
||||
LinkedItemMetadataEditView(item: item)
|
||||
@ -150,11 +150,12 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
Label(LocalText.genericProfile, systemImage: "person.circle")
|
||||
})
|
||||
Button(action: { addLinkPresented = true }, label: {
|
||||
Label("Add Link", systemImage: "plus.square")
|
||||
Label("Add Link", systemImage: "plus.circle")
|
||||
})
|
||||
}, label: {
|
||||
Image.utilityMenu
|
||||
})
|
||||
.foregroundColor(.appGrayTextContrast)
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
@ -241,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 {
|
||||
@ -260,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()
|
||||
}
|
||||
@ -272,19 +270,15 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
.autohideIn(2)
|
||||
.position(.bottom)
|
||||
.animation(.spring())
|
||||
.closeOnTapOutside(true)
|
||||
.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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -354,6 +348,7 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
}
|
||||
.listRowSeparator(.hidden)
|
||||
}
|
||||
.dynamicTypeSize(.small ... .accessibility1)
|
||||
}
|
||||
|
||||
func menuItems(for item: LinkedItem) -> some View {
|
||||
@ -438,7 +433,8 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
VStack {
|
||||
LinearGradient(gradient: Gradient(colors: [.black.opacity(0.06), .systemGray6]),
|
||||
startPoint: .top, endPoint: .bottom)
|
||||
.frame(maxWidth: .infinity, maxHeight: 6)
|
||||
.frame(maxWidth: .infinity, maxHeight: 3)
|
||||
.opacity(0.4)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ import Views
|
||||
Button(LocalText.cancelGeneric, role: .cancel) { self.itemToRemove = nil }
|
||||
}
|
||||
.sheet(item: $viewModel.itemUnderLabelEdit) { item in
|
||||
ApplyLabelsView(mode: .item(item), isSearchFocused: false, onSave: nil)
|
||||
ApplyLabelsView(mode: .item(item), onSave: nil)
|
||||
}
|
||||
.sheet(item: $viewModel.itemUnderTitleEdit) { item in
|
||||
LinkedItemMetadataEditView(item: item)
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -5,30 +5,14 @@ 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
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
var navView: some View {
|
||||
NavigationView {
|
||||
HomeFeedContainerView(viewModel: viewModel)
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
.accentColor(.appGrayTextContrast)
|
||||
}
|
||||
#endif
|
||||
|
||||
var body: some View {
|
||||
#if os(iOS)
|
||||
if UIDevice.isIPhone {
|
||||
navView
|
||||
} else {
|
||||
HomeFeedContainerView(viewModel: viewModel)
|
||||
}
|
||||
HomeFeedContainerView(viewModel: viewModel)
|
||||
#elseif os(macOS)
|
||||
HomeFeedView(viewModel: viewModel)
|
||||
.frame(minWidth: 320)
|
||||
|
||||
@ -61,6 +61,7 @@
|
||||
Form {
|
||||
TextField("Add Link", text: $newLinkURL)
|
||||
.keyboardType(.URL)
|
||||
.autocorrectionDisabled(true)
|
||||
.textFieldStyle(StandardTextFieldStyle())
|
||||
.focused($focusedField, equals: .addLinkEditor)
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import Services
|
||||
import SwiftUI
|
||||
import Views
|
||||
|
||||
@MainActor
|
||||
struct ApplyLabelsView: View {
|
||||
enum Mode {
|
||||
case item(LinkedItem)
|
||||
@ -29,7 +30,6 @@ struct ApplyLabelsView: View {
|
||||
}
|
||||
|
||||
let mode: Mode
|
||||
let isSearchFocused: Bool
|
||||
let onSave: (([LinkedItemLabel]) -> Void)?
|
||||
|
||||
@EnvironmentObject var dataService: DataService
|
||||
|
||||
@ -51,39 +51,37 @@ struct LibraryTabView: View {
|
||||
) {
|
||||
EmptyView()
|
||||
}
|
||||
// TabView(selection: $selection) {
|
||||
// BriefingView(
|
||||
// articleId: "98e017a3-79d5-4049-97bc-ff170153792a"
|
||||
// )
|
||||
// .tabItem {
|
||||
// Label {
|
||||
// Text("Your Briefing")
|
||||
// } icon: {
|
||||
// Image.tabBriefing.padding(.trailing, 5)
|
||||
// }
|
||||
// }.tag(0)
|
||||
// TabView(selection: $selection) {
|
||||
// BriefingView(
|
||||
// articleId: "98e017a3-79d5-4049-97bc-ff170153792a"
|
||||
// )
|
||||
// .tabItem {
|
||||
// Label {
|
||||
// Text("Your Briefing")
|
||||
// } icon: {
|
||||
// Image.tabBriefing.padding(.trailing, 5)
|
||||
// }
|
||||
// }.tag(0)
|
||||
|
||||
HomeView(viewModel: libraryViewModel)
|
||||
// .tabItem {
|
||||
// Label {
|
||||
// Text("Library")
|
||||
// } icon: {
|
||||
// Image.tabLibrary
|
||||
// }
|
||||
// }.tag(1)
|
||||
// .tabItem {
|
||||
// Label {
|
||||
// Text("Library")
|
||||
// } icon: {
|
||||
// Image.tabLibrary
|
||||
// }
|
||||
// }.tag(1)
|
||||
|
||||
// HomeView(viewModel: highlightsViewModel)
|
||||
// .tabItem {
|
||||
// Label {
|
||||
// Text("Highlights")
|
||||
// } icon: {
|
||||
// Image.tabHighlights
|
||||
// }
|
||||
// }.tag(2)
|
||||
// }
|
||||
// HomeView(viewModel: highlightsViewModel)
|
||||
// .tabItem {
|
||||
// Label {
|
||||
// Text("Highlights")
|
||||
// } icon: {
|
||||
// Image.tabHighlights
|
||||
// }
|
||||
// }.tag(2)
|
||||
// }
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -56,26 +56,39 @@ import Views
|
||||
}
|
||||
|
||||
@MainActor struct PrimaryContentSidebar: View {
|
||||
@State private var addLinkPresented = false
|
||||
@State private var selectedCategory: PrimaryContentCategory?
|
||||
let categories: [PrimaryContentCategory]
|
||||
|
||||
var innerBody: some View {
|
||||
List(categories) { category in
|
||||
NavigationLink(
|
||||
destination: category.destinationView,
|
||||
tag: category,
|
||||
selection: $selectedCategory,
|
||||
label: { category.listLabel }
|
||||
)
|
||||
#if os(iOS)
|
||||
.listRowBackground(
|
||||
category == selectedCategory
|
||||
? Color.appGraySolid.opacity(0.4).cornerRadius(8)
|
||||
: Color.clear.cornerRadius(8)
|
||||
List {
|
||||
ForEach(categories, id: \.self) { category in
|
||||
NavigationLink(
|
||||
destination: category.destinationView,
|
||||
tag: category,
|
||||
selection: $selectedCategory,
|
||||
label: { category.listLabel }
|
||||
)
|
||||
#endif
|
||||
#if os(iOS)
|
||||
.listRowBackground(
|
||||
category == selectedCategory
|
||||
? Color.appGraySolid.opacity(0.4).cornerRadius(8)
|
||||
: Color.clear.cornerRadius(8)
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
Button(action: { addLinkPresented = true }, label: {
|
||||
Label("Add Link", systemImage: "plus.circle")
|
||||
})
|
||||
}
|
||||
.dynamicTypeSize(.small ... .large)
|
||||
.listStyle(.sidebar)
|
||||
.sheet(isPresented: $addLinkPresented) {
|
||||
NavigationView {
|
||||
LibraryAddLinkView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -337,7 +337,7 @@ struct WebReaderContainerView: View {
|
||||
.foregroundColor(ThemeManager.currentTheme.isDark ? .white : .black)
|
||||
.background(ThemeManager.currentBgColor)
|
||||
.sheet(isPresented: $showLabelsModal) {
|
||||
ApplyLabelsView(mode: .item(item), isSearchFocused: false, onSave: { labels in
|
||||
ApplyLabelsView(mode: .item(item), onSave: { labels in
|
||||
showLabelsModal = false
|
||||
item.labels = NSSet(array: labels)
|
||||
readerSettingsChangedTransactionID = UUID()
|
||||
@ -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 {
|
||||
@ -478,7 +479,7 @@ struct WebReaderContainerView: View {
|
||||
}
|
||||
.sheet(isPresented: $showHighlightLabelsModal) {
|
||||
if let highlight = Highlight.lookup(byID: self.annotation, inContext: self.dataService.viewContext) {
|
||||
ApplyLabelsView(mode: .highlight(highlight), isSearchFocused: false) { selectedLabels in
|
||||
ApplyLabelsView(mode: .highlight(highlight)) { selectedLabels in
|
||||
viewModel.setLabelsForHighlight(highlightID: highlight.unwrappedID,
|
||||
labelIDs: selectedLabels.map(\.unwrappedID),
|
||||
dataService: dataService)
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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])
|
||||
}
|
||||
|
||||
@ -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])
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ public struct LibraryItemCard: View {
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
.draggableItem(item: item)
|
||||
.dynamicTypeSize(.xSmall ... .accessibility1)
|
||||
}
|
||||
|
||||
var isFullyRead: Bool {
|
||||
@ -126,22 +127,22 @@ public struct LibraryItemCard: View {
|
||||
AnyView(HStack {
|
||||
let fgcolor = Color.isDarkMode ? Color.themeDarkWhiteGray : Color.themeMiddleGray
|
||||
Text("\(estimatedReadingTime)")
|
||||
.font(Font.system(size: 11, weight: .medium))
|
||||
.font(.caption2).fontWeight(.medium)
|
||||
.foregroundColor(fgcolor)
|
||||
|
||||
+
|
||||
Text("\(readingProgress)")
|
||||
.font(Font.system(size: 11, weight: .medium))
|
||||
.font(.caption2).fontWeight(.medium)
|
||||
.foregroundColor(isPartiallyRead ? Color.appGreenSuccess : fgcolor)
|
||||
|
||||
+
|
||||
Text("\(highlightsText)")
|
||||
.font(Font.system(size: 11, weight: .medium))
|
||||
.font(.caption2).fontWeight(.medium)
|
||||
.foregroundColor(fgcolor)
|
||||
|
||||
+
|
||||
Text("\(notesText)")
|
||||
.font(Font.system(size: 11, weight: .medium))
|
||||
.font(.caption2).fontWeight(.medium)
|
||||
.foregroundColor(fgcolor)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading))
|
||||
@ -183,7 +184,7 @@ public struct LibraryItemCard: View {
|
||||
|
||||
var byLine: some View {
|
||||
Text(bylineStr)
|
||||
.font(Font.system(size: 11, weight: .regular))
|
||||
.font(.caption2)
|
||||
.foregroundColor(Color.isDarkMode ? Color.themeLightGray : Color.themeLightestGray)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
@ -192,9 +193,10 @@ public struct LibraryItemCard: View {
|
||||
public var articleInfo: some View {
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
readInfo
|
||||
.dynamicTypeSize(.xSmall ... .medium)
|
||||
|
||||
Text(item.unwrappedTitle)
|
||||
.font(Font.system(size: 14, weight: .semibold))
|
||||
.font(.body).fontWeight(.semibold)
|
||||
.lineSpacing(1.25)
|
||||
.foregroundColor(.appGrayTextContrast)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -43,13 +43,14 @@ public struct Snackbar: View {
|
||||
}
|
||||
.frame(maxWidth: 380)
|
||||
.frame(height: 44)
|
||||
.padding(.horizontal, 15)
|
||||
.padding(.horizontal, 10)
|
||||
.background(self.colorScheme == .light ? Color.black : Color.white)
|
||||
.cornerRadius(5)
|
||||
.clipped()
|
||||
|
||||
Spacer(minLength: 20)
|
||||
}
|
||||
.padding(.horizontal, 10)
|
||||
.background(Color.clear)
|
||||
.frame(height: 44 + 22)
|
||||
}
|
||||
|
||||
@ -78,13 +78,15 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
const focusedHighlightMousePos = useRef({ pageX: 0, pageY: 0 })
|
||||
|
||||
const [currentHighlightIdx, setCurrentHighlightIdx] = useState(0)
|
||||
const [focusedHighlight, setFocusedHighlight] =
|
||||
useState<Highlight | undefined>(undefined)
|
||||
const [focusedHighlight, setFocusedHighlight] = useState<
|
||||
Highlight | undefined
|
||||
>(undefined)
|
||||
|
||||
const [selectionData, setSelectionData] = useSelection(highlightLocations)
|
||||
|
||||
const [labelsTarget, setLabelsTarget] =
|
||||
useState<Highlight | undefined>(undefined)
|
||||
const [labelsTarget, setLabelsTarget] = useState<Highlight | undefined>(
|
||||
undefined
|
||||
)
|
||||
|
||||
const windowDimensions = useGetWindowDimensions()
|
||||
|
||||
@ -537,6 +539,10 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setFocusedHighlight(undefined)
|
||||
}, [selectionData])
|
||||
|
||||
const dispatchHighlightMessage = (actionID: string) => {
|
||||
if (props.isAppleAppEmbed) {
|
||||
window?.webkit?.messageHandlers.highlightAction?.postMessage({
|
||||
@ -672,7 +678,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
dispatchHighlightMessage('noteCreated')
|
||||
} else {
|
||||
try {
|
||||
await createHighlightCallback()
|
||||
await createHighlightCallback(event.annotation)
|
||||
dispatchHighlightMessage('noteCreated')
|
||||
} catch (error) {
|
||||
dispatchHighlightError('saveAnnotation', error)
|
||||
|
||||
Reference in New Issue
Block a user