yattee/Shared/Player/VideoDescription.swift
2022-08-29 00:42:40 +02:00

164 lines
5.2 KiB
Swift

#if os(iOS)
import ActiveLabel
#endif
import Defaults
import Foundation
import SwiftUI
struct VideoDescription: View {
@EnvironmentObject<NavigationModel> private var navigation
@EnvironmentObject<PlayerModel> private var player
@EnvironmentObject<RecentsModel> private var recents
@EnvironmentObject<SearchModel> private var search
@Default(.showKeywords) private var showKeywords
var video: Video
var detailsSize: CGSize?
var description: String {
video.description ?? ""
}
var body: some View {
VStack {
#if os(iOS)
ActiveLabelDescriptionRepresentable(description: description, detailsSize: detailsSize)
#else
textDescription
#endif
keywords
}
}
@ViewBuilder var textDescription: some View {
#if !os(iOS)
Group {
if #available(macOS 12, *) {
Text(description)
#if !os(tvOS)
.textSelection(.enabled)
#endif
} else {
Text(description)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.font(.system(size: 14))
.lineSpacing(3)
#endif
}
@ViewBuilder var keywords: some View {
if showKeywords {
ScrollView(.horizontal, showsIndicators: showScrollIndicators) {
HStack {
ForEach(video.keywords, id: \.self) { keyword in
Button {
NavigationModel.openSearchQuery(keyword, player: player, recents: recents, navigation: navigation, search: search)
} label: {
HStack(alignment: .center, spacing: 0) {
Text("#")
.font(.system(size: 14).bold())
Text(keyword)
.frame(maxWidth: 500)
}
.font(.caption)
.foregroundColor(.white)
.padding(.vertical, 4)
.padding(.horizontal, 8)
.background(Color("KeywordBackgroundColor"))
.mask(RoundedRectangle(cornerRadius: 3))
}
.buttonStyle(.plain)
}
}
}
}
}
var showScrollIndicators: Bool {
#if os(macOS)
false
#else
true
#endif
}
}
#if os(iOS)
struct ActiveLabelDescriptionRepresentable: UIViewRepresentable {
var description: String
var detailsSize: CGSize?
@State private var label = ActiveLabel()
@Environment(\.openURL) private var openURL
@EnvironmentObject<PlayerModel> private var player
func makeUIView(context _: Context) -> some UIView {
customizeLabel()
return label
}
func updateUIView(_: UIViewType, context _: Context) {
updatePreferredMaxLayoutWidth()
}
func customizeLabel() {
label.customize { label in
label.enabledTypes = [.url, .timestamp]
label.numberOfLines = 0
label.text = description
label.contentMode = .scaleAspectFill
label.font = .systemFont(ofSize: 14)
label.lineSpacing = 3
label.preferredMaxLayoutWidth = (detailsSize?.width ?? 330) - 30
label.URLColor = UIColor(Color.accentColor)
label.timestampColor = UIColor(Color.accentColor)
label.handleURLTap(urlTapHandler(_:))
label.handleTimestampTap(timestampTapHandler(_:))
}
}
func updatePreferredMaxLayoutWidth() {
label.preferredMaxLayoutWidth = (detailsSize?.width ?? 330) - 30
}
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)
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
} else if destination != nil {
urlToOpen = yatteeURL
}
}
}
openURL(urlToOpen)
}
func timestampTapHandler(_ timestamp: Timestamp) {
player.backend.seek(to: timestamp.timeInterval, seekType: .userInteracted)
}
}
#endif
struct VideoDescription_Previews: PreviewProvider {
static var previews: some View {
VideoDescription(video: .fixture)
.injectFixtureEnvironmentObjects()
}
}