Merge pull request #3104 from omnivore-app/fix/macos-update-extension
Fix build on MacOS
@ -7,4 +7,5 @@ graphql_gen:
|
||||
extension_gen:
|
||||
pushd ../pkg/extension/ && yarn build-prod && popd
|
||||
cp -r ../pkg/extension/dist/* Sources/SafariExtension/Resources/.
|
||||
mv Sources/SafariExtension/Resources/images/extension-transparent/* Sources/SafariExtension/Resources/images/extension/.
|
||||
cat ../pkg/extension/dist/manifest.json | jq '.background.persistent = false' | jq '.permissions = ["activeTab", "storage", "nativeMessaging", "https://*/**", "http://*/**"]' > Sources/SafariExtension/Resources/manifest.json
|
||||
@ -14,7 +14,6 @@
|
||||
042F48DC26DFD10E00BF98FC /* iOSLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 042F48DB26DFD10E00BF98FC /* iOSLaunchTests.swift */; };
|
||||
045B1681279147E7005047F7 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 045B1680279147E7005047F7 /* FirebaseMessaging */; };
|
||||
0465B9BE26CDD35F005558CD /* MainApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81BE98F0CB588F5FC577A13 /* MainApp.swift */; };
|
||||
046C5CD526A3F89A00AC5349 /* icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 046C5CD426A3F89A00AC5349 /* icon.icns */; };
|
||||
046C5CDF26A3F89A00AC5349 /* ShareExtension-Mac.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 046C5CD226A3F89A00AC5349 /* ShareExtension-Mac.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
046C5CE626A3FE8C00AC5349 /* App in Frameworks */ = {isa = PBXBuildFile; productRef = 046C5CE526A3FE8C00AC5349 /* App */; };
|
||||
047AD6F22724C934004FD5CF /* App in Frameworks */ = {isa = PBXBuildFile; productRef = 047AD6F12724C934004FD5CF /* App */; };
|
||||
@ -34,12 +33,8 @@
|
||||
260BBE4168E3C57D6D2E225D /* App in Frameworks */ = {isa = PBXBuildFile; productRef = 7DD0EF4D497D71D248C7B0DF /* App */; };
|
||||
4214F6EF2714D2260096B644 /* SafariExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 42FF1B16271154A700B38C38 /* SafariExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
4214F6F22714D28E0096B644 /* SafariExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 42FF1B20271154A700B38C38 /* SafariExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
4225D59227164F82002A680F /* fonts in Resources */ = {isa = PBXBuildFile; fileRef = 4225D59127164F82002A680F /* fonts */; };
|
||||
4225D59327164F82002A680F /* fonts in Resources */ = {isa = PBXBuildFile; fileRef = 4225D59127164F82002A680F /* fonts */; };
|
||||
42321E852714E6B00056429F /* scripts in Resources */ = {isa = PBXBuildFile; fileRef = 42321E822714E6B00056429F /* scripts */; };
|
||||
42321E862714E6B00056429F /* scripts in Resources */ = {isa = PBXBuildFile; fileRef = 42321E822714E6B00056429F /* scripts */; };
|
||||
42321E872714E6B00056429F /* styles in Resources */ = {isa = PBXBuildFile; fileRef = 42321E832714E6B00056429F /* styles */; };
|
||||
42321E882714E6B00056429F /* styles in Resources */ = {isa = PBXBuildFile; fileRef = 42321E832714E6B00056429F /* styles */; };
|
||||
42321E892714E6B00056429F /* views in Resources */ = {isa = PBXBuildFile; fileRef = 42321E842714E6B00056429F /* views */; };
|
||||
42321E8A2714E6B00056429F /* views in Resources */ = {isa = PBXBuildFile; fileRef = 42321E842714E6B00056429F /* views */; };
|
||||
42704E7328E6BDB000C8C73E /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42704E7228E6BDAF00C8C73E /* SnapshotHelper.swift */; };
|
||||
@ -192,7 +187,6 @@
|
||||
042F48DB26DFD10E00BF98FC /* iOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSLaunchTests.swift; sourceTree = "<group>"; };
|
||||
042F48DD26DFD10E00BF98FC /* iOSUITests.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = iOSUITests.plist; sourceTree = "<group>"; };
|
||||
046C5CD226A3F89A00AC5349 /* ShareExtension-Mac.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "ShareExtension-Mac.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
046C5CD426A3F89A00AC5349 /* icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = icon.icns; sourceTree = "<group>"; };
|
||||
046C5CDB26A3F89A00AC5349 /* ShareExtensionMac.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = ShareExtensionMac.plist; sourceTree = "<group>"; };
|
||||
046C5CDC26A3F89A00AC5349 /* ShareExtensionMac.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtensionMac.entitlements; sourceTree = "<group>"; };
|
||||
047AA41F297A17FB00F0BEBF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/LaunchScreen.strings"; sourceTree = "<group>"; };
|
||||
@ -215,9 +209,7 @@
|
||||
082449FB9F5F3730EE0D8D3F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
0BE7F7C593222747A96AA4E7 /* Omnivore.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Omnivore.plist; sourceTree = "<group>"; };
|
||||
1A39D8FDA25447FDBF07E24B /* Omnivore.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Omnivore.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4225D59127164F82002A680F /* fonts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = fonts; sourceTree = "<group>"; };
|
||||
42321E822714E6B00056429F /* scripts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = scripts; sourceTree = "<group>"; };
|
||||
42321E832714E6B00056429F /* styles */ = {isa = PBXFileReference; lastKnownFileType = folder; path = styles; sourceTree = "<group>"; };
|
||||
42321E842714E6B00056429F /* views */ = {isa = PBXFileReference; lastKnownFileType = folder; path = views; sourceTree = "<group>"; };
|
||||
42704E7228E6BDAF00C8C73E /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = "<group>"; };
|
||||
42E2BFB128E458E0007F29B2 /* AppStoreScreenshots.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppStoreScreenshots.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -473,9 +465,7 @@
|
||||
42FF1AEC271154A600B38C38 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4225D59127164F82002A680F /* fonts */,
|
||||
42321E822714E6B00056429F /* scripts */,
|
||||
42321E832714E6B00056429F /* styles */,
|
||||
42321E842714E6B00056429F /* views */,
|
||||
42FF1AED271154A600B38C38 /* _locales */,
|
||||
42FF1AEE271154A600B38C38 /* images */,
|
||||
@ -511,7 +501,6 @@
|
||||
B330B55BAF36E624637EE3BE /* ShareExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
046C5CD426A3F89A00AC5349 /* icon.icns */,
|
||||
FF9F2334402E6B10A665F1A7 /* ShareExtensionViewController.swift */,
|
||||
04E9706E26BDE47D002A523D /* ShareExtension.js */,
|
||||
);
|
||||
@ -878,7 +867,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
04E9707226BDE5F1002A523D /* ShareExtension.js in Resources */,
|
||||
046C5CD526A3F89A00AC5349 /* icon.icns in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -909,9 +897,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
42321E892714E6B00056429F /* views in Resources */,
|
||||
42321E872714E6B00056429F /* styles in Resources */,
|
||||
42321E852714E6B00056429F /* scripts in Resources */,
|
||||
4225D59227164F82002A680F /* fonts in Resources */,
|
||||
42FF1B37271154A700B38C38 /* images in Resources */,
|
||||
42FF1B39271154A700B38C38 /* manifest.json in Resources */,
|
||||
42FF1B35271154A700B38C38 /* _locales in Resources */,
|
||||
@ -923,9 +909,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
42321E8A2714E6B00056429F /* views in Resources */,
|
||||
42321E882714E6B00056429F /* styles in Resources */,
|
||||
42321E862714E6B00056429F /* scripts in Resources */,
|
||||
4225D59327164F82002A680F /* fonts in Resources */,
|
||||
42FF1B38271154A700B38C38 /* images in Resources */,
|
||||
42FF1B3A271154A700B38C38 /* manifest.json in Resources */,
|
||||
42FF1B36271154A700B38C38 /* _locales in Resources */,
|
||||
|
||||
@ -24,7 +24,9 @@ public struct AddNoteSheet: View {
|
||||
|
||||
public init(viewModel: ShareExtensionViewModel) {
|
||||
_viewModel = StateObject(wrappedValue: viewModel)
|
||||
UITextView.appearance().textContainerInset = UIEdgeInsets(top: 8, left: 4, bottom: 10, right: 4)
|
||||
#if os(iOS)
|
||||
UITextView.appearance().textContainerInset = UIEdgeInsets(top: 8, left: 4, bottom: 10, right: 4)
|
||||
#endif
|
||||
}
|
||||
|
||||
func saveNote() {
|
||||
@ -41,6 +43,7 @@ public struct AddNoteSheet: View {
|
||||
}
|
||||
.background(Color.extensionBackground)
|
||||
.navigationTitle("Add Note")
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(leading: Button(action: {
|
||||
dismiss()
|
||||
@ -53,6 +56,10 @@ public struct AddNoteSheet: View {
|
||||
}, label: {
|
||||
Text("Save").bold()
|
||||
}))
|
||||
}.navigationViewStyle(StackNavigationViewStyle())
|
||||
#endif
|
||||
}
|
||||
#if os(iOS)
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,17 +26,11 @@ public struct EditInfoSheet: View {
|
||||
|
||||
public init(viewModel: ShareExtensionViewModel) {
|
||||
_viewModel = StateObject(wrappedValue: viewModel)
|
||||
UITextView.appearance().textContainerInset = UIEdgeInsets(top: 8, left: 4, bottom: 10, right: 4)
|
||||
#if os(iOS)
|
||||
UITextView.appearance().textContainerInset = UIEdgeInsets(top: 8, left: 4, bottom: 10, right: 4)
|
||||
#endif
|
||||
}
|
||||
|
||||
// func saveInfo() {
|
||||
// if let linkedItem = viewModel.linkedItem {
|
||||
// _ = viewModel.services.dataService.updateLinkedItemTitleAndDescription(itemID: linkedItem.unwrappedID, title: title, description: description, author: author)
|
||||
// } else {
|
||||
// // Maybe we shouldn't even allow this UI without linkeditem existing
|
||||
// }
|
||||
// }
|
||||
|
||||
public var body: some View {
|
||||
if let item = viewModel.linkedItem {
|
||||
LinkedItemMetadataEditView(item: item) { title, _ in
|
||||
|
||||
@ -14,6 +14,7 @@ import Views
|
||||
@MainActor
|
||||
public struct EditLabelsSheet: View {
|
||||
@State var text = ""
|
||||
@State var isLabelsEntryFocused = false
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@EnvironmentObject var dataService: DataService
|
||||
|
||||
@ -29,8 +30,9 @@ public struct EditLabelsSheet: View {
|
||||
public init(viewModel: ShareExtensionViewModel, labelsViewModel: LabelsViewModel) {
|
||||
_viewModel = StateObject(wrappedValue: viewModel)
|
||||
_labelsViewModel = StateObject(wrappedValue: labelsViewModel)
|
||||
|
||||
UITextView.appearance().textContainerInset = UIEdgeInsets(top: 5, left: 2, bottom: 5, right: 2)
|
||||
#if os(iOS)
|
||||
UITextView.appearance().textContainerInset = UIEdgeInsets(top: 5, left: 2, bottom: 5, right: 2)
|
||||
#endif
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@ -55,6 +57,7 @@ public struct EditLabelsSheet: View {
|
||||
VStack {
|
||||
LabelsEntryView(
|
||||
searchTerm: $labelsViewModel.labelSearchFilter,
|
||||
isFocused: $isLabelsEntryFocused,
|
||||
viewModel: labelsViewModel
|
||||
)
|
||||
.padding(.horizontal, 10)
|
||||
@ -95,7 +98,9 @@ public struct EditLabelsSheet: View {
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.background(Color.extensionBackground)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}.frame(maxHeight: .infinity)
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
@ -104,6 +109,7 @@ public struct EditLabelsSheet: View {
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(Color.extensionBackground)
|
||||
.navigationTitle("Set Labels")
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(trailing: Button(action: {
|
||||
if let linkedItem = viewModel.linkedItem, let linkedItemId = linkedItem.id {
|
||||
@ -116,11 +122,14 @@ public struct EditLabelsSheet: View {
|
||||
}, label: {
|
||||
Text("Done").bold()
|
||||
}))
|
||||
#endif
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
#if os(iOS)
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
#endif
|
||||
.environmentObject(viewModel.services.dataService)
|
||||
.task {
|
||||
await labelsViewModel.loadLabelsFromStore(dataService: viewModel.services.dataService)
|
||||
}
|
||||
.task {
|
||||
await labelsViewModel.loadLabelsFromStore(dataService: viewModel.services.dataService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct MiniShareExtensionView: View {
|
||||
@StateObject var viewModel: ShareExtensionViewModel
|
||||
@StateObject var labelsViewModel: LabelsViewModel
|
||||
let extensionContext: NSExtensionContext?
|
||||
|
||||
@State var showToast = true
|
||||
|
||||
@ -20,7 +20,8 @@ public struct ShareExtensionView: View {
|
||||
@State var showAddNoteModal = false
|
||||
|
||||
enum FocusField: Hashable {
|
||||
case titleEditor
|
||||
case noteEditor
|
||||
case labelEditor
|
||||
}
|
||||
|
||||
enum ViewState {
|
||||
@ -39,6 +40,10 @@ public struct ShareExtensionView: View {
|
||||
_viewModel = StateObject(wrappedValue: viewModel)
|
||||
_labelsViewModel = StateObject(wrappedValue: labelsViewModel)
|
||||
self.extensionContext = extensionContext
|
||||
|
||||
#if os(iOS)
|
||||
UITextView.appearance().textContainerInset = UIEdgeInsets(top: 8, left: 4, bottom: 10, right: 4)
|
||||
#endif
|
||||
}
|
||||
|
||||
private func localImage(from url: URL) -> Image? {
|
||||
@ -100,7 +105,7 @@ public struct ShareExtensionView: View {
|
||||
Image(systemName: "checkmark.circle")
|
||||
.frame(width: 15, height: 15)
|
||||
.foregroundColor(.appGreenSuccess)
|
||||
// .opacity(isSynced ? 1.0 : 0.0)
|
||||
.opacity(isSynced ? 1.0 : 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,14 +165,16 @@ public struct ShareExtensionView: View {
|
||||
|
||||
var moreMenuButton: some View {
|
||||
Menu {
|
||||
Button(action: {
|
||||
NotificationCenter.default.post(name: Notification.Name("ShowEditInfoSheet"), object: nil)
|
||||
}, label: {
|
||||
Label(
|
||||
"Edit Info",
|
||||
systemImage: "info.circle"
|
||||
)
|
||||
})
|
||||
#if os(iOS)
|
||||
Button(action: {
|
||||
NotificationCenter.default.post(name: Notification.Name("ShowEditInfoSheet"), object: nil)
|
||||
}, label: {
|
||||
Label(
|
||||
"Edit Info",
|
||||
systemImage: "info.circle"
|
||||
)
|
||||
})
|
||||
#endif
|
||||
Button(action: {
|
||||
if let linkedItem = self.viewModel.linkedItem {
|
||||
self.viewModel.setLinkArchived(dataService: self.viewModel.services.dataService,
|
||||
@ -237,14 +244,34 @@ public struct ShareExtensionView: View {
|
||||
Text("Saved to Omnivore")
|
||||
.font(Font.system(size: 22, weight: .bold))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Spacer()
|
||||
moreMenuButton
|
||||
closeButton
|
||||
#if os(iOS)
|
||||
Spacer()
|
||||
moreMenuButton
|
||||
closeButton
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
var displayDismiss: Bool {
|
||||
#if os(iOS)
|
||||
if UIDevice.isIPhone {
|
||||
return false
|
||||
}
|
||||
#endif
|
||||
return true
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
#if os(iOS)
|
||||
iOSBody
|
||||
.environmentObject(viewModel.services.dataService)
|
||||
#else
|
||||
macOSBody
|
||||
.environmentObject(viewModel.services.dataService)
|
||||
#endif
|
||||
}
|
||||
|
||||
var iOSBody: some View {
|
||||
VStack(alignment: .leading, spacing: 15) {
|
||||
titleBar
|
||||
.padding(.top, 15)
|
||||
@ -254,32 +281,44 @@ public struct ShareExtensionView: View {
|
||||
Spacer(minLength: 1)
|
||||
|
||||
HStack {
|
||||
#if os(macOS)
|
||||
moreMenuButton
|
||||
.padding(.bottom, 15)
|
||||
#endif
|
||||
Spacer()
|
||||
if UIDevice.isIPad {
|
||||
if displayDismiss {
|
||||
Button(action: {
|
||||
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||
}, label: {
|
||||
Text("Dismiss")
|
||||
#if os(iOS)
|
||||
.font(Font.system(size: 17, weight: .semibold))
|
||||
.tint(Color.appGrayText)
|
||||
.padding(20)
|
||||
#endif
|
||||
})
|
||||
#if os(iOS)
|
||||
.frame(height: 50)
|
||||
.cornerRadius(24)
|
||||
.padding(.bottom, 15)
|
||||
#endif
|
||||
.padding(.bottom, 15)
|
||||
}
|
||||
Button(action: {
|
||||
viewModel.handleReadNowAction(extensionContext: extensionContext)
|
||||
}, label: {
|
||||
Text("Read Now")
|
||||
#if os(iOS)
|
||||
.font(Font.system(size: 17, weight: .semibold))
|
||||
.tint(Color.white)
|
||||
.padding(20)
|
||||
#endif
|
||||
})
|
||||
#if os(iOS)
|
||||
.frame(height: 50)
|
||||
.background(Color.blue)
|
||||
.cornerRadius(24)
|
||||
.padding(.bottom, 15)
|
||||
#endif
|
||||
.padding(.bottom, 15)
|
||||
}.frame(maxWidth: .infinity)
|
||||
}.padding(.horizontal, 15)
|
||||
.background(Color.extensionBackground)
|
||||
@ -287,4 +326,108 @@ public struct ShareExtensionView: View {
|
||||
viewModel.savePage(extensionContext: extensionContext)
|
||||
}
|
||||
}
|
||||
|
||||
@State var labelsSearch = ZWSP
|
||||
@State var isLabelsEntryFocused = false
|
||||
|
||||
func save() {
|
||||
if !viewModel.noteText.isEmpty {
|
||||
viewModel.saveNote()
|
||||
}
|
||||
if let itemID = viewModel.linkedItem?.id {
|
||||
labelsViewModel.saveItemLabelChanges(itemID: itemID, dataService: viewModel.services.dataService)
|
||||
}
|
||||
}
|
||||
|
||||
var macOSBody: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
HStack(spacing: 10) {
|
||||
Text("Saved to Omnivore")
|
||||
.font(Font.system(size: 17))
|
||||
Image(systemName: "checkmark.circle")
|
||||
.foregroundColor(.appGreenSuccess)
|
||||
.opacity(isSynced ? 1.0 : 0.0)
|
||||
Spacer()
|
||||
}.padding(15)
|
||||
|
||||
Divider()
|
||||
|
||||
ZStack(alignment: .topLeading) {
|
||||
TextEditor(text: $viewModel.noteText)
|
||||
.frame(maxWidth: .infinity)
|
||||
.font(Font.system(size: 14))
|
||||
.accentColor(.blue)
|
||||
#if os(macos)
|
||||
.introspectTextView { textView in
|
||||
textView.textContainerInset = NSSize(width: 10, height: 10)
|
||||
}
|
||||
#endif
|
||||
.focused($focusedField, equals: .noteEditor)
|
||||
if viewModel.noteText.isEmpty {
|
||||
Text("Notes")
|
||||
.fontWeight(.light)
|
||||
.font(Font.system(size: 14))
|
||||
.foregroundColor(.black.opacity(0.25))
|
||||
.padding(.leading, 15)
|
||||
.padding(.top, 10)
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
ZStack(alignment: .topLeading) {
|
||||
LabelsEntryView(searchTerm: $labelsSearch, isFocused: $isLabelsEntryFocused, viewModel: labelsViewModel)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.horizontal, 8)
|
||||
.focused($focusedField, equals: .labelEditor)
|
||||
|
||||
if labelsViewModel.selectedLabels.isEmpty, labelsSearch == ZWSP {
|
||||
Text("Type to add labels")
|
||||
.fontWeight(.light)
|
||||
.font(Font.system(size: 14))
|
||||
.foregroundColor(.black.opacity(0.25))
|
||||
.padding(.leading, 15)
|
||||
.padding(.top, 10)
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
HStack {
|
||||
moreMenuButton
|
||||
.padding(.bottom, 15)
|
||||
Spacer()
|
||||
Button(action: {
|
||||
save()
|
||||
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||
}, label: {
|
||||
Text("Dismiss")
|
||||
})
|
||||
.padding(.bottom, 15)
|
||||
Button(action: {
|
||||
save()
|
||||
viewModel.handleReadNowAction(extensionContext: extensionContext)
|
||||
}, label: {
|
||||
Text("Read Now")
|
||||
})
|
||||
.padding(.bottom, 15)
|
||||
|
||||
}.padding(15)
|
||||
|
||||
}.frame(maxWidth: .infinity)
|
||||
.background(Color.isDarkMode ? Color.systemBackground : Color.white)
|
||||
.onAppear {
|
||||
if let extensionContext = extensionContext {
|
||||
viewModel.savePage(extensionContext: extensionContext)
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
|
||||
focusedField = .labelEditor
|
||||
}
|
||||
Task {
|
||||
await labelsViewModel.loadLabels(dataService: viewModel.services.dataService, initiallySelectedLabels: [])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +106,6 @@ import Views
|
||||
.contextMenu {
|
||||
menuItems(item)
|
||||
}
|
||||
Divider().padding(5)
|
||||
}
|
||||
|
||||
if viewModel.isLoading {
|
||||
|
||||
@ -36,6 +36,7 @@ struct ApplyLabelsView: View {
|
||||
@EnvironmentObject var dataService: DataService
|
||||
@Environment(\.presentationMode) private var presentationMode
|
||||
@StateObject var viewModel = LabelsViewModel()
|
||||
@State var isLabelsEntryFocused = false
|
||||
|
||||
enum ViewState {
|
||||
case mainView
|
||||
@ -52,6 +53,7 @@ struct ApplyLabelsView: View {
|
||||
VStack {
|
||||
LabelsEntryView(
|
||||
searchTerm: $viewModel.labelSearchFilter,
|
||||
isFocused: $isLabelsEntryFocused,
|
||||
viewModel: viewModel
|
||||
)
|
||||
.padding(.horizontal, 10)
|
||||
@ -62,67 +64,60 @@ struct ApplyLabelsView: View {
|
||||
}
|
||||
|
||||
List {
|
||||
Section {
|
||||
ForEach(viewModel.labels.applySearchFilter(viewModel.labelSearchFilter), id: \.self) { label in
|
||||
Button(
|
||||
action: {
|
||||
if isSelected(label) {
|
||||
if let idx = viewModel.selectedLabels.firstIndex(of: label) {
|
||||
viewModel.selectedLabels.remove(at: idx)
|
||||
}
|
||||
} else {
|
||||
viewModel.labelSearchFilter = ZWSP
|
||||
viewModel.selectedLabels.append(label)
|
||||
ForEach(viewModel.labels.applySearchFilter(viewModel.labelSearchFilter), id: \.self) { label in
|
||||
Button(
|
||||
action: {
|
||||
if isSelected(label) {
|
||||
if let idx = viewModel.selectedLabels.firstIndex(of: label) {
|
||||
viewModel.selectedLabels.remove(at: idx)
|
||||
}
|
||||
},
|
||||
label: {
|
||||
HStack {
|
||||
TextChip(feedItemLabel: label).allowsHitTesting(false)
|
||||
Spacer()
|
||||
if isSelected(label) {
|
||||
Image(systemName: "checkmark")
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
} else {
|
||||
viewModel.labelSearchFilter = ZWSP
|
||||
viewModel.selectedLabels.append(label)
|
||||
}
|
||||
)
|
||||
.padding(.vertical, 5)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
#if os(macOS)
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
#endif
|
||||
}
|
||||
},
|
||||
label: {
|
||||
HStack {
|
||||
TextChip(feedItemLabel: label).allowsHitTesting(false)
|
||||
Spacer()
|
||||
if isSelected(label) {
|
||||
Image(systemName: "checkmark")
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
)
|
||||
.padding(.vertical, 5)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
#if os(macOS)
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
#endif
|
||||
}
|
||||
if !viewModel.labelSearchFilter.isEmpty, viewModel.labelSearchFilter != ZWSP {
|
||||
createLabelButton
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.background(Color.extensionBackground)
|
||||
|
||||
Spacer()
|
||||
.frame(maxHeight: .infinity)
|
||||
}
|
||||
.navigationTitle(mode.navTitle)
|
||||
.background(Color.extensionBackground)
|
||||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
cancelButton
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
saveItemChangesButton
|
||||
}
|
||||
}
|
||||
#else
|
||||
.toolbar {
|
||||
ToolbarItemGroup {
|
||||
cancelButton
|
||||
saveItemChangesButton
|
||||
}
|
||||
}
|
||||
#endif
|
||||
.sheet(isPresented: $viewModel.showCreateLabelModal) {
|
||||
CreateLabelView(viewModel: viewModel, newLabelName: viewModel.labelSearchFilter)
|
||||
}
|
||||
.onAppear {
|
||||
Task {
|
||||
switch mode {
|
||||
case let .item(feedItem):
|
||||
await viewModel.loadLabels(dataService: dataService, item: feedItem)
|
||||
case let .highlight(highlight):
|
||||
await viewModel.loadLabels(dataService: dataService, highlight: highlight)
|
||||
case let .list(labels):
|
||||
await viewModel.loadLabels(dataService: dataService, initiallySelectedLabels: labels)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var createLabelButton: some View {
|
||||
@ -176,32 +171,29 @@ struct ApplyLabelsView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
#if os(iOS)
|
||||
NavigationView {
|
||||
if viewModel.labels.isEmpty, viewModel.isLoading {
|
||||
EmptyView()
|
||||
} else {
|
||||
innerBody
|
||||
#if os(iOS)
|
||||
NavigationView {
|
||||
innerBody
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
cancelButton
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
saveItemChangesButton
|
||||
}
|
||||
}
|
||||
}
|
||||
#elseif os(macOS)
|
||||
innerBody
|
||||
.toolbar {
|
||||
ToolbarItemGroup {
|
||||
cancelButton
|
||||
saveItemChangesButton
|
||||
}
|
||||
}
|
||||
#elseif os(macOS)
|
||||
innerBody
|
||||
.frame(minWidth: 400, minHeight: 600)
|
||||
#endif
|
||||
}
|
||||
.onAppear {
|
||||
Task {
|
||||
switch mode {
|
||||
case let .item(feedItem):
|
||||
await viewModel.loadLabels(dataService: dataService, item: feedItem)
|
||||
case let .highlight(highlight):
|
||||
await viewModel.loadLabels(dataService: dataService, highlight: highlight)
|
||||
case let .list(labels):
|
||||
await viewModel.loadLabels(dataService: dataService, initiallySelectedLabels: labels)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 400, minHeight: 600)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,8 +202,11 @@ extension Sequence where Element == LinkedItemLabel {
|
||||
if searchFilter.isEmpty || searchFilter == ZWSP {
|
||||
return map { $0 } // return the identity of the sequence
|
||||
}
|
||||
let index = searchFilter.index(searchFilter.startIndex, offsetBy: 1)
|
||||
let trimmed = searchFilter.suffix(from: index).lowercased()
|
||||
return filter { ($0.name ?? "").lowercased().contains(trimmed) }
|
||||
if searchFilter.starts(with: ZWSP) {
|
||||
let index = searchFilter.index(searchFilter.startIndex, offsetBy: 1)
|
||||
let trimmed = searchFilter.suffix(from: index).lowercased()
|
||||
return filter { ($0.name ?? "").lowercased().contains(trimmed) }
|
||||
}
|
||||
return filter { ($0.name ?? "").lowercased().contains(searchFilter.lowercased()) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +29,6 @@ import SwiftUI
|
||||
highlight: Highlight? = nil,
|
||||
initiallySelectedLabels: [LinkedItemLabel]? = nil
|
||||
) async {
|
||||
isLoading = true
|
||||
let selLabels = initiallySelectedLabels ?? item?.sortedLabels ?? highlight?.sortedLabels ?? []
|
||||
|
||||
await loadLabelsFromStore(dataService: dataService)
|
||||
@ -42,7 +41,6 @@ import SwiftUI
|
||||
unselectedLabels.insert(label)
|
||||
}
|
||||
}
|
||||
isLoading = false
|
||||
|
||||
Task.detached(priority: .userInitiated) {
|
||||
if let labelIDs = try? await dataService.labels() {
|
||||
@ -59,7 +57,6 @@ import SwiftUI
|
||||
self.unselectedLabels.insert(label)
|
||||
}
|
||||
}
|
||||
self.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,38 +25,54 @@ private struct LabelEntry: Entry {
|
||||
@MainActor
|
||||
public struct LabelsEntryView: View {
|
||||
@Binding var searchTerm: String
|
||||
@Binding var isFocused: Bool
|
||||
@State var viewModel: LabelsViewModel
|
||||
@EnvironmentObject var dataService: DataService
|
||||
|
||||
let entries: [Entry]
|
||||
|
||||
#if os(macOS)
|
||||
@State var popoverIndex = -1
|
||||
@State var presentPopover = false
|
||||
#endif
|
||||
|
||||
@State private var totalHeight = CGFloat.zero
|
||||
@FocusState private var textFieldFocused: Bool
|
||||
|
||||
public init(
|
||||
searchTerm: Binding<String>,
|
||||
isFocused: Binding<Bool>,
|
||||
viewModel: LabelsViewModel
|
||||
) {
|
||||
self._searchTerm = searchTerm
|
||||
self.viewModel = viewModel
|
||||
self._isFocused = isFocused
|
||||
|
||||
self.viewModel = viewModel
|
||||
self.entries = Array(viewModel.selectedLabels.map { LabelEntry(label: $0) })
|
||||
}
|
||||
|
||||
func onTextSubmit() {
|
||||
let index = searchTerm.index(searchTerm.startIndex, offsetBy: 1)
|
||||
let trimmed = searchTerm.suffix(from: index).lowercased()
|
||||
func getSearchTermText() -> String {
|
||||
if searchTerm.starts(with: ZWSP) {
|
||||
let index = searchTerm.index(searchTerm.startIndex, offsetBy: 1)
|
||||
let trimmed = searchTerm.suffix(from: index)
|
||||
return String(trimmed)
|
||||
}
|
||||
return searchTerm
|
||||
}
|
||||
|
||||
func onTextSubmit() {
|
||||
let trimmed = getSearchTermText()
|
||||
if trimmed.count < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
if let label = viewModel.labels.first(where: { $0.name?.lowercased() == trimmed }) {
|
||||
let lowercased = trimmed.lowercased()
|
||||
if let label = viewModel.labels.first(where: { $0.name?.lowercased() == lowercased }) {
|
||||
if !viewModel.selectedLabels.contains(label) {
|
||||
viewModel.selectedLabels.append(label)
|
||||
}
|
||||
|
||||
searchTerm = ZWSP
|
||||
reset()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
|
||||
textFieldFocused = true
|
||||
}
|
||||
@ -67,27 +83,58 @@ public struct LabelsEntryView: View {
|
||||
color: Gradient.randomColor(str: trimmed, offset: 1),
|
||||
description: nil
|
||||
)
|
||||
searchTerm = ZWSP
|
||||
reset()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
|
||||
textFieldFocused = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var textFieldString: NSAttributedString {
|
||||
#if os(iOS)
|
||||
NSAttributedString(
|
||||
string: searchTerm,
|
||||
attributes: [
|
||||
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14)
|
||||
]
|
||||
)
|
||||
#else
|
||||
NSAttributedString(
|
||||
string: searchTerm,
|
||||
attributes: [
|
||||
NSAttributedString.Key.font: NSFont.systemFont(ofSize: 14)
|
||||
]
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
func reset() {
|
||||
searchTerm = ZWSP
|
||||
#if os(macOS)
|
||||
popoverIndex = -1
|
||||
presentPopover = false
|
||||
#endif
|
||||
}
|
||||
|
||||
var deletableTextField: some View {
|
||||
let str = NSAttributedString(
|
||||
string: searchTerm,
|
||||
attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14)]
|
||||
)
|
||||
// Round it up to avoid jitter when typing
|
||||
let textWidth = max(25.0, Double(Int(str.size().width + 1)))
|
||||
let result = TextField("", text: $searchTerm)
|
||||
let textWidth = max(25.0, Double(Int(textFieldString.size().width + 28)))
|
||||
var result = AnyView(TextField("", text: $searchTerm)
|
||||
.id("deletableTextField")
|
||||
.frame(alignment: .topLeading)
|
||||
.frame(height: 25)
|
||||
.frame(width: textWidth)
|
||||
.padding(5)
|
||||
.padding(.trailing, 5)
|
||||
.padding(.vertical, 5)
|
||||
.padding(EdgeInsets(top: 0, leading: 6, bottom: 0, trailing: 6))
|
||||
.cornerRadius(5)
|
||||
.accentColor(.blue)
|
||||
.font(Font.system(size: 14))
|
||||
.multilineTextAlignment(.leading)
|
||||
#if os(macOS)
|
||||
.textFieldStyle(.plain)
|
||||
.background(Color.clear)
|
||||
#endif
|
||||
.onChange(of: searchTerm, perform: { _ in
|
||||
if searchTerm.count >= 64 {
|
||||
searchTerm = String(searchTerm.prefix(64))
|
||||
@ -95,56 +142,108 @@ public struct LabelsEntryView: View {
|
||||
if searchTerm.isEmpty {
|
||||
if viewModel.selectedLabels.count > 0 {
|
||||
viewModel.selectedLabels.removeLast()
|
||||
searchTerm = ZWSP
|
||||
reset()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
|
||||
textFieldFocused = true
|
||||
}
|
||||
} else {
|
||||
searchTerm = ZWSP
|
||||
reset()
|
||||
}
|
||||
}
|
||||
})
|
||||
.onSubmit {
|
||||
onTextSubmit()
|
||||
.onSubmit {
|
||||
#if os(iOS)
|
||||
onTextSubmit()
|
||||
#else
|
||||
if popoverIndex == -1 || popoverIndex >= partialMatches.count {
|
||||
onTextSubmit()
|
||||
} else if popoverIndex >= 0, popoverIndex < partialMatches.count {
|
||||
let matched = partialMatches[popoverIndex]
|
||||
viewModel.selectedLabels.append(matched)
|
||||
reset()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.submitScope())
|
||||
|
||||
#if os(macOS)
|
||||
if #available(macOS 14.0, *) {
|
||||
result = AnyView(result
|
||||
.onKeyPress(.downArrow) {
|
||||
popoverIndex = ((popoverIndex + 1) % (partialMatches.count + 1))
|
||||
return .handled
|
||||
}
|
||||
.onKeyPress(.upArrow) {
|
||||
popoverIndex -= 1
|
||||
return .handled
|
||||
}
|
||||
.onKeyPress(.tab) {
|
||||
popoverIndex = ((popoverIndex + 1) % (partialMatches.count + 1))
|
||||
return .handled
|
||||
})
|
||||
}
|
||||
return result
|
||||
#endif
|
||||
|
||||
return AnyView(result)
|
||||
}
|
||||
|
||||
// func onTextDelete() -> Bool { if searchTerm.isEmpty {
|
||||
// if lastSelected {
|
||||
// if viewModel.selectedLabels.count > 0 {
|
||||
// viewModel.selectedLabels.removeLast()
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
|
||||
// textFieldFocused = true
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// lastSelected = true
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
|
||||
public var body: some View {
|
||||
// HStack(spacing: 0) {
|
||||
VStack {
|
||||
GeometryReader { geometry in
|
||||
self.generateLabelsContent(in: geometry)
|
||||
}
|
||||
}.padding(0)
|
||||
.frame(height: totalHeight)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.extensionPanelBackground)
|
||||
.cornerRadius(8)
|
||||
#if os(macOS)
|
||||
.onHover { isHovered in
|
||||
DispatchQueue.main.async {
|
||||
if isHovered {
|
||||
NSCursor.iBeam.push()
|
||||
} else {
|
||||
NSCursor.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
.onAppear {
|
||||
textFieldFocused = true
|
||||
}
|
||||
.onTapGesture {
|
||||
textFieldFocused = true
|
||||
}
|
||||
.transaction { $0.animation = nil }
|
||||
.onChange(of: textFieldFocused) { self.isFocused = $0 }
|
||||
}
|
||||
|
||||
var partialMatches: [LinkedItemLabel] {
|
||||
viewModel.labels.applySearchFilter(searchTerm)
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
private var createLabelButton: some View {
|
||||
let count = partialMatches.count
|
||||
return Button {
|
||||
viewModel.createLabel(
|
||||
dataService: dataService,
|
||||
name: searchTerm,
|
||||
color: Gradient.randomColor(str: searchTerm, offset: 1),
|
||||
description: nil
|
||||
)
|
||||
reset()
|
||||
} label: {
|
||||
Text("Create new label")
|
||||
.padding(6)
|
||||
}
|
||||
.background(popoverIndex == count ? Color.blue : Color.clear)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.cornerRadius(4)
|
||||
.buttonStyle(.borderless)
|
||||
.cornerRadius(4)
|
||||
}
|
||||
#endif
|
||||
|
||||
private func generateLabelsContent(in geom: GeometryProxy) -> some View {
|
||||
var width = CGFloat.zero
|
||||
var height = CGFloat.zero
|
||||
@ -183,6 +282,37 @@ public struct LabelsEntryView: View {
|
||||
height = 0
|
||||
return result
|
||||
}).focused($textFieldFocused)
|
||||
#if os(macOS)
|
||||
.onChange(of: searchTerm) { _ in
|
||||
presentPopover = !searchTerm.isEmpty && searchTerm != ZWSP && partialMatches.count < 14
|
||||
if popoverIndex >= partialMatches.count + 1 {
|
||||
popoverIndex = partialMatches.count + 1
|
||||
}
|
||||
}
|
||||
.popover(isPresented: $presentPopover, arrowEdge: .top) {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
ForEach(Array(partialMatches.enumerated()), id: \.offset) { idx, label in
|
||||
if let name = label.name {
|
||||
Button {
|
||||
reset()
|
||||
viewModel.selectedLabels.append(label)
|
||||
} label: {
|
||||
Text(name)
|
||||
.padding(6)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.background(idx == popoverIndex ? Color.blue : Color.clear)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.cornerRadius(4)
|
||||
.buttonStyle(.borderless)
|
||||
.cornerRadius(4)
|
||||
}
|
||||
}
|
||||
createLabelButton
|
||||
}.padding(4)
|
||||
}
|
||||
|
||||
#endif
|
||||
}.background(viewHeightReader($totalHeight))
|
||||
}
|
||||
|
||||
|
||||
@ -138,7 +138,7 @@ struct LinkedItemMetadataEditView: View {
|
||||
)
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 400, minHeight: 600)
|
||||
.frame(minWidth: 400, minHeight: 400)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -499,7 +499,9 @@ struct WebReaderContainerView: View {
|
||||
showErrorAlertMessage: $showErrorAlertMessage
|
||||
)
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
#if os(iOS)
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
#endif
|
||||
}
|
||||
.sheet(isPresented: $showHighlightLabelsModal) {
|
||||
if let highlight = Highlight.lookup(byID: self.annotation, inContext: self.dataService.viewContext) {
|
||||
|
||||
@ -89,6 +89,7 @@ struct WelcomeView: View {
|
||||
.sheet(isPresented: $showPrivacyModal) {
|
||||
NavigationView {
|
||||
BasicWebAppView.privacyPolicyWebView(baseURL: dataService.appEnvironment.webAppBaseURL)
|
||||
#if os(iOS)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button(
|
||||
@ -101,11 +102,24 @@ struct WelcomeView: View {
|
||||
)
|
||||
}
|
||||
}
|
||||
#else
|
||||
.toolbar {
|
||||
Button(
|
||||
action: {
|
||||
showPrivacyModal = false
|
||||
},
|
||||
label: {
|
||||
Text(LocalText.genericClose)
|
||||
}
|
||||
)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showTermsModal) {
|
||||
NavigationView {
|
||||
BasicWebAppView.termsConditionsWebView(baseURL: dataService.appEnvironment.webAppBaseURL)
|
||||
#if os(iOS)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button(
|
||||
@ -118,6 +132,18 @@ struct WelcomeView: View {
|
||||
)
|
||||
}
|
||||
}
|
||||
#else
|
||||
.toolbar {
|
||||
Button(
|
||||
action: {
|
||||
showTermsModal = false
|
||||
},
|
||||
label: {
|
||||
Text(LocalText.genericClose)
|
||||
}
|
||||
)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showAboutPage) {
|
||||
|
||||
@ -66,7 +66,7 @@ public struct LibraryItemCard: View {
|
||||
labels
|
||||
}
|
||||
|
||||
if let note = item.noteText {
|
||||
if let note = item.noteText, !note.isEmpty {
|
||||
HStack(alignment: .top, spacing: 10) {
|
||||
avatarImage
|
||||
.frame(width: 20, height: 20)
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xA8",
|
||||
"green" : "0xEB",
|
||||
"red" : "0xFB"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x4C",
|
||||
"green" : "0x4D",
|
||||
"red" : "0x4F"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 554 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 810 B |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 454 B |
|
Before Width: | Height: | Size: 569 B |
|
Before Width: | Height: | Size: 919 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 461 B |
|
Before Width: | Height: | Size: 565 B |
|
Before Width: | Height: | Size: 729 B |
|
Before Width: | Height: | Size: 899 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 450 B |
|
Before Width: | Height: | Size: 546 B |
|
Before Width: | Height: | Size: 699 B |
|
Before Width: | Height: | Size: 871 B |
|
Before Width: | Height: | Size: 1023 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 290 B |
|
Before Width: | Height: | Size: 465 B |
|
Before Width: | Height: | Size: 615 B |
|
Before Width: | Height: | Size: 941 B |
|
Before Width: | Height: | Size: 336 B |
|
Before Width: | Height: | Size: 525 B |
|
Before Width: | Height: | Size: 666 B |
|
Before Width: | Height: | Size: 1018 B |
|
Before Width: | Height: | Size: 236 B |
|
Before Width: | Height: | Size: 374 B |
|
Before Width: | Height: | Size: 498 B |
|
Before Width: | Height: | Size: 732 B |
|
Before Width: | Height: | Size: 313 B |
|
Before Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 631 B |
|
Before Width: | Height: | Size: 940 B |
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||
<path fill="black" d="M15.9 7.801c0 .507-.123 1.12-.248 1.656v.004l-.001.003a2.87 2.87 0 0 1-2.793 2.186h-.036c-1.625 0-2.649-1.334-2.649-2.828v-2.14l-1.21 1.794-.067.055a1.404 1.404 0 0 1-1.793 0l-.065-.053-1.248-1.82v4.414H4.6V6.268c0-.91 1.078-1.439 1.794-.802l.055.048 1.46 2.13a.21.21 0 0 0 .179 0l1.43-2.119.065-.054c.68-.567 1.78-.138 1.78.815v2.536c0 .971.619 1.638 1.46 1.638h.035c.78 0 1.45-.527 1.636-1.277.125-.534.216-1.026.216-1.378-.017-3.835-3.262-6.762-7.188-6.498-3.311.23-5.986 2.905-6.216 6.216A6.705 6.705 0 0 0 8 14.693v1.19a7.895 7.895 0 0 1-7.882-8.44C.39 3.536 3.536.39 7.44.118 12.017-.19 15.88 3.242 15.9 7.8z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 776 B |
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||
<path fill="white" d="M15.9 7.801c0 .507-.123 1.12-.248 1.656v.004l-.001.003a2.87 2.87 0 0 1-2.793 2.186h-.036c-1.625 0-2.649-1.334-2.649-2.828v-2.14l-1.21 1.794-.067.055a1.404 1.404 0 0 1-1.793 0l-.065-.053-1.248-1.82v4.414H4.6V6.268c0-.91 1.078-1.439 1.794-.802l.055.048 1.46 2.13a.21.21 0 0 0 .179 0l1.43-2.119.065-.054c.68-.567 1.78-.138 1.78.815v2.536c0 .971.619 1.638 1.46 1.638h.035c.78 0 1.45-.527 1.636-1.277.125-.534.216-1.026.216-1.378-.017-3.835-3.262-6.762-7.188-6.498-3.311.23-5.986 2.905-6.216 6.216A6.705 6.705 0 0 0 8 14.693v1.19a7.895 7.895 0 0 1-7.882-8.44C.39 3.536 3.536.39 7.44.118 12.017-.19 15.88 3.242 15.9 7.8z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 776 B |
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||
<path fill="white" d="M15.9 7.801c0 .507-.123 1.12-.248 1.656v.004l-.001.003a2.87 2.87 0 0 1-2.793 2.186h-.036c-1.625 0-2.649-1.334-2.649-2.828v-2.14l-1.21 1.794-.067.055a1.404 1.404 0 0 1-1.793 0l-.065-.053-1.248-1.82v4.414H4.6V6.268c0-.91 1.078-1.439 1.794-.802l.055.048 1.46 2.13a.21.21 0 0 0 .179 0l1.43-2.119.065-.054c.68-.567 1.78-.138 1.78.815v2.536c0 .971.619 1.638 1.46 1.638h.035c.78 0 1.45-.527 1.636-1.277.125-.534.216-1.026.216-1.378-.017-3.835-3.262-6.762-7.188-6.498-3.311.23-5.986 2.905-6.216 6.216A6.705 6.705 0 0 0 8 14.693v1.19a7.895 7.895 0 0 1-7.882-8.44C.39 3.536 3.536.39 7.44.118 12.017-.19 15.88 3.242 15.9 7.8z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 776 B |
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||
<path fill="white" d="M15.9 7.801c0 .507-.123 1.12-.248 1.656v.004l-.001.003a2.87 2.87 0 0 1-2.793 2.186h-.036c-1.625 0-2.649-1.334-2.649-2.828v-2.14l-1.21 1.794-.067.055a1.404 1.404 0 0 1-1.793 0l-.065-.053-1.248-1.82v4.414H4.6V6.268c0-.91 1.078-1.439 1.794-.802l.055.048 1.46 2.13a.21.21 0 0 0 .179 0l1.43-2.119.065-.054c.68-.567 1.78-.138 1.78.815v2.536c0 .971.619 1.638 1.46 1.638h.035c.78 0 1.45-.527 1.636-1.277.125-.534.216-1.026.216-1.378-.017-3.835-3.262-6.762-7.188-6.498-3.311.23-5.986 2.905-6.216 6.216A6.705 6.705 0 0 0 8 14.693v1.19a7.895 7.895 0 0 1-7.882-8.44C.39 3.536 3.536.39 7.44.118 12.017-.19 15.88 3.242 15.9 7.8z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 776 B |
|
Before Width: | Height: | Size: 428 B |
|
Before Width: | Height: | Size: 541 B |
|
Before Width: | Height: | Size: 673 B |
|
Before Width: | Height: | Size: 832 B |
|
Before Width: | Height: | Size: 989 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 442 B |
|
Before Width: | Height: | Size: 523 B |
|
Before Width: | Height: | Size: 689 B |
|
Before Width: | Height: | Size: 843 B |
|
Before Width: | Height: | Size: 1009 B |
|
Before Width: | Height: | Size: 1.2 KiB |
@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "Omnivore",
|
||||
"short_name": "Omnivore",
|
||||
"version": "2.6.2",
|
||||
"version": "2.8.2",
|
||||
"description": "Save PDFs and Articles to your Omnivore library",
|
||||
"author": "Omnivore Media, Inc",
|
||||
"default_locale": "en",
|
||||
|
||||
@ -1,101 +0,0 @@
|
||||
/**
|
||||
* Fonts
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-400.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-600.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-700.woff2") format("woff2");
|
||||
}
|
||||
|
||||
/**
|
||||
* Other
|
||||
*/
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.cta-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 24px 12px;
|
||||
background: linear-gradient(150deg, #fff 55%, #ffde8c 55%);
|
||||
background-color: #fff;
|
||||
color: #3d3d3d;
|
||||
font-family: Inter, sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cta-container__wrapper {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cta-container__title {
|
||||
padding: 0 24px 24px;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.cta-container__icon {
|
||||
vertical-align: unset;
|
||||
margin-bottom: -4px;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.cta-container__link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 184px;
|
||||
height: 34px;
|
||||
margin: 36px auto 14px;
|
||||
border-radius: 40px;
|
||||
background-color: #fff;
|
||||
color: inherit;
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cta-container__text {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.cta-container__textlink {
|
||||
color: inherit;
|
||||
font-weight: 700;
|
||||
}
|
||||
@ -7,33 +7,6 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<style>
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-400.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-600.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-700.woff2") format("woff2");
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
@ -55,7 +28,7 @@ body {
|
||||
background: linear-gradient(150deg, #fff 55%, #ffde8c 55%);
|
||||
background-color: #fff;
|
||||
color: #3d3d3d;
|
||||
font-family: Inter, sans-serif;
|
||||
font-family: sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@ -62,16 +62,54 @@ import Views
|
||||
import Cocoa
|
||||
|
||||
class ShareViewController: NSViewController {
|
||||
let labelsViewModel = LabelsViewModel()
|
||||
let viewModel = ShareExtensionViewModel()
|
||||
|
||||
override func loadView() {
|
||||
view = NSView(frame: NSRect(x: 0, y: 0, width: 400, height: 600))
|
||||
view = NSView(frame: NSRect(x: 0, y: 0, width: 400, height: 300))
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: Notification.Name("ShowAddNoteSheet"),
|
||||
object: nil,
|
||||
queue: OperationQueue.main
|
||||
) { _ in
|
||||
self.openSheet(AnyView(AddNoteSheet(viewModel: self.viewModel)))
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: Notification.Name("ShowEditLabelsSheet"),
|
||||
object: nil,
|
||||
queue: OperationQueue.main
|
||||
) { _ in
|
||||
self.openSheet(AnyView(EditLabelsSheet(viewModel: self.viewModel, labelsViewModel: self.labelsViewModel)))
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: Notification.Name("ShowEditInfoSheet"),
|
||||
object: nil,
|
||||
queue: OperationQueue.main
|
||||
) { _ in
|
||||
self.openSheet(AnyView(EditInfoSheet(viewModel: self.viewModel)))
|
||||
}
|
||||
|
||||
embed(
|
||||
childViewController: NSViewController.makeShareExtensionController(extensionContext: extensionContext)
|
||||
childViewController: NSViewController.makeShareExtensionController(
|
||||
viewModel: viewModel,
|
||||
labelsViewModel: labelsViewModel,
|
||||
extensionContext: extensionContext
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func openSheet(_ rootView: AnyView) {
|
||||
let hostingController = PlatformHostingController(rootView: rootView)
|
||||
|
||||
presentAsSheet(hostingController)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BIN
pkg/extension/src/images/extension-transparent/icon-128.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
pkg/extension/src/images/extension-transparent/icon-16.png
Normal file
|
After Width: | Height: | Size: 554 B |
BIN
pkg/extension/src/images/extension-transparent/icon-24.png
Normal file
|
After Width: | Height: | Size: 810 B |
BIN
pkg/extension/src/images/extension-transparent/icon-256.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
pkg/extension/src/images/extension-transparent/icon-32.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
pkg/extension/src/images/extension-transparent/icon-48.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
pkg/extension/src/images/extension-transparent/icon-96.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
@ -1,101 +0,0 @@
|
||||
/**
|
||||
* Fonts
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-400.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-600.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-700.woff2") format("woff2");
|
||||
}
|
||||
|
||||
/**
|
||||
* Other
|
||||
*/
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.cta-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 24px 12px;
|
||||
background: linear-gradient(150deg, #fff 55%, #ffde8c 55%);
|
||||
background-color: #fff;
|
||||
color: #3d3d3d;
|
||||
font-family: Inter, sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cta-container__wrapper {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cta-container__title {
|
||||
padding: 0 24px 24px;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.cta-container__icon {
|
||||
vertical-align: unset;
|
||||
margin-bottom: -4px;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.cta-container__link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 184px;
|
||||
height: 34px;
|
||||
margin: 36px auto 14px;
|
||||
border-radius: 40px;
|
||||
background-color: #fff;
|
||||
color: inherit;
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cta-container__text {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.cta-container__textlink {
|
||||
color: inherit;
|
||||
font-weight: 700;
|
||||
}
|
||||
@ -7,33 +7,6 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<style>
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-400.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-600.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-family: Inter;
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/inter-v3-latin-700.woff2") format("woff2");
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
@ -55,7 +28,7 @@ body {
|
||||
background: linear-gradient(150deg, #fff 55%, #ffde8c 55%);
|
||||
background-color: #fff;
|
||||
color: #3d3d3d;
|
||||
font-family: Inter, sans-serif;
|
||||
font-family: sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
border-radius: 4px;
|
||||
color: #3D3D3D;
|
||||
background: #fff;
|
||||
font: 400 12px Inter, sans-serif;
|
||||
font: 400 12px sans-serif;
|
||||
line-height: 20px;
|
||||
box-shadow: 0px 5px 20px rgba(32, 31, 29, 0.12);
|
||||
transition: all 300ms ease;
|
||||
@ -304,6 +304,7 @@
|
||||
fill: #898989;
|
||||
}
|
||||
.omnivore-save-button button {
|
||||
margin-top: 10px;
|
||||
color: #333333;
|
||||
}
|
||||
#omnivore-toast-container #omnivore-edit-labels-list button {
|
||||
|
||||