reordering works better now with proper db update, but only one direction
parent
2d6bff5fc6
commit
5294ee7c4d
|
@ -50,7 +50,7 @@ struct RibbonRequest: Queryable {
|
|||
// This method is not required by Queryable, but it makes it easier
|
||||
func fetchValue(_ db: Database) throws -> [Ribbon] {
|
||||
if (id == nil) {
|
||||
return try Ribbon.fetchAll(db)
|
||||
return try Ribbon.order(Column("pos")).fetchAll(db)
|
||||
} else {
|
||||
return try Ribbon.filter(idColumn == id).fetchAll(db)
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ struct AppDatabase {
|
|||
|
||||
try db.create(table: "Ribbon") { t in
|
||||
t.autoIncrementedPrimaryKey("id")
|
||||
t.column("pos", .integer).notNull()
|
||||
t.column("title", .text).notNull()
|
||||
t.column("book", .text).notNull()
|
||||
t.column("scrollOffset", .integer).notNull()
|
||||
|
@ -69,7 +70,7 @@ struct AppDatabase {
|
|||
t.column("scrollOffset", .integer).notNull()
|
||||
}
|
||||
|
||||
try db.create(table: "foo4") { t in
|
||||
try db.create(table: "foo2") { t in
|
||||
t.autoIncrementedPrimaryKey("id")
|
||||
t.column("ribbonId", .integer).notNull()
|
||||
}
|
||||
|
@ -112,6 +113,58 @@ func load<T: Decodable>(_ filename: String) -> T {
|
|||
|
||||
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 {
|
||||
// if ribbon.name.isEmpty {
|
||||
// throw ValidationError.missingName
|
||||
|
@ -121,7 +174,6 @@ extension AppDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func saveSelectedRibbon(_ selectedRibbon: inout SelectedRibbon) async throws {
|
||||
// if ribbon.name.isEmpty {
|
||||
// throw ValidationError.missingName
|
||||
|
@ -140,7 +192,6 @@ extension AppDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func importJson(_ filename: String, _ db: Database) throws {
|
||||
let importJson : JsonImport = load(filename)
|
||||
|
||||
|
@ -159,39 +210,20 @@ extension AppDatabase {
|
|||
|
||||
/// Create random Lines if the database is empty.
|
||||
func initDatabase() throws {
|
||||
|
||||
|
||||
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, 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: 1, pos: 1, title: "John", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db)
|
||||
_ = try Ribbon(id: 2, pos: 2, title: "Gospel of Mark", book: "bible.mark", scrollId: "1", scrollOffset: 300).inserted(db)
|
||||
_ = try Ribbon(id: 3, pos: 3, title: "John 2", book: "bible.john", scrollId: "1", scrollOffset: 0).inserted(db)
|
||||
_ = try SelectedRibbon(id: 1, ribbonId: 1).inserted(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
|
@ -127,7 +127,7 @@ struct RibbonCrown : View {
|
|||
@Binding var showOverlay : Bool
|
||||
@Binding var refresh : Bool
|
||||
|
||||
var height = CGFloat(50)
|
||||
var height = CGFloat(45)
|
||||
var xOffset = CGFloat(25)
|
||||
var scale = 0.65
|
||||
|
||||
|
@ -138,12 +138,16 @@ struct RibbonCrown : View {
|
|||
|
||||
@State var saveOffset = CGFloat()
|
||||
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)
|
||||
.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(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(.yellow)
|
||||
.multilineTextAlignment(.center)
|
||||
|
@ -151,15 +155,30 @@ struct RibbonCrown : View {
|
|||
.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)
|
||||
}
|
||||
|
||||
// Text(ribbon.title)
|
||||
// .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(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
|
||||
// let ribbonString = String(sr[0].id!) + " " + String(ribbon.id!) + " " + String(ribbon.scrollOffset) + " " + ribbon.scrollId
|
||||
|
@ -312,7 +331,8 @@ struct ContentView: View {
|
|||
|
||||
@State var refresh2: Bool = false
|
||||
|
||||
@State var data = ["Imitation of Christ", "Psalms", "Fundamentals of Dogmatic Theology"]
|
||||
@State var draggedRibbon: Ribbon?
|
||||
|
||||
@State var reorder = true
|
||||
|
||||
|
||||
|
@ -341,91 +361,15 @@ struct ContentView: View {
|
|||
|
||||
var width = CGFloat(100 * 1.66 * scale)
|
||||
|
||||
// ForEach(ribbons) { ribbon in
|
||||
// data.append(ribbon)
|
||||
// }
|
||||
|
||||
GeometryReader { geometry in
|
||||
ZStack (alignment: .top) {
|
||||
VStack (alignment: .leading) {
|
||||
VStack {
|
||||
|
||||
ReorderableForEach($data, allowReordering: $reorder) { item, isDragged in
|
||||
Text(item)
|
||||
.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: width + 30, 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: 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)))
|
||||
// // // .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)
|
||||
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||
.zIndex(0)
|
||||
|
||||
// Spacer()
|
||||
|
||||
// ReorderableForEach($data, allowReordering: $reorder) { item, isDragged in
|
||||
ForEach(ribbons) { ribbon in
|
||||
RibbonCrown(ribbon: ribbon,
|
||||
scrollId:$scrollId,
|
||||
|
@ -433,12 +377,38 @@ struct ContentView: View {
|
|||
showOverlay: $showOverlay,
|
||||
refresh:$refresh
|
||||
)
|
||||
// .buttonStyle(BlueButtonStyle())
|
||||
// .frame(alignment: .topLeading)
|
||||
|
||||
// .background(Color(red: 0.4, green: 0.4, blue: 0.1))
|
||||
.animation(nil)
|
||||
.onDrag {
|
||||
self.draggedRibbon = ribbon
|
||||
return NSItemProvider()
|
||||
}
|
||||
.onDrop(of: [.text],
|
||||
delegate: DropViewDelegate(destinationItem: ribbon, draggedItem: $draggedRibbon, appDatabase: appDatabase)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// .foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
|
||||
}
|
||||
.frame(width: geometry.size.width, height: geometry.size.height - 100, alignment: .topLeading)
|
||||
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||
.zIndex(0)
|
||||
.animation(.default, value: ribbons)
|
||||
|
||||
// Spacer()
|
||||
|
||||
// ForEach(ribbons) { ribbon in
|
||||
// RibbonCrown(ribbon: ribbon,
|
||||
// scrollId:$scrollId,
|
||||
// scrollOffset:$scrollOffset,
|
||||
// showOverlay: $showOverlay,
|
||||
// refresh:$refresh
|
||||
// )
|
||||
// // .buttonStyle(BlueButtonStyle())
|
||||
// // .frame(alignment: .topLeading)
|
||||
|
||||
// // .background(Color(red: 0.4, green: 0.4, blue: 0.1))
|
||||
// .animation(nil)
|
||||
// }
|
||||
}
|
||||
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
|
||||
.frame(alignment: .topLeading)
|
||||
|
@ -861,6 +831,7 @@ struct ContentView: View {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func handleVisibilityChanged2(_ id: String, change: VisibilityChange, tracker: VisibilityTracker<String>) {
|
||||
}
|
||||
|
||||
|
@ -920,6 +891,50 @@ private let itemFormatter: DateFormatter = {
|
|||
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 {
|
||||
static var previews: some View {
|
||||
ContentView()
|
||||
|
|
|
@ -17,6 +17,7 @@ struct Ribbon: Identifiable, Equatable {
|
|||
/// Int64 is the recommended type for auto-incremented database ids.
|
||||
/// Use nil for players that are not inserted yet in the database.
|
||||
var id: Int64?
|
||||
var pos: Int
|
||||
var title: String
|
||||
var book: String
|
||||
var scrollId: String
|
||||
|
@ -35,6 +36,7 @@ extension Ribbon: Codable, FetchableRecord, MutablePersistableRecord {
|
|||
// Define database columns from CodingKeys
|
||||
fileprivate enum Columns {
|
||||
static let id = Column(CodingKeys.id)
|
||||
static let pos = Column(CodingKeys.pos)
|
||||
static let book = Column(CodingKeys.book)
|
||||
static let title = Column(CodingKeys.title)
|
||||
static let scrollOffset = Column(CodingKeys.scrollOffset)
|
||||
|
|
Loading…
Reference in New Issue