2023-02-27 14:27:15 -08:00
|
|
|
//
|
|
|
|
// ContentView.swift
|
|
|
|
// gloss
|
|
|
|
//
|
|
|
|
// Created by Saint on 10/23/22.
|
|
|
|
//
|
|
|
|
|
|
|
|
import GRDB
|
|
|
|
import GRDBQuery
|
|
|
|
import os
|
2024-05-15 08:57:41 -07:00
|
|
|
import SwiftUI
|
2024-05-16 13:08:52 -07:00
|
|
|
import SwiftUIIntrospect
|
|
|
|
|
2023-02-27 14:27:15 -08:00
|
|
|
let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "network")
|
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
var currentId: String?
|
|
|
|
var currentOffset: CGFloat?
|
2023-04-23 20:41:17 -07:00
|
|
|
|
2023-08-16 12:19:14 -07:00
|
|
|
var disableDrop = false
|
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
public extension UserDefaults {
|
|
|
|
func optionalInt(forKey defaultName: String) -> Int? {
|
2023-02-28 14:03:58 -08:00
|
|
|
let defaults = self
|
|
|
|
if let value = defaults.value(forKey: defaultName) {
|
|
|
|
return value as? Int
|
|
|
|
}
|
2024-05-15 08:57:41 -07:00
|
|
|
return nil
|
2023-02-28 14:03:58 -08:00
|
|
|
}
|
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
func optionalBool(forKey defaultName: String) -> Bool? {
|
2023-02-28 14:03:58 -08:00
|
|
|
let defaults = self
|
|
|
|
if let value = defaults.value(forKey: defaultName) {
|
|
|
|
return value as? Bool
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-03 15:26:56 -07:00
|
|
|
func createUndoState(selectedRibbon: Ribbon,
|
|
|
|
appDatabase : AppDatabase,
|
|
|
|
paneConnector : PaneConnector) async throws -> [Ribbon]
|
|
|
|
|
2024-05-31 10:04:43 -07:00
|
|
|
{
|
2024-06-03 15:26:56 -07:00
|
|
|
let updateThreshold = 30
|
2024-05-31 10:04:43 -07:00
|
|
|
var updatedRibbon = selectedRibbon
|
|
|
|
var scrollOffsetToSave = Int(floor(paneConnector.currentOffset))
|
|
|
|
var scrollIdToSave = paneConnector.currentId
|
2024-06-03 15:26:56 -07:00
|
|
|
|
2024-05-31 10:04:43 -07:00
|
|
|
var offsetDiff = abs(scrollOffsetToSave - updatedRibbon.scrollOffset) > 30
|
2024-06-03 15:26:56 -07:00
|
|
|
|
2024-05-31 10:04:43 -07:00
|
|
|
var idDiff = Int(updatedRibbon.scrollId) != Int(scrollIdToSave)
|
|
|
|
|
|
|
|
if idDiff || offsetDiff {
|
|
|
|
updatedRibbon.scrollId = scrollIdToSave
|
|
|
|
updatedRibbon.scrollOffset = scrollOffsetToSave
|
|
|
|
|
|
|
|
print("meow bumping")
|
|
|
|
let ret = try await appDatabase.bumpRibbon(&updatedRibbon)
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
print("meow no bump")
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
2023-11-14 13:39:46 -08:00
|
|
|
func goToRibbon(selectedRibbon: Ribbon,
|
|
|
|
destRibbon: Ribbon,
|
2023-06-01 12:45:36 -07:00
|
|
|
appDatabase: AppDatabase,
|
2024-05-23 14:21:58 -07:00
|
|
|
paneConnector: PaneConnector,
|
2024-06-03 20:08:35 -07:00
|
|
|
loading: Bool)
|
2023-06-01 12:45:36 -07:00
|
|
|
{
|
2024-05-31 10:04:43 -07:00
|
|
|
print("meow goto ribbon - selected ribbon: \(selectedRibbon), dest ribbon: \(destRibbon) ")
|
2023-05-31 13:57:17 -07:00
|
|
|
|
2024-05-23 14:21:58 -07:00
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
|
|
|
Task {
|
|
|
|
var scrollOffsetToSave = paneConnector.currentOffset
|
|
|
|
var scrollIdToSave = paneConnector.currentId
|
2023-05-31 13:57:17 -07:00
|
|
|
|
2024-05-23 14:21:58 -07:00
|
|
|
print("go to ribbon")
|
|
|
|
print("\(selectedRibbon.id) \(destRibbon.id!)")
|
2023-11-14 13:39:46 -08:00
|
|
|
|
2024-06-03 15:26:56 -07:00
|
|
|
// if selectedRibbon.id != destRibbon.id! || loading {
|
|
|
|
if true {
|
2024-05-23 14:21:58 -07:00
|
|
|
print("switching ribbons")
|
2023-05-31 13:57:17 -07:00
|
|
|
|
2024-05-23 14:21:58 -07:00
|
|
|
paneConnector.showOverlay = true
|
|
|
|
|
|
|
|
if loading {
|
|
|
|
paneConnector.currentId = destRibbon.scrollId
|
|
|
|
}
|
|
|
|
|
|
|
|
paneConnector.scrollId = destRibbon.scrollId
|
|
|
|
paneConnector.scrollOffset = CGFloat(destRibbon.scrollOffset)
|
|
|
|
paneConnector.refresh.toggle()
|
|
|
|
|
|
|
|
print("toggling")
|
|
|
|
print("paneconnector: \(paneConnector.refresh)")
|
2023-06-01 12:45:36 -07:00
|
|
|
|
2024-05-28 13:15:07 -07:00
|
|
|
var updateSelectRibbon = SelectedRibbon(id: Int64(1), ribbonGroupId: Int64(destRibbon.groupId))
|
2024-05-23 14:21:58 -07:00
|
|
|
// print("Saving selected ribbon")
|
|
|
|
// print(updateSelectRibbon)
|
|
|
|
do {
|
|
|
|
_ = try await appDatabase.saveSelectedRibbon(&updateSelectRibbon)
|
|
|
|
} catch {
|
|
|
|
// Print("something wrong")
|
|
|
|
}
|
2023-05-31 13:57:17 -07:00
|
|
|
}
|
|
|
|
|
2023-06-01 12:45:36 -07:00
|
|
|
}
|
2023-05-31 13:57:17 -07:00
|
|
|
}
|
2024-05-23 14:21:58 -07:00
|
|
|
|
2023-05-31 13:57:17 -07:00
|
|
|
}
|
2023-11-14 13:39:46 -08:00
|
|
|
|
2023-08-03 12:40:12 -07:00
|
|
|
extension View {
|
|
|
|
@ViewBuilder
|
|
|
|
func `if`<Transform: View>(_ condition: Bool, transform: (Self) -> Transform) -> some View {
|
|
|
|
if condition { transform(self) }
|
|
|
|
else { self }
|
|
|
|
}
|
|
|
|
}
|
2023-05-31 13:57:17 -07:00
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
struct RibbonCrown: View {
|
2023-03-01 10:47:34 -08:00
|
|
|
var ribbon: Ribbon
|
2024-05-23 14:21:58 -07:00
|
|
|
@ObservedObject var paneConnector: PaneConnector
|
2024-05-15 08:57:41 -07:00
|
|
|
var draggedRibbon: Ribbon?
|
|
|
|
var isDragging: Bool
|
2023-02-28 14:03:58 -08:00
|
|
|
|
2024-06-07 08:24:33 -07:00
|
|
|
var height = CGFloat(41)
|
|
|
|
var width = CGFloat(70)
|
|
|
|
var scale = 0.70
|
2023-07-21 17:19:30 -07:00
|
|
|
|
2023-02-28 14:03:58 -08:00
|
|
|
@Environment(\.appDatabase) private var appDatabase
|
2023-02-27 14:27:15 -08:00
|
|
|
|
2024-05-31 10:04:43 -07:00
|
|
|
@Query(SelectedRibbonRequest()) private var selectedRibbon: [Ribbon]
|
2023-02-28 14:03:58 -08:00
|
|
|
|
|
|
|
@State var saveOffset = CGFloat()
|
2024-05-23 11:41:16 -07:00
|
|
|
@Binding var refresh: Bool
|
|
|
|
|
2023-02-27 14:27:15 -08:00
|
|
|
var body: some View {
|
2023-08-02 16:26:17 -07:00
|
|
|
ZStack {
|
2023-11-14 13:39:46 -08:00
|
|
|
MyIcon().frame(
|
|
|
|
width: CGFloat(100 * 1.66 * scale),
|
|
|
|
height: CGFloat(100 * scale),
|
|
|
|
alignment: .center
|
2024-05-15 08:57:41 -07:00
|
|
|
).foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))
|
2024-05-15 11:20:01 -07:00
|
|
|
.contentShape(.dragPreview, RoundedRectangle(cornerRadius: 32))
|
2023-11-14 13:39:46 -08:00
|
|
|
.if(draggedRibbon != nil && draggedRibbon!.id == ribbon.id && isDragging) { $0.overlay(Color(red: 0.1, green: 0.1, blue: 0.1)) }
|
2023-08-02 16:26:17 -07:00
|
|
|
|
2023-11-14 13:39:46 -08:00
|
|
|
Text(ribbon.title)
|
|
|
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
2024-06-07 08:24:33 -07:00
|
|
|
.frame(minWidth: width,
|
|
|
|
maxWidth: width,
|
2024-05-15 08:57:41 -07:00
|
|
|
minHeight: height,
|
|
|
|
maxHeight: height,
|
|
|
|
alignment: .center)
|
2023-08-03 12:40:12 -07:00
|
|
|
|
2023-11-14 13:39:46 -08:00
|
|
|
.if(draggedRibbon != nil && draggedRibbon!.id == ribbon.id && isDragging) { $0.overlay(Color(red: 0.1, green: 0.1, blue: 0.1)) }
|
|
|
|
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
|
|
|
.multilineTextAlignment(.center)
|
|
|
|
// .minimumScaleFactor(0.5)
|
|
|
|
// .padding([.top, .bottom], 10)
|
|
|
|
.font(Font.custom("AveriaSerifLibre-Regular", size: CGFloat(10)))
|
2023-08-02 16:26:17 -07:00
|
|
|
}
|
2024-05-20 10:53:47 -07:00
|
|
|
.onTapGesture {
|
|
|
|
Task {
|
2024-05-31 10:04:43 -07:00
|
|
|
let sr = selectedRibbon[0]
|
2024-06-03 15:26:56 -07:00
|
|
|
let updatedRibbon = try await createUndoState(selectedRibbon: sr,
|
|
|
|
appDatabase: appDatabase,
|
|
|
|
paneConnector: paneConnector)
|
|
|
|
|
|
|
|
if sr.id == ribbon.id {
|
|
|
|
paneConnector.scrollId = paneConnector.currentId
|
|
|
|
paneConnector.scrollOffset = paneConnector.currentOffset
|
|
|
|
paneConnector.hasMoved = false
|
|
|
|
} else {
|
|
|
|
goToRibbon(selectedRibbon: sr,
|
|
|
|
destRibbon: ribbon,
|
|
|
|
appDatabase: appDatabase,
|
|
|
|
paneConnector: paneConnector,
|
2024-06-03 20:08:35 -07:00
|
|
|
loading: false)
|
2024-06-03 15:26:56 -07:00
|
|
|
}
|
|
|
|
}
|
2024-05-20 10:53:47 -07:00
|
|
|
}
|
2024-05-15 08:57:41 -07:00
|
|
|
.frame(width: CGFloat(100 * 1.66 * scale + 10), height: CGFloat(100 * scale + 5))
|
2023-03-01 10:47:34 -08:00
|
|
|
}
|
|
|
|
}
|
2023-02-28 14:03:58 -08:00
|
|
|
|
2024-06-07 08:24:33 -07:00
|
|
|
// object used for JSON decoding of verses
|
2023-03-11 20:31:48 -08:00
|
|
|
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()
|
2024-05-15 08:57:41 -07:00
|
|
|
for (index, item) in segSplit.enumerated() {
|
2023-03-11 20:31:48 -08:00
|
|
|
let verse = try! decoder.decode(Verse.self, from: item.data(using: .utf8)!)
|
|
|
|
|
|
|
|
retView = retView + Text(String(verse.verse))
|
2024-05-15 08:57:41 -07:00
|
|
|
.font(Font.custom("AveriaSerifLibre-Regular", size: 6))
|
|
|
|
.baselineOffset(6.0)
|
|
|
|
.foregroundColor(Color.white)
|
2023-03-11 20:31:48 -08:00
|
|
|
|
|
|
|
retView = retView + Text(verse.body)
|
2024-05-15 08:57:41 -07:00
|
|
|
.foregroundColor(Color.white)
|
|
|
|
.font(Font.custom("AveriaSerifLibre-Regular", size: 15))
|
|
|
|
}
|
2023-03-11 20:31:48 -08:00
|
|
|
return retView
|
|
|
|
}
|
2023-03-01 10:47:34 -08:00
|
|
|
|
2024-05-23 14:21:58 -07:00
|
|
|
class PaneConnector: NSObject, ObservableObject {
|
2024-05-20 13:40:32 -07:00
|
|
|
var showOverlay: Bool = false
|
2024-05-23 14:21:58 -07:00
|
|
|
@Published var refresh: Bool = false
|
2024-05-24 07:36:58 -07:00
|
|
|
@Published var vertSep = CGFloat(20)
|
2024-05-21 09:20:23 -07:00
|
|
|
var currentId = ""
|
|
|
|
var currentOffset = CGFloat()
|
|
|
|
var visibilityTracker: VisibilityTracker<String>?
|
2024-05-31 11:23:36 -07:00
|
|
|
@Published var scrollId = ""
|
|
|
|
@Published var scrollOffset = CGFloat()
|
2024-06-03 15:26:56 -07:00
|
|
|
@Published var hasMoved = false
|
2024-05-21 09:20:23 -07:00
|
|
|
var setScrollOffset: CGFloat?
|
2024-06-03 15:26:56 -07:00
|
|
|
|
2024-05-23 07:46:13 -07:00
|
|
|
}
|
2024-05-20 13:40:32 -07:00
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
struct ContentView: View {
|
2024-05-20 13:24:00 -07:00
|
|
|
// this is for the whole view swiping
|
2024-06-07 08:24:33 -07:00
|
|
|
@State var mainSwipe = CGSize.zero
|
2023-02-27 14:27:15 -08:00
|
|
|
@State var pulledOut = CGSize.zero
|
2023-02-28 14:03:58 -08:00
|
|
|
|
2024-05-27 14:24:56 -07:00
|
|
|
@State var selection = 0
|
|
|
|
|
2024-05-23 14:21:58 -07:00
|
|
|
@StateObject var paneConnector = PaneConnector()
|
2024-05-20 13:40:32 -07:00
|
|
|
|
|
|
|
@State var refresh: Bool = false
|
2023-04-22 01:01:09 -07:00
|
|
|
|
2023-02-28 14:03:58 -08:00
|
|
|
@State var readOffset = CGPoint()
|
2023-11-14 13:39:46 -08:00
|
|
|
|
2024-05-26 07:45:30 -07:00
|
|
|
@Query(SegDenormRequest(book: "bible.john")) private var segs: [SegDenorm]
|
2023-11-14 13:39:46 -08:00
|
|
|
|
2023-08-02 16:26:17 -07:00
|
|
|
@State var draggedRibbon: Ribbon?
|
2023-08-03 12:40:12 -07:00
|
|
|
@State var isDragging = false
|
2023-11-14 13:39:46 -08:00
|
|
|
|
2024-06-03 20:08:35 -07:00
|
|
|
@State var dragOffset = CGFloat(0)
|
|
|
|
|
2024-06-08 15:53:23 -07:00
|
|
|
enum SwipeStartState {
|
|
|
|
case center
|
|
|
|
case right
|
|
|
|
case left
|
|
|
|
}
|
|
|
|
|
|
|
|
enum SwipeStartDir {
|
|
|
|
case left
|
|
|
|
case right
|
|
|
|
}
|
|
|
|
|
|
|
|
// @State var curSwipeState: SwipeState = .start
|
|
|
|
@State var startSwipeState: SwipeStartState?
|
|
|
|
@State var startSwipeDir: SwipeStartDir?
|
|
|
|
|
2024-05-20 13:40:32 -07:00
|
|
|
@Environment(\.appDatabase) private var appDatabase
|
2024-05-23 07:46:13 -07:00
|
|
|
|
2023-02-27 14:27:15 -08:00
|
|
|
@Query(RibbonRequest()) private var ribbons: [Ribbon]
|
2023-06-01 12:45:36 -07:00
|
|
|
@Query<SelectedRibbonRequest> var selectedRibbon: [Ribbon]
|
2023-02-27 14:27:15 -08:00
|
|
|
|
|
|
|
init() {
|
|
|
|
UITableView.appearance().backgroundColor = UIColor(Color(red: 0.2, green: 0.2, blue: 0.2))
|
2024-05-15 08:57:41 -07:00
|
|
|
_selectedRibbon = Query(SelectedRibbonRequest())
|
2023-02-27 14:27:15 -08:00
|
|
|
}
|
2023-11-14 13:39:46 -08:00
|
|
|
|
2023-02-27 14:27:15 -08:00
|
|
|
var body: some View {
|
2023-03-11 20:31:48 -08:00
|
|
|
|
2023-06-30 16:47:39 -07:00
|
|
|
var fontSize = CGFloat(15)
|
|
|
|
var scale = 0.65
|
|
|
|
var height = CGFloat(50)
|
2023-07-21 17:19:30 -07:00
|
|
|
|
2023-07-31 10:54:42 -07:00
|
|
|
var width = CGFloat(100 * 1.66 * scale)
|
|
|
|
|
2023-02-27 14:27:15 -08:00
|
|
|
GeometryReader { geometry in
|
2024-05-15 08:57:41 -07:00
|
|
|
ZStack(alignment: .top) {
|
|
|
|
VStack(alignment: .leading) {
|
2023-07-21 17:19:30 -07:00
|
|
|
VStack {
|
2024-06-07 08:24:33 -07:00
|
|
|
|
2023-08-02 16:26:17 -07:00
|
|
|
ForEach(ribbons) { ribbon in
|
|
|
|
RibbonCrown(ribbon: ribbon,
|
2024-05-23 14:21:58 -07:00
|
|
|
paneConnector: paneConnector,
|
2023-08-03 12:40:12 -07:00
|
|
|
draggedRibbon: draggedRibbon,
|
2024-05-23 11:41:16 -07:00
|
|
|
isDragging: isDragging,
|
|
|
|
refresh: $refresh)
|
2024-05-16 13:08:52 -07:00
|
|
|
.onDrag {
|
2024-05-15 08:57:41 -07:00
|
|
|
self.draggedRibbon = ribbon
|
|
|
|
return NSItemProvider()
|
2024-05-16 13:08:52 -07:00
|
|
|
}
|
2024-05-15 08:57:41 -07:00
|
|
|
.onDrop(of: [.item],
|
|
|
|
delegate: DropViewDelegate(destinationItem: ribbon,
|
|
|
|
draggedItem: $draggedRibbon,
|
|
|
|
isDragging: $isDragging,
|
|
|
|
appDatabase: appDatabase))
|
|
|
|
.offset(x: 6, y: 6)
|
2023-07-31 10:54:42 -07:00
|
|
|
}
|
2023-07-21 17:19:30 -07: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)
|
2023-08-02 16:26:17 -07:00
|
|
|
.animation(.default, value: ribbons)
|
2023-08-03 15:22:18 -07:00
|
|
|
.onDrop(of: [.item],
|
2024-05-15 08:57:41 -07:00
|
|
|
delegate: DropViewDelegate2(isDragging: $isDragging))
|
2023-02-27 14:27:15 -08:00
|
|
|
}
|
2023-03-11 20:31:48 -08:00
|
|
|
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
2023-07-21 17:19:30 -07:00
|
|
|
.frame(alignment: .topLeading)
|
|
|
|
|
2024-05-31 10:04:43 -07:00
|
|
|
|
|
|
|
VStack {
|
|
|
|
NaviBar(paneConnector: paneConnector)
|
|
|
|
StatsPanel(paneConnector: paneConnector)
|
2024-06-07 08:24:33 -07:00
|
|
|
.offset(y:20)
|
2024-05-31 10:04:43 -07:00
|
|
|
}
|
2024-06-07 08:24:33 -07:00
|
|
|
// .frame(maxWidth: 300)
|
|
|
|
.offset(x: geometry.size.width - 300)
|
2024-05-27 14:24:56 -07:00
|
|
|
|
2023-07-21 17:19:30 -07:00
|
|
|
VStack {
|
2024-05-23 14:21:58 -07:00
|
|
|
// Top pane
|
2024-06-03 20:08:35 -07:00
|
|
|
|
2024-05-23 07:57:00 -07:00
|
|
|
Pane(paneConnector: paneConnector,
|
2024-05-23 09:19:18 -07:00
|
|
|
selectedRibbon: selectedRibbon,
|
2024-06-07 08:24:33 -07:00
|
|
|
width: geometry.size.width - 15,
|
|
|
|
height: geometry.size.height + 20,
|
2024-06-03 20:08:35 -07:00
|
|
|
dragOffset: dragOffset)
|
2023-07-21 17:19:30 -07:00
|
|
|
|
2024-06-07 08:24:33 -07:00
|
|
|
///////////////////////////////////
|
|
|
|
|
|
|
|
// Text("separator").foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
|
|
|
// .gesture(
|
|
|
|
// DragGesture()
|
|
|
|
// .onChanged { gesture in
|
|
|
|
// paneConnector.vertSep = paneConnector.vertSep - gesture.translation.height
|
|
|
|
// }
|
|
|
|
// )
|
|
|
|
|
|
|
|
// // Bottom pane
|
|
|
|
// ScrollViewReader { _ 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)")
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// .background(Color(red: 0.18, green: 0.18, blue: 0.18))
|
|
|
|
// }
|
|
|
|
|
|
|
|
// .onAppear {
|
|
|
|
// Print("APPEAR")
|
|
|
|
// }
|
|
|
|
// .listStyle(PlainListStyle())
|
|
|
|
// }
|
|
|
|
// .zIndex(1)
|
|
|
|
// .background(Color(red: 0.2, green: 0.2, blue: 0.2))
|
2023-06-27 16:04:24 -07:00
|
|
|
|
2024-06-07 08:24:33 -07:00
|
|
|
///////////////////////////////////
|
2024-05-20 10:53:47 -07:00
|
|
|
}
|
2024-06-07 08:24:33 -07:00
|
|
|
.offset(x: 20, y: 0)
|
2024-05-20 10:53:47 -07:00
|
|
|
.offset(x: pulledOut.width)
|
2024-06-08 15:53:23 -07:00
|
|
|
.offset(x: mainSwipe.width)
|
2024-05-20 10:53:47 -07:00
|
|
|
.gesture(
|
|
|
|
DragGesture()
|
2024-06-08 15:53:23 -07:00
|
|
|
.onChanged { value in
|
|
|
|
// Calculate the offset
|
|
|
|
|
2024-06-09 07:32:59 -07:00
|
|
|
let margin = CGFloat(30)
|
|
|
|
|
2024-06-08 15:53:23 -07:00
|
|
|
var newOffset = value.translation.width
|
|
|
|
if startSwipeState == nil {
|
|
|
|
if pulledOut.width == 0 {
|
|
|
|
startSwipeState = .center
|
|
|
|
} else if pulledOut.width < 0 {
|
|
|
|
startSwipeState = .left
|
|
|
|
} else {
|
|
|
|
startSwipeState = .right
|
|
|
|
}
|
2024-05-20 10:53:47 -07:00
|
|
|
|
2024-06-08 15:53:23 -07:00
|
|
|
print("start swipe meow: \(startSwipeState)")
|
2024-06-08 16:41:44 -07:00
|
|
|
}
|
|
|
|
|
2024-06-08 15:53:23 -07:00
|
|
|
if newOffset > 0 {
|
|
|
|
startSwipeDir = .right
|
|
|
|
} else {
|
|
|
|
startSwipeDir = .left
|
|
|
|
}
|
2024-06-04 04:34:15 -07:00
|
|
|
|
2024-06-07 08:24:33 -07:00
|
|
|
|
2024-06-08 15:53:23 -07:00
|
|
|
// Apply resistance if out of bounds
|
2024-06-08 16:41:44 -07:00
|
|
|
var maxOffsetRight: CGFloat = 110
|
|
|
|
var maxOffsetLeft: CGFloat = 200
|
|
|
|
|
|
|
|
|
|
|
|
if startSwipeState == .right {
|
|
|
|
if startSwipeDir == .right {
|
|
|
|
maxOffsetRight = .zero
|
|
|
|
maxOffsetLeft = .zero
|
|
|
|
} else if startSwipeDir == .left {
|
|
|
|
maxOffsetRight = CGFloat(110)
|
|
|
|
maxOffsetLeft = CGFloat(-30)
|
|
|
|
}
|
|
|
|
} else if startSwipeState == .left {
|
|
|
|
|
|
|
|
if startSwipeDir == .left {
|
|
|
|
maxOffsetLeft = .zero
|
|
|
|
maxOffsetRight = .zero
|
2024-06-09 07:25:08 -07:00
|
|
|
} else if startSwipeDir == .right {
|
2024-06-08 16:41:44 -07:00
|
|
|
maxOffsetLeft = CGFloat(200)
|
2024-06-09 07:25:08 -07:00
|
|
|
maxOffsetRight = CGFloat(10)
|
2024-06-08 16:41:44 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if newOffset + pulledOut.width < -maxOffsetLeft {
|
|
|
|
if startSwipeState == .right && startSwipeDir == .left {
|
|
|
|
newOffset = -maxOffsetLeft + rubberBandEffect(newOffset + maxOffsetLeft) - pulledOut.width
|
|
|
|
} else {
|
|
|
|
newOffset = -maxOffsetLeft + rubberBandEffect(newOffset + maxOffsetLeft)
|
|
|
|
}
|
2024-06-07 15:50:24 -07:00
|
|
|
|
2024-06-08 16:41:44 -07:00
|
|
|
} else if newOffset + pulledOut.width > maxOffsetRight {
|
2024-06-09 07:25:08 -07:00
|
|
|
if startSwipeState == .left, startSwipeDir == .right {
|
|
|
|
newOffset = maxOffsetRight + rubberBandEffect(newOffset - maxOffsetRight) - pulledOut.width
|
|
|
|
} else {
|
|
|
|
newOffset = maxOffsetRight + rubberBandEffect(newOffset - maxOffsetRight)
|
|
|
|
}
|
2024-06-08 15:53:23 -07:00
|
|
|
}
|
2024-06-07 08:24:33 -07:00
|
|
|
|
2024-06-08 15:53:23 -07:00
|
|
|
self.mainSwipe.width = newOffset
|
2024-06-07 08:24:33 -07:00
|
|
|
|
|
|
|
// dragOffset is what is used to make the text be readable
|
|
|
|
// with the right pane being visible
|
|
|
|
|
|
|
|
// if mainSwipe.width < -margin && pulledOut.width <= 0 {
|
|
|
|
|
2024-06-09 07:32:59 -07:00
|
|
|
if mainSwipe.width < -margin && pulledOut.width <= 0 {
|
|
|
|
dragOffset = margin + mainSwipe.width + pulledOut.width
|
|
|
|
}
|
2024-06-08 15:53:23 -07:00
|
|
|
|
2024-06-09 07:32:59 -07:00
|
|
|
if mainSwipe.width > 0 && pulledOut.width < 0 {
|
|
|
|
dragOffset = margin + mainSwipe.width + pulledOut.width
|
|
|
|
}
|
2024-05-20 10:53:47 -07:00
|
|
|
}
|
|
|
|
.onEnded { _ in
|
2024-06-08 15:53:23 -07:00
|
|
|
var finalSwipe = CGFloat(0)
|
|
|
|
let swipeLeftFinal = CGFloat(-200)
|
|
|
|
let swipeRightFinal = CGFloat(110)
|
2024-06-09 07:32:59 -07:00
|
|
|
|
|
|
|
let margin = CGFloat(30)
|
|
|
|
var setDragOffset = CGFloat(0)
|
2024-06-08 15:53:23 -07:00
|
|
|
|
|
|
|
if startSwipeState == .center {
|
|
|
|
if startSwipeDir == .right {
|
|
|
|
finalSwipe = swipeRightFinal
|
|
|
|
} else {
|
|
|
|
finalSwipe = swipeLeftFinal
|
2024-06-09 07:32:59 -07:00
|
|
|
setDragOffset = margin + swipeLeftFinal
|
2024-06-08 15:53:23 -07:00
|
|
|
}
|
|
|
|
} else if startSwipeState == .right {
|
|
|
|
finalSwipe = .zero
|
|
|
|
|
|
|
|
if startSwipeDir == .left {
|
|
|
|
finalSwipe = .zero
|
|
|
|
} else {
|
|
|
|
finalSwipe = swipeRightFinal
|
|
|
|
}
|
|
|
|
} else if startSwipeState == .left {
|
|
|
|
|
|
|
|
if startSwipeDir == .left {
|
|
|
|
finalSwipe = swipeLeftFinal
|
2024-06-09 07:32:59 -07:00
|
|
|
setDragOffset = margin + swipeLeftFinal
|
2024-06-08 15:53:23 -07:00
|
|
|
} else {
|
|
|
|
finalSwipe = .zero
|
|
|
|
}
|
|
|
|
}
|
2024-06-04 04:26:31 -07:00
|
|
|
|
2024-06-08 15:53:23 -07:00
|
|
|
startSwipeState = nil
|
|
|
|
startSwipeDir = nil
|
2024-06-07 08:24:33 -07:00
|
|
|
|
2024-06-08 15:53:23 -07:00
|
|
|
// if mainSwipe.width < 0 && pulledOut.width > 0 {
|
|
|
|
// setPulledOutWith = CGFloat(0)
|
|
|
|
// } else if mainSwipe.width > 0 && pulledOut.width < 0 {
|
|
|
|
// setPulledOutWith = CGFloat(0)
|
|
|
|
|
|
|
|
// } else if (mainSwipe.width < 0 && pulledOut.width < 0) ||
|
|
|
|
// (mainSwipe.width < 0 && pulledOut.width == 0) {
|
|
|
|
// setPulledOutWith = pulledOutRight
|
|
|
|
// setDragOffset = margin + setPulledOutWith
|
|
|
|
// } else if abs(mainSwipe.width + pulledOut.width) > 30 {
|
|
|
|
// setPulledOutWith = pulledOutLeft
|
|
|
|
// }
|
2023-02-27 14:27:15 -08:00
|
|
|
|
2024-05-20 10:53:47 -07:00
|
|
|
withAnimation(.spring(response: 0.2)) {
|
2024-06-08 15:53:23 -07:00
|
|
|
pulledOut.width = finalSwipe
|
2024-06-09 07:32:59 -07:00
|
|
|
dragOffset = setDragOffset
|
|
|
|
mainSwipe = .zero
|
2023-07-21 17:19:30 -07:00
|
|
|
}
|
2024-05-20 10:53:47 -07:00
|
|
|
}
|
2023-07-21 17:19:30 -07:00
|
|
|
)
|
2023-02-27 14:27:15 -08:00
|
|
|
}
|
|
|
|
}
|
2024-06-07 08:24:33 -07:00
|
|
|
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
2023-02-27 14:27:15 -08:00
|
|
|
}
|
2024-05-15 08:57:41 -07:00
|
|
|
func handleVisibilityChanged2(_: String, change _: VisibilityChange, tracker _: VisibilityTracker<String>) {}
|
2024-06-08 15:53:23 -07:00
|
|
|
|
|
|
|
func rubberBandEffect(_ offset: CGFloat) -> CGFloat {
|
|
|
|
let resistance: CGFloat = 0.55
|
|
|
|
return resistance * pow(abs(offset), 0.7) * (offset < 0 ? -1 : 1)
|
|
|
|
}
|
2023-02-27 14:27:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private let itemFormatter: DateFormatter = {
|
|
|
|
let formatter = DateFormatter()
|
|
|
|
formatter.dateStyle = .short
|
|
|
|
formatter.timeStyle = .medium
|
|
|
|
return formatter
|
|
|
|
}()
|
|
|
|
|
2023-08-03 15:22:18 -07:00
|
|
|
struct DropViewDelegate2: DropDelegate {
|
|
|
|
@Binding var isDragging: Bool
|
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
func dropUpdated(info _: DropInfo) -> DropProposal? {
|
2023-08-03 15:22:18 -07:00
|
|
|
return DropProposal(operation: .move)
|
|
|
|
}
|
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
func performDrop(info _: DropInfo) -> Bool {
|
2023-08-04 12:02:22 -07:00
|
|
|
isDragging = false
|
2023-08-03 15:22:18 -07:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
func dropEntered(info _: DropInfo) {
|
2023-08-03 15:22:18 -07:00
|
|
|
isDragging = true
|
|
|
|
print("SECOND DROPPPOOO")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-02 16:26:17 -07:00
|
|
|
struct DropViewDelegate: DropDelegate {
|
|
|
|
let destinationItem: Ribbon
|
|
|
|
// @Binding var colors: [Color]
|
|
|
|
@Binding var draggedItem: Ribbon?
|
2023-08-03 12:40:12 -07:00
|
|
|
@Binding var isDragging: Bool
|
2023-08-02 16:26:17 -07:00
|
|
|
let appDatabase: AppDatabase
|
2023-11-14 13:39:46 -08:00
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
func dropUpdated(info _: DropInfo) -> DropProposal? {
|
2023-08-02 16:26:17 -07:00
|
|
|
return DropProposal(operation: .move)
|
|
|
|
}
|
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
func dropExited(info _: DropInfo) {
|
2023-08-03 12:40:12 -07:00
|
|
|
print("EXITED")
|
|
|
|
isDragging = false
|
|
|
|
}
|
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
func dropEntered(info _: DropInfo) {
|
2023-08-02 16:26:17 -07:00
|
|
|
Task {
|
2023-08-03 12:40:12 -07:00
|
|
|
isDragging = true
|
2024-05-15 08:57:41 -07:00
|
|
|
if draggedItem == nil {
|
2023-08-03 12:40:12 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-11-14 13:39:46 -08:00
|
|
|
if disableDrop {
|
2024-05-15 08:57:41 -07:00
|
|
|
return
|
2023-11-14 13:39:46 -08:00
|
|
|
}
|
2023-08-16 12:19:14 -07:00
|
|
|
|
2023-11-14 13:39:46 -08:00
|
|
|
var newRibbon = draggedItem!
|
2023-08-02 16:26:17 -07:00
|
|
|
var newDest = destinationItem
|
|
|
|
|
|
|
|
var oldPos = draggedItem!.pos
|
|
|
|
var newPos = destinationItem.pos
|
|
|
|
|
|
|
|
print("dragged item")
|
|
|
|
print(draggedItem)
|
|
|
|
print("dest item")
|
|
|
|
print(destinationItem)
|
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
if draggedItem!.id! == destinationItem.id! {
|
2023-08-02 16:26:17 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
newRibbon.pos = destinationItem.pos
|
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
_ = try await appDatabase.updateRibbonPosition(&newRibbon, oldPos, newPos)
|
2023-08-16 12:19:14 -07:00
|
|
|
disableDrop = true
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
|
|
disableDrop = false
|
|
|
|
}
|
2023-08-03 12:40:12 -07:00
|
|
|
draggedItem!.pos = newPos
|
2023-08-02 16:26:17 -07:00
|
|
|
|
2023-08-03 12:40:12 -07:00
|
|
|
// draggedItem = nil
|
2023-08-02 16:26:17 -07:00
|
|
|
}
|
|
|
|
}
|
2023-11-14 13:39:46 -08:00
|
|
|
|
2024-05-15 08:57:41 -07:00
|
|
|
func performDrop(info _: DropInfo) -> Bool {
|
2023-08-03 12:40:12 -07:00
|
|
|
print("PERFORMED DROPPPP")
|
2023-08-02 16:26:17 -07:00
|
|
|
draggedItem = nil
|
2023-08-03 12:40:12 -07:00
|
|
|
isDragging = false
|
2023-08-02 16:26:17 -07:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-27 14:27:15 -08:00
|
|
|
struct ContentView_Previews: PreviewProvider {
|
|
|
|
static var previews: some View {
|
|
|
|
ContentView()
|
|
|
|
|
|
|
|
ContentView().environment(\.appDatabase, .random())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension View {
|
2024-05-23 07:46:13 -07:00
|
|
|
@discardableResult
|
2023-02-27 14:27:15 -08:00
|
|
|
func Print(_ vars: Any...) -> some View {
|
2024-05-15 08:57:41 -07:00
|
|
|
for v in vars {
|
|
|
|
print(v)
|
|
|
|
}
|
2023-02-27 14:27:15 -08:00
|
|
|
return EmptyView()
|
|
|
|
}
|
|
|
|
}
|