mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
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:
parent
af75afa912
commit
7e346bf49c
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user