Better handling of the label editor
This commit is contained 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
|
||||
|
||||
@ -56,6 +57,7 @@ public struct EditLabelsSheet: View {
|
||||
VStack {
|
||||
LabelsEntryView(
|
||||
searchTerm: $labelsViewModel.labelSearchFilter,
|
||||
isFocused: $isLabelsEntryFocused,
|
||||
viewModel: labelsViewModel
|
||||
)
|
||||
.padding(.horizontal, 10)
|
||||
|
||||
@ -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? {
|
||||
@ -55,12 +60,13 @@ public struct ShareExtensionView: View {
|
||||
}
|
||||
|
||||
var isSynced: Bool {
|
||||
switch viewModel.status {
|
||||
case .synced:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
true
|
||||
// switch viewModel.status {
|
||||
// case .synced:
|
||||
// return true
|
||||
// default:
|
||||
// return false
|
||||
// }
|
||||
}
|
||||
|
||||
var articleInfoBox: some View {
|
||||
@ -100,7 +106,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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,22 +243,32 @@ 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 true
|
||||
}
|
||||
#endif
|
||||
return false
|
||||
#if os(iOS)
|
||||
if UIDevice.isIPhone {
|
||||
return false
|
||||
}
|
||||
#endif
|
||||
return true
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
#if os(iOS)
|
||||
iOSBody
|
||||
#else
|
||||
macOSBody
|
||||
#endif
|
||||
}
|
||||
|
||||
var iOSBody: some View {
|
||||
VStack(alignment: .leading, spacing: 15) {
|
||||
titleBar
|
||||
.padding(.top, 15)
|
||||
@ -262,32 +278,44 @@ public struct ShareExtensionView: View {
|
||||
Spacer(minLength: 1)
|
||||
|
||||
HStack {
|
||||
#if os(macOS)
|
||||
moreMenuButton
|
||||
.padding(.bottom, 15)
|
||||
#endif
|
||||
Spacer()
|
||||
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)
|
||||
@ -295,4 +323,99 @@ public struct ShareExtensionView: View {
|
||||
viewModel.savePage(extensionContext: extensionContext)
|
||||
}
|
||||
}
|
||||
|
||||
@State var notes = ""
|
||||
@State var labelsSearch = ZWSP
|
||||
@State var isLabelsEntryFocused = false
|
||||
|
||||
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: $notes)
|
||||
.frame(maxWidth: .infinity)
|
||||
.font(Font.system(size: 14))
|
||||
.accentColor(.blue)
|
||||
.introspectTextView { textView in
|
||||
textView.textContainerInset = NSSize(width: 10, height: 10)
|
||||
}
|
||||
.focused($focusedField, equals: .noteEditor)
|
||||
if notes.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, 10)
|
||||
.focused($focusedField, equals: .labelEditor)
|
||||
.onHover { isHovered in
|
||||
DispatchQueue.main.async {
|
||||
if isHovered {
|
||||
NSCursor.iBeam.push()
|
||||
} else {
|
||||
NSCursor.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
if !isLabelsEntryFocused, labelsViewModel.selectedLabels.isEmpty, labelsSearch == ZWSP {
|
||||
Text("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: {
|
||||
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||
}, label: {
|
||||
Text("Dismiss")
|
||||
})
|
||||
.padding(.bottom, 15)
|
||||
Button(action: {
|
||||
viewModel.handleReadNowAction(extensionContext: extensionContext)
|
||||
}, label: {
|
||||
Text("Read Now")
|
||||
})
|
||||
.padding(.bottom, 15)
|
||||
|
||||
}.padding(15)
|
||||
|
||||
}.frame(maxWidth: .infinity)
|
||||
.background(Color.isDarkMode ? Color.systemBackground : Color.white)
|
||||
.onAppear {
|
||||
viewModel.savePage(extensionContext: extensionContext)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
|
||||
focusedField = .noteEditor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -25,6 +25,7 @@ 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
|
||||
|
||||
@ -35,11 +36,13 @@ public struct LabelsEntryView: View {
|
||||
|
||||
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) })
|
||||
}
|
||||
|
||||
@ -100,6 +103,7 @@ public struct LabelsEntryView: View {
|
||||
.frame(height: 25)
|
||||
.frame(width: textWidth)
|
||||
.padding(5)
|
||||
.accentColor(.blue)
|
||||
.font(Font.system(size: 14))
|
||||
.multilineTextAlignment(.leading)
|
||||
.onChange(of: searchTerm, perform: { _ in
|
||||
@ -121,6 +125,10 @@ public struct LabelsEntryView: View {
|
||||
.onSubmit {
|
||||
onTextSubmit()
|
||||
}
|
||||
#if os(macOS)
|
||||
.textFieldStyle(.plain)
|
||||
.background(Color.clear)
|
||||
#endif
|
||||
return result
|
||||
}
|
||||
|
||||
@ -157,6 +165,7 @@ public struct LabelsEntryView: View {
|
||||
textFieldFocused = true
|
||||
}
|
||||
.transaction { $0.animation = nil }
|
||||
.onChange(of: textFieldFocused) { self.isFocused = $0 }
|
||||
}
|
||||
|
||||
private func generateLabelsContent(in geom: GeometryProxy) -> some View {
|
||||
|
||||
@ -66,11 +66,36 @@ import Views
|
||||
let viewModel = ShareExtensionViewModel()
|
||||
|
||||
override func loadView() {
|
||||
view = NSView(frame: NSRect(x: 0, y: 0, width: 400, height: 400))
|
||||
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(
|
||||
viewModel: viewModel,
|
||||
@ -79,6 +104,12 @@ import Views
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func openSheet(_ rootView: AnyView) {
|
||||
let hostingController = PlatformHostingController(rootView: rootView)
|
||||
|
||||
presentAsSheet(hostingController)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user