reordering works better now with proper db update, but only one direction

undo
saint 2023-08-02 19:26:17 -04:00
parent 2d6bff5fc6
commit 5294ee7c4d
4 changed files with 168 additions and 119 deletions

View File

@ -50,7 +50,7 @@ struct RibbonRequest: Queryable {
// 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) { if (id == nil) {
return try Ribbon.fetchAll(db) return try Ribbon.order(Column("pos")).fetchAll(db)
} else { } else {
return try Ribbon.filter(idColumn == id).fetchAll(db) return try Ribbon.filter(idColumn == id).fetchAll(db)
} }

View File

@ -52,6 +52,7 @@ 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("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()
@ -69,7 +70,7 @@ struct AppDatabase {
t.column("scrollOffset", .integer).notNull() t.column("scrollOffset", .integer).notNull()
} }
try db.create(table: "foo4") { t in try db.create(table: "foo2") { t in
t.autoIncrementedPrimaryKey("id") t.autoIncrementedPrimaryKey("id")
t.column("ribbonId", .integer).notNull() t.column("ribbonId", .integer).notNull()
} }
@ -112,6 +113,58 @@ func load<T: Decodable>(_ filename: String) -> T {
extension AppDatabase { extension AppDatabase {
func updateRibbonPosition(_ ribbon: inout Ribbon, _ oldPos: Int, _ newPos: Int) async throws {
try await dbWriter.write { [ribbon] db in
// This is only for moving back rn
print("MEOW HERE")
print(oldPos)
print(newPos)
print(ribbon)
print(ribbon.id!)
print("MEOW HERE 2")
// try db.execute(sql: """
// BEGIN TRANSACTION;
// """)
do {
try db.execute(sql: """
UPDATE Ribbon
SET pos =
CASE
WHEN pos = ? THEN ?
ELSE
pos + 1
END
WHERE (pos >= ? AND pos <= ?)
""", arguments: [oldPos, newPos, newPos, oldPos])
// try db.execute(sql: """
// UPDATE Ribbon
// SET pos = ?
// WHERE (id = ?)
// """, arguments: [newPos, ribbon.id!])
var ret = try Ribbon.fetchAll(db, sql: "SELECT * FROM Ribbon ORDER BY pos ASC") // [Player]
// print(ret)
print("all")
print(ret)
} catch {
print("Error info: \(error)")
}
// try ribbon.saved(db)
// try db.execute(sql: """
// COMMIT;
// """)
}
}
func saveRibbon(_ ribbon: inout Ribbon) async throws { func saveRibbon(_ ribbon: inout Ribbon) async throws {
// if ribbon.name.isEmpty { // if ribbon.name.isEmpty {
// throw ValidationError.missingName // throw ValidationError.missingName
@ -121,7 +174,6 @@ extension AppDatabase {
} }
} }
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
@ -140,7 +192,6 @@ 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)
@ -159,39 +210,20 @@ extension AppDatabase {
/// 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 try dbWriter.write { db in
if try Line.all().isEmpty(db) { if try Line.all().isEmpty(db) {
try importJson("john_export.json", db) try importJson("john_export.json", db)
try importJson("mark_export.json", db) try importJson("mark_export.json", db)
_ = try Ribbon(id: 1, title: "John", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db) _ = try Ribbon(id: 1, pos: 1, title: "John", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db)
_ = try Ribbon(id: 2, title: "Gospel of Mark", book: "bible.mark", scrollId: "1", scrollOffset: 300).inserted(db) _ = try Ribbon(id: 2, pos: 2, title: "Gospel of Mark", book: "bible.mark", scrollId: "1", scrollOffset: 300).inserted(db)
_ = try Ribbon(id: 3, pos: 3, title: "John 2", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db)
_ = try SelectedRibbon(id: 1, ribbonId: 1).inserted(db) _ = try SelectedRibbon(id: 1, ribbonId: 1).inserted(db)
} }
} }
} }
// static let uiTestLines = [
// Line(id: nil, name: "Arthur", score: 5),
// Line(id: nil, name: "Barbara", score: 6),
// Line(id: nil, name: "Craig", score: 8),
// Line(id: nil, name: "David", score: 4),
// Line(id: nil, name: "Elena", score: 1),
// Line(id: nil, name: "Frederik", score: 2),
// Line(id: nil, name: "Gilbert", score: 7),
// Line(id: nil, name: "Henriette", score: 3)]
// func createLinesForUITests() throws {
// try dbWriter.write { db in
// try AppDatabase.uiTestLines.forEach { Line in
// _ = try Line.inserted(db) // insert but ignore inserted id
// }
// }
// }
} }
// MARK: - Database Access: Reads // MARK: - Database Access: Reads

View File

@ -127,7 +127,7 @@ struct RibbonCrown : View {
@Binding var showOverlay : Bool @Binding var showOverlay : Bool
@Binding var refresh : Bool @Binding var refresh : Bool
var height = CGFloat(50) var height = CGFloat(45)
var xOffset = CGFloat(25) var xOffset = CGFloat(25)
var scale = 0.65 var scale = 0.65
@ -138,12 +138,16 @@ struct RibbonCrown : View {
@State var saveOffset = CGFloat() @State var saveOffset = CGFloat()
var body: some View { var body: some View {
ZStack {
MyIcon().frame(width: CGFloat(100 * 1.66 * scale), height: CGFloat(100 * scale), alignment: .center) .foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))
Text(ribbon.title) Text(ribbon.title)
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00))) .foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
// // .background(Color(red: 0.3, green: 0.3, blue: 0.3))
// .foregroundColor(.white)) // .foregroundColor(.white))
// .foregroundColor(.black) // .foregroundColor(.black)
.frame(minWidth: CGFloat(75), maxWidth: CGFloat(75), minHeight: height, maxHeight: height, alignment: .center) .frame(minWidth: CGFloat(70), maxWidth: CGFloat(70), minHeight: height, maxHeight: height, alignment: .center)
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
// .background(.red) // .background(.red)
// .background(.yellow) // .background(.yellow)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
@ -151,14 +155,29 @@ struct RibbonCrown : View {
.padding([.top, .bottom], 10) .padding([.top, .bottom], 10)
.font(Font.custom("AveriaSerifLibre-Regular", size: CGFloat(10))) .font(Font.custom("AveriaSerifLibre-Regular", size: CGFloat(10)))
.overlay( MyIcon().frame(width: CGFloat(100 * 1.66 * scale), height: CGFloat(100 * scale), alignment: .center) .foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))) }
.offset(x: xOffset) // Text(ribbon.title)
.onTapGesture { // .foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
Print("TAP") // // // .background(Color(red: 0.3, green: 0.3, blue: 0.3))
// selectedLine = seg.id // // .foregroundColor(.white))
// Print(selectedLine) // // .foregroundColor(.black)
} // .frame(minWidth: CGFloat(75), maxWidth: CGFloat(75), minHeight: height, maxHeight: height, alignment: .center)
// // .background(.red)
// // .background(.yellow)
// .multilineTextAlignment(.center)
// // .minimumScaleFactor(0.5)
// .padding([.top, .bottom], 10)
// .font(Font.custom("AveriaSerifLibre-Regular", size: CGFloat(10)))
// .overlay( MyIcon().frame(width: CGFloat(100 * 1.66 * scale), height: CGFloat(100 * scale), alignment: .center) .foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4))))
// .offset(x: xOffset)
// .onTapGesture {
// Print("TAP")
// // selectedLine = seg.id
// // Print(selectedLine)
// }
// ForEach(sr) { selectedRibbon in // ForEach(sr) { selectedRibbon in
@ -312,8 +331,9 @@ struct ContentView: View {
@State var refresh2: Bool = false @State var refresh2: Bool = false
@State var data = ["Imitation of Christ", "Psalms", "Fundamentals of Dogmatic Theology"] @State var draggedRibbon: Ribbon?
@State var reorder = true
@State var reorder = true
@Query(RibbonRequest()) private var ribbons: [Ribbon] @Query(RibbonRequest()) private var ribbons: [Ribbon]
@ -341,104 +361,54 @@ struct ContentView: View {
var width = CGFloat(100 * 1.66 * scale) var width = CGFloat(100 * 1.66 * scale)
// ForEach(ribbons) { ribbon in
// data.append(ribbon)
// }
GeometryReader { geometry in GeometryReader { geometry in
ZStack (alignment: .top) { ZStack (alignment: .top) {
VStack (alignment: .leading) { VStack (alignment: .leading) {
VStack { VStack {
// ReorderableForEach($data, allowReordering: $reorder) { item, isDragged in
ReorderableForEach($data, allowReordering: $reorder) { item, isDragged in ForEach(ribbons) { ribbon in
Text(item) RibbonCrown(ribbon: ribbon,
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00))) scrollId:$scrollId,
// // .background(Color(red: 0.3, green: 0.3, blue: 0.3)) scrollOffset:$scrollOffset,
// .foregroundColor(.white)) showOverlay: $showOverlay,
// .foregroundColor(.black) refresh:$refresh
.frame(minWidth: CGFloat(75), maxWidth: width + 30, minHeight: height, maxHeight: height, alignment: .center) )
// .background(.red) .onDrag {
// .background(.yellow) self.draggedRibbon = ribbon
.multilineTextAlignment(.center) return NSItemProvider()
// .minimumScaleFactor(0.5) }
.padding([.top, .bottom], 10) .onDrop(of: [.text],
.font(Font.custom("AveriaSerifLibre-Regular", size: CGFloat(10))) delegate: DropViewDelegate(destinationItem: ribbon, draggedItem: $draggedRibbon, appDatabase: appDatabase)
)
.overlay( MyIcon().frame(width: width, height: CGFloat(100 * scale), alignment: .center) .foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4))))
.offset(x: xOffset)
.onTapGesture {
Print("TAP")
// selectedLine = seg.id
// Print(selectedLine)
} }
// .overlay(isDragged ? Color.white.opacity(0.6) : Color.clear)
}
// Text("Imitation of Christ")
// Text("Imitation of Christ")
// .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)))
// // // .background(Color(red: 0.3, green: 0.3, blue: 0.3))
// // .foregroundColor(.white))
// // .foregroundColor(.black)
// .frame(minWidth: CGFloat(75), maxWidth: CGFloat(150), minHeight: height, maxHeight: height, alignment: .center)
// // .background(.red)
// // .background(.yellow)
// .multilineTextAlignment(.center)
// // .minimumScaleFactor(0.5)
// .padding([.top, .bottom], 10)
// .font(Font.custom("AveriaSerifLibre-Regular", size: CGFloat(10)))
// .overlay( MyIcon().frame(width: CGFloat(100 * 1.66 * scale), height: CGFloat(100 * scale), alignment: .center) .foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4))))
// .offset(x: xOffset)
// .onTapGesture {
// Print("TAP")
// // selectedLine = seg.id
// // Print(selectedLine)
// }
// .onDrag {
// // self.draggedColor = color
// return NSItemProvider()
// }
// Text("Psalms")
// .foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
// // .background(Color(red: 0.3, green: 0.3, blue: 0.3))
// // .foregroundColor(.white))
// // .foregroundColor(.black)
// .overlay( MyIcon().frame(width: CGFloat(100 * 1.66 * scale), height: CGFloat(100 * scale)) .foregroundColor(Color(UIColor(red: 0.56, green: 0.30, blue: 0.30, alpha: 0.4))))
// .frame(minWidth: CGFloat(75), maxWidth: CGFloat(75), minHeight: height, maxHeight: height, alignment: .center)
// // .background(.red)
// .multilineTextAlignment(.center)
// .offset(x: xOffset)
// .padding([.top, .bottom], 10)
// // .minimumScaleFactor(0.5)
// .font(Font.custom("AveriaSerifLibre-Regular", size: CGFloat(14)))
} }
.frame(width: geometry.size.width, height: geometry.size.height - 100, alignment: .topLeading) .frame(width: geometry.size.width, height: geometry.size.height - 100, alignment: .topLeading)
.background(Color(red: 0.1, green: 0.1, blue: 0.1)) .background(Color(red: 0.1, green: 0.1, blue: 0.1))
.zIndex(0) .zIndex(0)
.animation(.default, value: ribbons)
// Spacer() // Spacer()
ForEach(ribbons) { ribbon in // ForEach(ribbons) { ribbon in
RibbonCrown(ribbon: ribbon, // RibbonCrown(ribbon: ribbon,
scrollId:$scrollId, // scrollId:$scrollId,
scrollOffset:$scrollOffset, // scrollOffset:$scrollOffset,
showOverlay: $showOverlay, // showOverlay: $showOverlay,
refresh:$refresh // refresh:$refresh
) // )
// .buttonStyle(BlueButtonStyle()) // // .buttonStyle(BlueButtonStyle())
// .frame(alignment: .topLeading) // // .frame(alignment: .topLeading)
// .background(Color(red: 0.4, green: 0.4, blue: 0.1)) // // .background(Color(red: 0.4, green: 0.4, blue: 0.1))
.animation(nil) // .animation(nil)
} // }
} }
.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)
@ -861,6 +831,7 @@ struct ContentView: View {
} }
} }
func handleVisibilityChanged2(_ id: String, change: VisibilityChange, tracker: VisibilityTracker<String>) { func handleVisibilityChanged2(_ id: String, change: VisibilityChange, tracker: VisibilityTracker<String>) {
} }
@ -920,6 +891,50 @@ private let itemFormatter: DateFormatter = {
return formatter return formatter
}() }()
struct DropViewDelegate: DropDelegate {
let destinationItem: Ribbon
// @Binding var colors: [Color]
@Binding var draggedItem: Ribbon?
let appDatabase: AppDatabase
func dropUpdated(info: DropInfo) -> DropProposal? {
return DropProposal(operation: .move)
}
func dropEntered(info: DropInfo) {
Task {
var newRibbon = draggedItem!
var newDest = destinationItem
var oldPos = draggedItem!.pos
var newPos = destinationItem.pos
print("dragged item")
print(draggedItem)
print("dest item")
print(destinationItem)
if (draggedItem!.id! == destinationItem.id!) {
return
}
newRibbon.pos = destinationItem.pos
_ = try await appDatabase.updateRibbonPosition(&newRibbon, oldPos, newPos )
}
}
func performDrop(info: DropInfo) -> Bool {
draggedItem = nil
return true
}
}
struct ContentView_Previews: PreviewProvider { struct ContentView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
ContentView() ContentView()

View File

@ -17,6 +17,7 @@ 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 pos: Int
var title: String var title: String
var book: String var book: String
var scrollId: String var scrollId: String
@ -35,6 +36,7 @@ extension Ribbon: 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 pos = Column(CodingKeys.pos)
static let book = Column(CodingKeys.book) static let book = Column(CodingKeys.book)
static let title = Column(CodingKeys.title) static let title = Column(CodingKeys.title)
static let scrollOffset = Column(CodingKeys.scrollOffset) static let scrollOffset = Column(CodingKeys.scrollOffset)