diff --git a/CHDataManagement.xcodeproj/project.pbxproj b/CHDataManagement.xcodeproj/project.pbxproj index e661b76..8b6b021 100644 --- a/CHDataManagement.xcodeproj/project.pbxproj +++ b/CHDataManagement.xcodeproj/project.pbxproj @@ -189,13 +189,20 @@ E2FD1D322D3AEB6300B48627 /* PostVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D312D3AEB6000B48627 /* PostVideo.swift */; }; E2FD1D342D3BA2E700B48627 /* SelectedContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D332D3BA2DE00B48627 /* SelectedContent.swift */; }; E2FD1D372D3BBCCA00B48627 /* Insert+Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D362D3BBCB500B48627 /* Insert+Image.swift */; }; - E2FD1D392D3BBED300B48627 /* InsertableItemsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D382D3BBECA00B48627 /* InsertableItemsView.swift */; }; + E2FD1D392D3BBED300B48627 /* InsertableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D382D3BBECA00B48627 /* InsertableView.swift */; }; E2FD1D3B2D3BC40500B48627 /* InsertableCommandSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D3A2D3BC40500B48627 /* InsertableCommandSheet.swift */; }; E2FD1D3D2D463CD800B48627 /* ContentLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D3C2D463CD800B48627 /* ContentLabel.swift */; }; E2FD1D3F2D46405000B48627 /* PostLabelsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D3E2D46404900B48627 /* PostLabelsView.swift */; }; E2FD1D462D46428100B48627 /* PageIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D452D46427B00B48627 /* PageIconView.swift */; }; E2FD1D522D4644B400B48627 /* SVGView in Frameworks */ = {isa = PBXBuildFile; productRef = E2FD1D512D4644B400B48627 /* SVGView */; }; E2FD1D542D46577700B48627 /* HtmlProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D532D46577700B48627 /* HtmlProducer.swift */; }; + E2FD1D562D46CED900B48627 /* Insert+Labels.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D552D46CED500B48627 /* Insert+Labels.swift */; }; + E2FD1D582D477A9400B48627 /* InsertableCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D572D477A9400B48627 /* InsertableCommand.swift */; }; + E2FD1D5A2D477AB200B48627 /* InsertableItemsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D592D477AB200B48627 /* InsertableItemsView.swift */; }; + E2FD1D5C2D47EEB800B48627 /* LinkedPageTagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D5B2D47EEB800B48627 /* LinkedPageTagView.swift */; }; + E2FD1D5E2D47EED200B48627 /* PostImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D5D2D47EED200B48627 /* PostImageView.swift */; }; + E2FD1D602D47EEEF00B48627 /* LocalizedPostContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D5F2D47EEEF00B48627 /* LocalizedPostContentView.swift */; }; + E2FD1D642D47EF4200B48627 /* DetailListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D632D47EF4200B48627 /* DetailListItem.swift */; }; E2FE0EE62D15A0B5002963B7 /* GenerationResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EE52D15A0B1002963B7 /* GenerationResults.swift */; }; E2FE0EE82D16D4A3002963B7 /* ConvertThrowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EE72D16D4A3002963B7 /* ConvertThrowing.swift */; }; E2FE0EEC2D1C1253002963B7 /* MultiFileSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EEB2D1C124E002963B7 /* MultiFileSelectionView.swift */; }; @@ -431,12 +438,19 @@ E2FD1D312D3AEB6000B48627 /* PostVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostVideo.swift; sourceTree = ""; }; E2FD1D332D3BA2DE00B48627 /* SelectedContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedContent.swift; sourceTree = ""; }; E2FD1D362D3BBCB500B48627 /* Insert+Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Insert+Image.swift"; sourceTree = ""; }; - E2FD1D382D3BBECA00B48627 /* InsertableItemsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertableItemsView.swift; sourceTree = ""; }; + E2FD1D382D3BBECA00B48627 /* InsertableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertableView.swift; sourceTree = ""; }; E2FD1D3A2D3BC40500B48627 /* InsertableCommandSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertableCommandSheet.swift; sourceTree = ""; }; E2FD1D3C2D463CD800B48627 /* ContentLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentLabel.swift; sourceTree = ""; }; E2FD1D3E2D46404900B48627 /* PostLabelsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostLabelsView.swift; sourceTree = ""; }; E2FD1D452D46427B00B48627 /* PageIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageIconView.swift; sourceTree = ""; }; E2FD1D532D46577700B48627 /* HtmlProducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HtmlProducer.swift; sourceTree = ""; }; + E2FD1D552D46CED500B48627 /* Insert+Labels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Insert+Labels.swift"; sourceTree = ""; }; + E2FD1D572D477A9400B48627 /* InsertableCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertableCommand.swift; sourceTree = ""; }; + E2FD1D592D477AB200B48627 /* InsertableItemsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertableItemsView.swift; sourceTree = ""; }; + E2FD1D5B2D47EEB800B48627 /* LinkedPageTagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedPageTagView.swift; sourceTree = ""; }; + E2FD1D5D2D47EED200B48627 /* PostImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostImageView.swift; sourceTree = ""; }; + E2FD1D5F2D47EEEF00B48627 /* LocalizedPostContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPostContentView.swift; sourceTree = ""; }; + E2FD1D632D47EF4200B48627 /* DetailListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailListItem.swift; sourceTree = ""; }; E2FE0EE52D15A0B1002963B7 /* GenerationResults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerationResults.swift; sourceTree = ""; }; E2FE0EE72D16D4A3002963B7 /* ConvertThrowing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConvertThrowing.swift; sourceTree = ""; }; E2FE0EEB2D1C124E002963B7 /* MultiFileSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiFileSelectionView.swift; sourceTree = ""; }; @@ -818,6 +832,7 @@ E2B85F4B2C4B8B7F0047CD0C /* Posts */ = { isa = PBXGroup; children = ( + E2FD1D632D47EF4200B48627 /* DetailListItem.swift */, E2FD1D452D46427B00B48627 /* PageIconView.swift */, E2FD1D3E2D46404900B48627 /* PostLabelsView.swift */, E29D31502D0616890051B7F4 /* PostListView.swift */, @@ -828,6 +843,9 @@ E21850082CEE01BF0090B18B /* PagePickerView.swift */, E2A21C072CB17B810060935B /* TagView.swift */, E29D31312D03B5610051B7F4 /* LocalizedPostDetailView.swift */, + E2FD1D5D2D47EED200B48627 /* PostImageView.swift */, + E2FD1D5F2D47EEEF00B48627 /* LocalizedPostContentView.swift */, + E2FD1D5B2D47EEB800B48627 /* LinkedPageTagView.swift */, ); path = Posts; sourceTree = ""; @@ -911,9 +929,12 @@ E2FD1D352D3BBCAF00B48627 /* Commands */ = { isa = PBXGroup; children = ( + E2FD1D592D477AB200B48627 /* InsertableItemsView.swift */, + E2FD1D572D477A9400B48627 /* InsertableCommand.swift */, E2FD1D3A2D3BC40500B48627 /* InsertableCommandSheet.swift */, + E2FD1D382D3BBECA00B48627 /* InsertableView.swift */, E2FD1D362D3BBCB500B48627 /* Insert+Image.swift */, - E2FD1D382D3BBECA00B48627 /* InsertableItemsView.swift */, + E2FD1D552D46CED500B48627 /* Insert+Labels.swift */, ); path = Commands; sourceTree = ""; @@ -1111,6 +1132,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E2FD1D562D46CED900B48627 /* Insert+Labels.swift in Sources */, E29D31242D0366860051B7F4 /* TagList.swift in Sources */, E22990282D0F596C009F8D77 /* IntegerPropertyView.swift in Sources */, E2FD1D1D2D2DE31800B48627 /* ItemType.swift in Sources */, @@ -1140,6 +1162,7 @@ E229902E2D0F7280009F8D77 /* IdPropertyView.swift in Sources */, E2FE0F462D2BC777002963B7 /* MarkdownImageProcessor.swift in Sources */, E29D31AD2D0DA5360051B7F4 /* AudioPlayerIcons.swift in Sources */, + E2FD1D5A2D477AB200B48627 /* InsertableItemsView.swift in Sources */, E25DA5452D00952E00AEF16D /* SettingsSection.swift in Sources */, E2A21C462CBAE2E60060935B /* FeedEntryContent.swift in Sources */, E2FE0F682D2D2CF6002963B7 /* LocalizedPageSettings.swift in Sources */, @@ -1219,7 +1242,7 @@ E2FE0F422D2B4821002963B7 /* OtherCodeBlock.swift in Sources */, E21850332CFAFA2F0090B18B /* Settings.swift in Sources */, E29D31892D0AED1F0051B7F4 /* ModelViewer.swift in Sources */, - E2FD1D392D3BBED300B48627 /* InsertableItemsView.swift in Sources */, + E2FD1D392D3BBED300B48627 /* InsertableView.swift in Sources */, E29D31412D04887F0051B7F4 /* SelectedDetailView.swift in Sources */, E29D31A32D0CC98C0051B7F4 /* Item.swift in Sources */, E25DA57A2D01C64400AEF16D /* PageContentGenerator.swift in Sources */, @@ -1244,6 +1267,7 @@ E2FE0F2A2D2AFBE6002963B7 /* ImageCompareIcons.swift in Sources */, E29D316B2D07488B0051B7F4 /* PostListPageGenerator.swift in Sources */, E218501D2CEE6CB60090B18B /* VerticalCenter.swift in Sources */, + E2FD1D5C2D47EEB800B48627 /* LinkedPageTagView.swift in Sources */, E22990382D0F7B32009F8D77 /* OptionalImagePropertyView.swift in Sources */, E2FE0F512D2BCDC8002963B7 /* ModelCommand.swift in Sources */, E2FE0F592D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift in Sources */, @@ -1278,6 +1302,7 @@ E2FE0EFA2D25AFBA002963B7 /* PageHeader.swift in Sources */, E25DA5252CFF73AB00AEF16D /* NSSize+Scaling.swift in Sources */, E21850372CFCA55F0090B18B /* LocalizedPostSettings.swift in Sources */, + E2FD1D642D47EF4200B48627 /* DetailListItem.swift in Sources */, E2FE0F0B2D2689FF002963B7 /* FeedGeneratorSource.swift in Sources */, E2DD04742C276F31003BFF1F /* MainView.swift in Sources */, E29D31452D0488CB0051B7F4 /* SelectedContentView.swift in Sources */, @@ -1312,6 +1337,7 @@ E25DA56D2D00EBCF00AEF16D /* NavigationBarSettingsView.swift in Sources */, E25DA5272CFF745700AEF16D /* URL+Extensions.swift in Sources */, E2FE0F642D2C2F4D002963B7 /* ButtonBlock.swift in Sources */, + E2FD1D5E2D47EED200B48627 /* PostImageView.swift in Sources */, E25DA5092CFD964E00AEF16D /* TagContentView.swift in Sources */, E29D31342D03B5D50051B7F4 /* IconButton.swift in Sources */, E25DA5712D01015400AEF16D /* GenerationContentView.swift in Sources */, @@ -1327,7 +1353,9 @@ E25DA50D2CFD9BA200AEF16D /* PostTagAssignmentView.swift in Sources */, E2FD1D0D2D2DBBA600B48627 /* LinkPreview.swift in Sources */, E22990362D0F79D2009F8D77 /* OptionalStringPropertyView.swift in Sources */, + E2FD1D602D47EEEF00B48627 /* LocalizedPostContentView.swift in Sources */, E229903C2D0F8A7B009F8D77 /* OptionalTextFieldPropertyView.swift in Sources */, + E2FD1D582D477A9400B48627 /* InsertableCommand.swift in Sources */, E29D31222D0363FD0051B7F4 /* ContentButtons.swift in Sources */, E2FE0F222D2A84A0002963B7 /* VideoCommand.swift in Sources */, E2FE0F192D2723E3002963B7 /* ImageSet.swift in Sources */, diff --git a/CHDataManagement/Views/Pages/Commands/Insert+Image.swift b/CHDataManagement/Views/Pages/Commands/Insert+Image.swift index b329da7..8795dab 100644 --- a/CHDataManagement/Views/Pages/Commands/Insert+Image.swift +++ b/CHDataManagement/Views/Pages/Commands/Insert+Image.swift @@ -1,7 +1,34 @@ import SwiftUI import SFSafeSymbols -struct InsertableImage: View, InsertableCommand { +struct InsertableImage: View, InsertableCommandView { + + final class Model: ObservableObject, InsertableCommandModel { + + @Published + var caption: String? + + @Published + var selectedImage: FileResource? + + var isReady: Bool { + selectedImage != nil + } + + init() { + + } + + var command: String? { + guard let selectedImage else { + return nil + } + guard let caption else { + return "![image](\(selectedImage.id))" + } + return "![image](\(selectedImage.id);\(caption))" + } + } static let title = "Image" @@ -9,17 +36,11 @@ struct InsertableImage: View, InsertableCommand { static let icon: SFSymbol = .photo - @State - private var selectedImage: FileResource? + @ObservedObject + private var model: Model - @State - private var caption: String? = "" - - @Binding - private var command: String? - - init(command: Binding) { - self._command = command + init(model: Model) { + self.model = model } var body: some View { @@ -27,29 +48,13 @@ struct InsertableImage: View, InsertableCommand { FilePropertyView( title: "Image", footer: "Select the image to insert", - selectedFile: $selectedImage, + selectedFile: $model.selectedImage, allowedType: .image) OptionalStringPropertyView( title: "Caption", - text: $caption, + text: $model.caption, prompt: "Image Caption", footer: "The caption to show on the fullscreen image") } - .onChange(of: caption) { updateCommand() } - .onChange(of: selectedImage) { updateCommand() } - } - - func updateCommand() { - command = currentCommand - } - - private var currentCommand: String? { - guard let selectedImage else { - return nil - } - guard let caption else { - return "![image](\(selectedImage.id))" - } - return "![image](\(selectedImage.id);\(caption))" } } diff --git a/CHDataManagement/Views/Pages/Commands/Insert+Labels.swift b/CHDataManagement/Views/Pages/Commands/Insert+Labels.swift new file mode 100644 index 0000000..3126a45 --- /dev/null +++ b/CHDataManagement/Views/Pages/Commands/Insert+Labels.swift @@ -0,0 +1,81 @@ +import SwiftUI +import SFSafeSymbols + +struct InsertableLabels: View, InsertableCommandView { + + static let title = "Labels" + + static let sheetTitle = "Insert labels" + + static let icon: SFSymbol = .squaresBelowRectangle + + final class Model: InsertableCommandModel { + + @Published + var labels: [ContentLabel] = [] + + var isReady: Bool { + !labels.isEmpty + } + + init() { + + } + + var command: String? { + guard !labels.isEmpty else { + return nil + } + var result = "```labels" + for label in labels { + result += "\n\(label.icon.rawValue): \(label.value)" + } + result += "\n```" + return result + } + } + + @Environment(\.colorScheme) + private var colorScheme + + @ObservedObject + private var model: Model + + + + init(model: Model) { + self.model = model + } + + var body: some View { + VStack(spacing: 2) { + ForEach(model.labels, id: \.icon) { label in + HStack { + Button(action: { remove(label) }) { + Image(systemSymbol: .minusCircleFill) + .foregroundStyle(.red) + } + .buttonStyle(.plain) + LabelEditingView(label: label) + } + .padding(.vertical, 2) + .padding(.horizontal, 8) + .background(colorScheme == .light ? Color.white : Color.black) + .cornerRadius(8) + } + Button("Add", action: addLabel) + .padding(.vertical, 2) + } + } + + private func addLabel() { + model.labels.append(.init(icon: .clockFill, value: "Value")) + } + + private func remove(_ label: ContentLabel) { + guard let index = model.labels.firstIndex(of: label) else { + return + } + model.labels.remove(at: index) + } +} diff --git a/CHDataManagement/Views/Pages/Commands/InsertableCommand.swift b/CHDataManagement/Views/Pages/Commands/InsertableCommand.swift new file mode 100644 index 0000000..81b2ae1 --- /dev/null +++ b/CHDataManagement/Views/Pages/Commands/InsertableCommand.swift @@ -0,0 +1,25 @@ +import SwiftUI +import SFSafeSymbols + +protocol InsertableCommandView: View { + + associatedtype Model: InsertableCommandModel + + static var title: String { get } + + static var sheetTitle: String { get } + + static var icon: SFSymbol { get } + + init(model: Model) +} + + +protocol InsertableCommandModel: ObservableObject { + + var isReady: Bool { get } + + var command: String? { get } + + init() +} diff --git a/CHDataManagement/Views/Pages/Commands/InsertableCommandSheet.swift b/CHDataManagement/Views/Pages/Commands/InsertableCommandSheet.swift index 6e34a00..5114761 100644 --- a/CHDataManagement/Views/Pages/Commands/InsertableCommandSheet.swift +++ b/CHDataManagement/Views/Pages/Commands/InsertableCommandSheet.swift @@ -1,18 +1,7 @@ import SwiftUI import SFSafeSymbols -protocol InsertableCommand: View { - - static var title: String { get } - - static var sheetTitle: String { get } - - static var icon: SFSymbol { get } - - init(command: Binding) -} - -struct InsertableCommandSheet: View where Presented: InsertableCommand { +struct InsertableCommandSheet: View where Command: InsertableCommandView { @Environment(\.dismiss) var dismiss @@ -21,15 +10,19 @@ struct InsertableCommandSheet: View where Presented: InsertableComman private var error: String? = nil @State - private var command: String? + private var isReady = false - init() { } + let model: Command.Model + + init() { + model = .init() + } var body: some View { VStack { - Text(Presented.sheetTitle) + Text(Command.sheetTitle) .font(.title) - Presented(command: $command) + Command(model: model) if let error { Text(error) .foregroundStyle(.red) @@ -37,6 +30,7 @@ struct InsertableCommandSheet: View where Presented: InsertableComman HStack { Button("Copy to clipboard", action: copyToClipboard) .padding() + .disabled(!model.isReady) Button("Cancel") { dismiss() }.padding() @@ -46,7 +40,7 @@ struct InsertableCommandSheet: View where Presented: InsertableComman } func copyToClipboard() { - guard let command else { + guard let command = model.command else { error = "Not all fields set" return } diff --git a/CHDataManagement/Views/Pages/Commands/InsertableItemsView.swift b/CHDataManagement/Views/Pages/Commands/InsertableItemsView.swift index a70ef91..a603d82 100644 --- a/CHDataManagement/Views/Pages/Commands/InsertableItemsView.swift +++ b/CHDataManagement/Views/Pages/Commands/InsertableItemsView.swift @@ -1,21 +1,4 @@ import SwiftUI -import SFSafeSymbols - -private struct InsertableView: View where Command: InsertableCommand { - - @State - private var showSheet: Bool = false - - var body: some View { - Button(action: { showSheet = true }) { - Label(Command.title, systemSymbol: Command.icon) - } - .sheet(isPresented: $showSheet) { - InsertableCommandSheet() - } - } - -} struct InsertableItemsView: View { @@ -24,6 +7,7 @@ struct InsertableItemsView: View { Text("Commands") .font(.headline) InsertableView() + InsertableView() } } } diff --git a/CHDataManagement/Views/Pages/Commands/InsertableView.swift b/CHDataManagement/Views/Pages/Commands/InsertableView.swift new file mode 100644 index 0000000..4d7877b --- /dev/null +++ b/CHDataManagement/Views/Pages/Commands/InsertableView.swift @@ -0,0 +1,17 @@ +import SwiftUI +import SFSafeSymbols + +struct InsertableView: View where Command: InsertableCommandView { + + @State + private var showSheet: Bool = false + + var body: some View { + Button(action: { showSheet = true }) { + Label(Command.title, systemSymbol: Command.icon) + } + .sheet(isPresented: $showSheet) { + InsertableCommandSheet() + } + } +}