iOS: make timestamps in comments touchable

Timestamps in comments can now be touched and jump to the corresponding part in the video.

Signed-off-by: Toni Förster <toni.foerster@gmail.com>
This commit is contained in:
Toni Förster 2024-08-20 00:52:04 +02:00
parent af75afa912
commit 7e346bf49c
No known key found for this signature in database
GPG Key ID: 292F3E5086C83FC7
2 changed files with 96 additions and 18 deletions

View File

@ -1,9 +1,13 @@
#if os(iOS)
import ActiveLabel
#endif
import SDWebImageSwiftUI import SDWebImageSwiftUI
import SwiftUI import SwiftUI
struct CommentView: View { struct CommentView: View {
let comment: Comment let comment: Comment
@Binding var repliesID: Comment.ID? @Binding var repliesID: Comment.ID?
var availableWidth: CGFloat
@State private var subscribed = false @State private var subscribed = false
@ -204,7 +208,7 @@ struct CommentView: View {
Group { Group {
let last = comments.replies.last let last = comments.replies.last
ForEach(comments.replies) { comment in ForEach(comments.replies) { comment in
Self(comment: comment, repliesID: $repliesID) Self(comment: comment, repliesID: $repliesID, availableWidth: availableWidth)
#if os(tvOS) #if os(tvOS)
.focusable() .focusable()
#endif #endif
@ -220,22 +224,25 @@ struct CommentView: View {
private var commentText: some View { private var commentText: some View {
Group { Group {
let text = Text(comment.text) let rawText = comment.text
#if os(macOS) if #available(iOS 15.0, macOS 12.0, *) {
#if os(iOS)
ActiveLabelCommentRepresentable(
text: rawText,
availableWidth: availableWidth
)
#elseif os(macOS)
Text(rawText)
.font(.system(size: 14)) .font(.system(size: 14))
#elseif os(iOS)
.font(.system(size: 15))
#endif
.lineSpacing(3) .lineSpacing(3)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
if #available(iOS 15.0, macOS 12.0, *) {
text
#if !os(tvOS)
.textSelection(.enabled) .textSelection(.enabled)
#endif #endif
} else { } else {
text Text(rawText)
.font(.system(size: 15))
.lineSpacing(3)
.fixedSize(horizontal: false, vertical: true)
} }
} }
} }
@ -248,13 +255,78 @@ struct CommentView: View {
} }
} }
#if os(iOS)
struct ActiveLabelCommentRepresentable: UIViewRepresentable {
var text: String
var availableWidth: CGFloat
@State private var label = ActiveLabel()
@Environment(\.openURL) private var openURL
var player = PlayerModel.shared
func makeUIView(context _: Context) -> some UIView {
customizeLabel()
return label
}
func updateUIView(_: UIViewType, context _: Context) {
label.preferredMaxLayoutWidth = availableWidth
}
func customizeLabel() {
label.customize { label in
label.enabledTypes = [.url, .timestamp]
label.text = text
label.font = .systemFont(ofSize: 15)
label.lineSpacing = 3
label.preferredMaxLayoutWidth = availableWidth
label.URLColor = UIColor(Color.accentColor)
label.timestampColor = UIColor(Color.accentColor)
label.handleURLTap(urlTapHandler(_:))
label.handleTimestampTap(timestampTapHandler(_:))
label.numberOfLines = 0
}
}
private func urlTapHandler(_ url: URL) {
var urlToOpen = url
if var components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
components.scheme = "yattee"
if let yatteeURL = components.url {
let parser = URLParser(url: urlToOpen, allowFileURLs: false)
let destination = parser.destination
if destination == .video,
parser.videoID == player.currentVideo?.videoID,
let time = parser.time
{
player.backend.seek(to: Double(time), seekType: .userInteracted)
return
}
if destination != nil {
urlToOpen = yatteeURL
}
}
}
openURL(urlToOpen)
}
private func timestampTapHandler(_ timestamp: Timestamp) {
player.backend.seek(to: timestamp.timeInterval, seekType: .userInteracted)
}
}
#endif
struct CommentView_Previews: PreviewProvider { struct CommentView_Previews: PreviewProvider {
static var fixture: Comment { static var fixture: Comment {
Comment.fixture Comment.fixture
} }
static var previews: some View { static var previews: some View {
CommentView(comment: fixture, repliesID: .constant(fixture.id)) CommentView(comment: fixture, repliesID: .constant(fixture.id), availableWidth: 375)
.padding(5) .padding(5)
} }
} }

View File

@ -2,6 +2,7 @@ import SwiftUI
struct CommentsView: View { struct CommentsView: View {
@State private var repliesID: Comment.ID? @State private var repliesID: Comment.ID?
@State private var availableWidth = 0.0
@ObservedObject private var comments = CommentsModel.shared @ObservedObject private var comments = CommentsModel.shared
@ -14,16 +15,21 @@ struct CommentsView: View {
} else if !comments.loaded { } else if !comments.loaded {
PlaceholderProgressView() PlaceholderProgressView()
} else { } else {
let last = comments.all.last
LazyVStack { LazyVStack {
ForEach(comments.all) { comment in ForEach(comments.all) { comment in
CommentView(comment: comment, repliesID: $repliesID) CommentView(comment: comment, repliesID: $repliesID, availableWidth: availableWidth)
.onAppear { .onAppear {
comments.loadNextPageIfNeeded(current: comment) comments.loadNextPageIfNeeded(current: comment)
} }
.borderBottom(height: comment != last ? 0.5 : 0, color: Color("ControlsBorderColor")) .borderBottom(height: comment != comments.all.last ? 0.5 : 0, color: Color("ControlsBorderColor"))
} }
} }
.background(GeometryReader { geometry in
Color.clear
.onAppear {
self.availableWidth = Double(geometry.size.width)
}
})
} }
} }
.padding(.horizontal) .padding(.horizontal)