Unified detail views, model

This commit is contained in:
Christoph Hagen
2024-12-16 09:54:21 +01:00
parent 1e67a99866
commit 31d1ecb8bd
57 changed files with 853 additions and 954 deletions

View File

@ -74,8 +74,8 @@ struct AddPostView: View {
startDate: .now,
endDate: nil,
tags: [],
german: .init(title: "Titel", content: "Text"),
english: .init(title: "Title", content: "Text"))
german: .init(content: content, title: "Titel", text: "Text"),
english: .init(content: content, title: "Title", text: "Text"))
content.posts.insert(post, at: 0)
selectedPost = post
dismissSheet()

View File

@ -1,49 +0,0 @@
import SwiftUI
struct DatePickerView: View {
@ObservedObject
var post: Post
@Binding var showDatePicker: Bool
var body: some View {
NavigationView {
VStack {
HStack(alignment: .top) {
VStack {
Text("Start date")
.font(.headline)
.padding(.vertical, 3)
DatePicker("", selection: $post.startDate, displayedComponents: .date)
.datePickerStyle(GraphicalDatePickerStyle())
.labelsHidden()
.padding()
}
VStack {
Toggle("End date", isOn: $post.hasEndDate)
.toggleStyle(.switch)
.font(.headline)
DatePicker("Select a date", selection: $post.startDate, displayedComponents: .date)
.datePickerStyle(GraphicalDatePickerStyle())
.labelsHidden()
.padding()
.disabled(!post.hasEndDate)
}
}
Button("Done") {
showDatePicker = false
}
Spacer()
}
.navigationTitle("Pick a Date")
.padding()
}
}
}
#Preview {
DatePickerView(post: .mock, showDatePicker: .constant(true))
}

View File

@ -1,59 +0,0 @@
import SwiftUI
struct ImagePickerView: View {
@Binding
var showImagePicker: Bool
private let selected: (FileResource) -> Void
@EnvironmentObject
private var content: Content
@Environment(\.language)
private var language
init(showImagePicker: Binding<Bool>, selected: @escaping (FileResource) -> Void) {
self._showImagePicker = showImagePicker
self.selected = selected
}
@State
private var selectedImage: FileResource?
var body: some View {
VStack {
Text("Select the image to add")
List(content.images, selection: $selectedImage) { image in
Text("\(image.id)")
.tag(image)
}
.frame(minHeight: 300)
HStack {
Button("Add") {
DispatchQueue.main.async {
if let selectedImage {
print("Added image")
selected(selectedImage)
} else {
print("No image to add")
}
}
showImagePicker = false
}
.disabled(selectedImage == nil)
Button("Cancel", role: .cancel) {
showImagePicker = false
}
}
}
.navigationTitle("Pick an image")
.padding()
}
}
#Preview {
ImagePickerView(showImagePicker: .constant(true)) { _ in
}
.environmentObject(Content.mock)
}

View File

@ -3,64 +3,25 @@ import SwiftUI
struct LocalizedPostDetailView: View {
@ObservedObject
private var item: LocalizedPost
init(post: LocalizedPost, showImagePicker: Bool = false) {
self.item = post
self.showImagePicker = showImagePicker
}
@State
private var showImagePicker = false
var post: LocalizedPost
var body: some View {
VStack(alignment: .leading) {
Text("Link Preview Title")
.font(.headline)
OptionalTextField("", text: $item.linkPreviewTitle,
prompt: item.title)
.textFieldStyle(.roundedBorder)
.padding(.bottom)
OptionalStringPropertyView(
title: "Preview Title",
text: $post.linkPreviewTitle,
prompt: post.title,
footer: "The title to use for the post when linking to it")
HStack {
Text("Link Preview Image")
.font(.headline)
IconButton(symbol: .squareAndPencilCircleFill,
size: 22,
color: .blue) {
showImagePicker = true
}.padding(.bottom)
OptionalImagePropertyView(
title: "Preview Image",
selectedImage: $post.linkPreviewImage,
footer: "The image to show for previews of this post")
IconButton(symbol: .trashCircleFill,
size: 22,
color: .red) {
item.linkPreviewImage = nil
}.disabled(item.linkPreviewImage == nil)
Spacer()
}
.buttonStyle(.plain)
if let image = item.linkPreviewImage {
image.imageToDisplay
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 400, maxHeight: 300)
.cornerRadius(8)
Text(image.id)
.font(.headline)
}
Text("Link Preview Description")
.font(.headline)
.padding(.top)
OptionalDescriptionField(text: $item.linkPreviewDescription)
.textFieldStyle(.roundedBorder)
.padding(.bottom)
}
.sheet(isPresented: $showImagePicker) {
ImagePickerView(showImagePicker: $showImagePicker) { image in
item.linkPreviewImage = image
}
OptionalTextFieldPropertyView(
title: "Preview Description",
text: $post.linkPreviewDescription,
footer: "The description to show in previews of the post")
}
}
}

View File

@ -2,21 +2,21 @@ import SwiftUI
struct PagePickerView: View {
@Binding var showPagePicker: Bool
@Binding var selectedPage: Page?
@EnvironmentObject
private var content: Content
@Environment(\.language)
private var language
@Environment(\.dismiss)
var dismiss
@Binding var selectedPage: Page?
@State
private var newSelection: Page?
init(showPagePicker: Binding<Bool>, selectedPage: Binding<Page?>) {
self._showPagePicker = showPagePicker
init(selectedPage: Binding<Page?>) {
self._selectedPage = selectedPage
self.newSelection = selectedPage.wrappedValue
// TODO: Fix assignment not working
@ -35,17 +35,17 @@ struct PagePickerView: View {
Button("Use selection") {
DispatchQueue.main.async {
self.selectedPage = self.newSelection
dismiss()
}
showPagePicker = false
}
Button("Remove page", role: .destructive) {
DispatchQueue.main.async {
self.selectedPage = nil
dismiss()
}
showPagePicker = false
}
Button("Cancel", role: .cancel) {
showPagePicker = false
dismiss()
}
}
}
@ -55,7 +55,6 @@ struct PagePickerView: View {
}
#Preview {
PagePickerView(showPagePicker: .constant(true),
selectedPage: .constant(nil))
.environmentObject(Content.mock)
PagePickerView(selectedPage: .constant(nil))
.environmentObject(Content.mock)
}

View File

@ -56,7 +56,7 @@ private struct LocalizedContentEditor: View {
}
var body: some View {
TextEditor(text: $post.content)
TextEditor(text: $post.text)
.font(.body)
.frame(minHeight: 150)
.textEditorStyle(.plain)

View File

@ -36,109 +36,51 @@ struct PostDetailView: View {
@ObservedObject
private var post: Post
@State
private var newId: String
@State
private var showLinkedPagePicker = false
init(post: Post) {
self.post = post
self.newId = post.id
}
private let allowedCharactersInPostId = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "-")).inverted
private var idExists: Bool {
post.content.posts.contains { $0.id == newId }
}
private var containsInvalidCharacters: Bool {
newId.rangeOfCharacter(from: allowedCharactersInPostId) != nil
}
var body: some View {
ScrollView {
VStack(alignment: .leading) {
Text("ID")
.font(.headline)
HStack {
TextField("", text: $newId)
.textFieldStyle(.roundedBorder)
Button("Update", action: setNewId)
.disabled(newId.isEmpty || containsInvalidCharacters || idExists)
}
.padding(.bottom)
DetailTitle(
title: "Post",
text: "Posts capture quick updates and can link to pages")
HStack {
Text("Draft")
.font(.headline)
Spacer()
Toggle("", isOn: $post.isDraft)
.toggleStyle(.switch)
}
.padding(.bottom)
IdPropertyView(
id: $post.id,
footer: "The id is used to link to post and store them",
validation: post.isValid,
update: { post.update(id: $0) })
HStack {
Text("Start")
.font(.headline)
Spacer()
DatePicker("", selection: $post.startDate, displayedComponents: .date)
.datePickerStyle(.compact)
.padding(.bottom)
}
BoolPropertyView(
title: "Draft",
value: $post.isDraft,
footer: "Indicate a post as a draft to hide it from the website")
HStack(alignment: .firstTextBaseline) {
Text("Has end date")
.font(.headline)
Spacer()
Toggle("", isOn: $post.hasEndDate)
.toggleStyle(.switch)
.padding(.bottom)
}
DatePropertyView(
title: "Start date",
value: $post.startDate,
footer: "The date when the post content started")
if post.hasEndDate {
HStack(alignment: .firstTextBaseline) {
Text("End date")
.font(.headline)
Spacer()
DatePicker("", selection: $post.endDate, displayedComponents: .date)
.datePickerStyle(.compact)
.padding(.bottom)
}
}
HStack {
Text("Linked page")
.font(.headline)
IconButton(symbol: .squareAndPencilCircleFill,
size: 22,
color: .blue) {
showLinkedPagePicker = true
}
Spacer()
}
Text(post.linkedPage?.localized(in: language).title ?? "No page linked")
OptionalDatePropertyView(
title: "End date",
isEnabled: $post.hasEndDate,
date: $post.endDate,
footer: "The date when the post content ended")
PagePropertyView(
title: "Linked page",
selectedPage: $post.linkedPage,
footer: "The page to open when clicking on the post")
LocalizedPostDetailView(post: post.localized(in: language))
}
.padding()
}
.sheet(isPresented: $showLinkedPagePicker) {
PagePickerView(
showPagePicker: $showLinkedPagePicker,
selectedPage: $post.linkedPage)
}
}
private func setNewId() {
guard post.update(id: newId) else {
newId = post.id
return
}
post.id = newId
}
}

View File

@ -50,11 +50,6 @@ struct PostImagesView: View {
.padding()
}
}
.sheet(isPresented: $showImagePicker) {
ImagePickerView(showImagePicker: $showImagePicker) { image in
post.images.append(image)
}
}
}
private func shiftLeft(_ image: FileResource) {

View File

@ -1,60 +0,0 @@
import SwiftUI
struct TextEntrySheet: View {
let title: String
@Binding
var text: String
@Binding
var isValid: Bool
@Environment(\.dismiss)
private var dismiss: DismissAction
var body: some View {
VStack {
Text(title)
.foregroundStyle(.secondary)
TextField("Text", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.overlay {
if isValid {
EmptyView()
} else {
RoundedRectangle(cornerRadius: 8)
.strokeBorder(lineWidth: 3)
.foregroundStyle(.red)
}
}
.frame(maxWidth: 300)
HStack {
Button(action: submit) {
Text("Submit")
}
.disabled(!isValid)
Button(role: .cancel, action: cancel) {
Text("Cancel")
}
}
}
.padding()
}
private func submit() {
dismiss()
}
private func cancel() {
text = ""
dismiss()
}
}
#Preview {
TextEntrySheet(
title: "Enter the id for the new post",
text: .constant("new"),
isValid: .constant(false))
}