Compare commits

..

No commits in common. "09ffa5f8e25af7f1b77c3bfaeefbb0d86157d7ec" and "2c9da90662fff4411cb6676733fdcfdb24c964cf" have entirely different histories.

27 changed files with 681 additions and 2727 deletions

View File

@ -1,2 +0,0 @@
*.json
*.xcodeproj/*

View File

@ -1,55 +0,0 @@
//
// 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
}
}

View File

@ -1,52 +0,0 @@
//
// 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
}
}

File diff suppressed because it is too large Load Diff

216
Fenestra.swift Normal file
View File

@ -0,0 +1,216 @@
//
// 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!]!
// }
// }

View File

@ -1,56 +0,0 @@
//
// 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
}
}

View File

@ -1,116 +0,0 @@
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)
}
}
}

View File

@ -1,287 +0,0 @@
//
// 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
}
}
}
}

View File

View File

@ -2,19 +2,30 @@ import Combine
import GRDB
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")
struct RibbonRequest: Queryable {
enum UndoDir {
case prev
case next
case current
}
// enum Ordering {
// case byScore
// case byName
// }
/// The ordering used by the player request.
// var ordering: Ordering
var id: Int64!
var dir: UndoDir?
var groupId: Int?
// MARK: - Queryable Implementation
@ -38,107 +49,11 @@ struct RibbonRequest: Queryable {
// This method is not required by Queryable, but it makes it easier
func fetchValue(_ db: Database) throws -> [Ribbon] {
var ret: [Ribbon]
var sql: String
// 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 []
if (id == nil) {
return try Ribbon.order(Column("pos")).fetchAll(db)
} else {
newCurrentLevel = (currentLevel + 1) %% totalLevels
return try Ribbon.filter(idColumn == id).fetchAll(db)
}
}
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 == "" {
// return try Ribbon.filter(bookColumn == Ribbon.randomBook()).fetchAll(db)
@ -154,14 +69,3 @@ struct RibbonRequest: Queryable {
// }
}
}
infix operator %%
extension Int {
static func %% (_ left: Int, _ right: Int) -> Int {
let mod = left % right
return mod >= 0 ? mod : mod + right
}
}

View File

@ -11,7 +11,6 @@ struct SegDenorm: Identifiable, Equatable {
var body: String
// var lineIds: [Int64]
var book: String
var chap: Int64
}
extension SegDenorm {

View File

@ -24,49 +24,15 @@ struct SegDenormRequest: Queryable {
}
func fetchValue(_ db: Database) throws -> [SegDenorm] {
// print("woof segs denorm fetching for \(book)")
// print(book)
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
{
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)
do {
var ret = try SegDenorm.fetchAll(db, sql: sql) // [Player]
// print("SEGS DENORM")
// print(ret[0])
// 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
// print(ret)
return ret
} catch let error {
print(error.localizedDescription)
print(error)

View File

@ -1,26 +0,0 @@
@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
}
}
}
}

View File

@ -17,7 +17,7 @@ struct SelectedRibbon: Identifiable, Equatable {
/// 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 ribbonGroupId: Int64
var ribbonId: Int64
}
extension SelectedRibbon {
@ -32,7 +32,7 @@ extension SelectedRibbon: Codable, FetchableRecord, MutablePersistableRecord {
// Define database columns from CodingKeys
fileprivate enum Columns {
static let id = Column(CodingKeys.id)
static let ribbonGroupId = Column(CodingKeys.ribbonGroupId)
static let ribbonId = Column(CodingKeys.ribbonId)
}
/// Updates a player id after it has been inserted in the database.

View File

@ -51,15 +51,11 @@ struct SelectedRibbonRequest: Queryable {
// var ret3 = try Ribbon.fetchAll(db, sql: "SELECT * FROM Ribbon") // [Player]
// print(ret3)
// print("FETCH JOIN RIBBON")
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 ret = try Ribbon.fetchAll(db, sql: "SELECT Ribbon.* FROM SelectedRibbon join Ribbon on SelectedRibbon.ribbonId = ribbon.rowId WHERE SelectedRibbon.rowId = 1") // [Player]
// print(ret)
return ret
}
}

View File

@ -1,125 +0,0 @@
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))
}
}
}
}
}

View File

@ -1,19 +1 @@
{
"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"
}
{"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"}

View File

@ -7,11 +7,6 @@
objects = {
/* 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 */; };
8514D5BF299F04710054F185 /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = 8514D5BE299F04710054F185 /* GRDB */; };
852774C129A150B100458CA7 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 852774C029A150B100458CA7 /* Line.swift */; };
@ -27,7 +22,7 @@
85431A902905F4F600EE0760 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85431A8F2905F4F600EE0760 /* Preview Assets.xcassets */; };
85431A922905F4F600EE0760 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85431A912905F4F600EE0760 /* Persistence.swift */; };
85431A9C2905F5D800EE0760 /* SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85431A9B2905F5D800EE0760 /* SwiftUIView.swift */; };
857C34492BFB7DC800661A63 /* Pane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857C34482BFB7DC800661A63 /* Pane.swift */; };
857C34492BFB7DC800661A63 /* Fenestra.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857C34482BFB7DC800661A63 /* Fenestra.swift */; };
8590D96729A183EE001EF84F /* AppDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590D96629A183EE001EF84F /* AppDatabase.swift */; };
8590D96929A18A6D001EF84F /* LineRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590D96829A18A6C001EF84F /* LineRequest.swift */; };
8590D96C29A92146001EF84F /* JsonImport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590D96B29A92146001EF84F /* JsonImport.swift */; };
@ -42,7 +37,6 @@
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 */; };
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 */; };
85E00E7E29F34D3700FF9E78 /* ScrollStateRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E00E7D29F34D3700FF9E78 /* ScrollStateRequest.swift */; };
85F01DF82978787800F317B4 /* AveriaSerifLibre-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85F01DF72978787800F317B4 /* AveriaSerifLibre-Regular.ttf */; };
@ -51,11 +45,6 @@
/* End PBXBuildFile 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>"; };
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>"; };
@ -69,7 +58,7 @@
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>"; };
85431A9B2905F5D800EE0760 /* SwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIView.swift; sourceTree = "<group>"; };
857C34482BFB7DC800661A63 /* Pane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pane.swift; sourceTree = "<group>"; };
857C34482BFB7DC800661A63 /* Fenestra.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fenestra.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>"; };
8590D96B29A92146001EF84F /* JsonImport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JsonImport.swift; sourceTree = "<group>"; };
@ -84,7 +73,6 @@
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>"; };
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>"; };
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>"; };
@ -120,10 +108,7 @@
85431A7C2905F4F500EE0760 = {
isa = PBXGroup;
children = (
851259B92C0F355D00BE70F8 /* BookmarkIcon.swift */,
851259B12C05299200BE70F8 /* ForwardArrow.swift */,
851259AF2C05281300BE70F8 /* BackButton.swift */,
857C34482BFB7DC800661A63 /* Pane.swift */,
857C34482BFB7DC800661A63 /* Fenestra.swift */,
8594ED972BF6845F001213F2 /* HexColor.swift */,
85E00E7B29F34D2D00FF9E78 /* ScrollState.swift */,
85E00E7D29F34D3700FF9E78 /* ScrollStateRequest.swift */,
@ -132,8 +117,6 @@
85942EEE29AEA18300307621 /* SelectedRibbonRequest.swift */,
85942EF429B108C600307621 /* Seg.swift */,
85942EF629B108EA00307621 /* SegDenormRequest.swift */,
851259B52C07560800BE70F8 /* NaviBar.swift */,
851259B72C0A145500BE70F8 /* Stats.swift */,
85942EF829B1150B00307621 /* SegDenorm.swift */,
85942EEA29AD55A400307621 /* RibbonRequest.swift */,
85942EE329ACF54A00307621 /* ScrollableView.swift */,
@ -189,7 +172,6 @@
isa = PBXGroup;
children = (
85942EFC29B11C0A00307621 /* john_export.json */,
85AAAF562C1A0BC700FCB723 /* acts_export.json */,
85942EFD29B11C0B00307621 /* mark_export.json */,
);
path = json;
@ -286,7 +268,6 @@
85F01DFB2978790400F317B4 /* xe-Dogma-Bold.ttf in Resources */,
85F01DF82978787800F317B4 /* AveriaSerifLibre-Regular.ttf in Resources */,
8514D5BC299EFB780054F185 /* store.db in Resources */,
85AAAF572C1A0BC700FCB723 /* acts_export.json in Resources */,
85942EFF29B11C0B00307621 /* mark_export.json in Resources */,
85942EFE29B11C0B00307621 /* john_export.json in Resources */,
85431A8D2905F4F600EE0760 /* Assets.xcassets in Resources */,
@ -301,11 +282,9 @@
buildActionMask = 2147483647;
files = (
85431A922905F4F600EE0760 /* Persistence.swift in Sources */,
857C34492BFB7DC800661A63 /* Pane.swift in Sources */,
857C34492BFB7DC800661A63 /* Fenestra.swift in Sources */,
85942EEB29AD55A400307621 /* RibbonRequest.swift in Sources */,
851259BA2C0F355D00BE70F8 /* BookmarkIcon.swift in Sources */,
85431A8B2905F4F500EE0760 /* ContentView.swift in Sources */,
851259B02C05281300BE70F8 /* BackButton.swift in Sources */,
85942EF529B108C600307621 /* Seg.swift in Sources */,
85E00E7C29F34D2D00FF9E78 /* ScrollState.swift in Sources */,
8594ED982BF6845F001213F2 /* HexColor.swift in Sources */,
@ -318,15 +297,12 @@
85942EEF29AEA18300307621 /* SelectedRibbonRequest.swift in Sources */,
8590D96C29A92146001EF84F /* JsonImport.swift in Sources */,
85942EED29AEA04200307621 /* SelectedRibbon.swift in Sources */,
851259B82C0A145500BE70F8 /* Stats.swift in Sources */,
85431A892905F4F500EE0760 /* glossApp.swift in Sources */,
8528897C29BD69B2003F2E16 /* VisibilityTrackingScrollView.swift in Sources */,
85942EF929B1150B00307621 /* SegDenorm.swift in Sources */,
85431A9C2905F5D800EE0760 /* SwiftUIView.swift in Sources */,
851259B22C05299200BE70F8 /* ForwardArrow.swift in Sources */,
8528897E29BD69B2003F2E16 /* VisibilityTracker.swift in Sources */,
85942EF729B108EA00307621 /* SegDenormRequest.swift in Sources */,
851259B62C07560800BE70F8 /* NaviBar.swift in Sources */,
8528897D29BD69B2003F2E16 /* VisibilityTrackingModifier.swift in Sources */,
85942EE429ACF54A00307621 /* ScrollableView.swift in Sources */,
);
@ -464,7 +440,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"gloss/Preview Content\"";
DEVELOPMENT_TEAM = C8XWX9329P;
DEVELOPMENT_TEAM = V8B2B34W7R;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = gloss/Info.plist;
@ -502,7 +478,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"gloss/Preview Content\"";
DEVELOPMENT_TEAM = C8XWX9329P;
DEVELOPMENT_TEAM = V8B2B34W7R;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = gloss/Info.plist;

View File

@ -1,10 +1,10 @@
import Foundation
import GRDB
let totalLevels = 3
/// AppDatabase lets the application access the database.
///
/// It applies the pratices recommended at
/// <https://github.com/groue/GRDB.swift/blob/master/Documentation/GoodPracticesForDesigningRecordTypes.md>
struct AppDatabase {
/// Creates an `AppDatabase`, and make sure the database schema is ready.
init(_ dbWriter: any DatabaseWriter) throws {
@ -39,7 +39,6 @@ struct AppDatabase {
t.autoIncrementedPrimaryKey("id")
t.column("body", .text).notNull()
t.column("chap", .integer).notNull()
t.column("line_id", .integer).notNull()
t.column("book", .text).notNull()
t.column("verse", .integer)
}
@ -54,13 +53,6 @@ struct AppDatabase {
try db.create(table: "Ribbon") { t in
t.autoIncrementedPrimaryKey("id")
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("book", .text).notNull()
t.column("scrollOffset", .integer).notNull()
@ -69,7 +61,7 @@ struct AppDatabase {
try db.create(table: "SelectedRibbon") { t in
t.autoIncrementedPrimaryKey("id")
t.column("ribbonGroupId", .integer).notNull()
t.column("ribbonId", .integer).notNull()
}
try db.create(table: "ScrollState") { t in
@ -78,8 +70,7 @@ struct AppDatabase {
t.column("scrollOffset", .integer).notNull()
}
// change this to nuke/remake the database
try db.create(table: "foo4") { t in
try db.create(table: "foo2") { t in
t.autoIncrementedPrimaryKey("id")
t.column("ribbonId", .integer).notNull()
}
@ -122,22 +113,6 @@ func load<T: Decodable>(_ filename: String) -> T {
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 {
try await dbWriter.write { [ribbon] db in
@ -218,135 +193,6 @@ 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 {
// if ribbon.name.isEmpty {
// throw ValidationError.missingName
@ -366,108 +212,35 @@ extension AppDatabase {
}
func importJson(_ filename: String, _ db: Database) throws {
let importJson: JsonImport = load(filename)
let importJson : JsonImport = load(filename)
var x = 0
// if try Line.all().isEmpty(db) {
if try Line.all().isEmpty(db) {
for l in importJson.lines {
// print("importing Lines")
if x < 5 {
print(l)
x += 1
}
print("importing Lines")
_ = try l.inserted(db)
}
x = 0
for l in importJson.segs {
// print("importing SEGS")
if x < 5 {
print(l)
x += 1
}
print("importing SEGS")
_ = try l.inserted(db)
}
}
}
/// Create random Lines if the database is empty.
func initDatabase() throws {
do {
try dbWriter.write { db in
if try Line.all().isEmpty(db)
{
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)
_ = 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 Ribbon(id: 3,
groupId: 3,
pos: 3,
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)
_ = try Ribbon(id: 1, pos: 1, title: "John", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db)
_ = try Ribbon(id: 2, pos: 2, title: "Gospel of Mark", book: "bible.mark", scrollId: "1", scrollOffset: 300).inserted(db)
_ = try Ribbon(id: 3, pos: 3, title: "John 2", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db)
_ = try SelectedRibbon(id: 1, ribbonId: 1).inserted(db)
}
}
} catch {
print("Error info: \(error)")
}
}
}

View File

@ -16,12 +16,10 @@ let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "network
var currentId: String?
var currentOffset: CGFloat?
var disableDrop = false
var gTracker: VisibilityTracker<String>?
var printCount: Int64 = 0
//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)
var disableDrop = false
public extension UserDefaults {
func optionalInt(forKey defaultName: String) -> Int? {
@ -41,66 +39,43 @@ 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,
destRibbon: Ribbon,
scrollId: Binding<String?>,
scrollOffset: Binding<CGFloat?>,
refresh: Binding<Bool>,
showOverlay: Binding<Bool>,
appDatabase: AppDatabase,
paneConnector: PaneConnector,
loading: Bool)
{
print("meow goto ribbon - selected ribbon: \(selectedRibbon), dest ribbon: \(destRibbon) ")
DispatchQueue.main.asyncAfter(deadline: .now()) {
Task {
var scrollOffsetToSave = paneConnector.currentOffset
var scrollIdToSave = paneConnector.currentId
// print("SELECTED RIBBON", selectedRibbon)
var scrollOffsetToSave = currentOffset
var scrollIdToSave = currentId
print("go to ribbon")
print("\(selectedRibbon.id) \(destRibbon.id!)")
var updatedRibbon = selectedRibbon
// if selectedRibbon.id != destRibbon.id! || loading {
if true {
if selectedRibbon.id != destRibbon.id! || loading {
print("switching ribbons")
paneConnector.showOverlay = true
// withAnimation(.spring(response: 0.05)) {
// showOverlay.wrappedValue = true
showOverlay.wrappedValue = true
// }
if loading {
paneConnector.currentId = destRibbon.scrollId
currentId = destRibbon.scrollId
// currentOffset = CGFloat(destRibbon.scrollOffset)
}
paneConnector.scrollId = destRibbon.scrollId
paneConnector.scrollOffset = CGFloat(destRibbon.scrollOffset)
paneConnector.refresh.toggle()
scrollId.wrappedValue = destRibbon.scrollId
// print("setting scroll offset")
scrollOffset.wrappedValue = CGFloat(destRibbon.scrollOffset)
// print(scrollOffset.wrappedValue)
// print("end setting scroll offset")
refresh.wrappedValue.toggle()
print("toggling")
print("paneconnector: \(paneConnector.refresh)")
var updateSelectRibbon = SelectedRibbon(id: Int64(1), ribbonGroupId: Int64(destRibbon.groupId))
var updateSelectRibbon = SelectedRibbon(id: Int64(1), ribbonId: destRibbon.id!)
// print("Saving selected ribbon")
// print(updateSelectRibbon)
do {
@ -110,9 +85,24 @@ func goToRibbon(selectedRibbon: Ribbon,
}
}
}
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 {
@ -125,21 +115,21 @@ extension View {
struct RibbonCrown: View {
var ribbon: Ribbon
@ObservedObject var paneConnector: PaneConnector
@Binding var scrollId: String?
@Binding var scrollOffset: CGFloat?
@Binding var showOverlay: Bool
@Binding var refresh: Bool
var draggedRibbon: Ribbon?
var isDragging: Bool
var height = CGFloat(41)
var width = CGFloat(70)
var scale = 0.70
var height = CGFloat(45)
var scale = 0.65
@Environment(\.appDatabase) private var appDatabase
@Query(SelectedRibbonRequest()) private var selectedRibbon: [Ribbon]
@Query(SelectedRibbonRequest()) private var sr: [Ribbon]
@State var saveOffset = CGFloat()
@Binding var refresh: Bool
var body: some View {
ZStack {
MyIcon().frame(
@ -152,8 +142,8 @@ struct RibbonCrown: View {
Text(ribbon.title)
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
.frame(minWidth: width,
maxWidth: width,
.frame(minWidth: CGFloat(70),
maxWidth: CGFloat(70),
minHeight: height,
maxHeight: height,
alignment: .center)
@ -167,29 +157,20 @@ struct RibbonCrown: View {
}
.onTapGesture {
Task {
let sr = selectedRibbon[0]
let updatedRibbon = try await createUndoState(selectedRibbon: sr,
appDatabase: appDatabase,
paneConnector: paneConnector)
if sr.id == ribbon.id {
paneConnector.scrollId = paneConnector.currentId
paneConnector.scrollOffset = paneConnector.currentOffset
paneConnector.hasMoved = false
} else {
goToRibbon(selectedRibbon: sr,
goToRibbon(selectedRibbon: sr[0],
destRibbon: ribbon,
scrollId: $scrollId,
scrollOffset: $scrollOffset,
refresh: $refresh,
showOverlay: $showOverlay,
appDatabase: appDatabase,
paneConnector: paneConnector,
loading: false)
}
}
}
.frame(width: CGFloat(100 * 1.66 * scale + 10), height: CGFloat(100 * scale + 5))
}
}
// object used for JSON decoding of verses
class Verse: NSObject, Codable {
var body: String
var verse: Int
@ -214,56 +195,38 @@ func makeVerseView(seg: SegDenorm) -> some View {
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 {
// this is for the whole view swiping
@State var mainSwipe = CGSize.zero
@State var viewState = CGSize.zero
@State var pulledOut = CGSize.zero
@State var selection = 0
@State var thisScrollView: UIScrollView?
@State var scrollUpdate = false
@State var initLoad = false
@StateObject var paneConnector = PaneConnector()
// set this to scroll to area
@State var scrollId: String?
@State var scrollOffset: CGFloat?
@State var refresh: Bool = false
@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()
@Query(SegDenormRequest(book: "bible.john")) private var segs: [SegDenorm]
@State var refresh: Bool = false
@State var refresh2: Bool = false
@State var draggedRibbon: Ribbon?
@State var isDragging = false
@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
@State var reorder = true
@Query(RibbonRequest()) private var ribbons: [Ribbon]
@Query<SelectedRibbonRequest> var selectedRibbon: [Ribbon]
@ -285,13 +248,14 @@ struct ContentView: View {
ZStack(alignment: .top) {
VStack(alignment: .leading) {
VStack {
ForEach(ribbons) { ribbon in
RibbonCrown(ribbon: ribbon,
paneConnector: paneConnector,
scrollId: $scrollId,
scrollOffset: $scrollOffset,
showOverlay: $showOverlay,
refresh: $refresh,
draggedRibbon: draggedRibbon,
isDragging: isDragging,
refresh: $refresh)
isDragging: isDragging)
.onDrag {
self.draggedRibbon = ribbon
return NSItemProvider()
@ -314,212 +278,266 @@ struct ContentView: View {
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
.frame(alignment: .topLeading)
VStack {
NaviBar(paneConnector: paneConnector)
StatsPanel(paneConnector: paneConnector)
.offset(y:20)
ScrollViewReader { proxy in
VisibilityTrackingScrollView(action: handleVisibilityChanged) {
// ScrollView {
LazyVStack {
ForEach(segs) { seg in
SegRow(seg: seg,
ribbonId: selectedRibbon[0].id!)
.id("\(seg.id)")
.offset(x: -dragOffset)
// .offset(x: pulledOut.width)
.padding(EdgeInsets(top: 10, leading: 20, bottom: 40, trailing: 20))
.trackVisibility(id: "\(seg.id)")
}
// .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: mainSwipe.width)
.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 { value in
// Calculate the offset
.onChanged { gesture in
let margin = CGFloat(30)
vertSep = vertSep - gesture.translation.height
Print(gesture.translation.width)
Print(gesture.translation.height)
var newOffset = value.translation.width
if startSwipeState == nil {
if pulledOut.width == 0 {
startSwipeState = .center
} else if pulledOut.width < 0 {
startSwipeState = .left
} else {
startSwipeState = .right
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")
}
print("start swipe meow: \(startSwipeState)")
}
if newOffset > 0 {
startSwipeDir = .right
} else {
startSwipeDir = .left
}
// 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)
}
}
if newOffset + pulledOut.width < -maxOffsetLeft {
if startSwipeState == .right && startSwipeDir == .left {
newOffset = -maxOffsetLeft + rubberBandEffect(newOffset + maxOffsetLeft) - pulledOut.width
} else {
newOffset = -maxOffsetLeft + rubberBandEffect(newOffset + maxOffsetLeft)
}
} else if newOffset + pulledOut.width > maxOffsetRight {
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
// .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)
.offset(x: pulledOut.width)
.offset(x: viewState.width, y: viewState.height)
.gesture(
DragGesture()
.onChanged { gesture in
if endedDrag {
endedDrag = false
scrollOffset = readOffset.y - 20
// _ = Print("meow")
}
// logger.error("hello222")
// NSLog("hellooo")
Print(viewState.width)
if abs(gesture.translation.width) > 20 {
viewState.width = gesture.translation.width
if gesture.translation.width < -50, pulledOut.width == CGFloat(0) {
dragOffset = gesture.translation.width + 50
}
}
// offset.y = gesture.translation.width
// logger.log("hello")
}
.onEnded { _ in
endedDrag = true
var pulledOutWidth = CGFloat(0)
if viewState.width < 0 {
pulledOutWidth = CGFloat(0)
} else if abs(viewState.width + pulledOut.width) > 30 {
pulledOutWidth = CGFloat(200)
}
withAnimation(.spring(response: 0.2)) {
pulledOut.width = finalSwipe
dragOffset = setDragOffset
mainSwipe = .zero
pulledOut.width = pulledOutWidth
viewState = .zero
dragOffset = .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 rubberBandEffect(_ offset: CGFloat) -> CGFloat {
let resistance: CGFloat = 0.55
return resistance * pow(abs(offset), 0.7) * (offset < 0 ? -1 : 1)
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!]!
}
}
@ -620,7 +638,6 @@ struct ContentView_Previews: PreviewProvider {
}
extension View {
@discardableResult
func Print(_ vars: Any...) -> some View {
for v in vars {
print(v)

View File

@ -1,17 +1,39 @@
import GRDB
/// The Line struct.
///
/// Identifiable conformance supports SwiftUI list animations, and type-safe
/// GRDB primary key methods.
/// Equatable conformance supports tests.
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 chap: Int
var line_id: Int // this is a line_id per book
var verse: Int
var body: String
var book: String
}
extension Line {
private static let books = [
"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

View File

@ -49,12 +49,11 @@ struct LineRequest: Queryable {
// This method is not required by Queryable, but it makes it easier
// to test LineRequest.
func fetchValue(_ db: Database) throws -> [Line] {
if book == "" {
return try Line.filter(bookColumn == Line.randomBook()).fetchAll(db)
} else {
return try Line.filter(bookColumn == book).fetchAll(db)
// if book == "" {
// return try Line.filter(bookColumn == Line.randomBook()).fetchAll(db)
// } else {
// return try Line.filter(bookColumn == book).fetchAll(db)
// }
}
// switch ordering {
// case .byScore:
// return try Line.all().fetchAll(db)

View File

@ -40,7 +40,6 @@ extension AppDatabase {
// // demo purpose.
// try appDatabase.createRandomPlayersIfEmpty()
// }
print("initing database")
try appDatabase.initDatabase()

View File

@ -17,12 +17,7 @@ struct Ribbon: Identifiable, Equatable {
/// 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 groupId: Int
var pos: Int
var undoLevel: Int
var currentLevel: Int
var minLevel: Int
var maxLevel: Int
var title: String
var book: String
var scrollId: String
@ -32,6 +27,7 @@ struct Ribbon: Identifiable, Equatable {
extension Ribbon {
}
// MARK: - Persistence
/// Make Line a Codable Record.
///

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