mirror of
				https://github.com/yattee/yattee.git
				synced 2025-11-04 14:42:05 +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:
		@@ -1,9 +1,13 @@
 | 
			
		||||
#if os(iOS)
 | 
			
		||||
    import ActiveLabel
 | 
			
		||||
#endif
 | 
			
		||||
import SDWebImageSwiftUI
 | 
			
		||||
import SwiftUI
 | 
			
		||||
 | 
			
		||||
struct CommentView: View {
 | 
			
		||||
    let comment: Comment
 | 
			
		||||
    @Binding var repliesID: Comment.ID?
 | 
			
		||||
    var availableWidth: CGFloat
 | 
			
		||||
 | 
			
		||||
    @State private var subscribed = false
 | 
			
		||||
 | 
			
		||||
@@ -204,7 +208,7 @@ struct CommentView: View {
 | 
			
		||||
        Group {
 | 
			
		||||
            let last = comments.replies.last
 | 
			
		||||
            ForEach(comments.replies) { comment in
 | 
			
		||||
                Self(comment: comment, repliesID: $repliesID)
 | 
			
		||||
                Self(comment: comment, repliesID: $repliesID, availableWidth: availableWidth)
 | 
			
		||||
                #if os(tvOS)
 | 
			
		||||
                    .focusable()
 | 
			
		||||
                #endif
 | 
			
		||||
@@ -220,22 +224,25 @@ struct CommentView: View {
 | 
			
		||||
 | 
			
		||||
    private var commentText: some View {
 | 
			
		||||
        Group {
 | 
			
		||||
            let text = Text(comment.text)
 | 
			
		||||
            #if os(macOS)
 | 
			
		||||
                .font(.system(size: 14))
 | 
			
		||||
            #elseif os(iOS)
 | 
			
		||||
                .font(.system(size: 15))
 | 
			
		||||
            #endif
 | 
			
		||||
                .lineSpacing(3)
 | 
			
		||||
                .fixedSize(horizontal: false, vertical: true)
 | 
			
		||||
 | 
			
		||||
            let rawText = comment.text
 | 
			
		||||
            if #available(iOS 15.0, macOS 12.0, *) {
 | 
			
		||||
                text
 | 
			
		||||
                #if !os(tvOS)
 | 
			
		||||
                .textSelection(.enabled)
 | 
			
		||||
                #if os(iOS)
 | 
			
		||||
                    ActiveLabelCommentRepresentable(
 | 
			
		||||
                        text: rawText,
 | 
			
		||||
                        availableWidth: availableWidth
 | 
			
		||||
                    )
 | 
			
		||||
                #elseif os(macOS)
 | 
			
		||||
                    Text(rawText)
 | 
			
		||||
                        .font(.system(size: 14))
 | 
			
		||||
                        .lineSpacing(3)
 | 
			
		||||
                        .fixedSize(horizontal: false, vertical: true)
 | 
			
		||||
                        .textSelection(.enabled)
 | 
			
		||||
                #endif
 | 
			
		||||
            } 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 {
 | 
			
		||||
    static var fixture: Comment {
 | 
			
		||||
        Comment.fixture
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static var previews: some View {
 | 
			
		||||
        CommentView(comment: fixture, repliesID: .constant(fixture.id))
 | 
			
		||||
        CommentView(comment: fixture, repliesID: .constant(fixture.id), availableWidth: 375)
 | 
			
		||||
            .padding(5)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import SwiftUI
 | 
			
		||||
 | 
			
		||||
struct CommentsView: View {
 | 
			
		||||
    @State private var repliesID: Comment.ID?
 | 
			
		||||
    @State private var availableWidth = 0.0
 | 
			
		||||
 | 
			
		||||
    @ObservedObject private var comments = CommentsModel.shared
 | 
			
		||||
 | 
			
		||||
@@ -14,16 +15,21 @@ struct CommentsView: View {
 | 
			
		||||
            } else if !comments.loaded {
 | 
			
		||||
                PlaceholderProgressView()
 | 
			
		||||
            } else {
 | 
			
		||||
                let last = comments.all.last
 | 
			
		||||
                LazyVStack {
 | 
			
		||||
                    ForEach(comments.all) { comment in
 | 
			
		||||
                        CommentView(comment: comment, repliesID: $repliesID)
 | 
			
		||||
                        CommentView(comment: comment, repliesID: $repliesID, availableWidth: availableWidth)
 | 
			
		||||
                            .onAppear {
 | 
			
		||||
                                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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user