Add tag overview, improve assets
This commit is contained in:
@ -37,11 +37,11 @@ struct FileDetailView: View {
|
||||
}
|
||||
Text("German Description")
|
||||
.font(.headline)
|
||||
TextField("", text: $file.germanDescription)
|
||||
TextField("", text: $file.german)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("English Description")
|
||||
.font(.headline)
|
||||
TextField("", text: $file.englishDescription)
|
||||
TextField("", text: $file.english)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
if file.type.isImage {
|
||||
Text("Image size")
|
||||
|
@ -73,16 +73,30 @@ struct FileListView: View {
|
||||
guard oldValue != newValue else {
|
||||
return
|
||||
}
|
||||
if let selectedFile,
|
||||
newValue.matches(selectedFile.type) {
|
||||
let newFile = filteredFiles.first
|
||||
|
||||
guard let selectedFile else {
|
||||
if let newFile {
|
||||
DispatchQueue.main.async {
|
||||
selectedFile = newFile
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
selectedFile = filteredFiles.first
|
||||
|
||||
if newValue.matches(selectedFile.type) {
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.selectedFile = newFile
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if selectedFile == nil {
|
||||
selectedFile = content.files.first
|
||||
DispatchQueue.main.async {
|
||||
selectedFile = content.files.first
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,17 +10,32 @@ struct FileSelectionView: View {
|
||||
|
||||
init(selectedFile: Binding<FileResource?>) {
|
||||
self._selectedFile = selectedFile
|
||||
self.newSelection = selectedFile.wrappedValue
|
||||
}
|
||||
|
||||
@State
|
||||
private var newSelection: FileResource?
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
FileListView(selectedFile: $selectedFile)
|
||||
FileListView(selectedFile: $newSelection)
|
||||
.frame(minHeight: 500, idealHeight: 600)
|
||||
HStack {
|
||||
Button("Cancel") {
|
||||
selectedFile = nil
|
||||
dismiss() }
|
||||
Button("Select") { dismiss() }
|
||||
DispatchQueue.main.async {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
Button("Remove") {
|
||||
DispatchQueue.main.async {
|
||||
selectedFile = nil
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
Button("Select") {
|
||||
selectedFile = newSelection
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
19
CHDataManagement/Views/Generic/DetailTitle.swift
Normal file
19
CHDataManagement/Views/Generic/DetailTitle.swift
Normal file
@ -0,0 +1,19 @@
|
||||
import SwiftUI
|
||||
|
||||
struct DetailTitle: View {
|
||||
|
||||
let title: String
|
||||
|
||||
let text: String
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text(title)
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
Text(text)
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom, 30)
|
||||
}
|
||||
}
|
||||
}
|
34
CHDataManagement/Views/Generic/FilePropertyView.swift
Normal file
34
CHDataManagement/Views/Generic/FilePropertyView.swift
Normal file
@ -0,0 +1,34 @@
|
||||
import SwiftUI
|
||||
|
||||
struct FilePropertyView: View {
|
||||
|
||||
let title: String
|
||||
|
||||
let description: String
|
||||
|
||||
@Binding
|
||||
var selectedFile: FileResource?
|
||||
|
||||
@State
|
||||
private var showFileSelectionSheet = false
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text(title)
|
||||
.font(.headline)
|
||||
HStack {
|
||||
Text(selectedFile?.id ?? "No file selected")
|
||||
Spacer()
|
||||
Button("Select") {
|
||||
showFileSelectionSheet = true
|
||||
}
|
||||
}
|
||||
Text(description)
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
}
|
||||
.sheet(isPresented: $showFileSelectionSheet) {
|
||||
FileSelectionView(selectedFile: $selectedFile)
|
||||
}
|
||||
}
|
||||
}
|
23
CHDataManagement/Views/Generic/IntegerPropertyView.swift
Normal file
23
CHDataManagement/Views/Generic/IntegerPropertyView.swift
Normal file
@ -0,0 +1,23 @@
|
||||
import SwiftUI
|
||||
|
||||
struct IntegerPropertyView: View {
|
||||
|
||||
@Binding
|
||||
var value: Int
|
||||
|
||||
let title: String
|
||||
|
||||
let footer: String
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text(title)
|
||||
.font(.headline)
|
||||
IntegerField("", number: $value)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text(footer)
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
}
|
||||
}
|
||||
}
|
111
CHDataManagement/Views/ItemSelectionView.swift
Normal file
111
CHDataManagement/Views/ItemSelectionView.swift
Normal file
@ -0,0 +1,111 @@
|
||||
import SwiftUI
|
||||
import SFSafeSymbols
|
||||
|
||||
struct ItemSelectionView: View {
|
||||
|
||||
@Binding
|
||||
var isPresented: Bool
|
||||
|
||||
@Binding
|
||||
var selectedItems: [Item]
|
||||
|
||||
@Environment(\.language)
|
||||
private var language
|
||||
|
||||
@EnvironmentObject
|
||||
private var content: Content
|
||||
|
||||
init(isPresented: Binding<Bool>, selectedItems: Binding<[Item]>) {
|
||||
self._isPresented = isPresented
|
||||
self._selectedItems = selectedItems
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
List {
|
||||
Section("Selected") {
|
||||
ForEach(selectedItems) { item in
|
||||
HStack {
|
||||
Image(systemSymbol: .minusCircleFill)
|
||||
.foregroundStyle(.red)
|
||||
.onTapGesture {
|
||||
deselect(item: item)
|
||||
}
|
||||
Text(item.title(in: language))
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.onMove(perform: moveItem)
|
||||
}
|
||||
if let tagOverview = content.tagOverview {
|
||||
Section("Special Pages") {
|
||||
HStack {
|
||||
Image(systemSymbol: .plusCircleFill)
|
||||
.foregroundStyle(.green)
|
||||
Text("Tags Overview")
|
||||
Spacer()
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
if !selectedItems.contains(where: { $0 is TagOverviewPage }) {
|
||||
selectedItems.append(tagOverview)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Section("Tags") {
|
||||
ForEach(content.tags) { item in
|
||||
HStack {
|
||||
Image(systemSymbol: .plusCircleFill)
|
||||
.foregroundStyle(.green)
|
||||
Text(item.title(in: language))
|
||||
Spacer()
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
select(item: item)
|
||||
}
|
||||
}
|
||||
}
|
||||
Section("Pages") {
|
||||
ForEach(content.pages) { item in
|
||||
HStack {
|
||||
Image(systemSymbol: .plusCircleFill)
|
||||
.foregroundStyle(.green)
|
||||
Text(item.title(in: language))
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
select(item: item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Button("Dismiss", action: dismiss)
|
||||
}
|
||||
.frame(minHeight: 500)
|
||||
.padding()
|
||||
}
|
||||
|
||||
private func moveItem(from source: IndexSet, to destination: Int) {
|
||||
selectedItems.move(fromOffsets: source, toOffset: destination)
|
||||
}
|
||||
|
||||
private func deselect(item: Item) {
|
||||
guard let index = selectedItems.firstIndex(of: item) else {
|
||||
return
|
||||
}
|
||||
selectedItems.remove(at: index)
|
||||
}
|
||||
|
||||
private func select(item: Item) {
|
||||
if selectedItems.contains(item) {
|
||||
return
|
||||
}
|
||||
selectedItems.append(item)
|
||||
}
|
||||
|
||||
private func dismiss() {
|
||||
isPresented = false
|
||||
}
|
||||
}
|
@ -210,7 +210,7 @@ struct PageIssueView: View {
|
||||
}
|
||||
|
||||
private func createPage(pageId: String) {
|
||||
guard content.isValidIdForTagOrTagOrPost(pageId) else {
|
||||
guard content.isValidIdForTagOrPageOrPost(pageId) else {
|
||||
show(error: "Invalid page id, can't create page")
|
||||
return
|
||||
}
|
||||
@ -245,7 +245,7 @@ struct PageIssueView: View {
|
||||
}
|
||||
|
||||
private func createTag(tagId: String) {
|
||||
guard content.isValidIdForTagOrTagOrPost(tagId) else {
|
||||
guard content.isValidIdForTagOrPageOrPost(tagId) else {
|
||||
show(error: "Invalid tag id, can't create tag")
|
||||
return
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ struct GenerationContentView: View {
|
||||
|
||||
var body: some View {
|
||||
switch selectedSection {
|
||||
case .folders, .navigationBar, .postFeed:
|
||||
case .folders, .navigationBar, .postFeed, .tagOverview:
|
||||
generationView
|
||||
case .pages:
|
||||
PageSettingsContentView()
|
||||
|
@ -17,6 +17,8 @@ struct GenerationDetailView: View {
|
||||
PostFeedSettingsView()
|
||||
case .pages:
|
||||
PageSettingsDetailView()
|
||||
case .tagOverview:
|
||||
TagOverviewDetailView()
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
|
@ -1,4 +1,5 @@
|
||||
import SwiftUI
|
||||
import SFSafeSymbols
|
||||
|
||||
struct NavigationBarSettingsView: View {
|
||||
|
||||
@ -9,7 +10,7 @@ struct NavigationBarSettingsView: View {
|
||||
private var content: Content
|
||||
|
||||
@State
|
||||
private var showTagPicker = false
|
||||
private var showItemPicker = false
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
@ -21,14 +22,10 @@ struct NavigationBarSettingsView: View {
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom, 30)
|
||||
|
||||
Text("Visible Tags")
|
||||
.font(.headline)
|
||||
FlowHStack {
|
||||
ForEach(content.settings.navigationTags) { tag in
|
||||
TagView(text: tag.localized(in: language).name)
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
Button(action: { showTagPicker = true }) {
|
||||
HStack {
|
||||
Text("Links")
|
||||
.font(.headline)
|
||||
Button(action: { showItemPicker = true }) {
|
||||
Image(systemSymbol: .squareAndPencilCircleFill)
|
||||
.resizable()
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
@ -40,15 +37,19 @@ struct NavigationBarSettingsView: View {
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
ForEach(content.settings.navigationItems) { tag in
|
||||
TagView(text: tag.title(in: language))
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
Text("Select the tags to show in the navigation bar. The number should be even.")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showTagPicker) {
|
||||
TagSelectionView(
|
||||
presented: $showTagPicker,
|
||||
selected: $content.settings.navigationTags,
|
||||
tags: $content.tags)
|
||||
.sheet(isPresented: $showItemPicker) {
|
||||
ItemSelectionView(
|
||||
isPresented: $showItemPicker,
|
||||
selectedItems: $content.settings.navigationItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,51 +11,49 @@ struct PageSettingsDetailView: View {
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Page Settings")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
Text("Change the way pages are displayed")
|
||||
.padding(.bottom, 30)
|
||||
DetailTitle(
|
||||
title: "Page Settings",
|
||||
text: "Change the way pages are displayed")
|
||||
|
||||
Text("Content Width")
|
||||
.font(.headline)
|
||||
IntegerField("", number: $content.settings.pages.contentWidth)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The maximum width of the content in pages (in pixels)")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
IntegerPropertyView(
|
||||
value: $content.settings.pages.contentWidth,
|
||||
title: "Content Width",
|
||||
footer: "The maximum width of the content in pages (in pixels)")
|
||||
|
||||
Text("Fullscreen Image Width")
|
||||
.font(.headline)
|
||||
IntegerField("", number: $content.settings.pages.largeImageWidth)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The maximum width of images that are diplayed fullscreen")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
IntegerPropertyView(
|
||||
value: $content.settings.pages.largeImageWidth,
|
||||
title: "Fullscreen Image Width",
|
||||
footer: "The maximum width of images that are diplayed fullscreen")
|
||||
|
||||
Text("Page Link Image Width")
|
||||
.font(.headline)
|
||||
IntegerField("", number: $content.settings.pages.pageLinkImageSize)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The maximum width of images diplayed as thumbnails on page links")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
IntegerPropertyView(
|
||||
value: $content.settings.pages.pageLinkImageSize,
|
||||
title: "Page Link Image Width",
|
||||
footer: "The maximum width of images diplayed as thumbnails on page links")
|
||||
|
||||
Text("Page URL Prefix")
|
||||
.font(.headline)
|
||||
TextField("", text: $content.settings.pages.pageUrlPrefix)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The URL prefix used for the links to pages")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
FilePropertyView(
|
||||
title: "Default CSS File",
|
||||
description: "The CSS file containing the styling of all pages",
|
||||
selectedFile: $content.settings.pages.defaultCssFile)
|
||||
|
||||
Text("Javascript Files Path")
|
||||
.font(.headline)
|
||||
TextField("", text: $content.settings.pages.javascriptFilesPath)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The path to the javascript files in the output folder")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
FilePropertyView(
|
||||
title: "Code Highlighting File",
|
||||
description: "The JavaScript file to provide syntax highlighting of code blocks",
|
||||
selectedFile: $content.settings.pages.codeHighlightingJsFile)
|
||||
|
||||
FilePropertyView(
|
||||
title: "Audio Player CSS File",
|
||||
description: "The CSS file to provide the style for the audio player",
|
||||
selectedFile: $content.settings.pages.audioPlayerCssFile)
|
||||
|
||||
FilePropertyView(
|
||||
title: "Audio Player JavaScript File",
|
||||
description: "The CSS file to provide the functionality for the audio player",
|
||||
selectedFile: $content.settings.pages.audioPlayerJsFile)
|
||||
|
||||
FilePropertyView(
|
||||
title: "3D Model Viewer File",
|
||||
description: "The JavaScript file to provide the functionality for the 3D model viewer",
|
||||
selectedFile: $content.settings.pages.modelViewerJsFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +85,14 @@ struct PathSettingsView: View {
|
||||
Text("The path in the output folder where the generated videos are stored")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Assets output folder")
|
||||
.font(.headline)
|
||||
TextField("", text: $content.settings.paths.assetsOutputFolderPath)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The path in the output folder where assets are stored")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,32 +11,36 @@ struct PostFeedSettingsView: View {
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Post Feed Settings")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
Text("Change the way the posts are displayed")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom, 30)
|
||||
DetailTitle(title: "Post Feed Settings",
|
||||
text: "Change the way the posts are displayed")
|
||||
|
||||
Text("Content Width")
|
||||
.font(.headline)
|
||||
IntegerField("", number: $content.settings.posts.contentWidth)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.frame(maxWidth: 400)
|
||||
Text("The maximum width of the content the post feed (in pixels)")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
IntegerPropertyView(
|
||||
value: $content.settings.posts.contentWidth,
|
||||
title: "Content Width",
|
||||
footer: "The maximum width of the content the post feed (in pixels)")
|
||||
|
||||
Text("Posts Per Page")
|
||||
.font(.headline)
|
||||
IntegerField("", number: $content.settings.posts.postsPerPage)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.frame(maxWidth: 400)
|
||||
Text("The maximum number of posts displayed on a single page")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
IntegerPropertyView(
|
||||
value: $content.settings.posts.postsPerPage,
|
||||
title: "Posts Per Page",
|
||||
footer: "The maximum number of posts displayed on a single page")
|
||||
|
||||
LocalizedPostFeedSettingsView(settings: content.settings.localized(in: language))
|
||||
FilePropertyView(
|
||||
title: "Default CSS File",
|
||||
description: "The CSS file containing the styling of all post pages",
|
||||
selectedFile: $content.settings.posts.defaultCssFile)
|
||||
|
||||
FilePropertyView(
|
||||
title: "Swiper CSS File",
|
||||
description: "The CSS file containing the styling of image galleries in post feeds",
|
||||
selectedFile: $content.settings.posts.swiperCssFile)
|
||||
|
||||
FilePropertyView(
|
||||
title: "Swiper JavaScript File",
|
||||
description: "The JavaScript file to load the image gallery code in post feeds",
|
||||
selectedFile: $content.settings.posts.swiperJsFile)
|
||||
|
||||
LocalizedPostFeedSettingsView(
|
||||
settings: content.settings.localized(in: language))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,17 +12,19 @@ enum SettingsSection: String {
|
||||
|
||||
case pages = "Pages"
|
||||
|
||||
case tagOverview = "Tag Overview"
|
||||
|
||||
}
|
||||
|
||||
extension SettingsSection {
|
||||
|
||||
var icon: SFSymbol {
|
||||
switch self {
|
||||
//case .generation: return .arrowTriangle2Circlepath
|
||||
case .folders: return .folder
|
||||
case .navigationBar: return .menubarRectangle
|
||||
case .postFeed: return .rectangleGrid1x2
|
||||
case .pages: return .docRichtext
|
||||
case .tagOverview: return .tag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
130
CHDataManagement/Views/Settings/TagOverviewDetailView.swift
Normal file
130
CHDataManagement/Views/Settings/TagOverviewDetailView.swift
Normal file
@ -0,0 +1,130 @@
|
||||
import SwiftUI
|
||||
|
||||
struct TagOverviewDetailView: View {
|
||||
|
||||
@Environment(\.language)
|
||||
private var language
|
||||
|
||||
@EnvironmentObject
|
||||
private var content: Content
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Tag Overview")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
Text("Configure the page showing all tags")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom, 30)
|
||||
|
||||
if let page = content.tagOverview?.localized(in: language) {
|
||||
TagOverviewDetails(page: page)
|
||||
} else {
|
||||
Button("Create", action: createTagOverviewPage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func createTagOverviewPage() {
|
||||
content.tagOverview = TagOverviewPage(
|
||||
content: content,
|
||||
german: .init(title: "Alle Tags", urlString: "alle"),
|
||||
english: .init(title: "All tags", urlString: "all"))
|
||||
}
|
||||
}
|
||||
|
||||
private struct TagOverviewDetails: View {
|
||||
|
||||
@ObservedObject
|
||||
var page: LocalizedTagOverviewPage
|
||||
|
||||
@EnvironmentObject
|
||||
var content: Content
|
||||
|
||||
@State
|
||||
private var showImagePicker = false
|
||||
|
||||
@State
|
||||
private var newUrlString: String = ""
|
||||
|
||||
init(page: LocalizedTagOverviewPage) {
|
||||
self.page = page
|
||||
}
|
||||
|
||||
private var newUrlCanBeUpdated: Bool {
|
||||
guard !newUrlString.isEmpty else { return false }
|
||||
guard content.isValidIdForTagOrPageOrPost(newUrlString) else { return false }
|
||||
return !content.containsTag(withUrlComponent: newUrlString)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Title")
|
||||
.font(.headline)
|
||||
TextField("", text: $page.title)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
|
||||
HStack {
|
||||
Text("Page URL String")
|
||||
.font(.headline)
|
||||
TextField("", text: $newUrlString)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Button("Update", action: setNewId)
|
||||
.disabled(!newUrlCanBeUpdated)
|
||||
}
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Link Preview Title")
|
||||
.font(.headline)
|
||||
OptionalTextField("", text: $page.linkPreviewTitle,
|
||||
prompt: page.title)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.padding(.bottom)
|
||||
|
||||
HStack {
|
||||
Text("Link Preview Image")
|
||||
.font(.headline)
|
||||
IconButton(symbol: .squareAndPencilCircleFill,
|
||||
size: 22,
|
||||
color: .blue) {
|
||||
showImagePicker = true
|
||||
}
|
||||
|
||||
IconButton(symbol: .trashCircleFill,
|
||||
size: 22,
|
||||
color: .red) {
|
||||
page.linkPreviewImage = nil
|
||||
}.disabled(page.linkPreviewImage == nil)
|
||||
}
|
||||
|
||||
.buttonStyle(.plain)
|
||||
if let image = page.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: $page.linkPreviewDescription)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.padding(.bottom)
|
||||
}
|
||||
.sheet(isPresented: $showImagePicker) {
|
||||
ImagePickerView(showImagePicker: $showImagePicker) { image in
|
||||
page.linkPreviewImage = image
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setNewId() {
|
||||
page.urlString = newUrlString
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ struct AddTagView: View {
|
||||
private func addNewTag() {
|
||||
let newTag = Tag(
|
||||
content: content,
|
||||
id: "tag",
|
||||
isVisible: true,
|
||||
german: .init(urlComponent: "tag", name: "Neuer Tag"),
|
||||
english: .init(urlComponent: "tag-en", name: "New Tag"))
|
||||
|
Reference in New Issue
Block a user