Display some UI for highlight on save

This commit is contained in:
Jackson Harper
2022-11-21 18:49:36 +08:00
committed by Hongbo Wu
parent 080a1982b2
commit fc2e4f7d8c
3 changed files with 190 additions and 61 deletions

View File

@ -9,6 +9,7 @@ public class ShareExtensionViewModel: ObservableObject {
@Published public var status: ShareExtensionStatus = .processing
@Published public var title: String = ""
@Published public var url: String?
@Published public var highlightData: HighlightData?
@Published public var linkedItem: LinkedItem?
@Published public var requestId = UUID().uuidString.lowercased()
@Published var debugText: String?
@ -83,13 +84,13 @@ public class ShareExtensionViewModel: ObservableObject {
DispatchQueue.main.async {
self.status = .saved
let url = URLComponents(string: payload.url)
let hostname = URL(string: payload.url)?.host ?? ""
switch payload.contentType {
case let .html(html: _, title: title, _):
case let .html(html: _, title: title, highlightData: highlightData):
self.title = title ?? ""
self.url = hostname
self.highlightData = highlightData
case .none:
self.url = hostname
self.title = payload.url

View File

@ -12,16 +12,22 @@ public struct ShareExtensionView: View {
@State var reminderTime: ReminderTime?
@State var hideUntilReminded = false
@State var editingTitle = false
@State var editingLabels = false
@State var viewingHighlight = false
@State var previousLabels: [LinkedItemLabel]?
@State var messageText: String?
@State var viewState = ViewState.mainView
enum FocusField: Hashable {
case titleEditor
}
enum ViewState {
case mainView
case editingTitle
case editingLabels
case viewingHighlight
}
@FocusState private var focusedField: FocusField?
private func handleReminderTimeSelection(_ selectedTime: ReminderTime) {
@ -102,14 +108,14 @@ public struct ShareExtensionView: View {
.font(.appFootnote)
.padding(.trailing, 8)
.onTapGesture {
editingTitle = true
viewState = .editingTitle
}
})
.disabled(editingTitle)
.opacity(editingTitle ? 0.0 : 1.0)
.disabled(viewState == .editingTitle)
.opacity(viewState == .editingTitle ? 0.0 : 1.0)
VStack(alignment: .leading) {
if !editingTitle {
if viewState != .editingTitle {
Text(self.viewModel.title)
.font(.appSubheadline)
.foregroundColor(.appGrayTextContrast)
@ -134,7 +140,7 @@ public struct ShareExtensionView: View {
var labelsSection: some View {
HStack {
if !editingLabels {
if viewState != .editingLabels {
ZStack {
Circle()
.foregroundColor(Color.blue)
@ -166,23 +172,37 @@ public struct ShareExtensionView: View {
Image(systemName: "chevron.right")
.font(.appCallout)
} else {
ScrollView {
LabelsMasonaryView(labels: labelsViewModel.labels,
selectedLabels: labelsViewModel.selectedLabels,
onLabelTap: onLabelTap)
}.background(Color.appButtonBackground)
.cornerRadius(8)
VStack {
ScrollView {
LabelsMasonaryView(labels: labelsViewModel.labels,
selectedLabels: labelsViewModel.selectedLabels,
onLabelTap: onLabelTap)
}.background(Color.appButtonBackground)
.cornerRadius(8)
Button(
action: { labelsViewModel.showCreateLabelModal = true },
label: {
HStack {
Spacer()
Image(systemName: "plus")
Text("Create label")
Spacer()
}
}
).buttonStyle(RoundedRectButtonStyle(color: .blue, textColor: .white))
}
}
}
.padding(16)
.frame(maxWidth: .infinity, maxHeight: self.editingLabels ? .infinity : 60)
.frame(maxWidth: .infinity, maxHeight: viewState == .editingLabels ? .infinity : 60)
.background(Color.appButtonBackground)
.cornerRadius(8)
}
var highlightSection: some View {
HStack {
if !viewingHighlight {
if viewState != .viewingHighlight {
ZStack {
Circle()
.foregroundColor(Color.appBackground)
@ -201,9 +221,8 @@ public struct ShareExtensionView: View {
.foregroundColor(Color.appGrayTextContrast)
.frame(maxWidth: .infinity, alignment: .leading)
let labelCount = 0
Text(labelCount > 0 ?
"\(labelCount) label\(labelCount > 1 ? "s" : "") selected"
Text(viewModel.highlightData != nil ?
viewModel.highlightData!.highlightText
: "Select text before saving to create highlight")
.font(.appFootnote)
.foregroundColor(Color.appGrayText)
@ -214,18 +233,15 @@ public struct ShareExtensionView: View {
Image(systemName: "chevron.right")
.font(.appCallout)
} else {
// Text(self.pageSc)
// ScrollView {
// LabelsMasonaryView(labels: labelsViewModel.labels,
// selectedLabels: labelsViewModel.selectedLabels,
// onLabelTap: onLabelTap)
// }.background(Color.appButtonBackground)
// .cornerRadius(8)
} else if let highlightText = self.viewModel.highlightData?.highlightText {
Text(highlightText)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.cornerRadius(8)
.padding(0)
}
}
.padding(16)
.frame(maxWidth: .infinity, maxHeight: self.editingLabels ? .infinity : 60)
.frame(maxWidth: .infinity, maxHeight: viewState == .viewingHighlight ? .infinity : 60)
.background(Color.appButtonBackground)
.cornerRadius(8)
}
@ -324,6 +340,19 @@ public struct ShareExtensionView: View {
}
}
var editingViewTitle: String {
switch viewState {
case .editingTitle:
return "Edit Title"
case .editingLabels:
return "Labels"
case .viewingHighlight:
return "Highlight"
default:
return ""
}
}
public var body: some View {
VStack(alignment: .center) {
Capsule()
@ -331,7 +360,7 @@ public struct ShareExtensionView: View {
.frame(width: 60, height: 4)
.padding(.top, 10)
if !editingLabels, !editingTitle {
if viewState == .mainView {
titleBar
.padding(.top, 10)
.padding(.bottom, 12)
@ -339,28 +368,28 @@ public struct ShareExtensionView: View {
ZStack {
Button(action: {
withAnimation {
if editingLabels {
if viewState == .editingLabels {
if let linkedItem = self.viewModel.linkedItem {
self.labelsViewModel.selectedLabels = previousLabels ?? []
self.labelsViewModel.saveItemLabelChanges(itemID: linkedItem.unwrappedID,
dataService: self.viewModel.services.dataService)
}
}
editingTitle = false
editingLabels = false
viewState = .mainView
}
}, label: { Text("Cancel") })
.frame(maxWidth: .infinity, alignment: .leading)
.opacity(viewState == .viewingHighlight ? 0.0 : 1.0)
// Don't show viewState when viewing the highlight
Text(editingTitle ? "Edit Title" : "Labels").bold()
Text(editingViewTitle).bold()
.frame(maxWidth: .infinity, alignment: .center)
Button(action: {
withAnimation {
editingTitle = false
editingLabels = false
viewState = .mainView
if editingTitle {
if viewState == .editingTitle {
if let linkedItem = self.viewModel.linkedItem {
viewModel.submitTitleEdit(dataService: self.viewModel.services.dataService,
itemID: linkedItem.unwrappedID,
@ -376,11 +405,11 @@ public struct ShareExtensionView: View {
.padding(.bottom, 4)
}
if !editingLabels, !editingTitle {
if viewState == .mainView {
titleBox
}
if editingTitle {
if viewState == .editingTitle {
ScrollView(showsIndicators: false) {
VStack(alignment: .center, spacing: 16) {
VStack(alignment: .leading, spacing: 6) {
@ -409,25 +438,29 @@ public struct ShareExtensionView: View {
Spacer()
}
if !editingTitle {
labelsSection
.onTapGesture {
withAnimation {
previousLabels = self.labelsViewModel.selectedLabels
editingLabels = true
if viewState != .editingTitle {
if viewState != .viewingHighlight {
labelsSection
.onTapGesture {
withAnimation {
previousLabels = self.labelsViewModel.selectedLabels
viewState = .editingLabels
}
}
}
highlightSection
.onTapGesture {
withAnimation {
viewingHighlight = true
}
if viewState != .editingLabels {
highlightSection
.onTapGesture {
withAnimation {
viewState = .viewingHighlight
}
}
}
}
}
Spacer()
if !editingLabels, !editingTitle {
if viewState == .mainView {
Divider()
.padding(.bottom, 20)
@ -445,7 +478,9 @@ public struct ShareExtensionView: View {
.onAppear {
viewModel.savePage(extensionContext: extensionContext)
}
.sheet(isPresented: $labelsViewModel.showCreateLabelModal) {
CreateLabelView(viewModel: labelsViewModel)
}
.environmentObject(viewModel.services.dataService)
.task {
await labelsViewModel.loadLabelsFromStore(dataService: viewModel.services.dataService)

View File

@ -1,8 +1,101 @@
//
// File.swift
//
//
// Created by Jackson Harper on 11/21/22.
//
import Models
import SwiftUI
import Utils
import Views
import WebKit
import Foundation
struct HighlightViewer: PlatformViewRepresentable {
let highlightData: HighlightData
func makeCoordinator() -> WebReaderCoordinator {
WebReaderCoordinator()
}
private func makePlatformView(context: Context) -> WKWebView {
let webView = WebViewManager.shared()
let contentController = WKUserContentController()
webView.navigationDelegate = context.coordinator
webView.configuration.userContentController = contentController
webView.configuration.userContentController.removeAllScriptMessageHandlers()
#if os(iOS)
webView.isOpaque = false
webView.backgroundColor = .clear
webView.scrollView.delegate = context.coordinator
webView.scrollView.contentInset.top = readerViewNavBarHeight
webView.scrollView.verticalScrollIndicatorInsets.top = readerViewNavBarHeight
webView.configuration.userContentController.add(webView, name: "viewerAction")
#else
webView.setValue(false, forKey: "drawsBackground")
#endif
for action in WebViewAction.allCases {
webView.configuration.userContentController.add(context.coordinator, name: action.rawValue)
}
webView.configuration.userContentController.addScriptMessageHandler(
context.coordinator, contentWorld: .page, name: "articleAction"
)
loadContent(webView: webView)
return webView
}
private func updatePlatformView(_: WKWebView, context _: Context) {
// If the webview had been terminated `needsReload` will have been set to true
// Or if the articleContent value has changed then it's id will be different from the coordinator's
// if context.coordinator.needsReload {
// loadContent(webView: webView)
// context.coordinator.needsReload = false
// return
// }
}
private func loadContent(webView: WKWebView) {
let themeKey = ThemeManager.currentThemeName
let content = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no' />
<style>
@import url("highlight\(themeKey == "Gray" ? "-dark" : "").css");
</style>
</head>
<body>
<div id="root" />
<div id='_omnivore-highlight' class="highlight">
\(highlightData.highlightHTML)
</div>
</body>
</html>
"""
webView.loadHTMLString(content, baseURL: ViewsPackage.resourceURL)
}
}
#if os(iOS)
extension HighlightViewer {
func makeUIView(context: Context) -> WKWebView {
makePlatformView(context: context)
}
func updateUIView(_ webView: WKWebView, context: Context) {
updatePlatformView(webView, context: context)
}
}
#else
extension WebReader {
func makeNSView(context: Context) -> WKWebView {
makePlatformView(context: context)
}
func updateNSView(_ webView: WKWebView, context: Context) {
updatePlatformView(webView, context: context)
}
}
#endif