Remove images, select tags
This commit is contained in:
@ -30,14 +30,25 @@ struct PostImageGalleryView: View {
|
||||
@State
|
||||
private var showImagePicker = false
|
||||
|
||||
private var imageAtCurrentIndex: Image? {
|
||||
guard !post.images.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
guard currentIndex < post.images.count else {
|
||||
return post.images.last?.imageToDisplay
|
||||
}
|
||||
return post.images[currentIndex].imageToDisplay
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .center) {
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
ZStack(alignment: .bottom) {
|
||||
post.images[currentIndex]
|
||||
.imageToDisplay
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
if let imageAtCurrentIndex {
|
||||
imageAtCurrentIndex
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
}
|
||||
if post.images.count > 1 {
|
||||
HStack(spacing: 8) {
|
||||
ForEach(0..<post.images.count, id: \.self) { index in
|
||||
@ -60,6 +71,9 @@ struct PostImageGalleryView: View {
|
||||
Button(action: { showImagePicker = true }) {
|
||||
NavigationIcon(symbol: .plus, edge: .all)
|
||||
}
|
||||
Button(action: removeImage) {
|
||||
NavigationIcon(symbol: .trash, edge: .all)
|
||||
}
|
||||
}.padding()
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
@ -120,6 +134,13 @@ struct PostImageGalleryView: View {
|
||||
post.images.swapAt(currentIndex, currentIndex+1)
|
||||
currentIndex += 1
|
||||
}
|
||||
|
||||
private func removeImage() {
|
||||
post.images.remove(at: currentIndex)
|
||||
if currentIndex >= post.images.count {
|
||||
currentIndex = post.images.count - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview(traits: .fixedLayout(width: 300, height: 250)) {
|
||||
|
@ -1,22 +1,5 @@
|
||||
import SwiftUI
|
||||
|
||||
|
||||
private struct CenteredPost<Content>: View where Content: View {
|
||||
|
||||
let content: Content
|
||||
|
||||
init(@ViewBuilder content: () -> Content) {
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HorizontalCenter {
|
||||
content
|
||||
}
|
||||
.listRowBackground(ColorPalette.listBackground)
|
||||
}
|
||||
}
|
||||
|
||||
struct PostList: View {
|
||||
|
||||
@EnvironmentObject
|
||||
@ -25,13 +8,13 @@ struct PostList: View {
|
||||
var body: some View {
|
||||
List {
|
||||
if content.posts.isEmpty {
|
||||
CenteredPost {
|
||||
HorizontalCenter {
|
||||
Text("No posts yet.")
|
||||
.padding()
|
||||
}
|
||||
.listRowSeparator(.hidden)
|
||||
}
|
||||
CenteredPost {
|
||||
HorizontalCenter {
|
||||
Button(action: addNewPost) {
|
||||
Text("Add post")
|
||||
}
|
||||
@ -39,7 +22,7 @@ struct PostList: View {
|
||||
.listRowSeparator(.hidden)
|
||||
}
|
||||
ForEach(content.posts) { post in
|
||||
CenteredPost {
|
||||
HorizontalCenter {
|
||||
PostView(post: post)
|
||||
.frame(maxWidth: 600)
|
||||
}
|
||||
@ -48,8 +31,7 @@ struct PostList: View {
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.background(ColorPalette.listBackground)
|
||||
.scrollContentBackground(.hidden)
|
||||
//.scrollContentBackground(.hidden)
|
||||
}
|
||||
|
||||
private func addNewPost() {
|
||||
|
@ -1,13 +1,28 @@
|
||||
import SwiftUI
|
||||
import SFSafeSymbols
|
||||
|
||||
struct PostView: View {
|
||||
|
||||
@ObservedObject
|
||||
var post: Post
|
||||
|
||||
@Environment(\.language)
|
||||
var language
|
||||
private var language
|
||||
|
||||
var body: some View {
|
||||
LocalizedPostView(post: post, localized: post.localized(in: language))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct LocalizedPostView: View {
|
||||
|
||||
@ObservedObject
|
||||
var post: Post
|
||||
|
||||
@ObservedObject
|
||||
var localized: LocalizedPost
|
||||
|
||||
@State
|
||||
private var showDatePicker = false
|
||||
|
||||
@ -20,6 +35,12 @@ struct PostView: View {
|
||||
@State
|
||||
private var showTagPicker = false
|
||||
|
||||
@Environment(\.language)
|
||||
private var language
|
||||
|
||||
@EnvironmentObject
|
||||
private var content: Content
|
||||
|
||||
private var linkedPageText: String {
|
||||
if let page = post.linkedPage {
|
||||
return page.localized(in: language).title
|
||||
@ -29,7 +50,7 @@ struct PostView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .center) {
|
||||
if post.localized(in: language).images.isEmpty {
|
||||
if localized.images.isEmpty {
|
||||
Button(action: { showImagePicker = true }) {
|
||||
Text("Add image")
|
||||
}
|
||||
@ -37,7 +58,7 @@ struct PostView: View {
|
||||
.foregroundStyle(.blue)
|
||||
.padding(.top)
|
||||
} else {
|
||||
PostImageGalleryView(post: post.localized(in: language))
|
||||
PostImageGalleryView(post: localized)
|
||||
.aspectRatio(1.33, contentMode: .fill)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
@ -49,7 +70,7 @@ struct PostView: View {
|
||||
Toggle("Draft", isOn: $post.isDraft)
|
||||
}
|
||||
.foregroundStyle(.secondary)
|
||||
TextField("", text: post.localized(in: language).editableTitle())
|
||||
TextField("", text: $localized.title)
|
||||
.font(.system(size: 24, weight: .bold))
|
||||
.foregroundStyle(Color.primary)
|
||||
.textFieldStyle(.plain)
|
||||
@ -60,22 +81,21 @@ struct PostView: View {
|
||||
en: tag.english.name,
|
||||
de: tag.german.name)
|
||||
)
|
||||
.onTapGesture {
|
||||
remove(tag: tag)
|
||||
}
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
Button(action: { showTagPicker = true }) {
|
||||
SwiftUI.Image(systemSymbol: .plusCircleFill)
|
||||
Image(systemSymbol: .squareAndPencilCircleFill)
|
||||
.resizable()
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.frame(height: 18)
|
||||
.frame(height: 22)
|
||||
.foregroundColor(Color.blue)
|
||||
//.opacity(0.7)
|
||||
.padding(.top, 3)
|
||||
.background(Circle()
|
||||
.fill(Color.white)
|
||||
.padding(1))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
TextEditor(text: post.localized(in: language).editableContent())
|
||||
TextEditor(text: $localized.content)
|
||||
.font(.body)
|
||||
.foregroundStyle(.secondary)
|
||||
.textEditorStyle(.plain)
|
||||
@ -91,7 +111,7 @@ struct PostView: View {
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.background(Color.secondary.colorInvert())
|
||||
.background(Color(NSColor.windowBackgroundColor))
|
||||
.cornerRadius(8)
|
||||
.sheet(isPresented: $showDatePicker) {
|
||||
DatePickerView(
|
||||
@ -106,8 +126,13 @@ struct PostView: View {
|
||||
.sheet(isPresented: $showImagePicker) {
|
||||
ImagePickerView(
|
||||
showImagePicker: $showImagePicker,
|
||||
post: post.localized(in: language)
|
||||
)
|
||||
post: localized)
|
||||
}
|
||||
.sheet(isPresented: $showTagPicker) {
|
||||
TagSelectionView(
|
||||
presented: $showTagPicker,
|
||||
selected: $post.tags,
|
||||
tags: $content.tags)
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,11 +145,10 @@ struct PostView: View {
|
||||
List {
|
||||
PostView(post: .fullMock)
|
||||
.listRowSeparator(.hidden)
|
||||
//.listRowBackground(ColorPalette.listBackground)
|
||||
.environment(\.language, ContentLanguage.german)
|
||||
PostView(post: .mock)
|
||||
.listRowSeparator(.hidden)
|
||||
//.listRowBackground(ColorPalette.listBackground)
|
||||
}
|
||||
.environmentObject(Content.mock)
|
||||
//.listStyle(.plain)
|
||||
}
|
||||
|
95
CHDataManagement/Views/Posts/TagSelectionView.swift
Normal file
95
CHDataManagement/Views/Posts/TagSelectionView.swift
Normal file
@ -0,0 +1,95 @@
|
||||
import SwiftUI
|
||||
import SFSafeSymbols
|
||||
|
||||
struct TagSelectionView: View {
|
||||
|
||||
@Binding
|
||||
var presented: Bool
|
||||
|
||||
@Binding
|
||||
private var selected: [Tag]
|
||||
|
||||
@Binding
|
||||
private var tags: [Tag]
|
||||
|
||||
@Environment(\.language)
|
||||
private var language
|
||||
|
||||
init(presented: Binding<Bool>, selected: Binding<[Tag]>, tags: Binding<[Tag]>) {
|
||||
self._presented = presented
|
||||
self._selected = selected
|
||||
self._tags = tags
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
List {
|
||||
Section("Selected tags") {
|
||||
ForEach(selected) { tag in
|
||||
HStack {
|
||||
Image(systemSymbol: .minusCircleFill)
|
||||
.foregroundStyle(.red)
|
||||
.onTapGesture {
|
||||
deselect(tag: tag)
|
||||
}
|
||||
Text(tag.localized(in: language).name)
|
||||
}
|
||||
}
|
||||
.onMove(perform: moveTag)
|
||||
}
|
||||
Section("Available tags") {
|
||||
ForEach(tags.filter({ !selected.contains($0) })) { tag in
|
||||
HStack {
|
||||
Image(systemSymbol: .plusCircleFill)
|
||||
.foregroundStyle(.green)
|
||||
Text(tag.localized(in: language).name)
|
||||
Spacer()
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
select(tag: tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(minHeight: 300)
|
||||
.padding()
|
||||
Button(action: dismiss) {
|
||||
Text("Save")
|
||||
}.padding()
|
||||
}
|
||||
}
|
||||
|
||||
private func moveTag(from source: IndexSet, to destination: Int) {
|
||||
selected.move(fromOffsets: source, toOffset: destination)
|
||||
}
|
||||
|
||||
private func deselect(tag: Tag) {
|
||||
guard let index = selected.firstIndex(of: tag) else {
|
||||
return
|
||||
}
|
||||
selected.remove(at: index)
|
||||
|
||||
let insertIndex = tags.firstIndex(where: { $0 > tag }) ?? tags.endIndex
|
||||
tags.insert(tag, at: insertIndex)
|
||||
}
|
||||
|
||||
private func select(tag: Tag) {
|
||||
guard let index = tags.firstIndex(of: tag) else {
|
||||
return
|
||||
}
|
||||
tags.remove(at: index)
|
||||
selected.append(tag)
|
||||
}
|
||||
|
||||
private func dismiss() {
|
||||
presented = false
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
TagSelectionView(
|
||||
presented: .constant(true),
|
||||
selected: .constant([.hiking, .nature]),
|
||||
tags: .constant([.sports, .mock]))
|
||||
}
|
Reference in New Issue
Block a user