mirror of
https://github.com/yattee/yattee.git
synced 2024-12-13 13:50:32 +05:30
Add video description expanding
This commit is contained in:
parent
b19918e219
commit
d52ccf2ce6
@ -13,6 +13,14 @@ struct Constants {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static var isIPad: Bool {
|
||||||
|
#if os(iOS)
|
||||||
|
UIDevice.current.userInterfaceIdiom == .pad
|
||||||
|
#else
|
||||||
|
false
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static var progressViewScale: Double {
|
static var progressViewScale: Double {
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
0.4
|
0.4
|
||||||
|
@ -132,6 +132,12 @@ extension Defaults.Keys {
|
|||||||
static let seekGestureSpeed = Key<Double>("seekGestureSpeed", default: 0.5)
|
static let seekGestureSpeed = Key<Double>("seekGestureSpeed", default: 0.5)
|
||||||
static let seekGestureSensitivity = Key<Double>("seekGestureSensitivity", default: 30.0)
|
static let seekGestureSensitivity = Key<Double>("seekGestureSensitivity", default: 30.0)
|
||||||
static let showKeywords = Key<Bool>("showKeywords", default: false)
|
static let showKeywords = Key<Bool>("showKeywords", default: false)
|
||||||
|
#if os(iOS)
|
||||||
|
static let expandVideoDescriptionDefault = Constants.isIPad
|
||||||
|
#else
|
||||||
|
static let expandVideoDescriptionDefault = true
|
||||||
|
#endif
|
||||||
|
static let expandVideoDescription = Key<Bool>("expandVideoDescription", default: expandVideoDescriptionDefault)
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
static let commentsPlacement = Key<CommentsPlacement>("commentsPlacement", default: .separate)
|
static let commentsPlacement = Key<CommentsPlacement>("commentsPlacement", default: .separate)
|
||||||
#endif
|
#endif
|
||||||
|
@ -6,27 +6,54 @@ import Foundation
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct VideoDescription: View {
|
struct VideoDescription: View {
|
||||||
|
static let collapsedLines = 5
|
||||||
|
|
||||||
private var search: SearchModel { .shared }
|
private var search: SearchModel { .shared }
|
||||||
@Default(.showKeywords) private var showKeywords
|
@Default(.showKeywords) private var showKeywords
|
||||||
|
@Default(.expandVideoDescription) private var expandVideoDescription
|
||||||
|
|
||||||
var video: Video
|
var video: Video
|
||||||
var detailsSize: CGSize?
|
var detailsSize: CGSize?
|
||||||
|
@Binding var expand: Bool
|
||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
video.description ?? ""
|
video.description ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
Group {
|
||||||
|
if !expandVideoDescription && !expand {
|
||||||
|
Button {
|
||||||
|
expand = true
|
||||||
|
} label: {
|
||||||
|
descriptionView
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
} else {
|
||||||
|
descriptionView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.id(video.videoID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var descriptionView: some View {
|
||||||
VStack {
|
VStack {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
ActiveLabelDescriptionRepresentable(description: description, detailsSize: detailsSize)
|
ActiveLabelDescriptionRepresentable(
|
||||||
|
description: description,
|
||||||
|
detailsSize: detailsSize,
|
||||||
|
expand: shouldExpand
|
||||||
|
)
|
||||||
#else
|
#else
|
||||||
textDescription
|
textDescription
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
keywords
|
keywords
|
||||||
}
|
}
|
||||||
.id(video.videoID)
|
}
|
||||||
|
|
||||||
|
var shouldExpand: Bool {
|
||||||
|
expandVideoDescription || expand
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder var textDescription: some View {
|
@ViewBuilder var textDescription: some View {
|
||||||
@ -34,14 +61,18 @@ struct VideoDescription: View {
|
|||||||
Group {
|
Group {
|
||||||
if #available(macOS 12, *) {
|
if #available(macOS 12, *) {
|
||||||
Text(description)
|
Text(description)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.lineLimit(shouldExpand ? 500 : Self.collapsedLines)
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
.textSelection(.enabled)
|
.textSelection(.enabled)
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
Text(description)
|
Text(description)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.lineLimit(shouldExpand ? 500 : Self.collapsedLines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.multilineTextAlignment(.leading)
|
||||||
.font(.system(size: 14))
|
.font(.system(size: 14))
|
||||||
.lineSpacing(3)
|
.lineSpacing(3)
|
||||||
#endif
|
#endif
|
||||||
@ -89,6 +120,7 @@ struct VideoDescription: View {
|
|||||||
struct ActiveLabelDescriptionRepresentable: UIViewRepresentable {
|
struct ActiveLabelDescriptionRepresentable: UIViewRepresentable {
|
||||||
var description: String
|
var description: String
|
||||||
var detailsSize: CGSize?
|
var detailsSize: CGSize?
|
||||||
|
var expand: Bool
|
||||||
|
|
||||||
@State private var label = ActiveLabel()
|
@State private var label = ActiveLabel()
|
||||||
|
|
||||||
@ -103,12 +135,12 @@ struct VideoDescription: View {
|
|||||||
|
|
||||||
func updateUIView(_: UIViewType, context _: Context) {
|
func updateUIView(_: UIViewType, context _: Context) {
|
||||||
updatePreferredMaxLayoutWidth()
|
updatePreferredMaxLayoutWidth()
|
||||||
|
updateNumberOfLines()
|
||||||
}
|
}
|
||||||
|
|
||||||
func customizeLabel() {
|
func customizeLabel() {
|
||||||
label.customize { label in
|
label.customize { label in
|
||||||
label.enabledTypes = [.url, .timestamp]
|
label.enabledTypes = [.url, .timestamp]
|
||||||
label.numberOfLines = 0
|
|
||||||
label.text = description
|
label.text = description
|
||||||
label.contentMode = .scaleAspectFill
|
label.contentMode = .scaleAspectFill
|
||||||
label.font = .systemFont(ofSize: 14)
|
label.font = .systemFont(ofSize: 14)
|
||||||
@ -119,12 +151,17 @@ struct VideoDescription: View {
|
|||||||
label.handleURLTap(urlTapHandler(_:))
|
label.handleURLTap(urlTapHandler(_:))
|
||||||
label.handleTimestampTap(timestampTapHandler(_:))
|
label.handleTimestampTap(timestampTapHandler(_:))
|
||||||
}
|
}
|
||||||
|
updateNumberOfLines()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePreferredMaxLayoutWidth() {
|
func updatePreferredMaxLayoutWidth() {
|
||||||
label.preferredMaxLayoutWidth = (detailsSize?.width ?? 330) - 30
|
label.preferredMaxLayoutWidth = (detailsSize?.width ?? 330) - 30
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateNumberOfLines() {
|
||||||
|
label.numberOfLines = expand ? 0 : VideoDescription.collapsedLines
|
||||||
|
}
|
||||||
|
|
||||||
func urlTapHandler(_ url: URL) {
|
func urlTapHandler(_ url: URL) {
|
||||||
var urlToOpen = url
|
var urlToOpen = url
|
||||||
|
|
||||||
@ -156,7 +193,7 @@ struct VideoDescription: View {
|
|||||||
|
|
||||||
struct VideoDescription_Previews: PreviewProvider {
|
struct VideoDescription_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
VideoDescription(video: .fixture)
|
VideoDescription(video: .fixture, expand: .constant(false))
|
||||||
.injectFixtureEnvironmentObjects()
|
.injectFixtureEnvironmentObjects()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,6 +160,7 @@ struct VideoDetails: View {
|
|||||||
@State private var subscribed = false
|
@State private var subscribed = false
|
||||||
@State private var subscriptionToggleButtonDisabled = false
|
@State private var subscriptionToggleButtonDisabled = false
|
||||||
@State private var page = DetailsPage.info
|
@State private var page = DetailsPage.info
|
||||||
|
@State private var descriptionExpanded = false
|
||||||
|
|
||||||
@Environment(\.navigationStyle) private var navigationStyle
|
@Environment(\.navigationStyle) private var navigationStyle
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@ -175,6 +176,7 @@ struct VideoDetails: View {
|
|||||||
@Default(.enableReturnYouTubeDislike) private var enableReturnYouTubeDislike
|
@Default(.enableReturnYouTubeDislike) private var enableReturnYouTubeDislike
|
||||||
@Default(.playerSidebar) private var playerSidebar
|
@Default(.playerSidebar) private var playerSidebar
|
||||||
@Default(.showInspector) private var showInspector
|
@Default(.showInspector) private var showInspector
|
||||||
|
@Default(.expandVideoDescription) private var expandVideoDescription
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
@ -281,7 +283,9 @@ struct VideoDetails: View {
|
|||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
} else if let description = video.description, !description.isEmpty {
|
} else if let description = video.description, !description.isEmpty {
|
||||||
VideoDescription(video: video, detailsSize: detailsSize)
|
Section(header: descriptionHeader) {
|
||||||
|
VideoDescription(video: video, detailsSize: detailsSize, expand: $descriptionExpanded)
|
||||||
|
}
|
||||||
} else if !video.isLocal {
|
} else if !video.isLocal {
|
||||||
Text("No description")
|
Text("No description")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
@ -345,6 +349,20 @@ struct VideoDetails: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var descriptionHeader: some View {
|
||||||
|
HStack {
|
||||||
|
Text("Description".localized())
|
||||||
|
|
||||||
|
if !expandVideoDescription, !descriptionExpanded {
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "arrow.up.and.down")
|
||||||
|
.imageScale(.small)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VideoDetails_Previews: PreviewProvider {
|
struct VideoDetails_Previews: PreviewProvider {
|
||||||
|
@ -8,6 +8,7 @@ struct PlayerSettings: View {
|
|||||||
@Default(.playerSidebar) private var playerSidebar
|
@Default(.playerSidebar) private var playerSidebar
|
||||||
|
|
||||||
@Default(.showKeywords) private var showKeywords
|
@Default(.showKeywords) private var showKeywords
|
||||||
|
@Default(.expandVideoDescription) private var expandVideoDescription
|
||||||
@Default(.pauseOnHidingPlayer) private var pauseOnHidingPlayer
|
@Default(.pauseOnHidingPlayer) private var pauseOnHidingPlayer
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
|
@Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
|
||||||
@ -85,6 +86,9 @@ struct PlayerSettings: View {
|
|||||||
|
|
||||||
if !accounts.isEmpty {
|
if !accounts.isEmpty {
|
||||||
keywordsToggle
|
keywordsToggle
|
||||||
|
#if !os(tvOS)
|
||||||
|
expandVideoDescriptionToggle
|
||||||
|
#endif
|
||||||
returnYouTubeDislikeToggle
|
returnYouTubeDislikeToggle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,6 +164,10 @@ struct PlayerSettings: View {
|
|||||||
Toggle("Show keywords", isOn: $showKeywords)
|
Toggle("Show keywords", isOn: $showKeywords)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var expandVideoDescriptionToggle: some View {
|
||||||
|
Toggle("Open video description expanded", isOn: $expandVideoDescription)
|
||||||
|
}
|
||||||
|
|
||||||
private var returnYouTubeDislikeToggle: some View {
|
private var returnYouTubeDislikeToggle: some View {
|
||||||
Toggle("Enable Return YouTube Dislike", isOn: $enableReturnYouTubeDislike)
|
Toggle("Enable Return YouTube Dislike", isOn: $enableReturnYouTubeDislike)
|
||||||
}
|
}
|
||||||
@ -212,7 +220,9 @@ struct PlayerSettings: View {
|
|||||||
Text("Always").tag(ShowInspectorSetting.always)
|
Text("Always").tag(ShowInspectorSetting.always)
|
||||||
Text("Only for local files and URLs").tag(ShowInspectorSetting.onlyLocal)
|
Text("Only for local files and URLs").tag(ShowInspectorSetting.onlyLocal)
|
||||||
}
|
}
|
||||||
|
#if os(macOS)
|
||||||
.labelsHidden()
|
.labelsHidden()
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ struct SettingsView: View {
|
|||||||
case .player:
|
case .player:
|
||||||
return 450
|
return 450
|
||||||
case .controls:
|
case .controls:
|
||||||
return 900
|
return 920
|
||||||
case .quality:
|
case .quality:
|
||||||
return 420
|
return 420
|
||||||
case .history:
|
case .history:
|
||||||
|
Loading…
Reference in New Issue
Block a user