can request separate books

undo
saint 2024-05-26 10:45:30 -04:00
parent 9eb488a87a
commit 32696d7b58
11 changed files with 114 additions and 88 deletions

2
.ignore Normal file
View File

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

View File

@ -5,6 +5,8 @@
// Created by Saint on 5/20/24.
//
import GRDB
import GRDBQuery
import Foundation
import SwiftUI
import WrappingHStack
@ -14,8 +16,11 @@ struct SegRow: View {
var ribbonId: Int64
@State var highlights = Set<Int>()
let intraWordSpacing = CGFloat(1.6)
var body: some View {
var segSplit = seg.body.components(separatedBy: ";;")
Print("got here meow")
Print(segSplit)
let decoder = JSONDecoder()
var retView = WrappingHStack(alignment: .leading, horizontalSpacing: 0) {
ForEach(0 ..< segSplit.count, id: \.self) { segIndex in
@ -53,7 +58,8 @@ struct SegRow: View {
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
.padding(.horizontal, intraWordSpacing) // intra word spacing
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
.foregroundColor(Color.white)
.onTapGesture {
@ -79,7 +85,6 @@ struct SegRow: View {
struct Pane: View {
@ObservedObject var paneConnector: PaneConnector
@State var segs: [SegDenorm]
@State var selectedRibbon: [Ribbon]
@State var width: CGFloat
@ -89,6 +94,8 @@ struct Pane: View {
@State var refresh: Bool = false
@Query(SegDenormRequest(book: "bible.mark")) private var segs: [SegDenorm]
@Environment(\.appDatabase) private var appDatabase
// var handleVisibilityChanged: (String, VisibilityChange, VisibilityTracker<String>) -> Void
@ -125,6 +132,9 @@ struct Pane: View {
Task {
DispatchQueue.main.async {
if paneConnector.visibilityTracker == nil {
return
}
let gTracker = paneConnector.visibilityTracker!
Print("scroll Id target: \(paneConnector.scrollId)")

View File

@ -11,9 +11,9 @@ struct SegDenormRequest: Queryable {
var book: String
// MARK: - Queryable Implementation
static var defaultValue: [SegDenorm] { [] }
func publisher(in appDatabase: AppDatabase) -> AnyPublisher<[SegDenorm], Error> {
ValueObservation
.tracking(fetchValue(_:))
@ -22,17 +22,39 @@ struct SegDenormRequest: Queryable {
scheduling: .immediate)
.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 {
var ret = try SegDenorm.fetchAll(db, sql: sql) // [Player]
// print("SEGS DENORM")
// print(ret)
func fetchValue(_ db: Database) throws -> [SegDenorm] {
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(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
} catch let error {
print(error.localizedDescription)
print(error)

View File

@ -24,7 +24,7 @@ struct SelectedRibbonRequest: Queryable {
/// The ordering used by the player request.
// var ordering: Ordering
static var defaultValue: [Ribbon] { [] }
func publisher(in appDatabase: AppDatabase) -> AnyPublisher<[Ribbon], Error> {
// Build the publisher from the general-purpose read-only access
// granted by `appDatabase.reader`.
@ -39,7 +39,7 @@ struct SelectedRibbonRequest: Queryable {
scheduling: .immediate)
.eraseToAnyPublisher()
}
// This method is not required by Queryable, but it makes it easier
func fetchValue(_ db: Database) throws -> [Ribbon] {
@ -51,8 +51,6 @@ 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 Ribbon on SelectedRibbon.ribbonId = ribbon.rowId WHERE SelectedRibbon.rowId = 1") // [Player]
// print(ret)

View File

@ -11,7 +11,7 @@ struct AppDatabase {
self.dbWriter = dbWriter
try migrator.migrate(dbWriter)
}
/// Provides access to the database.
///
/// Application can use a `DatabasePool`, while SwiftUI previews and tests
@ -19,19 +19,19 @@ struct AppDatabase {
///
/// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseconnections>
private let dbWriter: any DatabaseWriter
/// The DatabaseMigrator that defines the database schema.
///
/// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/migrations>
private var migrator: DatabaseMigrator {
var migrator = DatabaseMigrator()
#if DEBUG
// Speed up development by nuking the database when migrations change
// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/migrations>
migrator.eraseDatabaseOnSchemaChange = true
#endif
migrator.registerMigration("createLine") { db in
// Create a table
// See <https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseschema>
@ -39,6 +39,7 @@ 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)
}
@ -70,18 +71,19 @@ struct AppDatabase {
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.column("ribbonId", .integer).notNull()
}
}
// Migrations for future application versions will be inserted here:
// migrator.registerMigration(...) { db in
// ...
// }
return migrator
}
}
@ -134,9 +136,9 @@ extension AppDatabase {
if (newPos < oldPos) {
try db.execute(sql: """
UPDATE Ribbon
SET pos =
CASE
UPDATE Ribbon
SET pos =
CASE
WHEN pos = ? THEN ?
ELSE
pos + 1
@ -148,9 +150,9 @@ extension AppDatabase {
print("DIFFFFF")
try db.execute(sql: """
UPDATE Ribbon
SET pos =
CASE
UPDATE Ribbon
SET pos =
CASE
WHEN pos = ? THEN ?
ELSE
pos - 1
@ -161,7 +163,7 @@ extension AppDatabase {
}
// try db.execute(sql: """
// UPDATE Ribbon
// UPDATE Ribbon
// SET pos = ?
// WHERE (id = ?)
// """, arguments: [newPos, ribbon.id!])
@ -212,27 +214,36 @@ extension AppDatabase {
}
func importJson(_ filename: String, _ db: Database) throws {
let importJson : JsonImport = load(filename)
let importJson: JsonImport = load(filename)
if try Line.all().isEmpty(db) {
for l in importJson.lines {
print("importing Lines")
_ = try l.inserted(db)
var x = 0
// if try Line.all().isEmpty(db) {
for l in importJson.lines {
// print("importing Lines")
if x < 5 {
print(l)
x += 1
}
_ = try l.inserted(db)
}
for l in importJson.segs {
print("importing SEGS")
_ = try l.inserted(db)
x = 0
for l in importJson.segs {
// print("importing SEGS")
if x < 5 {
print(l)
x += 1
}
_ = 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) {
try importJson("john_export.json", db)
try importJson("mark_export.json", db)
_ = try Ribbon(id: 1, pos: 1, title: "John", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db)
@ -241,6 +252,11 @@ extension AppDatabase {
_ = try SelectedRibbon(id: 1, ribbonId: 1).inserted(db)
}
}
} catch {
print("Error info: \(error)")
}
}
}

View File

@ -214,14 +214,13 @@ struct ContentView: View {
@State var readOffset = CGPoint()
@State var dragOffset = CGFloat()
@Query(SegDenormRequest(book: "bible.john")) private var segs: [SegDenorm]
@State var draggedRibbon: Ribbon?
@State var isDragging = false
@Environment(\.appDatabase) private var appDatabase
@Query(SegDenormRequest(book: "bible.mark")) private var segs: [SegDenorm]
@Query(RibbonRequest()) private var ribbons: [Ribbon]
@Query<SelectedRibbonRequest> var selectedRibbon: [Ribbon]
@ -273,7 +272,6 @@ struct ContentView: View {
VStack {
// Top pane
Pane(paneConnector: paneConnector,
segs: segs,
selectedRibbon: selectedRibbon,
width: geometry.size.width - 50,
height: geometry.size.height / 2)

View File

@ -1,39 +1,17 @@
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
@ -47,7 +25,7 @@ extension Line: Codable, FetchableRecord, MutablePersistableRecord {
static let id = Column(CodingKeys.id)
static let chap = Column(CodingKeys.chap)
}
/// Updates a player id after it has been inserted in the database.
mutating func didInsert(_ inserted: InsertionSuccess) {
id = inserted.rowID
@ -73,7 +51,7 @@ extension DerivableRequest<Line> {
// // See https://github.com/groue/GRDB.swift/blob/master/README.md#string-comparison
// order(Line.Columns.name.collating(.localizedCaseInsensitiveCompare))
//}
///// A request of players ordered by score.
/////
///// For example:

View File

@ -26,11 +26,11 @@ struct LineRequest: Queryable {
var ordering: Ordering
var book: String
// MARK: - Queryable Implementation
static var defaultValue: [Line] { [] }
func publisher(in appDatabase: AppDatabase) -> AnyPublisher<[Line], Error> {
// Build the publisher from the general-purpose read-only access
// granted by `appDatabase.reader`.
@ -45,15 +45,16 @@ struct LineRequest: Queryable {
scheduling: .immediate)
.eraseToAnyPublisher()
}
// 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)
}
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

@ -5,7 +5,7 @@ import Foundation
extension AppDatabase {
/// The database for the application
static let shared = makeShared()
private static func makeShared() -> AppDatabase {
do {
// Pick a folder for storing the SQLite database, as well as
@ -20,18 +20,18 @@ extension AppDatabase {
if CommandLine.arguments.contains("-reset") {
try? fileManager.removeItem(at: folderURL)
}
// Create the database folder if needed
try fileManager.createDirectory(at: folderURL, withIntermediateDirectories: true)
// Connect to a database on disk
// See https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/databaseconnections
let dbURL = folderURL.appendingPathComponent("db.sqlite")
let dbPool = try DatabasePool(path: dbURL.path)
// Create the AppDatabase
let appDatabase = try AppDatabase(dbPool)
// // Prepare the database with test fixtures if requested
// if CommandLine.arguments.contains("-fixedTestData") {
// try appDatabase.createPlayersForUITests()
@ -40,9 +40,10 @@ extension AppDatabase {
// // demo purpose.
// try appDatabase.createRandomPlayersIfEmpty()
// }
print("initing database")
try appDatabase.initDatabase()
return appDatabase
} catch {
// Replace this implementation with code to handle the error appropriately.
@ -57,7 +58,7 @@ extension AppDatabase {
fatalError("Unresolved error \(error)")
}
}
/// Creates an empty database for SwiftUI previews
static func empty() -> AppDatabase {
// Connect to an in-memory database
@ -65,7 +66,7 @@ extension AppDatabase {
let dbQueue = try! DatabaseQueue()
return try! AppDatabase(dbQueue)
}
/// Creates a database full of random players for SwiftUI previews
static func random() -> AppDatabase {
let appDatabase = empty()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long