// // ContentView.swift // gloss // // Created by Saint on 10/23/22. // import SwiftUI import GRDB import GRDBQuery import Introspect import os import SwiftUIReorderableForEach let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "network") var currentId : String? var currentOffset : CGFloat? var gTracker: VisibilityTracker? var printCount: Int64 = 0 var disableDrop = false // var curBook = "John" extension UserDefaults { public func optionalInt(forKey defaultName: String) -> Int? { let defaults = self if let value = defaults.value(forKey: defaultName) { return value as? Int } return nil } public func optionalBool(forKey defaultName: String) -> Bool? { let defaults = self if let value = defaults.value(forKey: defaultName) { return value as? Bool } return nil } } func goToRibbon(selectedRibbon: Ribbon, destRibbon: Ribbon, scrollId: Binding, scrollOffset: Binding, refresh: Binding, showOverlay: Binding, appDatabase: AppDatabase, loading: Bool ) { Task { // print("SELECTED RIBBON", selectedRibbon) var scrollOffsetToSave = currentOffset var scrollIdToSave = currentId var updatedRibbon = selectedRibbon if (selectedRibbon.id != destRibbon.id! || loading) { print("switching ribbons") // withAnimation(.spring(response: 0.05)) { showOverlay.wrappedValue = true // } if (loading) { currentId = destRibbon.scrollId // currentOffset = CGFloat(destRibbon.scrollOffset) } scrollId.wrappedValue = destRibbon.scrollId // print("setting scroll offset") scrollOffset.wrappedValue = CGFloat(destRibbon.scrollOffset) // print(scrollOffset.wrappedValue) // print("end setting scroll offset") refresh.wrappedValue.toggle() var updateSelectRibbon = SelectedRibbon(id: Int64(1), ribbonId: destRibbon.id!) // print("Saving selected ribbon") // print(updateSelectRibbon) do { _ = try await appDatabase.saveSelectedRibbon(&updateSelectRibbon) } catch { // Print("something wrong") } } if (!loading) { print("not loading") updatedRibbon.scrollOffset = Int(floor(scrollOffsetToSave!)) updatedRibbon.scrollId = scrollIdToSave! _ = try await appDatabase.saveRibbon(&updatedRibbon) // print("saved updatedRibbon", updatedRibbon) // print("UPDATED") // scrollOffsetToSave = userDefaults.object(forKey: "currentOffset") as? CGFloat // scrollIdToSave = userDefaults.object(forKey: "currentId") as? String } else { print("loading") } // print("scrollOffsetToSave: ", scrollOffsetToSave) // print("scrollIdToSave: ", scrollIdToSave) } } extension View { @ViewBuilder func `if`(_ condition: Bool, transform: (Self) -> Transform) -> some View { if condition { transform(self) } else { self } } } struct RibbonCrown : View { var ribbon: Ribbon @Binding var scrollId : String? @Binding var scrollOffset : CGFloat? @Binding var showOverlay : Bool @Binding var refresh : Bool var draggedRibbon : Ribbon? var isDragging : Bool var height = CGFloat(45) var xOffset = CGFloat(25) var scale = 0.65 @Environment(\.appDatabase) private var appDatabase @Query(SelectedRibbonRequest()) private var sr: [Ribbon] @State var saveOffset = CGFloat() var body: some View { ZStack { MyIcon().frame( width: CGFloat(100 * 1.66 * scale), height: CGFloat(100 * scale), alignment: .center ) .foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4))) .if(draggedRibbon != nil && draggedRibbon!.id == ribbon.id && isDragging) { $0.overlay(Color(red: 0.1, green: 0.1, blue: 0.1)) } // .offset(x: 10) Text(ribbon.title) .foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00))) // .foregroundColor(.white)) // .foregroundColor(.black) .frame(minWidth: CGFloat(70), maxWidth: CGFloat(70), minHeight: height, maxHeight: height, alignment: .center) .if(draggedRibbon != nil && draggedRibbon!.id == ribbon.id && isDragging) { $0.overlay(Color(red: 0.1, green: 0.1, blue: 0.1)) } // .if(!isDragging || draggedRibbon == nil || draggedRibbon!.id != ribbon.id) { $0.background(Color(red: 0.1, green: 0.1, blue: 0.1)) } // .if(draggedRibbon != nil && draggedRibbon!.id == ribbon.id) { $0.background(.red) } .background(Color(red: 0.1, green: 0.1, blue: 0.1)) // .offset(x: 10) // .background(Color(red: 0.1, green: 0.1, blue: 0.1)) // .background(.red) // .background(.yellow) .multilineTextAlignment(.center) // .minimumScaleFactor(0.5) // .padding([.top, .bottom], 10) .font(Font.custom("AveriaSerifLibre-Regular", size: CGFloat(10))) } .frame(width: CGFloat(100 * 1.66 * scale + 10 ), height:CGFloat(100 * scale + 5)) } } class Verse: NSObject, Codable { var body: String var verse: Int } func makeVerseView(seg: SegDenorm) -> some View { var retView = Text("") var segSplit = seg.body.components(separatedBy: ";;") let decoder = JSONDecoder() segSplit.enumerated().forEach({ (index, item) in let verse = try! decoder.decode(Verse.self, from: item.data(using: .utf8)!) retView = retView + Text(String(verse.verse)) .font(Font.custom("AveriaSerifLibre-Regular", size: 6)) .baselineOffset(6.0) .foregroundColor(Color.white) retView = retView + Text(verse.body) .foregroundColor(Color.white) .font(Font.custom("AveriaSerifLibre-Regular", size: 15)) }) return retView } private struct SegRow: View { var seg: SegDenorm var ribbonId: Int64 var body: some View { var retView = Text("") var segSplit = seg.body.components(separatedBy: ";;") let decoder = JSONDecoder() // Text(segSplit[0].body) // .id(seg.id) // VStack { // ForEach(segSplit.indices) { i in segSplit.enumerated().forEach({ (index, item) in let verse = try! decoder.decode(Verse.self, from: item.data(using: .utf8)!) var attributedString: AttributedString { // var result = AttributedString("Hello World!") var result = AttributedString(verse.body) // result.underlineStyle = Text.LineStyle( // pattern: .dot, color: .white) return result } retView = retView + Text(String(ribbonId)) //retView = retView + Text(attributedString) // Text(seg.body) // .contentShape(Rectangle()) .font(Font.custom("AveriaSerifLibre-Regular", size: 10)) .baselineOffset(6.0) .foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00))) retView = retView + Text(attributedString) // .frame(maxWidth: .infinity, alignment: .leading) // .contentShape(Rectangle()) .foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00))) .font(Font.custom("AveriaSerifLibre-Regular", size: 20)) // .listRowBackground(Color(red: 0.2, green: 0.8, blue: 0.2)) // .listRowInsets(EdgeInsets()) // .padding(EdgeInsets(top: 10, leading: 20, bottom: 40, trailing: 20)) // .listRowSeparator(.hidden) // .id(String(seg.id) + "body" + String(i)) // .id(seg.id) //.onTapGesture { // selectedLine = seg.id //Print(selectedLine) //} // .listRowBackground(Color(red: 0.2, green: 0.8, blue: 0.2)) // .listRowInsets(EdgeInsets()) // .padding(EdgeInsets(top: 10, leading: 20, bottom: 40, trailing: 20)) // .listRowSeparator(.hidden) // .id(String(seg.id) + "verse" + String(i)) // .id(seg.id) //.onTapGesture { // selectedLine = seg.id //Print(selectedLine) //} }) // } return retView } } struct ContentView: View { @State var viewState = CGSize.zero @State var pulledOut = CGSize.zero @State var taskTitle : String = "FIRST DOGGG" @State var curBook : String = "Matthew" @State var selectedLine : Int64? @State var thisScrollView : UIScrollView? @State var scrollUpdate = false @State var initLoad = false // set this to scroll to area @State var scrollId : String? @State var scrollOffset: CGFloat? @State var setScrollOffset: CGFloat? @State var showOverlay: Bool = false @State var vertSep = CGFloat(20) @Environment(\.appDatabase) private var appDatabase @Query(SegDenormRequest(book: "bible.mark")) private var segs: [SegDenorm] // @State var scrollDelegate: ScrollViewHandler // @State var scrollDelegate = ScrollViewHandler() // @State var selectedRibbonId = Int64(UserDefaults.standard.optionalInt(forKey: "selectedRibbonId") ?? 1) // ribbon // @Query(SelectedRibbonRequest()) private var selectedRibbon: [Ribbon] @State var endedDrag = true @State var readOffset = CGPoint() @State var dragOffset = CGFloat() @State var refresh: Bool = false @State var refresh2: Bool = false @State var draggedRibbon: Ribbon? @State var isDragging = false @State var reorder = true @Query(RibbonRequest()) private var ribbons: [Ribbon] @Query var selectedRibbon: [Ribbon] // @Query(RibbonRequest(id: Int64(UserDefaults.standard.optionalInt(forKey: "lastRibbonId") ?? 1))) private var selectedRibbon: [Ribbon] init() { UITableView.appearance().backgroundColor = UIColor(Color(red: 0.2, green: 0.2, blue: 0.2)) _selectedRibbon = Query(SelectedRibbonRequest()) // self._scrollDelegate = State(initialValue: ScrollViewHandler()) } var body: some View { // Print("rendering") var size1 = CGFloat(11) var size2 = CGFloat(100) var fontSize = CGFloat(15) var scale = 0.65 var height = CGFloat(50) var xOffset = CGFloat(25) var width = CGFloat(100 * 1.66 * scale) // ForEach(ribbons) { ribbon in // data.append(ribbon) // } GeometryReader { geometry in ZStack (alignment: .top) { VStack (alignment: .leading) { VStack { // ReorderableForEach($data, allowReordering: $reorder) { item, isDragged in ForEach(ribbons) { ribbon in RibbonCrown(ribbon: ribbon, scrollId:$scrollId, scrollOffset:$scrollOffset, showOverlay: $showOverlay, refresh:$refresh, draggedRibbon: draggedRibbon, isDragging: isDragging ) .onDrag { self.draggedRibbon = ribbon return NSItemProvider() } .onDrop(of: [.item], delegate: DropViewDelegate(destinationItem: ribbon, draggedItem: $draggedRibbon, isDragging: $isDragging, appDatabase: appDatabase) ) .offset(x: 6, y: 6) } // .foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00))) } .frame(width: geometry.size.width, height: geometry.size.height - 100, alignment: .topLeading) .background(Color(red: 0.1, green: 0.1, blue: 0.1)) .zIndex(0) .animation(.default, value: ribbons) .onDrop(of: [.item], delegate: DropViewDelegate2(isDragging: $isDragging) ) // Spacer() // ForEach(ribbons) { ribbon in // RibbonCrown(ribbon: ribbon, // scrollId:$scrollId, // scrollOffset:$scrollOffset, // showOverlay: $showOverlay, // refresh:$refresh // ) // // .buttonStyle(BlueButtonStyle()) // // .frame(alignment: .topLeading) // // .background(Color(red: 0.4, green: 0.4, blue: 0.1)) // .animation(nil) // } } .background(Color(red: 0.1, green: 0.1, blue: 0.1)) .frame(alignment: .topLeading) //.animation(nil) VStack { ScrollViewReader { proxy in VisibilityTrackingScrollView(action: handleVisibilityChanged) { // ScrollView { LazyVStack { ForEach(segs) { seg in SegRow(seg: seg, ribbonId: selectedRibbon[0].id! ) .id("\(seg.id)") .offset(x: -dragOffset) // .offset(x: pulledOut.width) .padding(EdgeInsets(top: 10, leading: 20, bottom: 40, trailing: 20)) .trackVisibility(id: "\(seg.id)") // .onChange(of: geometry.frame(in: .named("scrollView"))) { imageRect in //Print(imageRect) //Print(outerProxy) // if isInView(innerRect: imageRect, isIn: outerProxy) { // visibleIndex.insert(item) // } else { // visibleIndex.remove(item) // } // } } } .background(Color(red: 0.18, green: 0.18, blue: 0.18)) } .onAppear() { Print("APPEAR") // Print(selectedRibbon[0]) // scrollId = "3" // scrollOffset = 103 // refresh.toggle() goToRibbon(selectedRibbon: selectedRibbon[0], destRibbon: selectedRibbon[0], scrollId: $scrollId, scrollOffset: $scrollOffset, refresh: $refresh, showOverlay: $showOverlay, appDatabase: appDatabase, loading: true) } .onChange(of: refresh) { target in //if let target = target { //gTracker!.visibleViews["123123"] = CGFloat(100) // Print("ON CHANGE", gTracker!.visibleViews) // Print("removing", gTracker!.visibleViews.removeAll()) Task { DispatchQueue.main.async { Print("scroll Id target: \(scrollId)") proxy.scrollTo(scrollId! , anchor: .top) // proxy.scrollTo(String(Int(scrollId!)! + 1)) // currentId = scrollId! // if (currentId != scrollId!) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { Print(" scroll id target", scrollId) Print(" current id ", currentId) Print(gTracker!.sortedViewIDs) if (currentId! != scrollId!) { Print("NO MATCH") } // Print(" scroll offset target", scrollOffset) Print(" current offset ", gTracker!.visibleViews[scrollId!]) var curOffset = gTracker!.visibleViews[scrollId!] Print(" stats", gTracker!.visibleViews) // // setScrollOffset = CGFloat(Int(currentOffset!) * -1 + Int(scrollOffset!)) if (curOffset != nil) { setScrollOffset = CGFloat(Int(scrollOffset!) - Int(curOffset!)) Print("applying scroll offset \(setScrollOffset)") // // setScrollOffset = CGFloat(Int(scrollOffset!)) // Print("setting scroll offset", setScrollOffset) refresh2.toggle() } else { var adjust = (Int(scrollId!)! - Int(currentId!)!) * 200 Print("adjusting \(adjust)") setScrollOffset = CGFloat(adjust) refresh.toggle() } // // currentId = scrollId! // DispatchQueue.main.async { // currentOffset = scrollOffset! // currentId = scrollId! // } } } } } .introspectScrollView { scrollView in Print("introspect") // scrollView.delegate = scrollDelegate //Print("Scroll delegate offset", scrollDelegate.scrollOffset) if (setScrollOffset != nil) { // Print("Setting scroll offset in introspect", setScrollOffset) DispatchQueue.main.async { scrollView.contentOffset.y = scrollView.contentOffset.y + setScrollOffset! setScrollOffset = nil withAnimation { showOverlay = false } } } // if (thisScrollView == nil) { // Print("init scroll") // thisScrollView = scrollView // scrollView.contentOffset.y = CGFloat(selectedRibbon[0].scrollOffset) // } Print("end introspect") } .listStyle(PlainListStyle()) } .zIndex(1) .background(Color(red: 0.2, green: 0.2, blue: 0.2)) .frame(width: geometry.size.width - 50, height: geometry.size.height / 2 - vertSep) .offset(x:30 , y:0) .offset(x: pulledOut.width) .offset(x: viewState.width, y: viewState.height) .gesture( DragGesture() .onChanged { gesture in if (endedDrag) { endedDrag = false scrollOffset = readOffset.y - 20 // _ = Print("meow") } // logger.error("hello222") // NSLog("hellooo") Print(viewState.width) if (abs(gesture.translation.width) > 20) { viewState.width = gesture.translation.width if (gesture.translation.width < -50 && pulledOut.width == CGFloat(0)) { dragOffset = gesture.translation.width + 50 } } //offset.y = gesture.translation.width // logger.log("hello") } .onEnded { _ in endedDrag = true var pulledOutWidth = CGFloat(0) if (viewState.width < 0) { pulledOutWidth = CGFloat(0) } else if abs(viewState.width + pulledOut.width ) > 30 { pulledOutWidth = CGFloat(200) } withAnimation(.spring(response: 0.2)) { pulledOut.width = pulledOutWidth viewState = .zero dragOffset = .zero } } ) Text("DRAG").foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00))) .gesture( DragGesture() .onChanged { gesture in vertSep = vertSep - gesture.translation.height Print(gesture.translation.width) Print(gesture.translation.height) Print("drag") //if (endedDrag) { // endedDrag = false // scrollOffset = readOffset.y - 20 // // _ = Print("meow") //} //// logger.error("hello222") //// NSLog("hellooo") //Print(viewState.width) //if (abs(gesture.translation.width) > 20) { // viewState.width = gesture.translation.width //} ////offset.y = gesture.translation.width //// logger.log("hello") } // .onEnded { _ in // endedDrag = true // var pulledOutWidth = CGFloat(0) // if (viewState.width < 0) { // pulledOutWidth = CGFloat(0) // } // else if abs(viewState.width + pulledOut.width ) > 30 { // pulledOutWidth = CGFloat(200) // } // withAnimation(.spring(response: 0.2)) { // pulledOut.width = pulledOutWidth // viewState = .zero // } // } ) ScrollViewReader { proxy in VisibilityTrackingScrollView(action: handleVisibilityChanged2) { // ScrollView { LazyVStack { ForEach(segs) { seg in SegRow(seg: seg, ribbonId: selectedRibbon[0].id! ) .id("\(seg.id)") .padding(EdgeInsets(top: 10, leading: 20, bottom: 40, trailing: 20)) .trackVisibility(id: "\(seg.id)") // .onChange(of: geometry.frame(in: .named("scrollView"))) { imageRect in //Print(imageRect) //Print(outerProxy) // if isInView(innerRect: imageRect, isIn: outerProxy) { // visibleIndex.insert(item) // } else { // visibleIndex.remove(item) // } // } } } .background(Color(red: 0.18, green: 0.18, blue: 0.18)) } .onAppear() { Print("APPEAR") // Print(selectedRibbon[0]) // scrollId = "3" // scrollOffset = 103 // refresh.toggle() // goToRibbon(selectedRibbon: selectedRibbon[0], // destRibbon: selectedRibbon[0], // scrollId: $scrollId, // scrollOffset: $scrollOffset, // refresh: $refresh, // showOverlay: $showOverlay, // appDatabase: appDatabase, // loading: true) } //.onChange(of: refresh) { target in // //if let target = target { // //gTracker!.visibleViews["123123"] = CGFloat(100) // // Print("ON CHANGE", gTracker!.visibleViews) // // Print("removing", gTracker!.visibleViews.removeAll()) // Task { // DispatchQueue.main.async { // Print("scroll Id target: \(scrollId)") // proxy.scrollTo(scrollId! , anchor: .top) // // proxy.scrollTo(String(Int(scrollId!)! + 1)) // // currentId = scrollId! // // if (currentId != scrollId!) { // DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // Print(" scroll id target", scrollId) // Print(" current id ", currentId) // Print(gTracker!.sortedViewIDs) // if (currentId! != scrollId!) { // Print("NO MATCH") // } // // Print(" scroll offset target", scrollOffset) // Print(" current offset ", gTracker!.visibleViews[scrollId!]) // var curOffset = gTracker!.visibleViews[scrollId!] // Print(" stats", gTracker!.visibleViews) // // // setScrollOffset = CGFloat(Int(currentOffset!) * -1 + Int(scrollOffset!)) // if (curOffset != nil) { // setScrollOffset = CGFloat(Int(scrollOffset!) - Int(curOffset!)) // Print("applying scroll offset \(setScrollOffset)") // // // setScrollOffset = CGFloat(Int(scrollOffset!)) // // Print("setting scroll offset", setScrollOffset) // refresh2.toggle() // } else { // var adjust = (Int(scrollId!)! - Int(currentId!)!) * 200 // Print("adjusting \(adjust)") // setScrollOffset = CGFloat(adjust) // refresh.toggle() // } // // // currentId = scrollId! // // DispatchQueue.main.async { // // currentOffset = scrollOffset! // // currentId = scrollId! // // } // } // } // } //} //.introspectScrollView { scrollView in // Print("introspect") // // scrollView.delegate = scrollDelegate // //Print("Scroll delegate offset", scrollDelegate.scrollOffset) // if (setScrollOffset != nil) { // // Print("Setting scroll offset in introspect", setScrollOffset) // DispatchQueue.main.async { // scrollView.contentOffset.y = scrollView.contentOffset.y + setScrollOffset! // setScrollOffset = nil // withAnimation { // showOverlay = false // } // } // } // // if (thisScrollView == nil) { // // Print("init scroll") // // thisScrollView = scrollView // // scrollView.contentOffset.y = CGFloat(selectedRibbon[0].scrollOffset) // // } // Print("end instrospect") //} .listStyle(PlainListStyle()) } .zIndex(1) .background(Color(red: 0.2, green: 0.2, blue: 0.2)) .frame(width: geometry.size.width - 50) .offset(x:30 , y:0) .offset(x: pulledOut.width) .offset(x: viewState.width, y: viewState.height) .gesture( DragGesture() .onChanged { gesture in if (endedDrag) { endedDrag = false scrollOffset = readOffset.y - 20 // _ = Print("meow") } // logger.error("hello222") // NSLog("hellooo") Print(viewState.width) if (abs(gesture.translation.width) > 20) { viewState.width = gesture.translation.width } //offset.y = gesture.translation.width // logger.log("hello") } .onEnded { _ in endedDrag = true var pulledOutWidth = CGFloat(0) if (viewState.width < 0) { pulledOutWidth = CGFloat(0) } else if abs(viewState.width + pulledOut.width ) > 30 { pulledOutWidth = CGFloat(200) } withAnimation(.spring(response: 0.2)) { pulledOut.width = pulledOutWidth viewState = .zero } } ) } if (showOverlay) { Rectangle() .frame(width: geometry.size.width - 50, height: geometry.size.height + 200) .background(.ultraThinMaterial) //.blur(radius: 0.8) .offset(x:30, y:-100 ) .opacity(0.98) .transition(.opacity) // .frame(width: geometry.size.width - 50) .offset(x: pulledOut.width) .offset(x: viewState.width, y: viewState.height) .zIndex(2) } } } } func handleVisibilityChanged2(_ id: String, change: VisibilityChange, tracker: VisibilityTracker) { } func handleVisibilityChanged(_ id: String, change: VisibilityChange, tracker: VisibilityTracker) { // var printRate: Int64 = 10 gTracker = tracker // @Environment(\.appDatabase) var appDatabase // switch change { // case .shown: print("\(id) shown") // case .hidden: print("\(id) hidden") // } // if (printCount % printRate == 0) { // print("VISIBILITY CHANGED STARTED") // print(tracker.visibleViews) // print(tracker.sortedViewIDs) // print("VISIBILITY CHANGED ENDED") // } // printCount += 1 // if (currentId != nil) { // currentOffset = tracker.visibleViews[currentId!]! // } let visibleViews2 = Array(tracker.visibleViews.keys) if (visibleViews2.count == 0) { return } // currentId = tracker.sortedViewIDs[tracker.sortedViewIDs.count - 1] currentId = tracker.sortedViewIDs[0] currentOffset = tracker.visibleViews[currentId!]! // if (Int(currentOffset!) != -1 && Int(currentOffset!) < 0) { // if (tracker.sortedViewIDs.count > 1) { // currentId = tracker.sortedViewIDs[1] // currentOffset = tracker.visibleViews[currentId!]! // } // } // if (currentId != nil) { // // if (printCount % printRate == 0) { // // print(printCount) // // print("cat current ID:", currentId) // // } // // print("got here") // currentOffset = tracker.visibleViews[currentId!]! // } } } private let itemFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .medium return formatter }() struct DropViewDelegate2: DropDelegate { @Binding var isDragging: Bool func dropUpdated(info: DropInfo) -> DropProposal? { return DropProposal(operation: .move) } func performDrop(info: DropInfo) -> Bool { isDragging = false return true } func dropEntered(info: DropInfo) { isDragging = true print("SECOND DROPPPOOO") } } struct DropViewDelegate: DropDelegate { let destinationItem: Ribbon // @Binding var colors: [Color] @Binding var draggedItem: Ribbon? @Binding var isDragging: Bool let appDatabase: AppDatabase func dropUpdated(info: DropInfo) -> DropProposal? { return DropProposal(operation: .move) } func dropExited(info: DropInfo) { print("EXITED") isDragging = false } func dropEntered(info: DropInfo) { Task { isDragging = true if (draggedItem == nil) { return } if disableDrop { return; } var newRibbon = draggedItem! var newDest = destinationItem var oldPos = draggedItem!.pos var newPos = destinationItem.pos print("dragged item") print(draggedItem) print("dest item") print(destinationItem) if (draggedItem!.id! == destinationItem.id!) { return } newRibbon.pos = destinationItem.pos _ = try await appDatabase.updateRibbonPosition(&newRibbon, oldPos, newPos ) disableDrop = true DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { disableDrop = false } draggedItem!.pos = newPos // draggedItem = nil } } func performDrop(info: DropInfo) -> Bool { print("PERFORMED DROPPPP") draggedItem = nil isDragging = false return true } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() ContentView().environment(\.appDatabase, .random()) } } extension View { func Print(_ vars: Any...) -> some View { for v in vars { print(v) } return EmptyView() } }