Some checks failed
CI / UI / Build (pull_request) Failing after 7s
CI / Scraper / Test (pull_request) Failing after 10s
CI / Scraper / Lint (pull_request) Failing after 10s
CI / Scraper / Build (pull_request) Has been skipped
iOS CI / Build (push) Has been cancelled
iOS CI / Test (push) Has been cancelled
iOS CI / Build (pull_request) Failing after 2s
iOS CI / Test (pull_request) Has been skipped
- Remove swift-markdown-ui dependency (project.yml, pbxproj, Package.resolved) - Remove all debug print() statements (APIClient, BrowseViewModel, ChapterReaderViewModel, ChapterReaderView) - Remove dead UI stubs: isFavorited/isLiked state, heart/star buttons, empty Share/Add to Library menu items in FullPlayerView - Remove unused audioToolbarButton toolbar builder in ChapterReaderView - Remove unused model types: NovelListing, BookDetailData, ChapterContent - Remove amberLight color (never referenced) - Remove mini player interactive seek (horizontal drag/tap to seek) — progress bar is now display-only - Fix AccentColor asset to amber #f59e0b (was orange-pink mismatch) - Fix Discover tab icon: globe → globe.americas.fill - Fix speed slider max: 3.0 → 2.0 in ProfileView - Fix yearText: use static DateFormatter instead of allocating on every access - Fix ChangePasswordView.save(): DispatchQueue.main.asyncAfter → Task.sleep - Deduplicate navigationDestination blocks via .appNavigationDestination() View extension - Deduplicate .alert(Error) pattern via .errorAlert() View extension
50 lines
2.1 KiB
Swift
50 lines
2.1 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",
|
|
/// or "(One)4 years ago" where the number is attached without a preceding space).
|
|
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 }
|
|
// Everything before the suffix
|
|
let before = String(self[self.startIndex ..< suffixRange.lowerBound])
|
|
let trimmed = before.trimmingCharacters(in: .whitespaces)
|
|
// Strip trailing digits (the numeric count, which may be attached without a space)
|
|
var result = trimmed
|
|
while let last = result.last, last.isNumber {
|
|
result.removeLast()
|
|
}
|
|
result = result.trimmingCharacters(in: .whitespaces)
|
|
if result != trimmed {
|
|
// We actually stripped some digits — return cleaned result
|
|
return result
|
|
}
|
|
// Fallback: number preceded by space
|
|
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 {
|
|
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
|
|
}
|