// // ContentView.swift // gloss // // Created by Saint on 10/23/22. // import SwiftUI import GRDB import GRDBQuery import Introspect import os let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "network") var currentId : String? var currentOffset : CGFloat? var gTracker: VisibilityTracker? // var curBook = "John" extension UserDefaults { public func optionalInt(forKey defaultName: String) -> Int? { let defaults = self if let value = defaults.value(forKey: defaultName) { return value as? Int } return nil } public func optionalBool(forKey defaultName: String) -> Bool? { let defaults = self if let value = defaults.value(forKey: defaultName) { return value as? Bool } return nil } } struct BlueButtonStyle: ButtonStyle { func makeBody(configuration: Self.Configuration) -> some View { configuration.label .font(.headline) .frame(width: 160) .contentShape(Rectangle()) .foregroundColor(configuration.isPressed ? Color.white.opacity(0.5) : Color.black) .background(configuration.isPressed ? Color.purple.opacity(0.5) : Color.purple) .listRowBackground(configuration.isPressed ? Color.blue.opacity(0.5) : Color.black) } } func goToRibbon(selectedRibbon: Ribbon, destRibbon: Ribbon, scrollId: Binding, scrollOffset: Binding, refresh: Binding, appDatabase: AppDatabase ) { Task { // @Binding var scrollId : String? // @Binding var scrollOffset : CGFloat? // @Binding var refresh : Bool // print("SELECTED RIBBON", selectedRibbon) let userDefaults = UserDefaults.standard var scrollOffsetToSave = currentOffset var scrollIdToSave = currentId // Print("scrollOffsetToSave: ", scrollOffsetToSave) // Print("scrollIdToSave: ", scrollIdToSave) var updatedRibbon = selectedRibbon // scrollId = "10" // scrollOffset = CGFloat(100) // refresh.toggle() if (selectedRibbon.id != destRibbon.id!) { print("switching ribbons") scrollId.wrappedValue = destRibbon.scrollId // print("setting scroll offset") scrollOffset.wrappedValue = CGFloat(destRibbon.scrollOffset) // print(scrollOffset.wrappedValue) // print("end setting scroll offset") refresh.wrappedValue.toggle() //scrollId = "10" //scrollOffset = CGFloat(100) userDefaults.set(scrollId.wrappedValue, forKey: "currentId") userDefaults.set(scrollOffset.wrappedValue, forKey: "currentOffset") var updateSelectRibbon = SelectedRibbon(id: Int64(1), ribbonId: destRibbon.id!) // print("Saving selected ribbon") // print(updateSelectRibbon) do { _ = try await appDatabase.saveSelectedRibbon(&updateSelectRibbon) } catch { // Print("something wrong") } } updatedRibbon.scrollOffset = Int(scrollOffsetToSave!) updatedRibbon.scrollId = scrollIdToSave! _ = try await appDatabase.saveRibbon(&updatedRibbon) // print("saved updatedRibbon", updatedRibbon) // print("UPDATED") scrollOffsetToSave = userDefaults.object(forKey: "currentOffset") as? CGFloat scrollIdToSave = userDefaults.object(forKey: "currentId") as? String // print("scrollOffsetToSave: ", scrollOffsetToSave) // print("scrollIdToSave: ", scrollIdToSave) } } struct SwitchButton : View { var ribbon: Ribbon @Binding var scrollId : String? @Binding var scrollOffset : CGFloat? @Binding var refresh : Bool @Environment(\.appDatabase) private var appDatabase @Query(SelectedRibbonRequest()) private var sr: [Ribbon] @State var saveOffset = CGFloat() var body: some View { // ForEach(sr) { selectedRibbon in let ribbonString = String(sr[0].id!) + " " + String(ribbon.id!) + " " + String(ribbon.scrollOffset) + " " + ribbon.scrollId Button(ribbonString, action: { Task { goToRibbon(selectedRibbon: sr[0], destRibbon: ribbon, scrollId: $scrollId, scrollOffset: $scrollOffset, refresh: $refresh, appDatabase: appDatabase) } } ) .buttonStyle(BlueButtonStyle()) } // } } // class ScrollViewHandler: NSObject { // public var scrollOffset = CGFloat(10) // } // extension ScrollViewHandler: UIScrollViewDelegate { // func scrollViewDidScroll(_ scrollView: UIScrollView) { // scrollOffset = CGFloat(scrollView.contentOffset.y) // // print("delegate", scrollView.contentOffset.y) // // print("delegate prop", self.scrollOffset) // } // } class Verse: NSObject, Codable { var body: String var verse: Int } func makeVerseView(seg: SegDenorm) -> some View { var retView = Text("") var segSplit = seg.body.components(separatedBy: ";;") let decoder = JSONDecoder() segSplit.enumerated().forEach({ (index, item) in let verse = try! decoder.decode(Verse.self, from: item.data(using: .utf8)!) retView = retView + Text(String(verse.verse)) .font(Font.custom("AveriaSerifLibre-Regular", size: 6)) .baselineOffset(6.0) .foregroundColor(Color.white) retView = retView + Text(verse.body) .foregroundColor(Color.white) .font(Font.custom("AveriaSerifLibre-Regular", size: 15)) }) return retView } private struct SegRow: View { var seg: SegDenorm var body: some View { // makeVerseView(seg: seg) var retView = Text("") var segSplit = seg.body.components(separatedBy: ";;") let decoder = JSONDecoder() // Text(segSplit[0].body) // .id(seg.id) // VStack { // ForEach(segSplit.indices) { i in segSplit.enumerated().forEach({ (index, item) in let verse = try! decoder.decode(Verse.self, from: item.data(using: .utf8)!) var attributedString: AttributedString { // var result = AttributedString("Hello World!") var result = AttributedString(verse.body) result.underlineStyle = Text.LineStyle( pattern: .dot, color: .white) return result } retView = retView + Text(String(verse.verse)) //retView = retView + Text(attributedString) // Text(seg.body) // .contentShape(Rectangle()) .font(Font.custom("AveriaSerifLibre-Regular", size: 10)) .baselineOffset(6.0) .foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00))) retView = retView + Text(attributedString) // .frame(maxWidth: .infinity, alignment: .leading) // .contentShape(Rectangle()) .foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00))) .font(Font.custom("AveriaSerifLibre-Regular", size: 20)) // .listRowBackground(Color(red: 0.2, green: 0.8, blue: 0.2)) // .listRowInsets(EdgeInsets()) // .padding(EdgeInsets(top: 10, leading: 20, bottom: 40, trailing: 20)) // .listRowSeparator(.hidden) // .id(String(seg.id) + "body" + String(i)) // .id(seg.id) //.onTapGesture { // selectedLine = seg.id //Print(selectedLine) //} // .listRowBackground(Color(red: 0.2, green: 0.8, blue: 0.2)) // .listRowInsets(EdgeInsets()) // .padding(EdgeInsets(top: 10, leading: 20, bottom: 40, trailing: 20)) // .listRowSeparator(.hidden) // .id(String(seg.id) + "verse" + String(i)) // .id(seg.id) //.onTapGesture { // selectedLine = seg.id //Print(selectedLine) //} }) // } return retView } } struct ContentView: View { @State var viewState = CGSize.zero @State var pulledOut = CGSize.zero @State var taskTitle : String = "FIRST DOGGG" @State var curBook : String = "Matthew" @State var selectedLine : Int64? @State var thisScrollView : UIScrollView? @State var scrollUpdate = false @State var initLoad = false // @State var currentId : String? // @State var currentOffset = CGFloat() // set this to scroll to area @State var scrollId : String? @State var scrollOffset: CGFloat? @State var setScrollOffset: CGFloat? @Query(SegDenormRequest(book: "bible.mark")) private var segs: [SegDenorm] // @State var scrollDelegate: ScrollViewHandler // @State var scrollDelegate = ScrollViewHandler() // @State var selectedRibbonId = Int64(UserDefaults.standard.optionalInt(forKey: "selectedRibbonId") ?? 1) // ribbon @Query(SelectedRibbonRequest()) private var selectedRibbon: [Ribbon] @State var endedDrag = true @State var readOffset = CGPoint() @State var refresh: Bool = false @State var refresh2: Bool = false @Query(RibbonRequest()) private var ribbons: [Ribbon] // @Query(RibbonRequest(id: Int64(UserDefaults.standard.optionalInt(forKey: "lastRibbonId") ?? 1))) private var selectedRibbon: [Ribbon] init() { UITableView.appearance().backgroundColor = UIColor(Color(red: 0.2, green: 0.2, blue: 0.2)) // _selectedRibbon = Query(RibbonRequest(id: Int64(UserDefaults.standard.optionalInt(forKey: "lastRibbonId") ?? 1))) // self._scrollDelegate = State(initialValue: ScrollViewHandler()) } var body: some View { // Print("rendering") var size1 = CGFloat(70) var size2 = CGFloat(120) var fontSize = CGFloat(20) GeometryReader { geometry in ZStack{ VStack{ Text("MK") .font(Font.custom("AveriaSerifLibre-Regular", size: fontSize)) .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)) .overlay( MyCustomShape().frame(width: size1, height: size1) .foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))) // .foregroundColor(.white)) .frame(width: size2, height: size2) Text("Ps") .font(Font.custom("AveriaSerifLibre-Regular", size: fontSize)) .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)) .overlay( MyCustomShape().frame(width: size1, height: size1) .foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))) // .foregroundColor(.white)) .frame(width: size1, height: size1) Text("Jn") .font(Font.custom("AveriaSerifLibre-Regular", size: fontSize)) .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)) .overlay( MyCustomShape().frame(width: size1, height: size1) .foregroundColor(Color(UIColor(red: 0.30, green: 0.30, blue: 0.30, alpha: 0.4)))) // .foregroundColor(.white)) .frame(width: size1, height: size1) // Text("MTW") // .font(Font.custom("AveriaSerifLibre-Regular", size: 20)) // .foregroundColor(Color.white) // .overlay( MyCustomShape().frame(width: 120, height: 100)) // .frame(width: 120, height: 120) ForEach(ribbons) { ribbon in SwitchButton(ribbon: ribbon, scrollId:$scrollId, scrollOffset:$scrollOffset, refresh:$refresh ) .buttonStyle(BlueButtonStyle()) // Print("RIBBON") // Print(ribbon) } } .frame(width: geometry.size.width, height: geometry.size.height, alignment: .topLeading) .background(Color(red: 0.1, green: 0.1, blue: 0.1)) ScrollViewReader { proxy in VisibilityTrackingScrollView(action: handleVisibilityChanged) { // ScrollView { LazyVStack { Text(refresh ? "Selected" : "not Selected") Button("Jump to #8") { scrollId = "20" } ForEach(segs) { seg in SegRow(seg: seg) .id("\(seg.id)") .padding(EdgeInsets(top: 10, leading: 20, bottom: 40, trailing: 20)) .trackVisibility(id: "\(seg.id)") // .onChange(of: geometry.frame(in: .named("scrollView"))) { imageRect in //Print(imageRect) //Print(outerProxy) // if isInView(innerRect: imageRect, isIn: outerProxy) { // visibleIndex.insert(item) // } else { // visibleIndex.remove(item) // } // } } } .background(Color(red: 0.2, green: 0.2, blue: 0.2)) } .onChange(of: refresh) { target in //if let target = target { //gTracker!.visibleViews["123123"] = CGFloat(100) // Print("ON CHANGE", gTracker!.visibleViews) // Print("removing", gTracker!.visibleViews.removeAll()) Print("first scroll to") Print(scrollId) proxy.scrollTo(scrollId! , anchor: .top) currentId = scrollId! Task { // try? await Task.sleep(nanoseconds: 1_000_000_000) // try? await Task.sleep(nanoseconds: 1_000_000_000) proxy.scrollTo(scrollId! , anchor: .top) DispatchQueue.main.async { // setScrollOffset = CGFloat(scrollOffset!) setScrollOffset = CGFloat(Int(currentOffset!) * -1 + Int(scrollOffset!)) currentOffset = scrollOffset! refresh2.toggle() } } //} } .introspectScrollView { scrollView in // Print("introspect") // scrollView.delegate = scrollDelegate //Print("Scroll delegate offset", scrollDelegate.scrollOffset) if (setScrollOffset != nil) { // Print("Setting scroll offset in introspect", setScrollOffset) scrollView.contentOffset.y = scrollView.contentOffset.y + setScrollOffset! DispatchQueue.main.async { setScrollOffset = nil } } // if (thisScrollView == nil) { // Print("init scroll") // thisScrollView = scrollView // scrollView.contentOffset.y = CGFloat(selectedRibbon[0].scrollOffset) // } } .listStyle(PlainListStyle()) } .background(Color(red: 0.2, green: 0.2, blue: 0.2)) .frame(width: geometry.size.width - 50) .offset(x:30 , y:0) .offset(x: pulledOut.width) .offset(x: viewState.width, y: viewState.height) .gesture( DragGesture() .onChanged { gesture in if (endedDrag) { endedDrag = false scrollOffset = readOffset.y - 20 // _ = Print("meow") } // logger.error("hello222") // NSLog("hellooo") Print(viewState.width) if (abs(gesture.translation.width) > 20) { viewState.width = gesture.translation.width } //offset.y = gesture.translation.width // logger.log("hello") } .onEnded { _ in endedDrag = true var pulledOutWidth = CGFloat(0) if (viewState.width < 0) { pulledOutWidth = CGFloat(0) } else if abs(viewState.width + pulledOut.width ) > 30 { pulledOutWidth = CGFloat(200) } withAnimation(.spring(response: 0.2)) { pulledOut.width = pulledOutWidth viewState = .zero } } ) } } } func handleVisibilityChanged(_ id: String, change: VisibilityChange, tracker: VisibilityTracker) { // @Environment(\.appDatabase) var appDatabase // switch change { // case .shown: print("\(id) shown") // case .hidden: print("\(id) hidden") // } // print("VISIBILITY CHANGED STARTED") // print(tracker.visibleViews) // gTracker = tracker // print(tracker.sortedViewIDs) // print("VISIBILITY CHANGED ENDED") // if (currentId != nil) { // currentOffset = tracker.visibleViews[currentId!]! // } let visibleViews2 = Array(tracker.visibleViews.keys) if (visibleViews2.count == 0) { return } currentId = tracker.sortedViewIDs[0] if (currentId != nil) { print("current ID:", currentId) // Access Shared Defaults Object // let userDefaults = UserDefaults.standard // Write/Set Value // Print(currentId!) // Print(tracker.visibleViews[currentId?]!) // userDefaults.set(currentId, forKey: "currentId") // userDefaults.set(tracker.visibleViews[currentId]!, forKey: "currentOffset") currentOffset = tracker.visibleViews[currentId!]! // userDefaults.set(tracker.visibleViews[currentId!]!, forKey: "currentOffset") // var updateScrollState = ScrollState(id: Int64(1), // scrollId: currentId!, // scrollOffset: Int64(tracker.visibleViews[currentId!]!)) // Print("Savingg") //Print(updateScrollState) // Task(priority: .default) { // do { // _ = try await appDatabase.saveScrollState(&updateScrollState) // } catch { // Print("something wrong") // } // } } // gTracker!.visibleViews.removeAll() } } private let itemFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .medium return formatter }() struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() ContentView().environment(\.appDatabase, .random()) } } extension View { func Print(_ vars: Any...) -> some View { for v in vars { print(v) } return EmptyView() } }