- Fully pill-shaped design (cornerRadius 40) with dark glassmorphic background - Larger cover thumbnail (56×56 with 12pt corner radius) - Improved layout: cover + chapter/book titles + play/pause + next chapter skip - Progress indicator moved to bottom 3pt amber border - Removed dismiss X button - swipe down to dismiss instead - Added swipe gestures: up to expand (0.4x resistance), down to dismiss (0.8x resistance) - Dismiss animation with fade and scale effects - Skip forward button navigates to next chapter (disabled when unavailable) - Updated mini player height to 92pt in AppLayout
41 lines
1.7 KiB
Swift
41 lines
1.7 KiB
Swift
import Foundation
|
|
|
|
// MARK: - String helpers for display purposes
|
|
|
|
extension String {
|
|
/// Strips trailing relative-date suffixes (e.g. "2 years ago", "3 days ago")
|
|
/// that may be embedded in chapter titles coming from older scraped records.
|
|
func strippingTrailingDate() -> String {
|
|
let units = ["second", "minute", "hour", "day", "week", "month", "year"]
|
|
let lower = self.lowercased()
|
|
for unit in units {
|
|
for suffix in [unit + "s ago", unit + " ago"] {
|
|
guard let suffixRange = lower.range(of: suffix, options: .backwards) else { continue }
|
|
// Walk backwards past whitespace to find the numeric token
|
|
let before = String(self[self.startIndex ..< suffixRange.lowerBound])
|
|
let trimmed = before.trimmingCharacters(in: .whitespaces)
|
|
// Find start of last word (should be a number)
|
|
if let spaceIdx = trimmed.lastIndex(of: " ") {
|
|
let potentialNum = String(trimmed[trimmed.index(after: spaceIdx)...])
|
|
if Int(potentialNum) != nil {
|
|
return String(trimmed[trimmed.startIndex ..< spaceIdx])
|
|
.trimmingCharacters(in: .whitespaces)
|
|
}
|
|
} else if Int(trimmed) != nil {
|
|
// The entire string is just a number + "ago"
|
|
return ""
|
|
}
|
|
}
|
|
}
|
|
return self
|
|
}
|
|
}
|
|
|
|
// MARK: - App-wide layout constants
|
|
|
|
enum AppLayout {
|
|
/// Height of the persistent mini-player bar:
|
|
/// 12pt vertical padding (top) + 56pt cover height + 12pt vertical padding (bottom) + 12pt horizontal margin.
|
|
static let miniPlayerBarHeight: CGFloat = 92
|
|
}
|