mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Dont try to highlight beyond the last item despite speech marks offset data
This commit is contained in:
parent
f9e2809a10
commit
b983a4336b
2 changed files with 91 additions and 2 deletions
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// ScrollingStackModifier.swift
|
||||
// ScrollView_Tests
|
||||
//
|
||||
// Created by Jean-Marc Boullianne on 8/7/20.
|
||||
//
|
||||
import SwiftUI
|
||||
|
||||
struct ScrollingHStackModifier: ViewModifier {
|
||||
|
||||
@State private var scrollOffset: CGFloat
|
||||
@State private var dragOffset: CGFloat
|
||||
|
||||
var items: Int
|
||||
var itemWidth: CGFloat
|
||||
var itemSpacing: CGFloat
|
||||
|
||||
init(items: Int, itemWidth: CGFloat, itemSpacing: CGFloat) {
|
||||
self.items = items
|
||||
self.itemWidth = itemWidth
|
||||
self.itemSpacing = itemSpacing
|
||||
|
||||
// Calculate Total Content Width
|
||||
let contentWidth: CGFloat = CGFloat(items) * itemWidth + CGFloat(items - 1) * itemSpacing
|
||||
let screenWidth = UIScreen.main.bounds.width
|
||||
|
||||
// Set Initial Offset to first Item
|
||||
let initialOffset = (contentWidth/2.0) - (screenWidth/2.0) + ((screenWidth - itemWidth) / 2.0)
|
||||
|
||||
self._scrollOffset = State(initialValue: initialOffset)
|
||||
self._dragOffset = State(initialValue: 0)
|
||||
}
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.offset(x: scrollOffset + dragOffset, y: 0)
|
||||
.gesture(DragGesture()
|
||||
.onChanged({ event in
|
||||
dragOffset = event.translation.width
|
||||
})
|
||||
.onEnded({ event in
|
||||
// Scroll to where user dragged
|
||||
scrollOffset += event.translation.width
|
||||
dragOffset = 0
|
||||
|
||||
// Now calculate which item to snap to
|
||||
let contentWidth: CGFloat = CGFloat(items) * itemWidth + CGFloat(items - 1) * itemSpacing
|
||||
let screenWidth = UIScreen.main.bounds.width
|
||||
|
||||
// Center position of current offset
|
||||
let center = scrollOffset + (screenWidth / 2.0) + (contentWidth / 2.0)
|
||||
|
||||
// Calculate which item we are closest to using the defined size
|
||||
var index = (center - (screenWidth / 2.0)) / (itemWidth + itemSpacing)
|
||||
|
||||
// Should we stay at current index or are we closer to the next item...
|
||||
if index.remainder(dividingBy: 1) > 0.5 {
|
||||
index += 1
|
||||
} else {
|
||||
index = CGFloat(Int(index))
|
||||
}
|
||||
|
||||
// Protect from scrolling out of bounds
|
||||
index = min(index, CGFloat(items) - 1)
|
||||
index = max(index, 0)
|
||||
|
||||
// Set final offset (snapping to item)
|
||||
let newOffset = index * itemWidth + (index - 1) * itemSpacing - (contentWidth / 2.0) + (screenWidth / 2.0) - ((screenWidth - itemWidth) / 2.0) + itemSpacing
|
||||
|
||||
// Animate snapping
|
||||
withAnimation {
|
||||
scrollOffset = newOffset
|
||||
}
|
||||
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -198,6 +198,7 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate
|
|||
@Published public var currentAudioIndex: Int = 0
|
||||
@Published public var readText: String = ""
|
||||
@Published public var unreadText: String = ""
|
||||
@Published public var numberOfSpeechItems: Int = 0
|
||||
|
||||
@Published public var itemAudioProperties: LinkedItemAudioProperties?
|
||||
|
||||
|
|
@ -254,6 +255,8 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate
|
|||
player?.removeAllItems()
|
||||
|
||||
document = nil
|
||||
numberOfSpeechItems = 0
|
||||
|
||||
timer = nil
|
||||
player = nil
|
||||
observer = nil
|
||||
|
|
@ -476,7 +479,13 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate
|
|||
|
||||
public var textItems: [String]? {
|
||||
if let document = self.document {
|
||||
return document.utterances.map(\.text)
|
||||
return document.utterances.map { utterance in
|
||||
if let regex = try? NSRegularExpression(pattern: "<[^>]*>", options: .caseInsensitive) {
|
||||
let modString = regex.stringByReplacingMatches(in: utterance.text, options: [], range: NSRange(location: 0, length: utterance.text.count), withTemplate: "")
|
||||
return modString
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -503,7 +512,7 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate
|
|||
|
||||
let idx = item.speechItem.audioIdx
|
||||
let currentItem = textItems[idx]
|
||||
let currentReadIndex = currentItem.index(currentItem.startIndex, offsetBy: currentItemOffset)
|
||||
let currentReadIndex = currentItem.index(currentItem.startIndex, offsetBy: max(currentItemOffset, currentItem.count))
|
||||
let lastItem = String(currentItem[..<currentReadIndex])
|
||||
let lastItemAfter = String(currentItem[currentReadIndex...])
|
||||
|
||||
|
|
@ -662,7 +671,9 @@ public class AudioController: NSObject, ObservableObject, AVAudioPlayerDelegate
|
|||
if let itemID = itemAudioProperties?.itemID {
|
||||
Task {
|
||||
let document = try? await downloadSpeechFile(itemID: itemID, priority: .high)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.numberOfSpeechItems = document?.utterances.count ?? 0
|
||||
if let document = document {
|
||||
self.startStreamingAudio(itemID: itemID, document: document)
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue