173 lines
4.8 KiB
Swift
173 lines
4.8 KiB
Swift
import SwiftUI
|
|
import SFSafeSymbols
|
|
import HighlightedTextEditor
|
|
|
|
enum PageContentSaveStatus {
|
|
case isSaved
|
|
case failedToSave
|
|
case needsSave
|
|
case notLoaded
|
|
|
|
var symbol: SFSymbol {
|
|
switch self {
|
|
case .notLoaded:
|
|
return .questionmarkSquareDashed
|
|
case .isSaved:
|
|
return .checkmarkCircleFill
|
|
case .needsSave:
|
|
return .hourglassCircleFill
|
|
case .failedToSave:
|
|
return .exclamationmarkTriangleFill
|
|
}
|
|
}
|
|
|
|
var color: Color {
|
|
switch self {
|
|
case .isSaved, .notLoaded:
|
|
return .green
|
|
case .needsSave:
|
|
return .yellow
|
|
case .failedToSave:
|
|
return .red
|
|
}
|
|
}
|
|
}
|
|
|
|
struct LocalizedPageContentView: View {
|
|
|
|
@EnvironmentObject
|
|
var content: Content
|
|
|
|
@ObservedObject
|
|
var page: Page
|
|
|
|
let language: ContentLanguage
|
|
|
|
@State
|
|
private var pageContent: String = ""
|
|
|
|
@State
|
|
private var generationResults: PageGenerationResults?
|
|
|
|
@State
|
|
private var saveState: PageContentSaveStatus = .notLoaded
|
|
|
|
@State
|
|
private var lastSave: Date = .now
|
|
|
|
@State
|
|
private var lastModification: Date = .now
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading) {
|
|
HStack {
|
|
Image(systemSymbol: saveState.symbol)
|
|
.foregroundStyle(saveState.color)
|
|
if let generationResults {
|
|
PageContentResultsView(results: generationResults)
|
|
}
|
|
let linkingPosts = content.posts.filter { $0.linkedPage == page }
|
|
TextWithPopup(
|
|
symbol: .ipadAndArrowForward,
|
|
title: "Post linking to page",
|
|
text: "\(linkingPosts.count) linking posts",
|
|
items: linkingPosts.map { $0.title(in: language) })
|
|
}.foregroundStyle(.secondary)
|
|
InsertableItemsView()
|
|
HighlightedTextEditor(
|
|
text: $pageContent,
|
|
highlightRules: .markdown)
|
|
.onChange(of: pageContent) {
|
|
didChangeContent()
|
|
}
|
|
}
|
|
.onAppear(perform: loadContent)
|
|
.onDisappear { saveContentIfNeeded(isFinalSave: true) }
|
|
}
|
|
|
|
private func didChangeContent() {
|
|
if saveState == .notLoaded {
|
|
// Content was changed due to loading
|
|
saveState = .isSaved
|
|
return
|
|
}
|
|
saveState = .needsSave
|
|
|
|
// Wait a few seconds for a save, to allow additional changes
|
|
// Reduces the number of saves
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(10)) {
|
|
self.saveContentIfNeeded()
|
|
}
|
|
}
|
|
|
|
private func loadContent() {
|
|
let language = language
|
|
guard page.localized(in: language).hasContent else {
|
|
pageContent = "New file"
|
|
return
|
|
}
|
|
guard let content = page.pageContent(in: language) else {
|
|
print("Failed to load page content")
|
|
pageContent = "Failed to load"
|
|
return
|
|
}
|
|
guard content != "" else {
|
|
pageContent = "New file"
|
|
return
|
|
}
|
|
pageContent = content
|
|
checkContent()
|
|
}
|
|
|
|
private func saveContentIfNeeded(isFinalSave: Bool = false) {
|
|
switch saveState {
|
|
case .isSaved, .notLoaded:
|
|
return
|
|
default:
|
|
break
|
|
}
|
|
|
|
if !isFinalSave, Date.now.timeIntervalSince(lastModification) < 10 {
|
|
// Additional modification made
|
|
// Wait for next scheduled invocation of saveIfNeeded()
|
|
// if the overall unsaved time is not too long
|
|
if Date.now.timeIntervalSince(lastSave) < 60 {
|
|
//print("Waiting while modifying")
|
|
return
|
|
}
|
|
print("Saving content after 30 seconds of modifications")
|
|
}
|
|
saveUnconditionally()
|
|
}
|
|
|
|
private func saveUnconditionally() {
|
|
guard pageContent != "New file", pageContent != "" else {
|
|
guard page.removeContent(in: language) else {
|
|
print("Failed to remove empty content from disk")
|
|
saveState = .failedToSave
|
|
return
|
|
}
|
|
saveState = .notLoaded
|
|
generationResults = nil
|
|
return
|
|
}
|
|
|
|
guard page.save(pageContent: pageContent, in: language) else {
|
|
print("Failed to save content")
|
|
saveState = .failedToSave
|
|
return
|
|
}
|
|
saveState = .isSaved
|
|
checkContent()
|
|
}
|
|
|
|
private func checkContent() {
|
|
guard !self.content.isGeneratingWebsite else {
|
|
return
|
|
}
|
|
self.content.check(content: pageContent, of: page, for: language) {
|
|
self.generationResults = $0
|
|
}
|
|
}
|
|
}
|