Compare commits
37 Commits
2c9da90662
...
09ffa5f8e2
Author | SHA1 | Date |
---|---|---|
|
09ffa5f8e2 | |
|
495b052db0 | |
|
ac45ae5b20 | |
|
c04469ef19 | |
|
a8648b8508 | |
|
56b32c39ab | |
|
f23cff87ab | |
|
ed1377d9c5 | |
|
f202589f89 | |
|
678400c05e | |
|
0ac847c88c | |
|
6f7eed1e81 | |
|
8c1a284151 | |
|
8774723f43 | |
|
1aae36dfbc | |
|
983bba2849 | |
|
bd1923cfb4 | |
|
19ca7be678 | |
|
168a057a2a | |
|
14e1bec9e5 | |
|
08b6116a28 | |
|
efd05ce33b | |
|
4dfc93ea90 | |
|
32696d7b58 | |
|
9eb488a87a | |
|
c069860c43 | |
|
ae49a5cbfe | |
|
3a7c83c2d9 | |
|
5c48d1387a | |
|
c934dd5c21 | |
|
4615dbd433 | |
|
3746682941 | |
|
1275ad4f96 | |
|
4154f8f46d | |
|
cce075abaa | |
|
a9b85c90d1 | |
|
82fec8363e |
|
@ -0,0 +1,55 @@
|
||||||
|
//
|
||||||
|
// BackButton.swift
|
||||||
|
// gloss
|
||||||
|
//
|
||||||
|
// Created by Saint on 5/27/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
struct BackArrow: Shape {
|
||||||
|
func path(in rect: CGRect) -> Path {
|
||||||
|
var path = Path()
|
||||||
|
let width = rect.size.width
|
||||||
|
let height = rect.size.height
|
||||||
|
path.move(to: CGPoint(x: 0.83333*width, y: 0.45833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.83333*width, y: 0.54167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33333*width, y: 0.54167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33333*width, y: 0.625*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.25*width, y: 0.625*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.25*width, y: 0.54167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.16667*width, y: 0.54167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.16667*width, y: 0.45833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.25*width, y: 0.45833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.25*width, y: 0.375*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33333*width, y: 0.375*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33333*width, y: 0.45833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.83333*width, y: 0.45833*height))
|
||||||
|
path.closeSubpath()
|
||||||
|
path.move(to: CGPoint(x: 0.41667*width, y: 0.29167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33333*width, y: 0.29167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33333*width, y: 0.375*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41667*width, y: 0.375*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41667*width, y: 0.29167*height))
|
||||||
|
path.closeSubpath()
|
||||||
|
path.move(to: CGPoint(x: 0.41667*width, y: 0.29167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.5*width, y: 0.29167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.5*width, y: 0.20833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41667*width, y: 0.20833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41667*width, y: 0.29167*height))
|
||||||
|
path.closeSubpath()
|
||||||
|
path.move(to: CGPoint(x: 0.41667*width, y: 0.70833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33333*width, y: 0.70833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33333*width, y: 0.625*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41667*width, y: 0.625*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41667*width, y: 0.70833*height))
|
||||||
|
path.closeSubpath()
|
||||||
|
path.move(to: CGPoint(x: 0.41667*width, y: 0.70833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.5*width, y: 0.70833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.5*width, y: 0.79167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41667*width, y: 0.79167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41667*width, y: 0.70833*height))
|
||||||
|
path.closeSubpath()
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
//
|
||||||
|
// BookmarkIcon.swift
|
||||||
|
// gloss
|
||||||
|
//
|
||||||
|
// Created by Saint on 6/4/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct BookmarkIcon: Shape {
|
||||||
|
func path(in rect: CGRect) -> Path {
|
||||||
|
var path = Path()
|
||||||
|
let width = rect.size.width
|
||||||
|
let height = rect.size.height
|
||||||
|
path.move(to: CGPoint(x: 0.75*width, y: 0.08333*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.25*width, y: 0.08333*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.25*width, y: 0.16667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.74998*width, y: 0.16667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.74998*width, y: 0.83333*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66666*width, y: 0.83333*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66666*width, y: 0.75*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58332*width, y: 0.75*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58332*width, y: 0.66667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41666*width, y: 0.66667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41666*width, y: 0.75*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33332*width, y: 0.75*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33332*width, y: 0.83333*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.24999*width, y: 0.83333*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.24999*width, y: 0.08334*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.16666*width, y: 0.08334*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.16666*width, y: 0.91667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.24999*width, y: 0.91667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.24999*width, y: 0.91667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33332*width, y: 0.91667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.33332*width, y: 0.83333*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41666*width, y: 0.83333*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.41666*width, y: 0.75*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58332*width, y: 0.75*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58332*width, y: 0.83333*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66666*width, y: 0.83333*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66666*width, y: 0.91667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.74998*width, y: 0.91667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.74998*width, y: 0.91667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.83331*width, y: 0.91667*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.83331*width, y: 0.08334*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.75*width, y: 0.08334*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.75*width, y: 0.08333*height))
|
||||||
|
path.closeSubpath()
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
1171
CrownOfThorns.swift
1171
CrownOfThorns.swift
File diff suppressed because it is too large
Load Diff
216
Fenestra.swift
216
Fenestra.swift
|
@ -1,216 +0,0 @@
|
||||||
//
|
|
||||||
// Fenestra.swift
|
|
||||||
// gloss
|
|
||||||
//
|
|
||||||
// Created by Saint on 5/20/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import SwiftUI
|
|
||||||
import WrappingHStack
|
|
||||||
|
|
||||||
struct SegRow: View {
|
|
||||||
var seg: SegDenorm
|
|
||||||
var ribbonId: Int64
|
|
||||||
@State var highlights = Set<Int>()
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
var segSplit = seg.body.components(separatedBy: ";;")
|
|
||||||
let decoder = JSONDecoder()
|
|
||||||
var retView = WrappingHStack(alignment: .leading, horizontalSpacing: 0) {
|
|
||||||
ForEach(0 ..< segSplit.count, id: \.self) { segIndex in
|
|
||||||
|
|
||||||
let verse = try! decoder.decode(Verse.self, from: segSplit[segIndex].data(using: .utf8)!)
|
|
||||||
let arrayOfText = verse.body.components(separatedBy: " ")
|
|
||||||
|
|
||||||
let lineHeight = CGFloat(30)
|
|
||||||
let fontSize = CGFloat(20)
|
|
||||||
let highlightColor = "470000"
|
|
||||||
ForEach(0 ..< arrayOfText.count, id: \.self) { index in
|
|
||||||
HStack(spacing: 0) {
|
|
||||||
if index == 0 {
|
|
||||||
Text("")
|
|
||||||
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
|
||||||
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
|
||||||
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
ZStack {
|
|
||||||
Text(" ")
|
|
||||||
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
|
||||||
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
|
||||||
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
|
||||||
Text(String(verse.verse))
|
|
||||||
.font(Font.custom("AveriaSerifLibre-Regular", size: 10))
|
|
||||||
.padding(.horizontal, 0)
|
|
||||||
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
|
||||||
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
|
||||||
}
|
|
||||||
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(arrayOfText[index])
|
|
||||||
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
|
||||||
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
|
||||||
.padding(.horizontal, 1.5) // intra word spacing
|
|
||||||
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
|
||||||
.foregroundColor(Color.white)
|
|
||||||
.onTapGesture {
|
|
||||||
Print(arrayOfText[index])
|
|
||||||
Print(verse.verse)
|
|
||||||
if self.highlights.contains(verse.verse) {
|
|
||||||
self.highlights.remove(verse.verse)
|
|
||||||
} else {
|
|
||||||
self.highlights.insert(verse.verse)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(height: 16) // intra line spacing
|
|
||||||
.padding(.vertical, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// struct Fenestra: View {
|
|
||||||
// @State var segs: [SegDenorm]
|
|
||||||
// @State var selectedRibbon: [Ribbon]
|
|
||||||
|
|
||||||
// @State var dragOffset = CGFloat()
|
|
||||||
|
|
||||||
// @State var refresh: Bool = false
|
|
||||||
// @State var refresh2: Bool = false
|
|
||||||
|
|
||||||
// // var handleVisibilityChanged: (String, VisibilityChange, VisibilityTracker<String>) -> Void
|
|
||||||
|
|
||||||
// var body: some View {
|
|
||||||
// ScrollViewReader { proxy in
|
|
||||||
// VisibilityTrackingScrollView(action: handleVisibilityChanged) {
|
|
||||||
// LazyVStack {
|
|
||||||
// ForEach(segs) { seg in
|
|
||||||
// SegRow(seg: seg,
|
|
||||||
// ribbonId: selectedRibbon[0].id!)
|
|
||||||
// .id("\(seg.id)")
|
|
||||||
// .offset(x: -dragOffset)
|
|
||||||
// .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")
|
|
||||||
// goToRibbon(selectedRibbon: selectedRibbon[0],
|
|
||||||
// destRibbon: selectedRibbon[0],
|
|
||||||
// scrollId: $scrollId,
|
|
||||||
// scrollOffset: $scrollOffset,
|
|
||||||
// refresh: $refresh,
|
|
||||||
// showOverlay: $showOverlay,
|
|
||||||
// appDatabase: appDatabase,
|
|
||||||
// loading: true)
|
|
||||||
// }
|
|
||||||
// .onChange(of: refresh) { _ in
|
|
||||||
// Task {
|
|
||||||
// DispatchQueue.main.async {
|
|
||||||
// Print("scroll Id target: \(scrollId)")
|
|
||||||
// proxy.scrollTo(scrollId!, anchor: .top)
|
|
||||||
|
|
||||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
||||||
// Print(" scroll id target", scrollId)
|
|
||||||
// Print(" current id ", currentId)
|
|
||||||
// Print(gTracker!.sortedViewIDs)
|
|
||||||
// if currentId! != scrollId! {
|
|
||||||
// Print("NO MATCH")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Print(" current offset ", gTracker!.visibleViews[scrollId!])
|
|
||||||
// var curOffset = gTracker!.visibleViews[scrollId!]
|
|
||||||
// Print(" stats", gTracker!.visibleViews)
|
|
||||||
// if curOffset != nil {
|
|
||||||
// setScrollOffset = CGFloat(Int(scrollOffset!) - Int(curOffset!))
|
|
||||||
// Print("applying scroll offset \(setScrollOffset)")
|
|
||||||
// refresh2.toggle()
|
|
||||||
// } else {
|
|
||||||
// var adjust = (Int(scrollId!)! - Int(currentId!)!) * 200
|
|
||||||
// Print("adjusting \(adjust)")
|
|
||||||
// setScrollOffset = CGFloat(adjust)
|
|
||||||
// refresh.toggle()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in Print("introspect")
|
|
||||||
// if setScrollOffset != nil {
|
|
||||||
// DispatchQueue.main.async {
|
|
||||||
// scrollView.contentOffset.y = scrollView.contentOffset.y + setScrollOffset!
|
|
||||||
// setScrollOffset = nil
|
|
||||||
// withAnimation {
|
|
||||||
// showOverlay = false
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .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(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
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .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
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func handleVisibilityChanged(_: String, change _: VisibilityChange, tracker: VisibilityTracker<String>) {
|
|
||||||
// // var printRate: Int64 = 10
|
|
||||||
// gTracker = tracker
|
|
||||||
|
|
||||||
// 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!]!
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// ForwardArrow.swift
|
||||||
|
// gloss
|
||||||
|
//
|
||||||
|
// Created by Saint on 5/27/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ForwardArrow: Shape {
|
||||||
|
func path(in rect: CGRect) -> Path {
|
||||||
|
var path = Path()
|
||||||
|
let width = rect.size.width
|
||||||
|
let height = rect.size.height
|
||||||
|
path.move(to: CGPoint(x: 0.16667*width, y: 0.45833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.16667*width, y: 0.54167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66667*width, y: 0.54167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66667*width, y: 0.625*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.75*width, y: 0.625*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.75*width, y: 0.54167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.83333*width, y: 0.54167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.83333*width, y: 0.45833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.75*width, y: 0.45833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.75*width, y: 0.375*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66667*width, y: 0.375*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66667*width, y: 0.45833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.16667*width, y: 0.45833*height))
|
||||||
|
path.closeSubpath()
|
||||||
|
path.move(to: CGPoint(x: 0.58333*width, y: 0.29167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66667*width, y: 0.29167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66667*width, y: 0.375*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58333*width, y: 0.375*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58333*width, y: 0.29167*height))
|
||||||
|
path.closeSubpath()
|
||||||
|
path.move(to: CGPoint(x: 0.58333*width, y: 0.29167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.5*width, y: 0.29167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.5*width, y: 0.20833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58333*width, y: 0.20833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58333*width, y: 0.29167*height))
|
||||||
|
path.closeSubpath()
|
||||||
|
path.move(to: CGPoint(x: 0.58333*width, y: 0.70833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66667*width, y: 0.70833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.66667*width, y: 0.625*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58333*width, y: 0.625*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58333*width, y: 0.70833*height))
|
||||||
|
path.closeSubpath()
|
||||||
|
path.move(to: CGPoint(x: 0.58333*width, y: 0.70833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.5*width, y: 0.70833*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.5*width, y: 0.79167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58333*width, y: 0.79167*height))
|
||||||
|
path.addLine(to: CGPoint(x: 0.58333*width, y: 0.70833*height))
|
||||||
|
path.closeSubpath()
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
|
||||||
|
import GRDB
|
||||||
|
import GRDBQuery
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct NaviBar: View {
|
||||||
|
@ObservedObject var paneConnector: PaneConnector
|
||||||
|
|
||||||
|
@Query(RibbonRequest(dir: .prev, groupId: 1)) private var backRibbon: [Ribbon]
|
||||||
|
@Query(RibbonRequest(dir: .next, groupId: 1)) private var nextRibbon: [Ribbon]
|
||||||
|
|
||||||
|
@Query(SelectedRibbonRequest()) private var selectedRibbon: [Ribbon]
|
||||||
|
@Environment(\.appDatabase) private var appDatabase
|
||||||
|
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
var iconSize = CGFloat(25)
|
||||||
|
VStack {
|
||||||
|
HStack(spacing: 30) {
|
||||||
|
BackArrow()
|
||||||
|
.frame(width: iconSize, height: iconSize)
|
||||||
|
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||||
|
.if(paneConnector.hasMoved) { $0.foregroundColor(Color.black) }
|
||||||
|
.if(!paneConnector.hasMoved) { $0.foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4))) }
|
||||||
|
.onTapGesture {
|
||||||
|
Task {
|
||||||
|
var br = backRibbon
|
||||||
|
var sr = selectedRibbon
|
||||||
|
|
||||||
|
do {
|
||||||
|
if paneConnector.hasMoved {
|
||||||
|
print("has moved")
|
||||||
|
|
||||||
|
let updatedRibbon = try await createUndoState(selectedRibbon: sr[0],
|
||||||
|
appDatabase: appDatabase,
|
||||||
|
paneConnector: paneConnector)
|
||||||
|
goToRibbon(selectedRibbon: sr[0],
|
||||||
|
destRibbon: sr[0],
|
||||||
|
appDatabase: appDatabase,
|
||||||
|
paneConnector: paneConnector,
|
||||||
|
loading: false)
|
||||||
|
} else {
|
||||||
|
if backRibbon.count == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try await appDatabase.undoRibbon(&sr[0])
|
||||||
|
|
||||||
|
goToRibbon(selectedRibbon: sr[0],
|
||||||
|
destRibbon: br[0],
|
||||||
|
appDatabase: appDatabase,
|
||||||
|
paneConnector: paneConnector,
|
||||||
|
loading: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
print("Back Arrow Error info: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BookmarkIcon()
|
||||||
|
.frame(width: iconSize, height: iconSize)
|
||||||
|
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))
|
||||||
|
|
||||||
|
ForwardArrow()
|
||||||
|
.frame(width: iconSize, height: iconSize)
|
||||||
|
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))
|
||||||
|
.onTapGesture {
|
||||||
|
if nextRibbon.count == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Task {
|
||||||
|
var sr = selectedRibbon[0]
|
||||||
|
var nr = nextRibbon[0]
|
||||||
|
try await appDatabase.redoRibbon(&sr)
|
||||||
|
|
||||||
|
goToRibbon(selectedRibbon: sr,
|
||||||
|
destRibbon: nr,
|
||||||
|
appDatabase: appDatabase,
|
||||||
|
paneConnector: paneConnector,
|
||||||
|
loading: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rectangle()
|
||||||
|
// .fill(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||||
|
// .frame(width: iconSize, height: iconSize)
|
||||||
|
|
||||||
|
// BackArrow()
|
||||||
|
// .frame(width: iconSize, height: iconSize)
|
||||||
|
// .background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||||
|
|
||||||
|
// BookmarkIcon()
|
||||||
|
// .frame(width: iconSize, height: iconSize)
|
||||||
|
// .background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||||
|
// .foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))
|
||||||
|
|
||||||
|
// ForwardArrow()
|
||||||
|
// .frame(width: iconSize, height: iconSize)
|
||||||
|
// .background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||||
|
// .foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.cornerRadius(5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,287 @@
|
||||||
|
//
|
||||||
|
// Fenestra.swift
|
||||||
|
// gloss
|
||||||
|
//
|
||||||
|
// Created by Saint on 5/20/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import GRDB
|
||||||
|
import GRDBQuery
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
import WrappingHStack
|
||||||
|
|
||||||
|
struct SegRow: View {
|
||||||
|
var seg: SegDenorm
|
||||||
|
var ribbonId: Int64
|
||||||
|
var width: CGFloat
|
||||||
|
@State var highlights = Set<Int>()
|
||||||
|
@ObservedObject var showTitle: ShowTitle
|
||||||
|
|
||||||
|
|
||||||
|
let intraWordSpacing = CGFloat(1.6)
|
||||||
|
var body: some View {
|
||||||
|
var segSplit = seg.body.components(separatedBy: ";;")
|
||||||
|
let decoder = JSONDecoder()
|
||||||
|
var retView = VStack {
|
||||||
|
Text("\(seg.chap)")
|
||||||
|
.frame(width: width)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
|
||||||
|
.if(showTitle.chapVisible) { $0.foregroundColor(mainTextColor) }
|
||||||
|
.if(!showTitle.chapVisible) { $0.foregroundColor(secondBackgroundColor) }
|
||||||
|
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
.background(secondBackgroundColor)
|
||||||
|
// .background(mainBackgroundColor)
|
||||||
|
.onTapGesture {
|
||||||
|
showTitle.chapVisible.toggle()
|
||||||
|
}
|
||||||
|
WrappingHStack(alignment: .leading, horizontalSpacing: 0) {
|
||||||
|
ForEach(0 ..< segSplit.count, id: \.self) { segIndex in
|
||||||
|
|
||||||
|
let verse = try! decoder.decode(Verse.self, from: segSplit[segIndex].data(using: .utf8)!)
|
||||||
|
let arrayOfText = verse.body.components(separatedBy: " ")
|
||||||
|
|
||||||
|
let lineHeight = CGFloat(30)
|
||||||
|
let fontSize = CGFloat(18)
|
||||||
|
let highlightColor = "470000"
|
||||||
|
ForEach(0 ..< arrayOfText.count, id: \.self) { index in
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
if index == 0 {
|
||||||
|
Text("")
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
||||||
|
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
ZStack {
|
||||||
|
Text(" ")
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
||||||
|
Text(String(verse.verse))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: 10))
|
||||||
|
.padding(.horizontal, 0)
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
||||||
|
}
|
||||||
|
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(arrayOfText[index])
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
|
||||||
|
.padding(.horizontal, intraWordSpacing) // intra word spacing
|
||||||
|
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
||||||
|
.foregroundColor(Color.white)
|
||||||
|
.onTapGesture {
|
||||||
|
Print(arrayOfText[index])
|
||||||
|
Print(verse.verse)
|
||||||
|
if self.highlights.contains(verse.verse) {
|
||||||
|
self.highlights.remove(verse.verse)
|
||||||
|
} else {
|
||||||
|
self.highlights.insert(verse.verse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(height: 16) // intra line spacing
|
||||||
|
.padding(.vertical, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShowTitle: NSObject, ObservableObject {
|
||||||
|
@Published var chapVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Pane: View {
|
||||||
|
@ObservedObject var paneConnector: PaneConnector
|
||||||
|
@StateObject var showTitle = ShowTitle()
|
||||||
|
|
||||||
|
// @State var chapVisible = false
|
||||||
|
|
||||||
|
@State var selectedRibbon: [Ribbon]
|
||||||
|
|
||||||
|
@State var width: CGFloat
|
||||||
|
@State var height: CGFloat
|
||||||
|
|
||||||
|
|
||||||
|
var dragOffset = CGFloat()
|
||||||
|
|
||||||
|
@State var refresh: Bool = false
|
||||||
|
|
||||||
|
@Query(SegDenormRequest(book: "bible.mark")) private var segs: [SegDenorm]
|
||||||
|
|
||||||
|
|
||||||
|
@Environment(\.appDatabase) private var appDatabase
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
var adjustedHeight = height - paneConnector.vertSep
|
||||||
|
ZStack {
|
||||||
|
ScrollViewReader { proxy in
|
||||||
|
VisibilityTrackingScrollView(action: handleVisibilityChanged) {
|
||||||
|
LazyVStack {
|
||||||
|
ForEach(segs) { seg in
|
||||||
|
SegRow(seg: seg,
|
||||||
|
ribbonId: selectedRibbon[0].id!,
|
||||||
|
width: width,
|
||||||
|
showTitle: showTitle
|
||||||
|
)
|
||||||
|
.id("\(seg.id)")
|
||||||
|
.offset(x: -dragOffset)
|
||||||
|
.padding(EdgeInsets(top: 10, leading: 20, bottom: 20, trailing: 20))
|
||||||
|
.trackVisibility(id: "\(seg.id)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background(Color(red: 0.18, green: 0.18, blue: 0.18))
|
||||||
|
}
|
||||||
|
|
||||||
|
.onAppear {
|
||||||
|
goToRibbon(selectedRibbon: selectedRibbon[0],
|
||||||
|
destRibbon: selectedRibbon[0],
|
||||||
|
appDatabase: appDatabase,
|
||||||
|
paneConnector: paneConnector,
|
||||||
|
loading: true)
|
||||||
|
}
|
||||||
|
.onChange(of: paneConnector.refresh) { _ in
|
||||||
|
print("inside change")
|
||||||
|
print("on change")
|
||||||
|
|
||||||
|
Task {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if paneConnector.visibilityTracker == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let gTracker = paneConnector.visibilityTracker!
|
||||||
|
|
||||||
|
Print("scroll Id target: \(paneConnector.scrollId)")
|
||||||
|
proxy.scrollTo(paneConnector.scrollId, anchor: .top)
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
|
Print(" scroll id target", paneConnector.scrollId)
|
||||||
|
Print(" current id ", paneConnector.currentId)
|
||||||
|
Print(gTracker.sortedViewIDs)
|
||||||
|
if paneConnector.currentId != paneConnector.scrollId {
|
||||||
|
Print("NO MATCH")
|
||||||
|
}
|
||||||
|
|
||||||
|
Print(" current offset ", gTracker.visibleViews[paneConnector.scrollId])
|
||||||
|
var curOffset = gTracker.visibleViews[paneConnector.scrollId]
|
||||||
|
Print(" stats", gTracker.visibleViews)
|
||||||
|
if curOffset != nil {
|
||||||
|
paneConnector.setScrollOffset = CGFloat(Int(paneConnector.scrollOffset) - Int(curOffset!))
|
||||||
|
// setScrollOffset = CGFloat(Int(paneConnector.scrollOffset) - Int(curOffset!))
|
||||||
|
// setScrollOffset = CGFloat(Int(paneConnector.scrollOffset) - Int(curOffset!))
|
||||||
|
Print("applying scroll offset \(paneConnector.setScrollOffset)")
|
||||||
|
|
||||||
|
// paneConnector = paneConnector.copy() as! PaneConnector
|
||||||
|
self.refresh.toggle()
|
||||||
|
} else {
|
||||||
|
var adjust = (Int(paneConnector.scrollId)! - Int(paneConnector.currentId)!) * 200
|
||||||
|
Print("adjusting \(adjust)")
|
||||||
|
|
||||||
|
paneConnector.setScrollOffset = CGFloat(adjust)
|
||||||
|
refresh.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in
|
||||||
|
Print("introspect")
|
||||||
|
|
||||||
|
// Weird hack for reactivity
|
||||||
|
if self.refresh {
|
||||||
|
let reactive = self.refresh
|
||||||
|
}
|
||||||
|
|
||||||
|
// if self.paneConnector != nil {
|
||||||
|
// let reactive = self.paneConnector
|
||||||
|
// }
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if paneConnector.setScrollOffset != nil {
|
||||||
|
scrollView.contentOffset.y = scrollView.contentOffset.y + paneConnector.setScrollOffset!
|
||||||
|
paneConnector.setScrollOffset = nil
|
||||||
|
|
||||||
|
withAnimation(.easeIn(duration: 0.2)) {
|
||||||
|
paneConnector.showOverlay = false
|
||||||
|
self.refresh.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Print("end introspect")
|
||||||
|
}
|
||||||
|
.listStyle(PlainListStyle())
|
||||||
|
}
|
||||||
|
.zIndex(1)
|
||||||
|
.background(Color(red: 0.2, green: 0.2, blue: 0.2))
|
||||||
|
.frame(width: width, height: adjustedHeight)
|
||||||
|
|
||||||
|
if self.paneConnector.showOverlay {
|
||||||
|
Rectangle()
|
||||||
|
// .frame(width: width, height: height + 200)
|
||||||
|
.background(.ultraThinMaterial)
|
||||||
|
.opacity(0.98)
|
||||||
|
.offset(y: -50)
|
||||||
|
.frame(width: width, height: adjustedHeight + 100)
|
||||||
|
|
||||||
|
// .blur(radius: 0.8)
|
||||||
|
// .opacity(1)
|
||||||
|
.transition(.opacity)
|
||||||
|
|
||||||
|
// .frame(width: geometry.size.width - 50)
|
||||||
|
// .offset(x: pulledOut.width)
|
||||||
|
// .offset(x: viewState.width, y: viewState.height)
|
||||||
|
.zIndex(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(width: width, height: adjustedHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleVisibilityChanged(_: String, change _: VisibilityChange, tracker: VisibilityTracker<String>) {
|
||||||
|
// var printRate: Int64 = 10
|
||||||
|
// gTracker = tracker
|
||||||
|
self.paneConnector.visibilityTracker = tracker
|
||||||
|
|
||||||
|
|
||||||
|
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 self.paneConnector.currentId != tracker.sortedViewIDs[0] {
|
||||||
|
self.paneConnector.currentId = tracker.sortedViewIDs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
self.paneConnector.currentOffset = tracker.visibleViews[currentId!]!
|
||||||
|
|
||||||
|
if self.paneConnector.currentId == self.paneConnector.scrollId
|
||||||
|
&& abs(self.paneConnector.currentOffset - self.paneConnector.scrollOffset) < 10 {
|
||||||
|
if self.paneConnector.hasMoved {
|
||||||
|
self.paneConnector.hasMoved = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !self.paneConnector.hasMoved {
|
||||||
|
self.paneConnector.hasMoved = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -2,36 +2,25 @@ import Combine
|
||||||
import GRDB
|
import GRDB
|
||||||
import GRDBQuery
|
import GRDBQuery
|
||||||
|
|
||||||
/// A player request can be used with the `@Query` property wrapper in order to
|
|
||||||
/// feed a view with a list of players.
|
|
||||||
///
|
|
||||||
/// For example:
|
|
||||||
///
|
|
||||||
/// struct MyView: View {
|
|
||||||
/// @Query(RibbonRequest(ordering: .byName)) private var players: [Ribbon]
|
|
||||||
///
|
|
||||||
/// var body: some View {
|
|
||||||
/// List(players) { player in ... )
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
|
|
||||||
|
|
||||||
var idColumn = Column("id")
|
var idColumn = Column("id")
|
||||||
struct RibbonRequest: Queryable {
|
struct RibbonRequest: Queryable {
|
||||||
// enum Ordering {
|
enum UndoDir {
|
||||||
// case byScore
|
case prev
|
||||||
// case byName
|
case next
|
||||||
// }
|
case current
|
||||||
|
}
|
||||||
|
|
||||||
/// The ordering used by the player request.
|
/// The ordering used by the player request.
|
||||||
// var ordering: Ordering
|
// var ordering: Ordering
|
||||||
var id: Int64!
|
var id: Int64!
|
||||||
|
var dir: UndoDir?
|
||||||
|
var groupId: Int?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Queryable Implementation
|
// MARK: - Queryable Implementation
|
||||||
|
|
||||||
static var defaultValue: [Ribbon] { [] }
|
static var defaultValue: [Ribbon] { [] }
|
||||||
|
|
||||||
func publisher(in appDatabase: AppDatabase) -> AnyPublisher<[Ribbon], Error> {
|
func publisher(in appDatabase: AppDatabase) -> AnyPublisher<[Ribbon], Error> {
|
||||||
// Build the publisher from the general-purpose read-only access
|
// Build the publisher from the general-purpose read-only access
|
||||||
// granted by `appDatabase.reader`.
|
// granted by `appDatabase.reader`.
|
||||||
|
@ -46,15 +35,111 @@ struct RibbonRequest: Queryable {
|
||||||
scheduling: .immediate)
|
scheduling: .immediate)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is not required by Queryable, but it makes it easier
|
// This method is not required by Queryable, but it makes it easier
|
||||||
func fetchValue(_ db: Database) throws -> [Ribbon] {
|
func fetchValue(_ db: Database) throws -> [Ribbon] {
|
||||||
if (id == nil) {
|
|
||||||
return try Ribbon.order(Column("pos")).fetchAll(db)
|
var ret: [Ribbon]
|
||||||
} else {
|
var sql: String
|
||||||
return try Ribbon.filter(idColumn == id).fetchAll(db)
|
|
||||||
|
// this has to be a global variable
|
||||||
|
let totalLevels = 3
|
||||||
|
|
||||||
|
do {
|
||||||
|
if dir != nil && groupId != nil {
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
SELECT * FROM Ribbon \
|
||||||
|
WHERE groupId = ?
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
ret = try Ribbon.fetchAll(db, sql: sql, arguments: [groupId])
|
||||||
|
|
||||||
|
let currentLevel = ret[0].currentLevel
|
||||||
|
let minLevel = ret[0].minLevel
|
||||||
|
let maxLevel = ret[0].maxLevel
|
||||||
|
|
||||||
|
if dir == .current {
|
||||||
|
sql = """
|
||||||
|
SELECT * FROM Ribbon \
|
||||||
|
WHERE groupId = ?
|
||||||
|
AND undoLevel = ?
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
ret = try Ribbon.fetchAll(db, sql: sql, arguments: [groupId, currentLevel])
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
var newCurrentLevel = (currentLevel - 1) %% totalLevels
|
||||||
|
|
||||||
|
// probably need more error checking to check
|
||||||
|
// if current level gets into an error state
|
||||||
|
// between minLevel and maxLevel somehow but there
|
||||||
|
// are probably a bunch of edge cases casue of the
|
||||||
|
// mod stuff
|
||||||
|
if dir == .prev {
|
||||||
|
print("calling ribbon request prev")
|
||||||
|
// no back undo steps left
|
||||||
|
if currentLevel == minLevel {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
} else if dir == .next {
|
||||||
|
|
||||||
|
// no forward redo steps left
|
||||||
|
if currentLevel == maxLevel {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
newCurrentLevel = (currentLevel + 1) %% totalLevels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
SELECT * FROM Ribbon \
|
||||||
|
WHERE groupId = ? AND
|
||||||
|
undoLevel = ?
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
ret = try Ribbon.fetchAll(db, sql: sql, arguments: [groupId, newCurrentLevel])
|
||||||
|
print("dog returning: \(ret)")
|
||||||
|
return ret
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if groupId != nil {
|
||||||
|
let sql = """
|
||||||
|
SELECT * from Ribbon r1 \
|
||||||
|
WHERE r1.groupId = ?
|
||||||
|
ORDER BY undoLevel ASC
|
||||||
|
"""
|
||||||
|
|
||||||
|
var ret = try Ribbon.fetchAll(db, sql: sql, arguments: [groupId])
|
||||||
|
print("all fetching ribbons: \(ret)")
|
||||||
|
return ret
|
||||||
|
} else {
|
||||||
|
|
||||||
|
let sql = """
|
||||||
|
select distinct r1.* from Ribbon r1 join Ribbon r2 ON \
|
||||||
|
r1.undoLevel = r2.currentLevel AND r1.id = r2.id ORDER BY pos ASC
|
||||||
|
"""
|
||||||
|
var ret = try Ribbon.fetchAll(db, sql: sql)
|
||||||
|
print("xxxxx fetching ribbons")
|
||||||
|
print(ret)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print(error.localizedDescription)
|
||||||
|
print(error)
|
||||||
|
print("Error")
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if id == nil {
|
||||||
|
// return try Ribbon.order(Column("pos")).fetchAll(db)
|
||||||
|
// } else {
|
||||||
|
// return try Ribbon.filter(idColumn == id).fetchAll(db)
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
|
||||||
// if book == "" {
|
// if book == "" {
|
||||||
// return try Ribbon.filter(bookColumn == Ribbon.randomBook()).fetchAll(db)
|
// return try Ribbon.filter(bookColumn == Ribbon.randomBook()).fetchAll(db)
|
||||||
// } else {
|
// } else {
|
||||||
|
@ -69,3 +154,14 @@ struct RibbonRequest: Queryable {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
infix operator %%
|
||||||
|
|
||||||
|
extension Int {
|
||||||
|
|
||||||
|
static func %% (_ left: Int, _ right: Int) -> Int {
|
||||||
|
let mod = left % right
|
||||||
|
return mod >= 0 ? mod : mod + right
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ struct SegDenorm: Identifiable, Equatable {
|
||||||
var body: String
|
var body: String
|
||||||
// var lineIds: [Int64]
|
// var lineIds: [Int64]
|
||||||
var book: String
|
var book: String
|
||||||
|
var chap: Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SegDenorm {
|
extension SegDenorm {
|
||||||
|
@ -27,7 +28,7 @@ extension SegDenorm: Codable, FetchableRecord, MutablePersistableRecord {
|
||||||
// static let id = Column(CodingKeys.id)
|
// static let id = Column(CodingKeys.id)
|
||||||
// static let book = Column(CodingKeys.book)
|
// static let book = Column(CodingKeys.book)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// /// Updates a player id after it has been inserted in the database.
|
// /// Updates a player id after it has been inserted in the database.
|
||||||
// mutating func didInsert(_ inserted: InsertionSuccess) {
|
// mutating func didInsert(_ inserted: InsertionSuccess) {
|
||||||
// id = inserted.rowID
|
// id = inserted.rowID
|
||||||
|
|
|
@ -11,9 +11,9 @@ struct SegDenormRequest: Queryable {
|
||||||
var book: String
|
var book: String
|
||||||
|
|
||||||
// MARK: - Queryable Implementation
|
// MARK: - Queryable Implementation
|
||||||
|
|
||||||
static var defaultValue: [SegDenorm] { [] }
|
static var defaultValue: [SegDenorm] { [] }
|
||||||
|
|
||||||
func publisher(in appDatabase: AppDatabase) -> AnyPublisher<[SegDenorm], Error> {
|
func publisher(in appDatabase: AppDatabase) -> AnyPublisher<[SegDenorm], Error> {
|
||||||
ValueObservation
|
ValueObservation
|
||||||
.tracking(fetchValue(_:))
|
.tracking(fetchValue(_:))
|
||||||
|
@ -22,17 +22,51 @@ struct SegDenormRequest: Queryable {
|
||||||
scheduling: .immediate)
|
scheduling: .immediate)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchValue(_ db: Database) throws -> [SegDenorm] {
|
|
||||||
print("WOOOOOOF")
|
|
||||||
var sql = "select seg_id as id, seg.book as book, group_concat(line.body, ';;') as body from seg join line on seg.line_id = line.rowid WHERE seg.book = 'bible.john' group by seg.seg_id"
|
|
||||||
|
|
||||||
do {
|
func fetchValue(_ db: Database) throws -> [SegDenorm] {
|
||||||
var ret = try SegDenorm.fetchAll(db, sql: sql) // [Player]
|
// print("woof segs denorm fetching for \(book)")
|
||||||
|
// print(book)
|
||||||
|
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
print("fetching segs")
|
||||||
|
|
||||||
|
var ret = try Ribbon.fetchAll(db, sql: """
|
||||||
|
SELECT Ribbon.* FROM SelectedRibbon \
|
||||||
|
JOIN (select distinct r1.* from Ribbon r1 join Ribbon r2 ON \
|
||||||
|
r1.undoLevel = r2.currentLevel AND r1.id = r2.id ORDER BY pos ASC) as Ribbon \
|
||||||
|
ON SelectedRibbon.ribbonGroupId = Ribbon.groupId \
|
||||||
|
WHERE SelectedRibbon.rowId = 1
|
||||||
|
""")
|
||||||
|
// print("Selected Ribbon query result: \(ret)")
|
||||||
|
var book = ret[0].book
|
||||||
|
|
||||||
|
var sql = """
|
||||||
|
select seg_id as id, line.chap as chap, seg.book as book, group_concat(line.body, ';;') as body from \
|
||||||
|
(select * from seg where seg.book = '\(book)') as seg \
|
||||||
|
join (select * from line where line.book = '\(book)') as line \
|
||||||
|
on seg.line_id = line.line_id group by seg.seg_id
|
||||||
|
"""
|
||||||
|
|
||||||
|
var ret2 = try SegDenorm.fetchAll(db, sql: sql)
|
||||||
|
|
||||||
// print("SEGS DENORM")
|
// print("SEGS DENORM")
|
||||||
// print(ret)
|
// print(ret[0])
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
|
// var sql2 = """
|
||||||
|
// select count(1) from seg where seg.book = '\(book)'
|
||||||
|
// """
|
||||||
|
|
||||||
|
// var ret2 = try SegDenorm.fetchAll(db, sql: sql2)
|
||||||
|
|
||||||
|
// print("test sql result")
|
||||||
|
// print(ret2[0])
|
||||||
|
|
||||||
|
return ret2
|
||||||
|
|
||||||
} catch let error {
|
} catch let error {
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
print(error)
|
print(error)
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
@State var selection = 0
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
BackArrow()
|
||||||
|
.frame(width: CGFloat(30), height: CGFloat(30))
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))
|
||||||
|
.if(selection == 0) { $0.background(Color.white) }
|
||||||
|
.if(selection != 0) { $0.background(Color.black) }
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation(.spring(response: 0.5)) {
|
||||||
|
self.selection = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ForwardArrow()
|
||||||
|
.frame(width: CGFloat(30), height: CGFloat(30))
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))
|
||||||
|
.if(selection == 1) { $0.background(Color.white) }
|
||||||
|
.if(selection != 1) { $0.background(Color.black) }
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation(.spring(response: 0.5)) {
|
||||||
|
self.selection = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ struct SelectedRibbon: Identifiable, Equatable {
|
||||||
/// Int64 is the recommended type for auto-incremented database ids.
|
/// Int64 is the recommended type for auto-incremented database ids.
|
||||||
/// Use nil for players that are not inserted yet in the database.
|
/// Use nil for players that are not inserted yet in the database.
|
||||||
var id: Int64?
|
var id: Int64?
|
||||||
var ribbonId: Int64
|
var ribbonGroupId: Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SelectedRibbon {
|
extension SelectedRibbon {
|
||||||
|
@ -32,9 +32,9 @@ extension SelectedRibbon: Codable, FetchableRecord, MutablePersistableRecord {
|
||||||
// Define database columns from CodingKeys
|
// Define database columns from CodingKeys
|
||||||
fileprivate enum Columns {
|
fileprivate enum Columns {
|
||||||
static let id = Column(CodingKeys.id)
|
static let id = Column(CodingKeys.id)
|
||||||
static let ribbonId = Column(CodingKeys.ribbonId)
|
static let ribbonGroupId = Column(CodingKeys.ribbonGroupId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a player id after it has been inserted in the database.
|
/// Updates a player id after it has been inserted in the database.
|
||||||
mutating func didInsert(_ inserted: InsertionSuccess) {
|
mutating func didInsert(_ inserted: InsertionSuccess) {
|
||||||
id = inserted.rowID
|
id = inserted.rowID
|
||||||
|
|
|
@ -24,7 +24,7 @@ struct SelectedRibbonRequest: Queryable {
|
||||||
/// The ordering used by the player request.
|
/// The ordering used by the player request.
|
||||||
// var ordering: Ordering
|
// var ordering: Ordering
|
||||||
static var defaultValue: [Ribbon] { [] }
|
static var defaultValue: [Ribbon] { [] }
|
||||||
|
|
||||||
func publisher(in appDatabase: AppDatabase) -> AnyPublisher<[Ribbon], Error> {
|
func publisher(in appDatabase: AppDatabase) -> AnyPublisher<[Ribbon], Error> {
|
||||||
// Build the publisher from the general-purpose read-only access
|
// Build the publisher from the general-purpose read-only access
|
||||||
// granted by `appDatabase.reader`.
|
// granted by `appDatabase.reader`.
|
||||||
|
@ -39,7 +39,7 @@ struct SelectedRibbonRequest: Queryable {
|
||||||
scheduling: .immediate)
|
scheduling: .immediate)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is not required by Queryable, but it makes it easier
|
// This method is not required by Queryable, but it makes it easier
|
||||||
func fetchValue(_ db: Database) throws -> [Ribbon] {
|
func fetchValue(_ db: Database) throws -> [Ribbon] {
|
||||||
|
|
||||||
|
@ -51,11 +51,15 @@ struct SelectedRibbonRequest: Queryable {
|
||||||
// var ret3 = try Ribbon.fetchAll(db, sql: "SELECT * FROM Ribbon") // [Player]
|
// var ret3 = try Ribbon.fetchAll(db, sql: "SELECT * FROM Ribbon") // [Player]
|
||||||
// print(ret3)
|
// print(ret3)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// print("FETCH JOIN RIBBON")
|
// print("FETCH JOIN RIBBON")
|
||||||
var ret = try Ribbon.fetchAll(db, sql: "SELECT Ribbon.* FROM SelectedRibbon join Ribbon on SelectedRibbon.ribbonId = ribbon.rowId WHERE SelectedRibbon.rowId = 1") // [Player]
|
var ret = try Ribbon.fetchAll(db, sql: """
|
||||||
// print(ret)
|
SELECT Ribbon.* FROM SelectedRibbon \
|
||||||
|
JOIN (select distinct r1.* from Ribbon r1 join Ribbon r2 ON \
|
||||||
|
r1.undoLevel = r2.currentLevel AND r1.id = r2.id ORDER BY pos ASC) as Ribbon \
|
||||||
|
ON SelectedRibbon.ribbonGroupId = Ribbon.groupId \
|
||||||
|
WHERE SelectedRibbon.rowId = 1
|
||||||
|
""")
|
||||||
|
// print("Selected Ribbon query result: \(ret)")
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
import GRDB
|
||||||
|
import GRDBQuery
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
var fontSize = CGFloat(12)
|
||||||
|
|
||||||
|
struct StatsPanel: View {
|
||||||
|
@ObservedObject var paneConnector: PaneConnector
|
||||||
|
|
||||||
|
@Query(RibbonRequest(dir: .prev, groupId: 1)) private var backRibbon: [Ribbon]
|
||||||
|
@Query(RibbonRequest(dir: .next, groupId: 1)) private var nextRibbon: [Ribbon]
|
||||||
|
@Query(RibbonRequest(groupId: 1)) private var allRibbons: [Ribbon]
|
||||||
|
@Query(SelectedRibbonRequest()) private var selectedRibbon: [Ribbon]
|
||||||
|
@Environment(\.appDatabase) private var appDatabase
|
||||||
|
|
||||||
|
let columnCount = 2
|
||||||
|
var columns: [GridItem] {
|
||||||
|
Array(repeatElement(GridItem(.flexible()), count: columnCount))
|
||||||
|
}
|
||||||
|
var body: some View {
|
||||||
|
HStack(spacing: 5) {
|
||||||
|
VStack (spacing: 5) {
|
||||||
|
|
||||||
|
if backRibbon.count > 0 {
|
||||||
|
VStack {
|
||||||
|
Text("back Ribbon")
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
RibbonDebug(ribbonDebug: RibbonDebugPrint(ribbon: backRibbon[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
Text("next Ribbon")
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
if nextRibbon.count > 0 {
|
||||||
|
RibbonDebug(ribbonDebug: RibbonDebugPrint(ribbon: nextRibbon[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
Text("selected Ribbon")
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
RibbonDebug(ribbonDebug: RibbonDebugPrint(ribbon: selectedRibbon[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
Text("pc offset: \(paneConnector.currentOffset)")
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
Text("pc id: \(paneConnector.currentId)")
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(spacing: 5) {
|
||||||
|
|
||||||
|
let fr = allRibbons[0]
|
||||||
|
let currentPos = (fr.currentLevel - fr.minLevel) %% totalLevels
|
||||||
|
let text = "current pos: \(currentPos)"
|
||||||
|
|
||||||
|
Text(text)
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
ForEach(RibbonMap(ribbons: allRibbons), id: \.self) { ribbon in
|
||||||
|
RibbonDebug(ribbonDebug: ribbon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func RibbonMap(ribbons: [Ribbon]) -> [[String]] {
|
||||||
|
var retStrings = [[String]]()
|
||||||
|
for r in ribbons {
|
||||||
|
var debugString = RibbonDebugPrint(ribbon:r)
|
||||||
|
retStrings.append(debugString)
|
||||||
|
}
|
||||||
|
|
||||||
|
let sortedStats = retStrings.sorted {
|
||||||
|
Int($0[0])! > Int($1[0])!
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortedStats
|
||||||
|
}
|
||||||
|
|
||||||
|
func RibbonDebugPrint(ribbon: Ribbon) -> [String] {
|
||||||
|
var ribbonStats = [String]()
|
||||||
|
let undoPos = (ribbon.undoLevel - ribbon.minLevel) %% totalLevels
|
||||||
|
ribbonStats.append("\(undoPos)")
|
||||||
|
ribbonStats.append("id: \(ribbon.id) ")
|
||||||
|
ribbonStats.append("scrollOffset: \(ribbon.scrollOffset)")
|
||||||
|
ribbonStats.append("scrollId: \(ribbon.scrollId)")
|
||||||
|
|
||||||
|
// ribbonStats.append("undoLevel: \(ribbon.undoLevel)")
|
||||||
|
// ribbonStats.append("currentLevel: \(ribbon.currentLevel)")
|
||||||
|
// ribbonStats.append("minLevel: \(ribbon.minLevel)")
|
||||||
|
// ribbonStats.append("maxLevel: \(ribbon.maxLevel)")
|
||||||
|
return ribbonStats
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RibbonDebug: View {
|
||||||
|
var ribbonDebug: [String]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
VStack {
|
||||||
|
// let ribbonStats = RibbonDebugPrint(ribbon: ribbon)
|
||||||
|
ForEach(ribbonDebug, id: \.self) {
|
||||||
|
Text($0)
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,19 @@
|
||||||
{"argv":["/Users/saint/.local/share/xbase/xbase-sourcekit-helper"],"bspVersion":"0.2","languages":["swift","objective-c","objective-cpp","c","cpp"],"name":"XBase","version":"0.3"}
|
{
|
||||||
|
"argv": [
|
||||||
|
"/usr/local/bin/xcode-build-server"
|
||||||
|
],
|
||||||
|
"bspVersion": "2.0",
|
||||||
|
"languages": [
|
||||||
|
"c",
|
||||||
|
"cpp",
|
||||||
|
"objective-c",
|
||||||
|
"objective-cpp",
|
||||||
|
"swift"
|
||||||
|
],
|
||||||
|
"name": "xcode build server",
|
||||||
|
"version": "0.2",
|
||||||
|
"workspace": "/Users/saint/code/gloss/gloss.xcodeproj/project.xcworkspace",
|
||||||
|
"build_root": "/Users/saint/Library/Developer/Xcode/DerivedData/gloss-ajphzxkxxghgqicpumudnmcgeuwg",
|
||||||
|
"scheme": "gloss",
|
||||||
|
"kind": "xcode"
|
||||||
|
}
|
|
@ -7,6 +7,11 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
851259B02C05281300BE70F8 /* BackButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851259AF2C05281300BE70F8 /* BackButton.swift */; };
|
||||||
|
851259B22C05299200BE70F8 /* ForwardArrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851259B12C05299200BE70F8 /* ForwardArrow.swift */; };
|
||||||
|
851259B62C07560800BE70F8 /* NaviBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851259B52C07560800BE70F8 /* NaviBar.swift */; };
|
||||||
|
851259B82C0A145500BE70F8 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851259B72C0A145500BE70F8 /* Stats.swift */; };
|
||||||
|
851259BA2C0F355D00BE70F8 /* BookmarkIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851259B92C0F355D00BE70F8 /* BookmarkIcon.swift */; };
|
||||||
8514D5BC299EFB780054F185 /* store.db in Resources */ = {isa = PBXBuildFile; fileRef = 8514D5BB299EFB780054F185 /* store.db */; };
|
8514D5BC299EFB780054F185 /* store.db in Resources */ = {isa = PBXBuildFile; fileRef = 8514D5BB299EFB780054F185 /* store.db */; };
|
||||||
8514D5BF299F04710054F185 /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = 8514D5BE299F04710054F185 /* GRDB */; };
|
8514D5BF299F04710054F185 /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = 8514D5BE299F04710054F185 /* GRDB */; };
|
||||||
852774C129A150B100458CA7 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 852774C029A150B100458CA7 /* Line.swift */; };
|
852774C129A150B100458CA7 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 852774C029A150B100458CA7 /* Line.swift */; };
|
||||||
|
@ -22,7 +27,7 @@
|
||||||
85431A902905F4F600EE0760 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85431A8F2905F4F600EE0760 /* Preview Assets.xcassets */; };
|
85431A902905F4F600EE0760 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85431A8F2905F4F600EE0760 /* Preview Assets.xcassets */; };
|
||||||
85431A922905F4F600EE0760 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85431A912905F4F600EE0760 /* Persistence.swift */; };
|
85431A922905F4F600EE0760 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85431A912905F4F600EE0760 /* Persistence.swift */; };
|
||||||
85431A9C2905F5D800EE0760 /* SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85431A9B2905F5D800EE0760 /* SwiftUIView.swift */; };
|
85431A9C2905F5D800EE0760 /* SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85431A9B2905F5D800EE0760 /* SwiftUIView.swift */; };
|
||||||
857C34492BFB7DC800661A63 /* Fenestra.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857C34482BFB7DC800661A63 /* Fenestra.swift */; };
|
857C34492BFB7DC800661A63 /* Pane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857C34482BFB7DC800661A63 /* Pane.swift */; };
|
||||||
8590D96729A183EE001EF84F /* AppDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590D96629A183EE001EF84F /* AppDatabase.swift */; };
|
8590D96729A183EE001EF84F /* AppDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590D96629A183EE001EF84F /* AppDatabase.swift */; };
|
||||||
8590D96929A18A6D001EF84F /* LineRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590D96829A18A6C001EF84F /* LineRequest.swift */; };
|
8590D96929A18A6D001EF84F /* LineRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590D96829A18A6C001EF84F /* LineRequest.swift */; };
|
||||||
8590D96C29A92146001EF84F /* JsonImport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590D96B29A92146001EF84F /* JsonImport.swift */; };
|
8590D96C29A92146001EF84F /* JsonImport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590D96B29A92146001EF84F /* JsonImport.swift */; };
|
||||||
|
@ -37,6 +42,7 @@
|
||||||
85942EFE29B11C0B00307621 /* john_export.json in Resources */ = {isa = PBXBuildFile; fileRef = 85942EFC29B11C0A00307621 /* john_export.json */; };
|
85942EFE29B11C0B00307621 /* john_export.json in Resources */ = {isa = PBXBuildFile; fileRef = 85942EFC29B11C0A00307621 /* john_export.json */; };
|
||||||
85942EFF29B11C0B00307621 /* mark_export.json in Resources */ = {isa = PBXBuildFile; fileRef = 85942EFD29B11C0B00307621 /* mark_export.json */; };
|
85942EFF29B11C0B00307621 /* mark_export.json in Resources */ = {isa = PBXBuildFile; fileRef = 85942EFD29B11C0B00307621 /* mark_export.json */; };
|
||||||
8594ED982BF6845F001213F2 /* HexColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8594ED972BF6845F001213F2 /* HexColor.swift */; };
|
8594ED982BF6845F001213F2 /* HexColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8594ED972BF6845F001213F2 /* HexColor.swift */; };
|
||||||
|
85AAAF572C1A0BC700FCB723 /* acts_export.json in Resources */ = {isa = PBXBuildFile; fileRef = 85AAAF562C1A0BC700FCB723 /* acts_export.json */; };
|
||||||
85E00E7C29F34D2D00FF9E78 /* ScrollState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E00E7B29F34D2D00FF9E78 /* ScrollState.swift */; };
|
85E00E7C29F34D2D00FF9E78 /* ScrollState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E00E7B29F34D2D00FF9E78 /* ScrollState.swift */; };
|
||||||
85E00E7E29F34D3700FF9E78 /* ScrollStateRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E00E7D29F34D3700FF9E78 /* ScrollStateRequest.swift */; };
|
85E00E7E29F34D3700FF9E78 /* ScrollStateRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E00E7D29F34D3700FF9E78 /* ScrollStateRequest.swift */; };
|
||||||
85F01DF82978787800F317B4 /* AveriaSerifLibre-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85F01DF72978787800F317B4 /* AveriaSerifLibre-Regular.ttf */; };
|
85F01DF82978787800F317B4 /* AveriaSerifLibre-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85F01DF72978787800F317B4 /* AveriaSerifLibre-Regular.ttf */; };
|
||||||
|
@ -45,6 +51,11 @@
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
851259AF2C05281300BE70F8 /* BackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackButton.swift; sourceTree = "<group>"; };
|
||||||
|
851259B12C05299200BE70F8 /* ForwardArrow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardArrow.swift; sourceTree = "<group>"; };
|
||||||
|
851259B52C07560800BE70F8 /* NaviBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NaviBar.swift; sourceTree = "<group>"; };
|
||||||
|
851259B72C0A145500BE70F8 /* Stats.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stats.swift; sourceTree = "<group>"; };
|
||||||
|
851259B92C0F355D00BE70F8 /* BookmarkIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkIcon.swift; sourceTree = "<group>"; };
|
||||||
8514D5BB299EFB780054F185 /* store.db */ = {isa = PBXFileReference; lastKnownFileType = file; path = store.db; sourceTree = "<group>"; };
|
8514D5BB299EFB780054F185 /* store.db */ = {isa = PBXFileReference; lastKnownFileType = file; path = store.db; sourceTree = "<group>"; };
|
||||||
852774C029A150B100458CA7 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = "<group>"; };
|
852774C029A150B100458CA7 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = "<group>"; };
|
||||||
8528897429B2B86B003F2E16 /* CrownOfThorns.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrownOfThorns.swift; sourceTree = "<group>"; };
|
8528897429B2B86B003F2E16 /* CrownOfThorns.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrownOfThorns.swift; sourceTree = "<group>"; };
|
||||||
|
@ -58,7 +69,7 @@
|
||||||
85431A8F2905F4F600EE0760 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
85431A8F2905F4F600EE0760 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||||
85431A912905F4F600EE0760 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
|
85431A912905F4F600EE0760 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
|
||||||
85431A9B2905F5D800EE0760 /* SwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIView.swift; sourceTree = "<group>"; };
|
85431A9B2905F5D800EE0760 /* SwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIView.swift; sourceTree = "<group>"; };
|
||||||
857C34482BFB7DC800661A63 /* Fenestra.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fenestra.swift; sourceTree = "<group>"; };
|
857C34482BFB7DC800661A63 /* Pane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pane.swift; sourceTree = "<group>"; };
|
||||||
8590D96629A183EE001EF84F /* AppDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDatabase.swift; sourceTree = "<group>"; };
|
8590D96629A183EE001EF84F /* AppDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDatabase.swift; sourceTree = "<group>"; };
|
||||||
8590D96829A18A6C001EF84F /* LineRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineRequest.swift; sourceTree = "<group>"; };
|
8590D96829A18A6C001EF84F /* LineRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineRequest.swift; sourceTree = "<group>"; };
|
||||||
8590D96B29A92146001EF84F /* JsonImport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JsonImport.swift; sourceTree = "<group>"; };
|
8590D96B29A92146001EF84F /* JsonImport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JsonImport.swift; sourceTree = "<group>"; };
|
||||||
|
@ -73,6 +84,7 @@
|
||||||
85942EFC29B11C0A00307621 /* john_export.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = john_export.json; sourceTree = "<group>"; };
|
85942EFC29B11C0A00307621 /* john_export.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = john_export.json; sourceTree = "<group>"; };
|
||||||
85942EFD29B11C0B00307621 /* mark_export.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = mark_export.json; sourceTree = "<group>"; };
|
85942EFD29B11C0B00307621 /* mark_export.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = mark_export.json; sourceTree = "<group>"; };
|
||||||
8594ED972BF6845F001213F2 /* HexColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HexColor.swift; sourceTree = "<group>"; };
|
8594ED972BF6845F001213F2 /* HexColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HexColor.swift; sourceTree = "<group>"; };
|
||||||
|
85AAAF562C1A0BC700FCB723 /* acts_export.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = acts_export.json; sourceTree = "<group>"; };
|
||||||
85E00E7B29F34D2D00FF9E78 /* ScrollState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollState.swift; sourceTree = "<group>"; };
|
85E00E7B29F34D2D00FF9E78 /* ScrollState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollState.swift; sourceTree = "<group>"; };
|
||||||
85E00E7D29F34D3700FF9E78 /* ScrollStateRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollStateRequest.swift; sourceTree = "<group>"; };
|
85E00E7D29F34D3700FF9E78 /* ScrollStateRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollStateRequest.swift; sourceTree = "<group>"; };
|
||||||
85F01DF72978787800F317B4 /* AveriaSerifLibre-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AveriaSerifLibre-Regular.ttf"; sourceTree = "<group>"; };
|
85F01DF72978787800F317B4 /* AveriaSerifLibre-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AveriaSerifLibre-Regular.ttf"; sourceTree = "<group>"; };
|
||||||
|
@ -108,7 +120,10 @@
|
||||||
85431A7C2905F4F500EE0760 = {
|
85431A7C2905F4F500EE0760 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
857C34482BFB7DC800661A63 /* Fenestra.swift */,
|
851259B92C0F355D00BE70F8 /* BookmarkIcon.swift */,
|
||||||
|
851259B12C05299200BE70F8 /* ForwardArrow.swift */,
|
||||||
|
851259AF2C05281300BE70F8 /* BackButton.swift */,
|
||||||
|
857C34482BFB7DC800661A63 /* Pane.swift */,
|
||||||
8594ED972BF6845F001213F2 /* HexColor.swift */,
|
8594ED972BF6845F001213F2 /* HexColor.swift */,
|
||||||
85E00E7B29F34D2D00FF9E78 /* ScrollState.swift */,
|
85E00E7B29F34D2D00FF9E78 /* ScrollState.swift */,
|
||||||
85E00E7D29F34D3700FF9E78 /* ScrollStateRequest.swift */,
|
85E00E7D29F34D3700FF9E78 /* ScrollStateRequest.swift */,
|
||||||
|
@ -117,6 +132,8 @@
|
||||||
85942EEE29AEA18300307621 /* SelectedRibbonRequest.swift */,
|
85942EEE29AEA18300307621 /* SelectedRibbonRequest.swift */,
|
||||||
85942EF429B108C600307621 /* Seg.swift */,
|
85942EF429B108C600307621 /* Seg.swift */,
|
||||||
85942EF629B108EA00307621 /* SegDenormRequest.swift */,
|
85942EF629B108EA00307621 /* SegDenormRequest.swift */,
|
||||||
|
851259B52C07560800BE70F8 /* NaviBar.swift */,
|
||||||
|
851259B72C0A145500BE70F8 /* Stats.swift */,
|
||||||
85942EF829B1150B00307621 /* SegDenorm.swift */,
|
85942EF829B1150B00307621 /* SegDenorm.swift */,
|
||||||
85942EEA29AD55A400307621 /* RibbonRequest.swift */,
|
85942EEA29AD55A400307621 /* RibbonRequest.swift */,
|
||||||
85942EE329ACF54A00307621 /* ScrollableView.swift */,
|
85942EE329ACF54A00307621 /* ScrollableView.swift */,
|
||||||
|
@ -172,6 +189,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
85942EFC29B11C0A00307621 /* john_export.json */,
|
85942EFC29B11C0A00307621 /* john_export.json */,
|
||||||
|
85AAAF562C1A0BC700FCB723 /* acts_export.json */,
|
||||||
85942EFD29B11C0B00307621 /* mark_export.json */,
|
85942EFD29B11C0B00307621 /* mark_export.json */,
|
||||||
);
|
);
|
||||||
path = json;
|
path = json;
|
||||||
|
@ -268,6 +286,7 @@
|
||||||
85F01DFB2978790400F317B4 /* xe-Dogma-Bold.ttf in Resources */,
|
85F01DFB2978790400F317B4 /* xe-Dogma-Bold.ttf in Resources */,
|
||||||
85F01DF82978787800F317B4 /* AveriaSerifLibre-Regular.ttf in Resources */,
|
85F01DF82978787800F317B4 /* AveriaSerifLibre-Regular.ttf in Resources */,
|
||||||
8514D5BC299EFB780054F185 /* store.db in Resources */,
|
8514D5BC299EFB780054F185 /* store.db in Resources */,
|
||||||
|
85AAAF572C1A0BC700FCB723 /* acts_export.json in Resources */,
|
||||||
85942EFF29B11C0B00307621 /* mark_export.json in Resources */,
|
85942EFF29B11C0B00307621 /* mark_export.json in Resources */,
|
||||||
85942EFE29B11C0B00307621 /* john_export.json in Resources */,
|
85942EFE29B11C0B00307621 /* john_export.json in Resources */,
|
||||||
85431A8D2905F4F600EE0760 /* Assets.xcassets in Resources */,
|
85431A8D2905F4F600EE0760 /* Assets.xcassets in Resources */,
|
||||||
|
@ -282,9 +301,11 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
85431A922905F4F600EE0760 /* Persistence.swift in Sources */,
|
85431A922905F4F600EE0760 /* Persistence.swift in Sources */,
|
||||||
857C34492BFB7DC800661A63 /* Fenestra.swift in Sources */,
|
857C34492BFB7DC800661A63 /* Pane.swift in Sources */,
|
||||||
85942EEB29AD55A400307621 /* RibbonRequest.swift in Sources */,
|
85942EEB29AD55A400307621 /* RibbonRequest.swift in Sources */,
|
||||||
|
851259BA2C0F355D00BE70F8 /* BookmarkIcon.swift in Sources */,
|
||||||
85431A8B2905F4F500EE0760 /* ContentView.swift in Sources */,
|
85431A8B2905F4F500EE0760 /* ContentView.swift in Sources */,
|
||||||
|
851259B02C05281300BE70F8 /* BackButton.swift in Sources */,
|
||||||
85942EF529B108C600307621 /* Seg.swift in Sources */,
|
85942EF529B108C600307621 /* Seg.swift in Sources */,
|
||||||
85E00E7C29F34D2D00FF9E78 /* ScrollState.swift in Sources */,
|
85E00E7C29F34D2D00FF9E78 /* ScrollState.swift in Sources */,
|
||||||
8594ED982BF6845F001213F2 /* HexColor.swift in Sources */,
|
8594ED982BF6845F001213F2 /* HexColor.swift in Sources */,
|
||||||
|
@ -297,12 +318,15 @@
|
||||||
85942EEF29AEA18300307621 /* SelectedRibbonRequest.swift in Sources */,
|
85942EEF29AEA18300307621 /* SelectedRibbonRequest.swift in Sources */,
|
||||||
8590D96C29A92146001EF84F /* JsonImport.swift in Sources */,
|
8590D96C29A92146001EF84F /* JsonImport.swift in Sources */,
|
||||||
85942EED29AEA04200307621 /* SelectedRibbon.swift in Sources */,
|
85942EED29AEA04200307621 /* SelectedRibbon.swift in Sources */,
|
||||||
|
851259B82C0A145500BE70F8 /* Stats.swift in Sources */,
|
||||||
85431A892905F4F500EE0760 /* glossApp.swift in Sources */,
|
85431A892905F4F500EE0760 /* glossApp.swift in Sources */,
|
||||||
8528897C29BD69B2003F2E16 /* VisibilityTrackingScrollView.swift in Sources */,
|
8528897C29BD69B2003F2E16 /* VisibilityTrackingScrollView.swift in Sources */,
|
||||||
85942EF929B1150B00307621 /* SegDenorm.swift in Sources */,
|
85942EF929B1150B00307621 /* SegDenorm.swift in Sources */,
|
||||||
85431A9C2905F5D800EE0760 /* SwiftUIView.swift in Sources */,
|
85431A9C2905F5D800EE0760 /* SwiftUIView.swift in Sources */,
|
||||||
|
851259B22C05299200BE70F8 /* ForwardArrow.swift in Sources */,
|
||||||
8528897E29BD69B2003F2E16 /* VisibilityTracker.swift in Sources */,
|
8528897E29BD69B2003F2E16 /* VisibilityTracker.swift in Sources */,
|
||||||
85942EF729B108EA00307621 /* SegDenormRequest.swift in Sources */,
|
85942EF729B108EA00307621 /* SegDenormRequest.swift in Sources */,
|
||||||
|
851259B62C07560800BE70F8 /* NaviBar.swift in Sources */,
|
||||||
8528897D29BD69B2003F2E16 /* VisibilityTrackingModifier.swift in Sources */,
|
8528897D29BD69B2003F2E16 /* VisibilityTrackingModifier.swift in Sources */,
|
||||||
85942EE429ACF54A00307621 /* ScrollableView.swift in Sources */,
|
85942EE429ACF54A00307621 /* ScrollableView.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
@ -440,7 +464,7 @@
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"gloss/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"gloss/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = V8B2B34W7R;
|
DEVELOPMENT_TEAM = C8XWX9329P;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = gloss/Info.plist;
|
INFOPLIST_FILE = gloss/Info.plist;
|
||||||
|
@ -478,7 +502,7 @@
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"gloss/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"gloss/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = V8B2B34W7R;
|
DEVELOPMENT_TEAM = C8XWX9329P;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = gloss/Info.plist;
|
INFOPLIST_FILE = gloss/Info.plist;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import GRDB
|
import GRDB
|
||||||
|
|
||||||
/// AppDatabase lets the application access the database.
|
|
||||||
///
|
|
||||||
/// It applies the pratices recommended at
|
let totalLevels = 3
|
||||||
/// <https://github.com/groue/GRDB.swift/blob/master/Documentation/GoodPracticesForDesigningRecordTypes.md>
|
|
||||||
struct AppDatabase {
|
struct AppDatabase {
|
||||||
/// Creates an `AppDatabase`, and make sure the database schema is ready.
|
/// Creates an `AppDatabase`, and make sure the database schema is ready.
|
||||||
init(_ dbWriter: any DatabaseWriter) throws {
|
init(_ dbWriter: any DatabaseWriter) throws {
|
||||||
self.dbWriter = dbWriter
|
self.dbWriter = dbWriter
|
||||||
try migrator.migrate(dbWriter)
|
try migrator.migrate(dbWriter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides access to the database.
|
/// Provides access to the database.
|
||||||
///
|
///
|
||||||
/// Application can use a `DatabasePool`, while SwiftUI previews and tests
|
/// Application can use a `DatabasePool`, while SwiftUI previews and tests
|
||||||
|
@ -19,19 +19,19 @@ struct AppDatabase {
|
||||||
///
|
///
|
||||||
/// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseconnections>
|
/// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseconnections>
|
||||||
private let dbWriter: any DatabaseWriter
|
private let dbWriter: any DatabaseWriter
|
||||||
|
|
||||||
/// The DatabaseMigrator that defines the database schema.
|
/// The DatabaseMigrator that defines the database schema.
|
||||||
///
|
///
|
||||||
/// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/migrations>
|
/// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/migrations>
|
||||||
private var migrator: DatabaseMigrator {
|
private var migrator: DatabaseMigrator {
|
||||||
var migrator = DatabaseMigrator()
|
var migrator = DatabaseMigrator()
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// Speed up development by nuking the database when migrations change
|
// Speed up development by nuking the database when migrations change
|
||||||
// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/migrations>
|
// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/migrations>
|
||||||
migrator.eraseDatabaseOnSchemaChange = true
|
migrator.eraseDatabaseOnSchemaChange = true
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
migrator.registerMigration("createLine") { db in
|
migrator.registerMigration("createLine") { db in
|
||||||
// Create a table
|
// Create a table
|
||||||
// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseschema>
|
// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseschema>
|
||||||
|
@ -39,6 +39,7 @@ struct AppDatabase {
|
||||||
t.autoIncrementedPrimaryKey("id")
|
t.autoIncrementedPrimaryKey("id")
|
||||||
t.column("body", .text).notNull()
|
t.column("body", .text).notNull()
|
||||||
t.column("chap", .integer).notNull()
|
t.column("chap", .integer).notNull()
|
||||||
|
t.column("line_id", .integer).notNull()
|
||||||
t.column("book", .text).notNull()
|
t.column("book", .text).notNull()
|
||||||
t.column("verse", .integer)
|
t.column("verse", .integer)
|
||||||
}
|
}
|
||||||
|
@ -53,6 +54,13 @@ struct AppDatabase {
|
||||||
try db.create(table: "Ribbon") { t in
|
try db.create(table: "Ribbon") { t in
|
||||||
t.autoIncrementedPrimaryKey("id")
|
t.autoIncrementedPrimaryKey("id")
|
||||||
t.column("pos", .integer).notNull()
|
t.column("pos", .integer).notNull()
|
||||||
|
t.column("groupId", .integer).notNull()
|
||||||
|
t.column("undoLevel", .integer).notNull()
|
||||||
|
|
||||||
|
t.column("currentLevel", .integer).notNull()
|
||||||
|
t.column("minLevel", .integer).notNull()
|
||||||
|
t.column("maxLevel", .integer).notNull()
|
||||||
|
.defaults(to: 1)
|
||||||
t.column("title", .text).notNull()
|
t.column("title", .text).notNull()
|
||||||
t.column("book", .text).notNull()
|
t.column("book", .text).notNull()
|
||||||
t.column("scrollOffset", .integer).notNull()
|
t.column("scrollOffset", .integer).notNull()
|
||||||
|
@ -61,7 +69,7 @@ struct AppDatabase {
|
||||||
|
|
||||||
try db.create(table: "SelectedRibbon") { t in
|
try db.create(table: "SelectedRibbon") { t in
|
||||||
t.autoIncrementedPrimaryKey("id")
|
t.autoIncrementedPrimaryKey("id")
|
||||||
t.column("ribbonId", .integer).notNull()
|
t.column("ribbonGroupId", .integer).notNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
try db.create(table: "ScrollState") { t in
|
try db.create(table: "ScrollState") { t in
|
||||||
|
@ -70,18 +78,19 @@ struct AppDatabase {
|
||||||
t.column("scrollOffset", .integer).notNull()
|
t.column("scrollOffset", .integer).notNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
try db.create(table: "foo2") { t in
|
// change this to nuke/remake the database
|
||||||
|
try db.create(table: "foo4") { t in
|
||||||
t.autoIncrementedPrimaryKey("id")
|
t.autoIncrementedPrimaryKey("id")
|
||||||
t.column("ribbonId", .integer).notNull()
|
t.column("ribbonId", .integer).notNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Migrations for future application versions will be inserted here:
|
// Migrations for future application versions will be inserted here:
|
||||||
// migrator.registerMigration(...) { db in
|
// migrator.registerMigration(...) { db in
|
||||||
// ...
|
// ...
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return migrator
|
return migrator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,6 +122,22 @@ func load<T: Decodable>(_ filename: String) -> T {
|
||||||
|
|
||||||
extension AppDatabase {
|
extension AppDatabase {
|
||||||
|
|
||||||
|
func getSelectedRibbon() async throws -> [Ribbon] {
|
||||||
|
try await dbWriter.write { db in
|
||||||
|
var sr = try Ribbon.fetchAll(db, sql: """
|
||||||
|
SELECT Ribbon.* FROM SelectedRibbon \
|
||||||
|
JOIN (select distinct r1.* from Ribbon r1 join Ribbon r2 ON \
|
||||||
|
r1.undoLevel = r2.currentLevel AND r1.id = r2.id ORDER BY pos ASC) as Ribbon \
|
||||||
|
ON SelectedRibbon.ribbonGroupId = Ribbon.groupId \
|
||||||
|
WHERE SelectedRibbon.rowId = 1
|
||||||
|
""")
|
||||||
|
|
||||||
|
print("meow get selected ribbon \(sr)")
|
||||||
|
return sr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func updateRibbonPosition(_ ribbon: inout Ribbon, _ oldPos: Int, _ newPos: Int) async throws {
|
func updateRibbonPosition(_ ribbon: inout Ribbon, _ oldPos: Int, _ newPos: Int) async throws {
|
||||||
try await dbWriter.write { [ribbon] db in
|
try await dbWriter.write { [ribbon] db in
|
||||||
|
|
||||||
|
@ -134,9 +159,9 @@ extension AppDatabase {
|
||||||
if (newPos < oldPos) {
|
if (newPos < oldPos) {
|
||||||
|
|
||||||
try db.execute(sql: """
|
try db.execute(sql: """
|
||||||
UPDATE Ribbon
|
UPDATE Ribbon
|
||||||
SET pos =
|
SET pos =
|
||||||
CASE
|
CASE
|
||||||
WHEN pos = ? THEN ?
|
WHEN pos = ? THEN ?
|
||||||
ELSE
|
ELSE
|
||||||
pos + 1
|
pos + 1
|
||||||
|
@ -148,9 +173,9 @@ extension AppDatabase {
|
||||||
print("DIFFFFF")
|
print("DIFFFFF")
|
||||||
|
|
||||||
try db.execute(sql: """
|
try db.execute(sql: """
|
||||||
UPDATE Ribbon
|
UPDATE Ribbon
|
||||||
SET pos =
|
SET pos =
|
||||||
CASE
|
CASE
|
||||||
WHEN pos = ? THEN ?
|
WHEN pos = ? THEN ?
|
||||||
ELSE
|
ELSE
|
||||||
pos - 1
|
pos - 1
|
||||||
|
@ -161,7 +186,7 @@ extension AppDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
// try db.execute(sql: """
|
// try db.execute(sql: """
|
||||||
// UPDATE Ribbon
|
// UPDATE Ribbon
|
||||||
// SET pos = ?
|
// SET pos = ?
|
||||||
// WHERE (id = ?)
|
// WHERE (id = ?)
|
||||||
// """, arguments: [newPos, ribbon.id!])
|
// """, arguments: [newPos, ribbon.id!])
|
||||||
|
@ -193,6 +218,135 @@ extension AppDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func redoRibbon(_ ribbon: inout Ribbon) async throws {
|
||||||
|
let currentLevel = ribbon.currentLevel
|
||||||
|
let minLevel = ribbon.maxLevel
|
||||||
|
|
||||||
|
if currentLevel == minLevel {
|
||||||
|
print("no where to redo")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let newCurrent = (ribbon.currentLevel + 1) %% totalLevels
|
||||||
|
|
||||||
|
do {
|
||||||
|
try await dbWriter.write { [ribbon] db in
|
||||||
|
try db.execute(sql: """
|
||||||
|
UPDATE Ribbon \
|
||||||
|
SET currentLevel = ? WHERE groupId = ?
|
||||||
|
""", arguments: [newCurrent, ribbon.groupId])
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
print("Redo Ribbon Error info: \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// this sets the current undoLevel to the previous value
|
||||||
|
// if you can go back
|
||||||
|
func undoRibbon(_ ribbon: inout Ribbon) async throws {
|
||||||
|
let currentLevel = ribbon.currentLevel
|
||||||
|
let minLevel = ribbon.minLevel
|
||||||
|
|
||||||
|
if currentLevel == minLevel {
|
||||||
|
print("no where to undo")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let newCurrent = (ribbon.currentLevel - 1) %% totalLevels
|
||||||
|
|
||||||
|
do {
|
||||||
|
try await dbWriter.write { [ribbon] db in
|
||||||
|
try db.execute(sql: """
|
||||||
|
UPDATE Ribbon \
|
||||||
|
SET currentLevel = ? WHERE groupId = ?
|
||||||
|
""", arguments: [newCurrent, ribbon.groupId])
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
print("Undo Ribbon Error info: \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// deletes all undo steps above the current undo level
|
||||||
|
// and adds new undo level at the new current level,
|
||||||
|
// adjusts the minLevel and maxLevel
|
||||||
|
func bumpRibbon(_ ribbon: inout Ribbon) async throws -> [Ribbon] {
|
||||||
|
var level = ribbon.currentLevel
|
||||||
|
let maxLevel = ribbon.maxLevel
|
||||||
|
|
||||||
|
|
||||||
|
// gets all the levels from the current to the max
|
||||||
|
// so they can be deleted
|
||||||
|
var delLevels2 = [Int]()
|
||||||
|
if level != maxLevel {
|
||||||
|
repeat {
|
||||||
|
level = (level + 1) %% totalLevels
|
||||||
|
delLevels2.append(level)
|
||||||
|
} while level != maxLevel
|
||||||
|
}
|
||||||
|
let delLevels = delLevels2
|
||||||
|
|
||||||
|
let newMax = (ribbon.currentLevel + 1) %% totalLevels
|
||||||
|
let newCurrent = newMax
|
||||||
|
let newMin = newMax == ribbon.minLevel ? (ribbon.minLevel + 1) %% totalLevels
|
||||||
|
: ribbon.minLevel
|
||||||
|
|
||||||
|
ribbon.minLevel = newMin
|
||||||
|
ribbon.maxLevel = newMax
|
||||||
|
ribbon.undoLevel = newCurrent
|
||||||
|
ribbon.currentLevel = newCurrent
|
||||||
|
ribbon.id = nil
|
||||||
|
|
||||||
|
do {
|
||||||
|
try await dbWriter.write { [ribbon] db in
|
||||||
|
for l in delLevels {
|
||||||
|
try db.execute(sql: """
|
||||||
|
DELETE FROM Ribbon \
|
||||||
|
WHERE groupId = ? \
|
||||||
|
AND undoLevel = ?
|
||||||
|
""", arguments: [ribbon.groupId, l])
|
||||||
|
}
|
||||||
|
|
||||||
|
try db.execute(sql: """
|
||||||
|
UPDATE Ribbon \
|
||||||
|
SET minLevel = ?, maxLevel = ?, currentLevel = ? WHERE groupId = ?
|
||||||
|
""", arguments: [newMin, newMax, newCurrent, ribbon.groupId])
|
||||||
|
|
||||||
|
|
||||||
|
// upsert
|
||||||
|
var ret = try Ribbon.fetchAll(db, sql: """
|
||||||
|
SELECT * from Ribbon WHERE groupId = ? AND undoLevel = ?
|
||||||
|
""", arguments: [ribbon.groupId, ribbon.undoLevel])
|
||||||
|
|
||||||
|
if ret.count == 0 {
|
||||||
|
// insert
|
||||||
|
_ = try ribbon.inserted(db)
|
||||||
|
} else {
|
||||||
|
var updatedRibbon = ret[0]
|
||||||
|
updatedRibbon.minLevel = newMin
|
||||||
|
updatedRibbon.maxLevel = newMax
|
||||||
|
updatedRibbon.undoLevel = newCurrent
|
||||||
|
updatedRibbon.currentLevel = newCurrent
|
||||||
|
updatedRibbon.scrollId = ribbon.scrollId
|
||||||
|
updatedRibbon.scrollOffset = ribbon.scrollOffset
|
||||||
|
try updatedRibbon.update(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = try Ribbon.fetchAll(db, sql: """
|
||||||
|
SELECT * from Ribbon WHERE groupId = ? AND undoLevel = ?
|
||||||
|
""", arguments: [ribbon.groupId, newCurrent])
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print("Error info: \(error)")
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
func saveSelectedRibbon(_ selectedRibbon: inout SelectedRibbon) async throws {
|
func saveSelectedRibbon(_ selectedRibbon: inout SelectedRibbon) async throws {
|
||||||
// if ribbon.name.isEmpty {
|
// if ribbon.name.isEmpty {
|
||||||
// throw ValidationError.missingName
|
// throw ValidationError.missingName
|
||||||
|
@ -212,35 +366,108 @@ extension AppDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func importJson(_ filename: String, _ db: Database) throws {
|
func importJson(_ filename: String, _ db: Database) throws {
|
||||||
let importJson : JsonImport = load(filename)
|
let importJson: JsonImport = load(filename)
|
||||||
|
|
||||||
if try Line.all().isEmpty(db) {
|
var x = 0
|
||||||
for l in importJson.lines {
|
// if try Line.all().isEmpty(db) {
|
||||||
print("importing Lines")
|
for l in importJson.lines {
|
||||||
_ = try l.inserted(db)
|
// print("importing Lines")
|
||||||
|
if x < 5 {
|
||||||
|
print(l)
|
||||||
|
x += 1
|
||||||
}
|
}
|
||||||
|
_ = try l.inserted(db)
|
||||||
|
}
|
||||||
|
|
||||||
for l in importJson.segs {
|
x = 0
|
||||||
print("importing SEGS")
|
for l in importJson.segs {
|
||||||
_ = try l.inserted(db)
|
// print("importing SEGS")
|
||||||
|
|
||||||
|
if x < 5 {
|
||||||
|
print(l)
|
||||||
|
x += 1
|
||||||
}
|
}
|
||||||
|
_ = try l.inserted(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create random Lines if the database is empty.
|
/// Create random Lines if the database is empty.
|
||||||
func initDatabase() throws {
|
func initDatabase() throws {
|
||||||
try dbWriter.write { db in
|
do {
|
||||||
|
try dbWriter.write { db in
|
||||||
|
if try Line.all().isEmpty(db)
|
||||||
|
{
|
||||||
|
try importJson("john_export.json", db)
|
||||||
|
try importJson("mark_export.json", db)
|
||||||
|
try importJson("acts_export.json", db)
|
||||||
|
_ = try Ribbon(id: 1,
|
||||||
|
groupId: 1,
|
||||||
|
pos: 1,
|
||||||
|
undoLevel: 0,
|
||||||
|
currentLevel: 0,
|
||||||
|
minLevel: 0,
|
||||||
|
maxLevel: 0,
|
||||||
|
title: "Gospel of John",
|
||||||
|
book: "bible.john",
|
||||||
|
scrollId: "1",
|
||||||
|
scrollOffset: 0).inserted(db)
|
||||||
|
|
||||||
if try Line.all().isEmpty(db) {
|
_ = try Ribbon(id: 2,
|
||||||
|
groupId: 2,
|
||||||
|
pos: 2,
|
||||||
|
undoLevel: 0,
|
||||||
|
currentLevel: 0,
|
||||||
|
minLevel: 0,
|
||||||
|
maxLevel: 0,
|
||||||
|
title: "Gospel according to Mark",
|
||||||
|
book: "bible.mark",
|
||||||
|
scrollId: "1",
|
||||||
|
scrollOffset: 300).inserted(db)
|
||||||
|
|
||||||
try importJson("john_export.json", db)
|
/////
|
||||||
try importJson("mark_export.json", db)
|
|
||||||
_ = try Ribbon(id: 1, pos: 1, title: "John", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db)
|
_ = try Ribbon(id: 3,
|
||||||
_ = try Ribbon(id: 2, pos: 2, title: "Gospel of Mark", book: "bible.mark", scrollId: "1", scrollOffset: 300).inserted(db)
|
groupId: 3,
|
||||||
_ = try Ribbon(id: 3, pos: 3, title: "John 2", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db)
|
pos: 3,
|
||||||
_ = try SelectedRibbon(id: 1, ribbonId: 1).inserted(db)
|
undoLevel: 0,
|
||||||
|
currentLevel: 2,
|
||||||
|
minLevel: 0,
|
||||||
|
maxLevel: 2,
|
||||||
|
title: "Acts",
|
||||||
|
book: "bible.acts",
|
||||||
|
scrollId: "1",
|
||||||
|
scrollOffset: 0).inserted(db)
|
||||||
|
|
||||||
|
_ = try Ribbon(id: 4,
|
||||||
|
groupId: 3,
|
||||||
|
pos: 3,
|
||||||
|
undoLevel: 1,
|
||||||
|
currentLevel: 2,
|
||||||
|
minLevel: 0,
|
||||||
|
maxLevel: 2,
|
||||||
|
title: "Acts",
|
||||||
|
book: "bible.acts",
|
||||||
|
scrollId: "1",
|
||||||
|
scrollOffset: 0).inserted(db)
|
||||||
|
|
||||||
|
_ = try Ribbon(id: 5,
|
||||||
|
groupId: 3,
|
||||||
|
pos: 3,
|
||||||
|
undoLevel: 2,
|
||||||
|
currentLevel: 2,
|
||||||
|
minLevel: 0,
|
||||||
|
maxLevel: 2,
|
||||||
|
title: "Acts",
|
||||||
|
book: "bible.acts",
|
||||||
|
scrollId: "1",
|
||||||
|
scrollOffset: 0).inserted(db)
|
||||||
|
|
||||||
|
_ = try SelectedRibbon(id: 1, ribbonGroupId: 1).inserted(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
print("Error info: \(error)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,13 @@ let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "network
|
||||||
var currentId: String?
|
var currentId: String?
|
||||||
var currentOffset: CGFloat?
|
var currentOffset: CGFloat?
|
||||||
|
|
||||||
var gTracker: VisibilityTracker<String>?
|
|
||||||
var printCount: Int64 = 0
|
|
||||||
|
|
||||||
var disableDrop = false
|
var disableDrop = false
|
||||||
|
|
||||||
|
//TODO: move to globals file
|
||||||
|
let mainBackgroundColor = Color(red: 0.1, green: 0.1, blue: 0.1)
|
||||||
|
let mainTextColor = Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00))
|
||||||
|
let secondBackgroundColor = Color(red: 0.18, green: 0.18, blue: 0.18)
|
||||||
|
|
||||||
public extension UserDefaults {
|
public extension UserDefaults {
|
||||||
func optionalInt(forKey defaultName: String) -> Int? {
|
func optionalInt(forKey defaultName: String) -> Int? {
|
||||||
let defaults = self
|
let defaults = self
|
||||||
|
@ -39,70 +41,78 @@ public extension UserDefaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createUndoState(selectedRibbon: Ribbon,
|
||||||
|
appDatabase : AppDatabase,
|
||||||
|
paneConnector : PaneConnector) async throws -> [Ribbon]
|
||||||
|
|
||||||
|
{
|
||||||
|
let updateThreshold = 30
|
||||||
|
var updatedRibbon = selectedRibbon
|
||||||
|
var scrollOffsetToSave = Int(floor(paneConnector.currentOffset))
|
||||||
|
var scrollIdToSave = paneConnector.currentId
|
||||||
|
|
||||||
|
var offsetDiff = abs(scrollOffsetToSave - updatedRibbon.scrollOffset) > 30
|
||||||
|
|
||||||
|
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 []
|
||||||
|
}
|
||||||
|
|
||||||
func goToRibbon(selectedRibbon: Ribbon,
|
func goToRibbon(selectedRibbon: Ribbon,
|
||||||
destRibbon: Ribbon,
|
destRibbon: Ribbon,
|
||||||
scrollId: Binding<String?>,
|
|
||||||
scrollOffset: Binding<CGFloat?>,
|
|
||||||
refresh: Binding<Bool>,
|
|
||||||
showOverlay: Binding<Bool>,
|
|
||||||
appDatabase: AppDatabase,
|
appDatabase: AppDatabase,
|
||||||
|
paneConnector: PaneConnector,
|
||||||
loading: Bool)
|
loading: Bool)
|
||||||
{
|
{
|
||||||
Task {
|
print("meow goto ribbon - selected ribbon: \(selectedRibbon), dest ribbon: \(destRibbon) ")
|
||||||
// print("SELECTED RIBBON", selectedRibbon)
|
|
||||||
var scrollOffsetToSave = currentOffset
|
|
||||||
var scrollIdToSave = currentId
|
|
||||||
|
|
||||||
var updatedRibbon = selectedRibbon
|
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
||||||
|
Task {
|
||||||
|
var scrollOffsetToSave = paneConnector.currentOffset
|
||||||
|
var scrollIdToSave = paneConnector.currentId
|
||||||
|
|
||||||
if selectedRibbon.id != destRibbon.id! || loading {
|
print("go to ribbon")
|
||||||
print("switching ribbons")
|
print("\(selectedRibbon.id) \(destRibbon.id!)")
|
||||||
|
|
||||||
// withAnimation(.spring(response: 0.05)) {
|
// if selectedRibbon.id != destRibbon.id! || loading {
|
||||||
// showOverlay.wrappedValue = true
|
if true {
|
||||||
showOverlay.wrappedValue = true
|
print("switching ribbons")
|
||||||
// }
|
|
||||||
|
|
||||||
if loading {
|
paneConnector.showOverlay = true
|
||||||
currentId = destRibbon.scrollId
|
|
||||||
// currentOffset = CGFloat(destRibbon.scrollOffset)
|
if loading {
|
||||||
|
paneConnector.currentId = destRibbon.scrollId
|
||||||
|
}
|
||||||
|
|
||||||
|
paneConnector.scrollId = destRibbon.scrollId
|
||||||
|
paneConnector.scrollOffset = CGFloat(destRibbon.scrollOffset)
|
||||||
|
paneConnector.refresh.toggle()
|
||||||
|
|
||||||
|
print("toggling")
|
||||||
|
print("paneconnector: \(paneConnector.refresh)")
|
||||||
|
|
||||||
|
var updateSelectRibbon = SelectedRibbon(id: Int64(1), ribbonGroupId: Int64(destRibbon.groupId))
|
||||||
|
// print("Saving selected ribbon")
|
||||||
|
// print(updateSelectRibbon)
|
||||||
|
do {
|
||||||
|
_ = try await appDatabase.saveSelectedRibbon(&updateSelectRibbon)
|
||||||
|
} catch {
|
||||||
|
// Print("something wrong")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
extension View {
|
||||||
|
@ -115,21 +125,21 @@ extension View {
|
||||||
|
|
||||||
struct RibbonCrown: View {
|
struct RibbonCrown: View {
|
||||||
var ribbon: Ribbon
|
var ribbon: Ribbon
|
||||||
@Binding var scrollId: String?
|
@ObservedObject var paneConnector: PaneConnector
|
||||||
@Binding var scrollOffset: CGFloat?
|
|
||||||
@Binding var showOverlay: Bool
|
|
||||||
@Binding var refresh: Bool
|
|
||||||
var draggedRibbon: Ribbon?
|
var draggedRibbon: Ribbon?
|
||||||
var isDragging: Bool
|
var isDragging: Bool
|
||||||
|
|
||||||
var height = CGFloat(45)
|
var height = CGFloat(41)
|
||||||
var scale = 0.65
|
var width = CGFloat(70)
|
||||||
|
var scale = 0.70
|
||||||
|
|
||||||
@Environment(\.appDatabase) private var appDatabase
|
@Environment(\.appDatabase) private var appDatabase
|
||||||
|
|
||||||
@Query(SelectedRibbonRequest()) private var sr: [Ribbon]
|
@Query(SelectedRibbonRequest()) private var selectedRibbon: [Ribbon]
|
||||||
|
|
||||||
@State var saveOffset = CGFloat()
|
@State var saveOffset = CGFloat()
|
||||||
|
@Binding var refresh: Bool
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
MyIcon().frame(
|
MyIcon().frame(
|
||||||
|
@ -142,8 +152,8 @@ struct RibbonCrown: View {
|
||||||
|
|
||||||
Text(ribbon.title)
|
Text(ribbon.title)
|
||||||
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
.frame(minWidth: CGFloat(70),
|
.frame(minWidth: width,
|
||||||
maxWidth: CGFloat(70),
|
maxWidth: width,
|
||||||
minHeight: height,
|
minHeight: height,
|
||||||
maxHeight: height,
|
maxHeight: height,
|
||||||
alignment: .center)
|
alignment: .center)
|
||||||
|
@ -157,20 +167,29 @@ struct RibbonCrown: View {
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
Task {
|
Task {
|
||||||
goToRibbon(selectedRibbon: sr[0],
|
let sr = selectedRibbon[0]
|
||||||
destRibbon: ribbon,
|
let updatedRibbon = try await createUndoState(selectedRibbon: sr,
|
||||||
scrollId: $scrollId,
|
appDatabase: appDatabase,
|
||||||
scrollOffset: $scrollOffset,
|
paneConnector: paneConnector)
|
||||||
refresh: $refresh,
|
|
||||||
showOverlay: $showOverlay,
|
if sr.id == ribbon.id {
|
||||||
appDatabase: appDatabase,
|
paneConnector.scrollId = paneConnector.currentId
|
||||||
loading: false)
|
paneConnector.scrollOffset = paneConnector.currentOffset
|
||||||
}
|
paneConnector.hasMoved = false
|
||||||
|
} else {
|
||||||
|
goToRibbon(selectedRibbon: sr,
|
||||||
|
destRibbon: ribbon,
|
||||||
|
appDatabase: appDatabase,
|
||||||
|
paneConnector: paneConnector,
|
||||||
|
loading: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.frame(width: CGFloat(100 * 1.66 * scale + 10), height: CGFloat(100 * scale + 5))
|
.frame(width: CGFloat(100 * 1.66 * scale + 10), height: CGFloat(100 * scale + 5))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// object used for JSON decoding of verses
|
||||||
class Verse: NSObject, Codable {
|
class Verse: NSObject, Codable {
|
||||||
var body: String
|
var body: String
|
||||||
var verse: Int
|
var verse: Int
|
||||||
|
@ -195,38 +214,56 @@ func makeVerseView(seg: SegDenorm) -> some View {
|
||||||
return retView
|
return retView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PaneConnector: NSObject, ObservableObject {
|
||||||
|
var showOverlay: Bool = false
|
||||||
|
@Published var refresh: Bool = false
|
||||||
|
@Published var vertSep = CGFloat(20)
|
||||||
|
var currentId = ""
|
||||||
|
var currentOffset = CGFloat()
|
||||||
|
var visibilityTracker: VisibilityTracker<String>?
|
||||||
|
@Published var scrollId = ""
|
||||||
|
@Published var scrollOffset = CGFloat()
|
||||||
|
@Published var hasMoved = false
|
||||||
|
var setScrollOffset: CGFloat?
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@State var viewState = CGSize.zero
|
// this is for the whole view swiping
|
||||||
|
@State var mainSwipe = CGSize.zero
|
||||||
@State var pulledOut = CGSize.zero
|
@State var pulledOut = CGSize.zero
|
||||||
|
|
||||||
@State var thisScrollView: UIScrollView?
|
@State var selection = 0
|
||||||
@State var scrollUpdate = false
|
|
||||||
@State var initLoad = false
|
|
||||||
|
|
||||||
// set this to scroll to area
|
@StateObject var paneConnector = PaneConnector()
|
||||||
@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 endedDrag = true
|
|
||||||
|
|
||||||
@State var readOffset = CGPoint()
|
|
||||||
@State var dragOffset = CGFloat()
|
|
||||||
|
|
||||||
@State var refresh: Bool = false
|
@State var refresh: Bool = false
|
||||||
@State var refresh2: Bool = false
|
|
||||||
|
@State var readOffset = CGPoint()
|
||||||
|
|
||||||
|
@Query(SegDenormRequest(book: "bible.john")) private var segs: [SegDenorm]
|
||||||
|
|
||||||
@State var draggedRibbon: Ribbon?
|
@State var draggedRibbon: Ribbon?
|
||||||
@State var isDragging = false
|
@State var isDragging = false
|
||||||
|
|
||||||
@State var reorder = true
|
@State var dragOffset = CGFloat(0)
|
||||||
|
|
||||||
|
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?
|
||||||
|
|
||||||
|
@Environment(\.appDatabase) private var appDatabase
|
||||||
|
|
||||||
@Query(RibbonRequest()) private var ribbons: [Ribbon]
|
@Query(RibbonRequest()) private var ribbons: [Ribbon]
|
||||||
@Query<SelectedRibbonRequest> var selectedRibbon: [Ribbon]
|
@Query<SelectedRibbonRequest> var selectedRibbon: [Ribbon]
|
||||||
|
@ -248,14 +285,13 @@ struct ContentView: View {
|
||||||
ZStack(alignment: .top) {
|
ZStack(alignment: .top) {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
VStack {
|
VStack {
|
||||||
|
|
||||||
ForEach(ribbons) { ribbon in
|
ForEach(ribbons) { ribbon in
|
||||||
RibbonCrown(ribbon: ribbon,
|
RibbonCrown(ribbon: ribbon,
|
||||||
scrollId: $scrollId,
|
paneConnector: paneConnector,
|
||||||
scrollOffset: $scrollOffset,
|
|
||||||
showOverlay: $showOverlay,
|
|
||||||
refresh: $refresh,
|
|
||||||
draggedRibbon: draggedRibbon,
|
draggedRibbon: draggedRibbon,
|
||||||
isDragging: isDragging)
|
isDragging: isDragging,
|
||||||
|
refresh: $refresh)
|
||||||
.onDrag {
|
.onDrag {
|
||||||
self.draggedRibbon = ribbon
|
self.draggedRibbon = ribbon
|
||||||
return NSItemProvider()
|
return NSItemProvider()
|
||||||
|
@ -278,266 +314,212 @@ struct ContentView: View {
|
||||||
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||||
.frame(alignment: .topLeading)
|
.frame(alignment: .topLeading)
|
||||||
|
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
ScrollViewReader { proxy in
|
NaviBar(paneConnector: paneConnector)
|
||||||
VisibilityTrackingScrollView(action: handleVisibilityChanged) {
|
StatsPanel(paneConnector: paneConnector)
|
||||||
// ScrollView {
|
.offset(y:20)
|
||||||
|
|
||||||
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)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.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) { _ 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() + 1) {
|
|
||||||
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!
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in Print("introspect")
|
|
||||||
if setScrollOffset != nil {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
scrollView.contentOffset.y = scrollView.contentOffset.y + setScrollOffset!
|
|
||||||
setScrollOffset = nil
|
|
||||||
|
|
||||||
// self.showOverlay = false
|
|
||||||
withAnimation {
|
|
||||||
showOverlay = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
Text("separator").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 { _ 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")
|
|
||||||
}
|
|
||||||
.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)
|
// .frame(maxWidth: 300)
|
||||||
|
.offset(x: geometry.size.width - 300)
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
// Top pane
|
||||||
|
|
||||||
|
Pane(paneConnector: paneConnector,
|
||||||
|
selectedRibbon: selectedRibbon,
|
||||||
|
width: geometry.size.width - 15,
|
||||||
|
height: geometry.size.height + 20,
|
||||||
|
dragOffset: dragOffset)
|
||||||
|
|
||||||
|
///////////////////////////////////
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
|
||||||
|
///////////////////////////////////
|
||||||
|
}
|
||||||
|
.offset(x: 20, y: 0)
|
||||||
.offset(x: pulledOut.width)
|
.offset(x: pulledOut.width)
|
||||||
.offset(x: viewState.width, y: viewState.height)
|
.offset(x: mainSwipe.width)
|
||||||
.gesture(
|
.gesture(
|
||||||
DragGesture()
|
DragGesture()
|
||||||
.onChanged { gesture in
|
.onChanged { value in
|
||||||
|
// Calculate the offset
|
||||||
|
|
||||||
if endedDrag {
|
let margin = CGFloat(30)
|
||||||
endedDrag = false
|
|
||||||
scrollOffset = readOffset.y - 20
|
var newOffset = value.translation.width
|
||||||
// _ = Print("meow")
|
if startSwipeState == nil {
|
||||||
|
if pulledOut.width == 0 {
|
||||||
|
startSwipeState = .center
|
||||||
|
} else if pulledOut.width < 0 {
|
||||||
|
startSwipeState = .left
|
||||||
|
} else {
|
||||||
|
startSwipeState = .right
|
||||||
|
}
|
||||||
|
|
||||||
|
print("start swipe meow: \(startSwipeState)")
|
||||||
}
|
}
|
||||||
// logger.error("hello222")
|
|
||||||
// NSLog("hellooo")
|
if newOffset > 0 {
|
||||||
Print(viewState.width)
|
startSwipeDir = .right
|
||||||
if abs(gesture.translation.width) > 20 {
|
} else {
|
||||||
viewState.width = gesture.translation.width
|
startSwipeDir = .left
|
||||||
if gesture.translation.width < -50, pulledOut.width == CGFloat(0) {
|
}
|
||||||
dragOffset = gesture.translation.width + 50
|
|
||||||
|
|
||||||
|
// Apply resistance if out of bounds
|
||||||
|
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
|
||||||
|
} else if startSwipeDir == .right {
|
||||||
|
maxOffsetLeft = CGFloat(200)
|
||||||
|
maxOffsetRight = CGFloat(10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// offset.y = gesture.translation.width
|
|
||||||
// logger.log("hello")
|
if newOffset + pulledOut.width < -maxOffsetLeft {
|
||||||
}
|
if startSwipeState == .right && startSwipeDir == .left {
|
||||||
.onEnded { _ in
|
newOffset = -maxOffsetLeft + rubberBandEffect(newOffset + maxOffsetLeft) - pulledOut.width
|
||||||
endedDrag = true
|
} else {
|
||||||
var pulledOutWidth = CGFloat(0)
|
newOffset = -maxOffsetLeft + rubberBandEffect(newOffset + maxOffsetLeft)
|
||||||
if viewState.width < 0 {
|
}
|
||||||
pulledOutWidth = CGFloat(0)
|
|
||||||
} else if abs(viewState.width + pulledOut.width) > 30 {
|
} else if newOffset + pulledOut.width > maxOffsetRight {
|
||||||
pulledOutWidth = CGFloat(200)
|
if startSwipeState == .left, startSwipeDir == .right {
|
||||||
|
newOffset = maxOffsetRight + rubberBandEffect(newOffset - maxOffsetRight) - pulledOut.width
|
||||||
|
} else {
|
||||||
|
newOffset = maxOffsetRight + rubberBandEffect(newOffset - maxOffsetRight)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.mainSwipe.width = newOffset
|
||||||
|
|
||||||
|
// dragOffset is what is used to make the text be readable
|
||||||
|
// with the right pane being visible
|
||||||
|
|
||||||
|
// if mainSwipe.width < -margin && pulledOut.width <= 0 {
|
||||||
|
|
||||||
|
if mainSwipe.width < -margin && pulledOut.width <= 0 {
|
||||||
|
dragOffset = margin + mainSwipe.width + pulledOut.width
|
||||||
|
}
|
||||||
|
|
||||||
|
if mainSwipe.width > 0 && pulledOut.width < 0 {
|
||||||
|
dragOffset = margin + mainSwipe.width + pulledOut.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onEnded { _ in
|
||||||
|
var finalSwipe = CGFloat(0)
|
||||||
|
let swipeLeftFinal = CGFloat(-200)
|
||||||
|
let swipeRightFinal = CGFloat(110)
|
||||||
|
|
||||||
|
let margin = CGFloat(30)
|
||||||
|
var setDragOffset = CGFloat(0)
|
||||||
|
|
||||||
|
if startSwipeState == .center {
|
||||||
|
if startSwipeDir == .right {
|
||||||
|
finalSwipe = swipeRightFinal
|
||||||
|
} else {
|
||||||
|
finalSwipe = swipeLeftFinal
|
||||||
|
setDragOffset = margin + swipeLeftFinal
|
||||||
|
}
|
||||||
|
} else if startSwipeState == .right {
|
||||||
|
finalSwipe = .zero
|
||||||
|
|
||||||
|
if startSwipeDir == .left {
|
||||||
|
finalSwipe = .zero
|
||||||
|
} else {
|
||||||
|
finalSwipe = swipeRightFinal
|
||||||
|
}
|
||||||
|
} else if startSwipeState == .left {
|
||||||
|
|
||||||
|
if startSwipeDir == .left {
|
||||||
|
finalSwipe = swipeLeftFinal
|
||||||
|
setDragOffset = margin + swipeLeftFinal
|
||||||
|
} else {
|
||||||
|
finalSwipe = .zero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startSwipeState = nil
|
||||||
|
startSwipeDir = nil
|
||||||
|
|
||||||
|
print("foo")
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// }
|
||||||
|
|
||||||
withAnimation(.spring(response: 0.2)) {
|
withAnimation(.spring(response: 0.2)) {
|
||||||
pulledOut.width = pulledOutWidth
|
pulledOut.width = finalSwipe
|
||||||
viewState = .zero
|
dragOffset = setDragOffset
|
||||||
dragOffset = .zero
|
mainSwipe = .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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleVisibilityChanged2(_: String, change _: VisibilityChange, tracker _: VisibilityTracker<String>) {}
|
func handleVisibilityChanged2(_: String, change _: VisibilityChange, tracker _: VisibilityTracker<String>) {}
|
||||||
|
|
||||||
func handleVisibilityChanged(_: String, change _: VisibilityChange, tracker: VisibilityTracker<String>) {
|
func rubberBandEffect(_ offset: CGFloat) -> CGFloat {
|
||||||
// var printRate: Int64 = 10
|
let resistance: CGFloat = 0.55
|
||||||
gTracker = tracker
|
return resistance * pow(abs(offset), 0.7) * (offset < 0 ? -1 : 1)
|
||||||
|
|
||||||
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!]!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -638,6 +620,7 @@ struct ContentView_Previews: PreviewProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
|
@discardableResult
|
||||||
func Print(_ vars: Any...) -> some View {
|
func Print(_ vars: Any...) -> some View {
|
||||||
for v in vars {
|
for v in vars {
|
||||||
print(v)
|
print(v)
|
||||||
|
|
|
@ -1,39 +1,17 @@
|
||||||
import GRDB
|
import GRDB
|
||||||
/// The Line struct.
|
/// The Line struct.
|
||||||
///
|
|
||||||
/// Identifiable conformance supports SwiftUI list animations, and type-safe
|
|
||||||
/// GRDB primary key methods.
|
|
||||||
/// Equatable conformance supports tests.
|
|
||||||
struct Line: Identifiable, Equatable {
|
struct Line: Identifiable, Equatable {
|
||||||
/// The player id.
|
|
||||||
///
|
|
||||||
/// Int64 is the recommended type for auto-incremented database ids.
|
|
||||||
/// Use nil for players that are not inserted yet in the database.
|
|
||||||
var id: Int64?
|
var id: Int64?
|
||||||
var chap: Int
|
var chap: Int
|
||||||
|
var line_id: Int // this is a line_id per book
|
||||||
var verse: Int
|
var verse: Int
|
||||||
var body: String
|
var body: String
|
||||||
var book: String
|
var book: String
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Line {
|
extension Line {
|
||||||
|
|
||||||
private static let books = [
|
private static let books = [
|
||||||
"John", "Matthew", "Imitation of Christ"]
|
"John", "Matthew", "Imitation of Christ"]
|
||||||
|
|
||||||
/// Creates a new player with empty name and zero score
|
|
||||||
// static func new() -> Line {
|
|
||||||
// Line(id: nil, chap: 1, body: "")
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Returns a random score
|
|
||||||
static func randomScore() -> Int {
|
|
||||||
10 * Int.random(in: 0...100)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func randomBook() -> String {
|
|
||||||
books.randomElement()!
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Persistence
|
// MARK: - Persistence
|
||||||
|
@ -47,7 +25,7 @@ extension Line: Codable, FetchableRecord, MutablePersistableRecord {
|
||||||
static let id = Column(CodingKeys.id)
|
static let id = Column(CodingKeys.id)
|
||||||
static let chap = Column(CodingKeys.chap)
|
static let chap = Column(CodingKeys.chap)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a player id after it has been inserted in the database.
|
/// Updates a player id after it has been inserted in the database.
|
||||||
mutating func didInsert(_ inserted: InsertionSuccess) {
|
mutating func didInsert(_ inserted: InsertionSuccess) {
|
||||||
id = inserted.rowID
|
id = inserted.rowID
|
||||||
|
@ -73,7 +51,7 @@ extension DerivableRequest<Line> {
|
||||||
// // See https://github.com/groue/GRDB.swift/blob/master/README.md#string-comparison
|
// // See https://github.com/groue/GRDB.swift/blob/master/README.md#string-comparison
|
||||||
// order(Line.Columns.name.collating(.localizedCaseInsensitiveCompare))
|
// order(Line.Columns.name.collating(.localizedCaseInsensitiveCompare))
|
||||||
//}
|
//}
|
||||||
|
|
||||||
///// A request of players ordered by score.
|
///// A request of players ordered by score.
|
||||||
/////
|
/////
|
||||||
///// For example:
|
///// For example:
|
||||||
|
|
|
@ -26,11 +26,11 @@ struct LineRequest: Queryable {
|
||||||
var ordering: Ordering
|
var ordering: Ordering
|
||||||
var book: String
|
var book: String
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Queryable Implementation
|
// MARK: - Queryable Implementation
|
||||||
|
|
||||||
static var defaultValue: [Line] { [] }
|
static var defaultValue: [Line] { [] }
|
||||||
|
|
||||||
func publisher(in appDatabase: AppDatabase) -> AnyPublisher<[Line], Error> {
|
func publisher(in appDatabase: AppDatabase) -> AnyPublisher<[Line], Error> {
|
||||||
// Build the publisher from the general-purpose read-only access
|
// Build the publisher from the general-purpose read-only access
|
||||||
// granted by `appDatabase.reader`.
|
// granted by `appDatabase.reader`.
|
||||||
|
@ -45,15 +45,16 @@ struct LineRequest: Queryable {
|
||||||
scheduling: .immediate)
|
scheduling: .immediate)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is not required by Queryable, but it makes it easier
|
// This method is not required by Queryable, but it makes it easier
|
||||||
// to test LineRequest.
|
// to test LineRequest.
|
||||||
func fetchValue(_ db: Database) throws -> [Line] {
|
func fetchValue(_ db: Database) throws -> [Line] {
|
||||||
if book == "" {
|
return try Line.filter(bookColumn == book).fetchAll(db)
|
||||||
return try Line.filter(bookColumn == Line.randomBook()).fetchAll(db)
|
// if book == "" {
|
||||||
} else {
|
// return try Line.filter(bookColumn == Line.randomBook()).fetchAll(db)
|
||||||
return try Line.filter(bookColumn == book).fetchAll(db)
|
// } else {
|
||||||
}
|
// return try Line.filter(bookColumn == book).fetchAll(db)
|
||||||
|
// }
|
||||||
// switch ordering {
|
// switch ordering {
|
||||||
// case .byScore:
|
// case .byScore:
|
||||||
// return try Line.all().fetchAll(db)
|
// return try Line.all().fetchAll(db)
|
||||||
|
|
|
@ -5,7 +5,7 @@ import Foundation
|
||||||
extension AppDatabase {
|
extension AppDatabase {
|
||||||
/// The database for the application
|
/// The database for the application
|
||||||
static let shared = makeShared()
|
static let shared = makeShared()
|
||||||
|
|
||||||
private static func makeShared() -> AppDatabase {
|
private static func makeShared() -> AppDatabase {
|
||||||
do {
|
do {
|
||||||
// Pick a folder for storing the SQLite database, as well as
|
// Pick a folder for storing the SQLite database, as well as
|
||||||
|
@ -20,18 +20,18 @@ extension AppDatabase {
|
||||||
if CommandLine.arguments.contains("-reset") {
|
if CommandLine.arguments.contains("-reset") {
|
||||||
try? fileManager.removeItem(at: folderURL)
|
try? fileManager.removeItem(at: folderURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the database folder if needed
|
// Create the database folder if needed
|
||||||
try fileManager.createDirectory(at: folderURL, withIntermediateDirectories: true)
|
try fileManager.createDirectory(at: folderURL, withIntermediateDirectories: true)
|
||||||
|
|
||||||
// Connect to a database on disk
|
// Connect to a database on disk
|
||||||
// See https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseconnections
|
// See https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseconnections
|
||||||
let dbURL = folderURL.appendingPathComponent("db.sqlite")
|
let dbURL = folderURL.appendingPathComponent("db.sqlite")
|
||||||
let dbPool = try DatabasePool(path: dbURL.path)
|
let dbPool = try DatabasePool(path: dbURL.path)
|
||||||
|
|
||||||
// Create the AppDatabase
|
// Create the AppDatabase
|
||||||
let appDatabase = try AppDatabase(dbPool)
|
let appDatabase = try AppDatabase(dbPool)
|
||||||
|
|
||||||
// // Prepare the database with test fixtures if requested
|
// // Prepare the database with test fixtures if requested
|
||||||
// if CommandLine.arguments.contains("-fixedTestData") {
|
// if CommandLine.arguments.contains("-fixedTestData") {
|
||||||
// try appDatabase.createPlayersForUITests()
|
// try appDatabase.createPlayersForUITests()
|
||||||
|
@ -40,9 +40,10 @@ extension AppDatabase {
|
||||||
// // demo purpose.
|
// // demo purpose.
|
||||||
// try appDatabase.createRandomPlayersIfEmpty()
|
// try appDatabase.createRandomPlayersIfEmpty()
|
||||||
// }
|
// }
|
||||||
|
print("initing database")
|
||||||
try appDatabase.initDatabase()
|
try appDatabase.initDatabase()
|
||||||
|
|
||||||
|
|
||||||
return appDatabase
|
return appDatabase
|
||||||
} catch {
|
} catch {
|
||||||
// Replace this implementation with code to handle the error appropriately.
|
// Replace this implementation with code to handle the error appropriately.
|
||||||
|
@ -57,7 +58,7 @@ extension AppDatabase {
|
||||||
fatalError("Unresolved error \(error)")
|
fatalError("Unresolved error \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an empty database for SwiftUI previews
|
/// Creates an empty database for SwiftUI previews
|
||||||
static func empty() -> AppDatabase {
|
static func empty() -> AppDatabase {
|
||||||
// Connect to an in-memory database
|
// Connect to an in-memory database
|
||||||
|
@ -65,7 +66,7 @@ extension AppDatabase {
|
||||||
let dbQueue = try! DatabaseQueue()
|
let dbQueue = try! DatabaseQueue()
|
||||||
return try! AppDatabase(dbQueue)
|
return try! AppDatabase(dbQueue)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a database full of random players for SwiftUI previews
|
/// Creates a database full of random players for SwiftUI previews
|
||||||
static func random() -> AppDatabase {
|
static func random() -> AppDatabase {
|
||||||
let appDatabase = empty()
|
let appDatabase = empty()
|
||||||
|
|
|
@ -17,7 +17,12 @@ struct Ribbon: Identifiable, Equatable {
|
||||||
/// Int64 is the recommended type for auto-incremented database ids.
|
/// Int64 is the recommended type for auto-incremented database ids.
|
||||||
/// Use nil for players that are not inserted yet in the database.
|
/// Use nil for players that are not inserted yet in the database.
|
||||||
var id: Int64?
|
var id: Int64?
|
||||||
|
var groupId: Int
|
||||||
var pos: Int
|
var pos: Int
|
||||||
|
var undoLevel: Int
|
||||||
|
var currentLevel: Int
|
||||||
|
var minLevel: Int
|
||||||
|
var maxLevel: Int
|
||||||
var title: String
|
var title: String
|
||||||
var book: String
|
var book: String
|
||||||
var scrollId: String
|
var scrollId: String
|
||||||
|
@ -27,7 +32,6 @@ struct Ribbon: Identifiable, Equatable {
|
||||||
extension Ribbon {
|
extension Ribbon {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Persistence
|
|
||||||
|
|
||||||
/// Make Line a Codable Record.
|
/// Make Line a Codable Record.
|
||||||
///
|
///
|
||||||
|
@ -41,7 +45,7 @@ extension Ribbon: Codable, FetchableRecord, MutablePersistableRecord {
|
||||||
static let title = Column(CodingKeys.title)
|
static let title = Column(CodingKeys.title)
|
||||||
static let scrollOffset = Column(CodingKeys.scrollOffset)
|
static let scrollOffset = Column(CodingKeys.scrollOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a player id after it has been inserted in the database.
|
/// Updates a player id after it has been inserted in the database.
|
||||||
mutating func didInsert(_ inserted: InsertionSuccess) {
|
mutating func didInsert(_ inserted: InsertionSuccess) {
|
||||||
id = inserted.rowID
|
id = inserted.rowID
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue