281 lines
9.0 KiB
Swift
281 lines
9.0 KiB
Swift
import SwiftUI
|
|
import SFSafeSymbols
|
|
|
|
@main
|
|
struct MainView: App {
|
|
|
|
private let sidebarWidth: CGFloat = 250
|
|
|
|
private let detailWidth: CGFloat = 300
|
|
|
|
@StateObject
|
|
private var server: WebServer = .init(port: 8000)
|
|
|
|
@StateObject
|
|
private var content: Content = .init()
|
|
|
|
@StateObject
|
|
private var upload: RemotePush = .init()
|
|
|
|
@StateObject
|
|
private var notifications: NotificationSender = .init()
|
|
|
|
@State
|
|
private var language: ContentLanguage = .english
|
|
|
|
@StateObject
|
|
private var selection: SelectedContent = .init()
|
|
|
|
@State
|
|
private var showAddSheet = false
|
|
|
|
@State
|
|
private var showInitialSetupSheet = false
|
|
|
|
@State
|
|
private var showStorageErrorSheet = false
|
|
|
|
@State
|
|
private var showSettingsSheet = false
|
|
|
|
@State
|
|
private var showGenerationSheet = false
|
|
|
|
@State
|
|
private var showPreviewSheet = false
|
|
|
|
@State
|
|
private var showUploadSheet = false
|
|
|
|
@State
|
|
private var showNotificationsSheet = false
|
|
|
|
@ViewBuilder
|
|
var sidebar: some View {
|
|
switch selection.tab {
|
|
case .posts: PostListView()
|
|
case .pages: PageListView()
|
|
case .tags: TagListView()
|
|
case .files: FileListView(selectedFile: $selection.file)
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
var viewContent: some View {
|
|
switch selection.tab {
|
|
case .posts:
|
|
SelectedContentView<PostContentView>(selected: $selection.post)
|
|
case .pages:
|
|
SelectedContentView<PageContentView>(selected: $selection.page)
|
|
case .tags:
|
|
SelectedContentView<TagContentView>(selected: $selection.tag)
|
|
case .files:
|
|
SelectedContentView<FileContentView>(selected: $selection.file)
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
var detail: some View {
|
|
switch selection.tab {
|
|
case .posts:
|
|
SelectedDetailView<PostDetailView>(selected: $selection.post)
|
|
case .pages:
|
|
SelectedDetailView<PageDetailView>(selected: $selection.page)
|
|
case .tags:
|
|
SelectedDetailView<TagDetailView>(selected: $selection.tag)
|
|
case .files:
|
|
SelectedDetailView<FileDetailView>(selected: $selection.file)
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
var addItemSheet: some View {
|
|
switch selection.tab {
|
|
case .posts:
|
|
AddPostView(selected: $selection.post)
|
|
case .pages:
|
|
AddPageView(selected: $selection.page)
|
|
case .tags:
|
|
AddTagView(selected: $selection.tag)
|
|
case .files:
|
|
AddFileView(selectedFile: $selection.file)
|
|
}
|
|
}
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
NavigationSplitView {
|
|
sidebar
|
|
.navigationSplitViewColumnWidth(min: sidebarWidth, ideal: sidebarWidth, max: sidebarWidth)
|
|
.toolbar {
|
|
ToolbarItem(placement: .automatic) {
|
|
Button(action: { showAddSheet = true }) {
|
|
Label("Add", systemSymbol: .plus)
|
|
}
|
|
}
|
|
}
|
|
} content: {
|
|
viewContent
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigation) {
|
|
HStack {
|
|
Picker("", selection: $selection.tab) {
|
|
Text("Posts").tag(MainViewTab.posts)
|
|
Text("Pages").tag(MainViewTab.pages)
|
|
Text("Tags").tag(MainViewTab.tags)
|
|
Text("Files").tag(MainViewTab.files)
|
|
}.pickerStyle(.segmented)
|
|
}.frame(minWidth: 400)
|
|
}
|
|
}
|
|
} detail: {
|
|
detail
|
|
.navigationSplitViewColumnWidth(min: detailWidth, ideal: detailWidth, max: detailWidth)
|
|
}
|
|
.toolbar {
|
|
ToolbarItem {
|
|
Picker("", selection: $language) {
|
|
Text("English")
|
|
.tag(ContentLanguage.english)
|
|
Text("German")
|
|
.tag(ContentLanguage.german)
|
|
}
|
|
.pickerStyle(.segmented)
|
|
.frame(minWidth: 200)
|
|
}
|
|
ToolbarItem {
|
|
Button(action: { showSettingsSheet = true }) {
|
|
Image(systemSymbol: .gearshape)
|
|
}
|
|
}
|
|
ToolbarItem {
|
|
Button(action: { showGenerationSheet = true }) {
|
|
if content.isGeneratingWebsite {
|
|
ProgressView()
|
|
.scaleEffect(0.6)
|
|
.frame(width: 20, height: 20, alignment: .center)
|
|
} else {
|
|
Image(systemSymbol: .globe)
|
|
.frame(width: 20, height: 20, alignment: .center)
|
|
}
|
|
}
|
|
}
|
|
ToolbarItem {
|
|
Button(action: { showPreviewSheet = true }) {
|
|
Image(systemSymbol: .eye)
|
|
}
|
|
}
|
|
ToolbarItem {
|
|
Button(action: { showUploadSheet = true }) {
|
|
Image(systemSymbol: .squareAndArrowUp)
|
|
}
|
|
}
|
|
ToolbarItem {
|
|
Button(action: { showNotificationsSheet = true }) {
|
|
Image(systemSymbol: .bell)
|
|
}
|
|
}
|
|
ToolbarItem {
|
|
Button(action: saveButtonPressed) {
|
|
Image(systemSymbol: content.saveState.symbol)
|
|
.foregroundStyle(content.saveState.color)
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("")
|
|
.environment(\.language, language)
|
|
.environmentObject(content)
|
|
.environmentObject(selection)
|
|
.onAppear(perform: loadContent)
|
|
.sheet(isPresented: $showAddSheet) {
|
|
addItemSheet
|
|
.environment(\.language, language)
|
|
.environmentObject(content)
|
|
.environmentObject(selection)
|
|
}
|
|
.sheet(isPresented: $showInitialSetupSheet) {
|
|
InitialSetupView()
|
|
.environment(\.language, language)
|
|
.environmentObject(content)
|
|
.environmentObject(selection)
|
|
}
|
|
.sheet(isPresented: $showStorageErrorSheet) {
|
|
StorageErrorView(isPresented: $showStorageErrorSheet)
|
|
.environmentObject(content)
|
|
}
|
|
.sheet(isPresented: $showSettingsSheet) {
|
|
SettingsSheet(language: $language)
|
|
.environmentObject(content)
|
|
.presentedWindowStyle(.titleBar)
|
|
}
|
|
.sheet(isPresented: $showGenerationSheet) {
|
|
GenerationContentView()
|
|
.environmentObject(content)
|
|
}
|
|
.sheet(isPresented: $showPreviewSheet) {
|
|
WebsitePreviewSheet()
|
|
.environmentObject(content)
|
|
.environmentObject(server)
|
|
}
|
|
.sheet(isPresented: $showUploadSheet) {
|
|
UploadSheet()
|
|
.environmentObject(content)
|
|
.environmentObject(upload)
|
|
}
|
|
.sheet(isPresented: $showNotificationsSheet) {
|
|
NotificationSheet()
|
|
.environmentObject(content)
|
|
.environmentObject(notifications)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func saveButtonPressed() {
|
|
switch content.saveState {
|
|
case .storageNotInitialized:
|
|
showInitialSheet()
|
|
case .isSaved, .needsSave:
|
|
content.saveUnconditionally()
|
|
case .isSaving:
|
|
break
|
|
case .failedToSave, .savingPausedDueToLoadErrors:
|
|
showStorageErrorSheet = true
|
|
}
|
|
}
|
|
|
|
private func loadContent() {
|
|
guard content.storage.contentScope != nil else {
|
|
showInitialSheet()
|
|
return
|
|
}
|
|
content.loadFromDisk {
|
|
prepareAfterLoad()
|
|
if !content.storageErrors.isEmpty {
|
|
self.showStorageErrorSheet = true
|
|
}
|
|
}
|
|
}
|
|
|
|
private func prepareAfterLoad() {
|
|
if selection.post == nil {
|
|
selection.post = content.posts.first
|
|
}
|
|
if selection.page == nil {
|
|
selection.page = content.pages.first
|
|
}
|
|
if selection.tag == nil {
|
|
selection.tag = content.tags.first
|
|
}
|
|
if selection.file == nil {
|
|
selection.file = content.files.first
|
|
}
|
|
}
|
|
|
|
private func showInitialSheet() {
|
|
DispatchQueue.main.async {
|
|
showInitialSetupSheet = true
|
|
}
|
|
}
|
|
}
|
|
|