Compare commits
10 Commits
ae49a5cbfe
...
bd1923cfb4
Author | SHA1 | Date |
---|---|---|
|
bd1923cfb4 | |
|
19ca7be678 | |
|
168a057a2a | |
|
14e1bec9e5 | |
|
08b6116a28 | |
|
efd05ce33b | |
|
4dfc93ea90 | |
|
32696d7b58 | |
|
9eb488a87a | |
|
c069860c43 |
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,8 @@
|
||||||
// Created by Saint on 5/20/24.
|
// Created by Saint on 5/20/24.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import GRDB
|
||||||
|
import GRDBQuery
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import WrappingHStack
|
import WrappingHStack
|
||||||
|
@ -14,6 +16,7 @@ struct SegRow: View {
|
||||||
var ribbonId: Int64
|
var ribbonId: Int64
|
||||||
@State var highlights = Set<Int>()
|
@State var highlights = Set<Int>()
|
||||||
|
|
||||||
|
let intraWordSpacing = CGFloat(1.6)
|
||||||
var body: some View {
|
var body: some View {
|
||||||
var segSplit = seg.body.components(separatedBy: ";;")
|
var segSplit = seg.body.components(separatedBy: ";;")
|
||||||
let decoder = JSONDecoder()
|
let decoder = JSONDecoder()
|
||||||
|
@ -53,7 +56,8 @@ struct SegRow: View {
|
||||||
Text(arrayOfText[index])
|
Text(arrayOfText[index])
|
||||||
.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)))
|
||||||
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
.padding(.horizontal, 1.5) // intra word spacing
|
|
||||||
|
.padding(.horizontal, intraWordSpacing) // intra word spacing
|
||||||
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
@ -79,7 +83,6 @@ struct SegRow: View {
|
||||||
struct Pane: View {
|
struct Pane: View {
|
||||||
@ObservedObject var paneConnector: PaneConnector
|
@ObservedObject var paneConnector: PaneConnector
|
||||||
|
|
||||||
@State var segs: [SegDenorm]
|
|
||||||
@State var selectedRibbon: [Ribbon]
|
@State var selectedRibbon: [Ribbon]
|
||||||
|
|
||||||
@State var width: CGFloat
|
@State var width: CGFloat
|
||||||
|
@ -89,13 +92,15 @@ struct Pane: View {
|
||||||
|
|
||||||
@State var refresh: Bool = false
|
@State var refresh: Bool = false
|
||||||
|
|
||||||
@State var vertSep = CGFloat(20)
|
@Query(SegDenormRequest(book: "bible.mark")) private var segs: [SegDenorm]
|
||||||
|
|
||||||
@Environment(\.appDatabase) private var appDatabase
|
@Environment(\.appDatabase) private var appDatabase
|
||||||
|
|
||||||
// var handleVisibilityChanged: (String, VisibilityChange, VisibilityTracker<String>) -> Void
|
// var handleVisibilityChanged: (String, VisibilityChange, VisibilityTracker<String>) -> Void
|
||||||
|
|
||||||
var body: some View{
|
var body: some View {
|
||||||
|
var adjustedHeight = height - paneConnector.vertSep
|
||||||
|
ZStack {
|
||||||
ScrollViewReader { proxy in
|
ScrollViewReader { proxy in
|
||||||
VisibilityTrackingScrollView(action: handleVisibilityChanged) {
|
VisibilityTrackingScrollView(action: handleVisibilityChanged) {
|
||||||
LazyVStack {
|
LazyVStack {
|
||||||
|
@ -116,8 +121,8 @@ struct Pane: View {
|
||||||
destRibbon: selectedRibbon[0],
|
destRibbon: selectedRibbon[0],
|
||||||
appDatabase: appDatabase,
|
appDatabase: appDatabase,
|
||||||
paneConnector: paneConnector,
|
paneConnector: paneConnector,
|
||||||
refresh: $refresh,
|
loading: true,
|
||||||
loading: true)
|
bump: false)
|
||||||
}
|
}
|
||||||
.onChange(of: paneConnector.refresh) { _ in
|
.onChange(of: paneConnector.refresh) { _ in
|
||||||
print("inside change")
|
print("inside change")
|
||||||
|
@ -125,6 +130,9 @@ struct Pane: View {
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
if paneConnector.visibilityTracker == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
let gTracker = paneConnector.visibilityTracker!
|
let gTracker = paneConnector.visibilityTracker!
|
||||||
|
|
||||||
Print("scroll Id target: \(paneConnector.scrollId)")
|
Print("scroll Id target: \(paneConnector.scrollId)")
|
||||||
|
@ -190,8 +198,28 @@ struct Pane: View {
|
||||||
}
|
}
|
||||||
.zIndex(1)
|
.zIndex(1)
|
||||||
.background(Color(red: 0.2, green: 0.2, blue: 0.2))
|
.background(Color(red: 0.2, green: 0.2, blue: 0.2))
|
||||||
.frame(width: width, height: height)
|
.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>) {
|
func handleVisibilityChanged(_: String, change _: VisibilityChange, tracker: VisibilityTracker<String>) {
|
||||||
// var printRate: Int64 = 10
|
// var printRate: Int64 = 10
|
||||||
|
|
|
@ -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,74 @@
|
||||||
|
|
||||||
|
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 {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
Print("meow navi reloaded back ribbon: \(backRibbon[0])")
|
||||||
|
|
||||||
|
BackArrow()
|
||||||
|
.frame(width: CGFloat(30), height: CGFloat(30))
|
||||||
|
.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 {
|
||||||
|
print("back backRibbon: \(backRibbon)")
|
||||||
|
Task {
|
||||||
|
print("back backRibbon: \(backRibbon)")
|
||||||
|
if backRibbon.count == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var br = backRibbon[0]
|
||||||
|
//var sr = selectedRibbon[0]
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
var sr = try await appDatabase.getSelectedRibbon()
|
||||||
|
print("meow first sr:\(sr)")
|
||||||
|
print("meow first selectedRibbon:\(selectedRibbon[0])")
|
||||||
|
try await updateRibbon(selectedRibbon: selectedRibbon[0],
|
||||||
|
appDatabase: appDatabase,
|
||||||
|
paneConnector: paneConnector)
|
||||||
|
|
||||||
|
print("meow back from update in main")
|
||||||
|
sr = try await appDatabase.getSelectedRibbon()
|
||||||
|
print("meow second sr:\(sr)")
|
||||||
|
print("meow backribbon br:\(br)")
|
||||||
|
|
||||||
|
_ = try await appDatabase.undoRibbon(&sr[0])
|
||||||
|
goToRibbon(selectedRibbon: sr[0],
|
||||||
|
destRibbon: br,
|
||||||
|
appDatabase: appDatabase,
|
||||||
|
paneConnector: paneConnector,
|
||||||
|
loading: false,
|
||||||
|
bump:false)
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
print("back 2 error")
|
||||||
|
print("Error info: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ForwardArrow()
|
||||||
|
.frame(width: CGFloat(30), height: CGFloat(30))
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,36 +2,24 @@ 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
|
||||||
// }
|
}
|
||||||
|
|
||||||
/// 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 +34,106 @@ 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 {
|
||||||
|
print("back reload")
|
||||||
|
if dir != nil && groupId != nil {
|
||||||
|
sql = """
|
||||||
|
SELECT * FROM Ribbon \
|
||||||
|
WHERE groupId = ?
|
||||||
|
"""
|
||||||
|
ret = try Ribbon.fetchAll(db, sql: sql, arguments: [groupId])
|
||||||
|
if ret.count == 0 {
|
||||||
|
print("error no ribbons found")
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
print("back all ribbon all: \(ret)")
|
||||||
|
|
||||||
|
let currentLevel = ret[0].currentLevel
|
||||||
|
let minLevel = ret[0].minLevel
|
||||||
|
let maxLevel = ret[0].maxLevel
|
||||||
|
|
||||||
|
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 {
|
||||||
|
// no back undo steps left
|
||||||
|
if currentLevel == minLevel {
|
||||||
|
newCurrentLevel = currentLevel
|
||||||
|
}
|
||||||
|
} else if dir == .prev {
|
||||||
|
// no forward redo steps left
|
||||||
|
if currentLevel == maxLevel {
|
||||||
|
newCurrentLevel = maxLevel
|
||||||
|
} else {
|
||||||
|
newCurrentLevel = (currentLevel + 1) %% totalLevels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print("back newcurrentlevel \(newCurrentLevel)")
|
||||||
|
sql = """
|
||||||
|
SELECT * FROM Ribbon \
|
||||||
|
WHERE groupId = ? AND
|
||||||
|
undoLevel = ?
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
ret = try Ribbon.fetchAll(db, sql: sql, arguments: [groupId, newCurrentLevel])
|
||||||
|
|
||||||
|
print("back ribbon return: \(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("xxxxx fetching ribbons")
|
||||||
|
print(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 +148,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,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,39 @@ 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("segs denorm fetching for \(book)")
|
||||||
|
print(book)
|
||||||
|
var sql = """
|
||||||
|
select seg_id as id, 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
|
||||||
|
"""
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var ret = try SegDenorm.fetchAll(db, sql: sql)
|
||||||
|
|
||||||
// print("SEGS DENORM")
|
// print("SEGS DENORM")
|
||||||
// print(ret)
|
// 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 ret
|
return ret
|
||||||
|
|
||||||
} 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,16 @@ 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
|
||||||
|
""")
|
||||||
|
// [Player]
|
||||||
|
print("Selected Ribbon query result: \(ret)")
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack (spacing: 10) {
|
||||||
|
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]))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ForEach(RibbonMap(ribbons: allRibbons), id: \.self) { ribbon in
|
||||||
|
Print("quack3: \(ribbon)")
|
||||||
|
RibbonDebug(ribbonDebug: ribbon)
|
||||||
|
}
|
||||||
|
|
||||||
|
//VStack(spacing: 10) {
|
||||||
|
// Text("All Ribbons")
|
||||||
|
// .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
|
||||||
|
// //Print("quack2: \(ribbon)")
|
||||||
|
// RibbonDebug(ribbonDebug: RibbonDebugPrint(ribbon: ribbon))
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func RibbonMap(ribbons: [Ribbon]) -> [[String]] {
|
||||||
|
var retStrings = [[String]]()
|
||||||
|
for r in ribbons {
|
||||||
|
var debugString = RibbonDebugPrint(ribbon:r)
|
||||||
|
retStrings.append(debugString)
|
||||||
|
}
|
||||||
|
return retStrings
|
||||||
|
}
|
||||||
|
|
||||||
|
func RibbonDebugPrint(ribbon: Ribbon) -> [String] {
|
||||||
|
var ribbonStats = [String]()
|
||||||
|
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 {
|
||||||
|
@State var ribbonDebug: [String]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
VStack {
|
||||||
|
// let ribbonStats = RibbonDebugPrint(ribbon: ribbon)
|
||||||
|
|
||||||
|
Print("quack2: \(ribbonDebug)")
|
||||||
|
ForEach(ribbonDebug, id: \.self) {
|
||||||
|
Print("quack2 : \($0)")
|
||||||
|
Text($0)
|
||||||
|
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,10 @@
|
||||||
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 */; };
|
||||||
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 */; };
|
||||||
|
@ -45,6 +49,10 @@
|
||||||
/* 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>"; };
|
||||||
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>"; };
|
||||||
|
@ -108,6 +116,8 @@
|
||||||
85431A7C2905F4F500EE0760 = {
|
85431A7C2905F4F500EE0760 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
851259B12C05299200BE70F8 /* ForwardArrow.swift */,
|
||||||
|
851259AF2C05281300BE70F8 /* BackButton.swift */,
|
||||||
857C34482BFB7DC800661A63 /* Fenestra.swift */,
|
857C34482BFB7DC800661A63 /* Fenestra.swift */,
|
||||||
8594ED972BF6845F001213F2 /* HexColor.swift */,
|
8594ED972BF6845F001213F2 /* HexColor.swift */,
|
||||||
85E00E7B29F34D2D00FF9E78 /* ScrollState.swift */,
|
85E00E7B29F34D2D00FF9E78 /* ScrollState.swift */,
|
||||||
|
@ -117,6 +127,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 */,
|
||||||
|
@ -285,6 +297,7 @@
|
||||||
857C34492BFB7DC800661A63 /* Fenestra.swift in Sources */,
|
857C34492BFB7DC800661A63 /* Fenestra.swift in Sources */,
|
||||||
85942EEB29AD55A400307621 /* RibbonRequest.swift in Sources */,
|
85942EEB29AD55A400307621 /* RibbonRequest.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 +310,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 */,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import GRDB
|
import GRDB
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let totalLevels = 3
|
||||||
|
|
||||||
/// AppDatabase lets the application access the database.
|
/// AppDatabase lets the application access the database.
|
||||||
///
|
///
|
||||||
/// It applies the pratices recommended at
|
/// It applies the pratices recommended at
|
||||||
|
@ -11,7 +15,7 @@ struct AppDatabase {
|
||||||
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 +23,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 +43,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 +58,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 +73,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 +82,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: "foo1") { 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 +126,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 +163,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 +177,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 +190,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 +222,121 @@ extension AppDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
print("meow new current: \(newCurrent)")
|
||||||
|
|
||||||
|
do {
|
||||||
|
try await dbWriter.write { [ribbon] db in
|
||||||
|
print("back executing")
|
||||||
|
try db.execute(sql: """
|
||||||
|
UPDATE Ribbon \
|
||||||
|
SET currentLevel = ? WHERE groupId = ?
|
||||||
|
""", arguments: [newCurrent, ribbon.groupId])
|
||||||
|
|
||||||
|
var ret3 = try Ribbon.fetchAll(db, sql: """
|
||||||
|
SELECT * from Ribbon \
|
||||||
|
WHERE groupId = ?
|
||||||
|
""", arguments: [ribbon.groupId])
|
||||||
|
|
||||||
|
print("back ret3: \(ret3)")
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
print("back error")
|
||||||
|
print("Error info: \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func bumpRibbon(_ ribbon: inout Ribbon) async throws -> [Ribbon] {
|
||||||
|
var level = ribbon.currentLevel
|
||||||
|
let maxLevel = ribbon.maxLevel
|
||||||
|
var delLevels2 = [Int]()
|
||||||
|
while level != maxLevel {
|
||||||
|
level = (level + 1) % totalLevels
|
||||||
|
delLevels2.append(level)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
print("meow enter bump 1")
|
||||||
|
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])
|
||||||
|
|
||||||
|
print("meow enter bump 3")
|
||||||
|
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, ribbon.undoLevel])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let ret2 = try Ribbon.fetchAll(db, sql: """
|
||||||
|
SELECT * from Ribbon WHERE groupId = 1 ORDER BY undoLevel ASC
|
||||||
|
""")
|
||||||
|
|
||||||
|
for r2 in ret2 {
|
||||||
|
print("meow ribbon dump: \(r2)")
|
||||||
|
}
|
||||||
|
|
||||||
|
print("meow bumped ribbon: \(ret)")
|
||||||
|
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 +356,106 @@ 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 Ribbon(id: 1,
|
||||||
|
groupId: 1,
|
||||||
|
pos: 1,
|
||||||
|
undoLevel: 0,
|
||||||
|
currentLevel: 0,
|
||||||
|
minLevel: 0,
|
||||||
|
maxLevel: 0,
|
||||||
|
title: "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 of Mark",
|
||||||
|
book: "bible.mark",
|
||||||
|
scrollId: "1",
|
||||||
|
scrollOffset: 300).inserted(db)
|
||||||
|
|
||||||
if try Line.all().isEmpty(db) {
|
/////
|
||||||
|
|
||||||
try importJson("john_export.json", db)
|
_ = try Ribbon(id: 3,
|
||||||
try importJson("mark_export.json", db)
|
groupId: 3,
|
||||||
_ = try Ribbon(id: 1, pos: 1, title: "John", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db)
|
pos: 3,
|
||||||
_ = try Ribbon(id: 2, pos: 2, title: "Gospel of Mark", book: "bible.mark", scrollId: "1", scrollOffset: 300).inserted(db)
|
undoLevel: 0,
|
||||||
_ = try Ribbon(id: 3, pos: 3, title: "John 2", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db)
|
currentLevel: 2,
|
||||||
_ = try SelectedRibbon(id: 1, ribbonId: 1).inserted(db)
|
minLevel: 0,
|
||||||
|
maxLevel: 2,
|
||||||
|
title: "bottom",
|
||||||
|
book: "bible.john",
|
||||||
|
scrollId: "1",
|
||||||
|
scrollOffset: 0).inserted(db)
|
||||||
|
|
||||||
|
_ = try Ribbon(id: 4,
|
||||||
|
groupId: 3,
|
||||||
|
pos: 3,
|
||||||
|
undoLevel: 1,
|
||||||
|
currentLevel: 2,
|
||||||
|
minLevel: 0,
|
||||||
|
maxLevel: 2,
|
||||||
|
title: "topp",
|
||||||
|
book: "bible.john",
|
||||||
|
scrollId: "1",
|
||||||
|
scrollOffset: 0).inserted(db)
|
||||||
|
|
||||||
|
_ = try Ribbon(id: 5,
|
||||||
|
groupId: 3,
|
||||||
|
pos: 3,
|
||||||
|
undoLevel: 2,
|
||||||
|
currentLevel: 2,
|
||||||
|
minLevel: 0,
|
||||||
|
maxLevel: 2,
|
||||||
|
title: "topp",
|
||||||
|
book: "bible.john",
|
||||||
|
scrollId: "1",
|
||||||
|
scrollOffset: 0).inserted(db)
|
||||||
|
|
||||||
|
_ = try SelectedRibbon(id: 1, ribbonGroupId: 1).inserted(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
print("Error info: \(error)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,20 +39,45 @@ public extension UserDefaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateRibbon(selectedRibbon: Ribbon,
|
||||||
|
appDatabase : AppDatabase,
|
||||||
|
paneConnector: PaneConnector) async throws -> [Ribbon]
|
||||||
|
{
|
||||||
|
print("meow updating ribbon")
|
||||||
|
var updatedRibbon = selectedRibbon
|
||||||
|
var scrollOffsetToSave = Int(floor(paneConnector.currentOffset))
|
||||||
|
var scrollIdToSave = paneConnector.currentId
|
||||||
|
print("meow scrolloffsets \(scrollIdToSave) \(scrollOffsetToSave)")
|
||||||
|
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)
|
||||||
|
print("meow finished bumping")
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
print("meow no bump")
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
func goToRibbon(selectedRibbon: Ribbon,
|
func goToRibbon(selectedRibbon: Ribbon,
|
||||||
destRibbon: Ribbon,
|
destRibbon: Ribbon,
|
||||||
appDatabase: AppDatabase,
|
appDatabase: AppDatabase,
|
||||||
paneConnector: PaneConnector,
|
paneConnector: PaneConnector,
|
||||||
refresh: Binding<Bool>,
|
loading: Bool,
|
||||||
loading: Bool)
|
bump: Bool)
|
||||||
{
|
{
|
||||||
|
print("meow goto ribbon - selected ribbon: \(selectedRibbon), dest ribbon: \(destRibbon) ")
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
||||||
Task {
|
Task {
|
||||||
var scrollOffsetToSave = paneConnector.currentOffset
|
var scrollOffsetToSave = paneConnector.currentOffset
|
||||||
var scrollIdToSave = paneConnector.currentId
|
var scrollIdToSave = paneConnector.currentId
|
||||||
|
|
||||||
var updatedRibbon = selectedRibbon
|
|
||||||
print("go to ribbon")
|
print("go to ribbon")
|
||||||
print("\(selectedRibbon.id) \(destRibbon.id!)")
|
print("\(selectedRibbon.id) \(destRibbon.id!)")
|
||||||
|
|
||||||
|
@ -72,9 +97,7 @@ func goToRibbon(selectedRibbon: Ribbon,
|
||||||
print("toggling")
|
print("toggling")
|
||||||
print("paneconnector: \(paneConnector.refresh)")
|
print("paneconnector: \(paneConnector.refresh)")
|
||||||
|
|
||||||
refresh.wrappedValue.toggle()
|
var updateSelectRibbon = SelectedRibbon(id: Int64(1), ribbonGroupId: Int64(destRibbon.groupId))
|
||||||
|
|
||||||
var updateSelectRibbon = SelectedRibbon(id: Int64(1), ribbonId: destRibbon.id!)
|
|
||||||
// print("Saving selected ribbon")
|
// print("Saving selected ribbon")
|
||||||
// print(updateSelectRibbon)
|
// print(updateSelectRibbon)
|
||||||
do {
|
do {
|
||||||
|
@ -84,15 +107,26 @@ func goToRibbon(selectedRibbon: Ribbon,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !loading {
|
// // this gets run regardless of if we switch ribbons or not
|
||||||
print("not loading")
|
// // so if you click the same ribbon or a different ribbon
|
||||||
updatedRibbon.scrollOffset = Int(floor(scrollOffsetToSave))
|
// if !loading || bump {
|
||||||
updatedRibbon.scrollId = scrollIdToSave
|
// print("not loading")
|
||||||
_ = try await appDatabase.saveRibbon(&updatedRibbon)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
print("loading")
|
// print("saving ribbon")
|
||||||
}
|
// // updating the ribbon location
|
||||||
|
// updatedRibbon.scrollOffset = Int(floor(scrollOffsetToSave))
|
||||||
|
// updatedRibbon.scrollId = scrollIdToSave
|
||||||
|
// // updatedRibbon.undoLevel = updatedRibbon.undoLevel + 1
|
||||||
|
// // updatedRibbon.currentLevel = updatedRibbon.currentLevel + 1
|
||||||
|
|
||||||
|
// if bump {
|
||||||
|
// _ = try await appDatabase.bumpRibbon(&updatedRibbon)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// } else {
|
||||||
|
// print("loading")
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +151,7 @@ struct RibbonCrown: View {
|
||||||
|
|
||||||
@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
|
@Binding var refresh: Bool
|
||||||
|
@ -149,12 +183,18 @@ struct RibbonCrown: View {
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
Task {
|
Task {
|
||||||
goToRibbon(selectedRibbon: sr[0],
|
|
||||||
|
let sr = selectedRibbon[0]
|
||||||
|
let updatedRibbon = try await updateRibbon(selectedRibbon: sr,
|
||||||
|
appDatabase: appDatabase,
|
||||||
|
paneConnector: paneConnector)
|
||||||
|
|
||||||
|
goToRibbon(selectedRibbon: sr,
|
||||||
destRibbon: ribbon,
|
destRibbon: ribbon,
|
||||||
appDatabase: appDatabase,
|
appDatabase: appDatabase,
|
||||||
paneConnector: paneConnector,
|
paneConnector: paneConnector,
|
||||||
refresh: $refresh,
|
loading: false,
|
||||||
loading: false)
|
bump: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.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))
|
||||||
|
@ -191,6 +231,7 @@ class PaneConnector: NSObject, ObservableObject {
|
||||||
// var setScrollOffset: CGFloat
|
// var setScrollOffset: CGFloat
|
||||||
var showOverlay: Bool = false
|
var showOverlay: Bool = false
|
||||||
@Published var refresh: Bool = false
|
@Published var refresh: Bool = false
|
||||||
|
@Published var vertSep = CGFloat(20)
|
||||||
var currentId = ""
|
var currentId = ""
|
||||||
var currentOffset = CGFloat()
|
var currentOffset = CGFloat()
|
||||||
var visibilityTracker: VisibilityTracker<String>?
|
var visibilityTracker: VisibilityTracker<String>?
|
||||||
|
@ -204,25 +245,21 @@ struct ContentView: View {
|
||||||
@State var viewState = CGSize.zero
|
@State var viewState = CGSize.zero
|
||||||
@State var pulledOut = CGSize.zero
|
@State var pulledOut = CGSize.zero
|
||||||
|
|
||||||
|
@State var selection = 0
|
||||||
|
|
||||||
@StateObject var paneConnector = PaneConnector()
|
@StateObject var paneConnector = PaneConnector()
|
||||||
|
|
||||||
@State var refresh: Bool = false
|
@State var refresh: Bool = false
|
||||||
|
|
||||||
@State var vertSep = CGFloat(20)
|
|
||||||
|
|
||||||
@State var endedDrag = true
|
|
||||||
|
|
||||||
@State var readOffset = CGPoint()
|
@State var readOffset = CGPoint()
|
||||||
@State var dragOffset = CGFloat()
|
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
@Environment(\.appDatabase) private var appDatabase
|
@Environment(\.appDatabase) private var appDatabase
|
||||||
|
|
||||||
@Query(SegDenormRequest(book: "bible.mark")) private var segs: [SegDenorm]
|
|
||||||
|
|
||||||
@Query(RibbonRequest()) private var ribbons: [Ribbon]
|
@Query(RibbonRequest()) private var ribbons: [Ribbon]
|
||||||
@Query<SelectedRibbonRequest> var selectedRibbon: [Ribbon]
|
@Query<SelectedRibbonRequest> var selectedRibbon: [Ribbon]
|
||||||
|
|
||||||
|
@ -271,13 +308,25 @@ 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 {
|
||||||
|
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 {
|
||||||
|
NaviBar(paneConnector: paneConnector)
|
||||||
|
StatsPanel(paneConnector: paneConnector)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: 250)
|
||||||
|
.offset(x: geometry.size.width - 250)
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
// Top pane
|
// Top pane
|
||||||
Pane(paneConnector: paneConnector,
|
Pane(paneConnector: paneConnector,
|
||||||
segs: segs,
|
|
||||||
selectedRibbon: selectedRibbon,
|
selectedRibbon: selectedRibbon,
|
||||||
width: geometry.size.width - 50,
|
width: geometry.size.width - 50,
|
||||||
height: geometry.size.height / 2 - vertSep)
|
height: geometry.size.height / 2)
|
||||||
//////
|
//////
|
||||||
|
|
||||||
Text("separator").foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
Text("separator").foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||||
|
@ -285,7 +334,7 @@ struct ContentView: View {
|
||||||
DragGesture()
|
DragGesture()
|
||||||
.onChanged { gesture in
|
.onChanged { gesture in
|
||||||
|
|
||||||
vertSep = vertSep - gesture.translation.height
|
paneConnector.vertSep = paneConnector.vertSep - gesture.translation.height
|
||||||
Print(gesture.translation.width)
|
Print(gesture.translation.width)
|
||||||
Print(gesture.translation.height)
|
Print(gesture.translation.height)
|
||||||
|
|
||||||
|
@ -335,33 +384,30 @@ struct ContentView: View {
|
||||||
DragGesture()
|
DragGesture()
|
||||||
.onChanged { gesture in
|
.onChanged { gesture in
|
||||||
|
|
||||||
if endedDrag {
|
print(viewState.width)
|
||||||
endedDrag = false
|
print(pulledOut.width)
|
||||||
|
// threshold of how much to swipe before the view drags
|
||||||
//TODO: should this still be adjusted
|
if abs(gesture.translation.width) > 10 {
|
||||||
//scrollOffset = readOffset.y - 20
|
|
||||||
}
|
|
||||||
Print(viewState.width)
|
|
||||||
if abs(gesture.translation.width) > 20 {
|
|
||||||
viewState.width = gesture.translation.width
|
viewState.width = gesture.translation.width
|
||||||
if gesture.translation.width < -50, pulledOut.width == CGFloat(0) {
|
// if gesture.translation.width < -50, pulledOut.width == CGFloat(0) {
|
||||||
dragOffset = gesture.translation.width + 50
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onEnded { _ in
|
.onEnded { _ in
|
||||||
endedDrag = true
|
var setPulledOutWith = CGFloat(0)
|
||||||
var pulledOutWidth = CGFloat(0)
|
if viewState.width < 0 && pulledOut.width > 0 {
|
||||||
if viewState.width < 0 {
|
setPulledOutWith = CGFloat(0)
|
||||||
pulledOutWidth = CGFloat(0)
|
} else if viewState.width > 0 && pulledOut.width < 0 {
|
||||||
|
setPulledOutWith = CGFloat(0)
|
||||||
|
} else if viewState.width < 0 && pulledOut.width == 0 {
|
||||||
|
setPulledOutWith = CGFloat(-200)
|
||||||
} else if abs(viewState.width + pulledOut.width) > 30 {
|
} else if abs(viewState.width + pulledOut.width) > 30 {
|
||||||
pulledOutWidth = CGFloat(200)
|
setPulledOutWith = CGFloat(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
withAnimation(.spring(response: 0.2)) {
|
withAnimation(.spring(response: 0.2)) {
|
||||||
pulledOut.width = pulledOutWidth
|
pulledOut.width = setPulledOutWith
|
||||||
viewState = .zero
|
viewState = .zero
|
||||||
dragOffset = .zero
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -389,27 +435,7 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleVisibilityChanged2(_: String, change _: VisibilityChange, tracker _: VisibilityTracker<String>) {}
|
func handleVisibilityChanged2(_: String, change _: VisibilityChange, tracker _: VisibilityTracker<String>) {}
|
||||||
|
|
||||||
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!]!
|
|
||||||
|
|
||||||
self.paneConnector.currentId = tracker.sortedViewIDs[0]
|
|
||||||
self.paneConnector.currentOffset = tracker.visibleViews[currentId!]!
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private let itemFormatter: DateFormatter = {
|
private let itemFormatter: DateFormatter = {
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue