diff --git a/RibbonRequest.swift b/RibbonRequest.swift index d07202e..b4309d7 100644 --- a/RibbonRequest.swift +++ b/RibbonRequest.swift @@ -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) } diff --git a/gloss/AppDatabase.swift b/gloss/AppDatabase.swift index a7f4c56..612b4dd 100644 --- a/gloss/AppDatabase.swift +++ b/gloss/AppDatabase.swift @@ -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(_ 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 diff --git a/gloss/ContentView.swift b/gloss/ContentView.swift index 3ca4567..e478156 100644 --- a/gloss/ContentView.swift +++ b/gloss/ContentView.swift @@ -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,14 +155,29 @@ 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 @@ -312,8 +331,9 @@ struct ContentView: View { @State var refresh2: Bool = false - @State var data = ["Imitation of Christ", "Psalms", "Fundamentals of Dogmatic Theology"] - @State var reorder = true + @State var draggedRibbon: Ribbon? + + @State var reorder = true @Query(RibbonRequest()) private var ribbons: [Ribbon] @@ -341,104 +361,54 @@ 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) + // ReorderableForEach($data, allowReordering: $reorder) { item, isDragged in + ForEach(ribbons) { ribbon in + RibbonCrown(ribbon: ribbon, + scrollId:$scrollId, + scrollOffset:$scrollOffset, + showOverlay: $showOverlay, + refresh:$refresh + ) + .onDrag { + self.draggedRibbon = ribbon + return NSItemProvider() + } + .onDrop(of: [.text], + delegate: DropViewDelegate(destinationItem: ribbon, draggedItem: $draggedRibbon, appDatabase: appDatabase) + ) } - // .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) + .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) + // 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.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) { } @@ -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() diff --git a/gloss/Ribbon.swift b/gloss/Ribbon.swift index 44ccff6..9957e71 100644 --- a/gloss/Ribbon.swift +++ b/gloss/Ribbon.swift @@ -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)