can request separate books
parent
9eb488a87a
commit
32696d7b58
|
@ -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)")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue