2022-11-13 21:50:42 +00:00
|
|
|
import Defaults
|
2022-11-13 17:52:15 +00:00
|
|
|
import SwiftUI
|
|
|
|
|
|
|
|
struct VideoDetailsToolbar: View {
|
2022-11-13 22:36:46 +00:00
|
|
|
static let lowOpacity = 0.5
|
2022-11-13 17:52:15 +00:00
|
|
|
var video: Video?
|
|
|
|
@Binding var page: VideoDetails.DetailsPage
|
|
|
|
var sidebarQueue: Bool
|
|
|
|
|
2022-11-18 21:22:57 +00:00
|
|
|
@State private var tools = VideoDetailsTool.all
|
2022-11-13 17:52:15 +00:00
|
|
|
|
|
|
|
@State private var activeTool: VideoDetailsTool?
|
|
|
|
@State private var startedToolPosition: CGRect = .zero
|
|
|
|
@State private var opacity = 1.0
|
|
|
|
|
2022-11-24 20:36:05 +00:00
|
|
|
@ObservedObject private var player = PlayerModel.shared
|
2022-11-13 21:50:42 +00:00
|
|
|
@Default(.playerDetailsPageButtonLabelStyle) private var playerDetailsPageButtonLabelStyle
|
|
|
|
|
2022-11-13 17:52:15 +00:00
|
|
|
var body: some View {
|
2022-11-13 21:50:42 +00:00
|
|
|
VStack {
|
|
|
|
HStack(spacing: 12) {
|
|
|
|
ForEach($tools) { $tool in
|
|
|
|
if $tool.wrappedValue.isAvailable(for: video, sidebarQueue: sidebarQueue) {
|
|
|
|
ToolView(tool: $tool)
|
|
|
|
.padding(.vertical, 10)
|
2022-11-13 17:52:15 +00:00
|
|
|
}
|
|
|
|
}
|
2022-11-13 21:50:42 +00:00
|
|
|
}
|
2022-11-14 18:07:16 +00:00
|
|
|
.id(video?.id)
|
2022-11-18 21:22:57 +00:00
|
|
|
.onAppear {
|
|
|
|
activeTool = .find(for: page)
|
|
|
|
}
|
2022-11-13 21:50:42 +00:00
|
|
|
.onChange(of: page) { newValue in
|
|
|
|
activeTool = tools.first { $0.id == newValue.rawValue }
|
|
|
|
}
|
|
|
|
.coordinateSpace(name: "toolbarArea")
|
2022-11-13 22:40:18 +00:00
|
|
|
#if !os(tvOS)
|
|
|
|
.gesture(
|
|
|
|
DragGesture(minimumDistance: 0)
|
|
|
|
.onChanged { value in
|
|
|
|
withAnimation(.linear(duration: 0.2)) {
|
|
|
|
opacity = 1
|
|
|
|
}
|
2022-11-13 21:50:42 +00:00
|
|
|
|
2022-11-13 22:40:18 +00:00
|
|
|
guard let firstTool = tools.first else { return }
|
|
|
|
if startedToolPosition == .zero {
|
|
|
|
startedToolPosition = firstTool.toolPostion
|
2022-11-13 17:52:15 +00:00
|
|
|
}
|
2022-11-13 22:40:18 +00:00
|
|
|
let location = CGPoint(x: value.location.x, y: value.location.y)
|
|
|
|
|
|
|
|
if let index = tools.firstIndex(where: { $0.toolPostion.contains(location) }),
|
|
|
|
activeTool?.id != tools[index].id,
|
|
|
|
tools[index].isAvailable(for: video, sidebarQueue: sidebarQueue)
|
|
|
|
{
|
|
|
|
withAnimation(.interpolatingSpring(stiffness: 230, damping: 22)) {
|
|
|
|
activeTool = tools[index]
|
|
|
|
}
|
|
|
|
withAnimation(.linear(duration: 0.25)) {
|
|
|
|
page = activeTool?.page ?? .info
|
|
|
|
}
|
2022-11-13 17:52:15 +00:00
|
|
|
}
|
|
|
|
}
|
2022-11-13 22:40:18 +00:00
|
|
|
.onEnded { _ in
|
|
|
|
withAnimation(.interactiveSpring(response: 0.5, dampingFraction: 1, blendDuration: 1)) {
|
|
|
|
startedToolPosition = .zero
|
|
|
|
}
|
|
|
|
Delay.by(2) {
|
|
|
|
lowerOpacity()
|
|
|
|
}
|
2022-11-13 21:50:42 +00:00
|
|
|
}
|
2022-11-13 22:40:18 +00:00
|
|
|
)
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#if !os(tvOS)
|
|
|
|
.onHover { hovering in
|
|
|
|
hovering ? resetOpacity(0.2) : lowerOpacity(0.2)
|
2022-11-13 21:50:42 +00:00
|
|
|
}
|
2022-11-13 22:40:18 +00:00
|
|
|
#endif
|
2022-11-13 21:50:42 +00:00
|
|
|
.onAppear {
|
|
|
|
Delay.by(2) { lowerOpacity() }
|
2022-11-13 17:52:15 +00:00
|
|
|
}
|
2022-11-13 21:50:42 +00:00
|
|
|
.opacity(opacity)
|
2022-11-13 17:52:15 +00:00
|
|
|
.background(
|
|
|
|
Rectangle()
|
|
|
|
.contentShape(Rectangle())
|
|
|
|
.foregroundColor(.clear)
|
|
|
|
)
|
2022-11-13 21:50:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func lowerOpacity(_ duration: Double = 1.0) {
|
|
|
|
withAnimation(.linear(duration: duration)) {
|
|
|
|
opacity = Self.lowOpacity
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func resetOpacity(_ duration: Double = 1.0) {
|
|
|
|
withAnimation(.linear(duration: duration)) {
|
|
|
|
opacity = 1
|
2022-11-13 17:52:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@ViewBuilder func ToolView(tool: Binding<VideoDetailsTool>) -> some View {
|
|
|
|
HStack(spacing: 0) {
|
|
|
|
Image(systemName: tool.wrappedValue.icon)
|
|
|
|
.font(.title2)
|
|
|
|
.foregroundColor(.white)
|
|
|
|
.frame(width: 30, height: 30)
|
|
|
|
.layoutPriority(1)
|
|
|
|
|
2022-11-13 21:50:42 +00:00
|
|
|
if activeToolID == tool.wrappedValue.id,
|
|
|
|
playerDetailsPageButtonLabelStyle.text,
|
|
|
|
player.playerSize.width > 450
|
|
|
|
{
|
2022-11-18 23:06:13 +00:00
|
|
|
Text(tool.wrappedValue.name.localized())
|
2022-11-13 17:52:15 +00:00
|
|
|
.font(.system(size: 14).bold())
|
2022-11-13 22:36:46 +00:00
|
|
|
.padding(.trailing, 4)
|
2022-11-13 17:52:15 +00:00
|
|
|
.foregroundColor(.white)
|
|
|
|
.allowsTightening(true)
|
|
|
|
.lineLimit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.padding(.horizontal, 10)
|
|
|
|
.padding(.vertical, 6)
|
|
|
|
.background(
|
|
|
|
RoundedRectangle(cornerRadius: 10, style: .continuous)
|
|
|
|
.fill(activeToolID == tool.wrappedValue.id ? Color.accentColor : Color.secondary)
|
|
|
|
)
|
2022-11-19 14:35:43 +00:00
|
|
|
.background(
|
|
|
|
GeometryReader { proxy in
|
|
|
|
let frame = proxy.frame(in: .named("toolbarArea"))
|
|
|
|
Color.clear
|
|
|
|
.preference(key: RectKey.self, value: frame)
|
|
|
|
.onPreferenceChange(RectKey.self) { rect in
|
|
|
|
tool.wrappedValue.toolPostion = rect
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
2022-11-13 17:52:15 +00:00
|
|
|
}
|
|
|
|
|
2022-11-13 22:36:46 +00:00
|
|
|
var visibleToolsCount: Int {
|
|
|
|
tools.filter { $0.isAvailable(for: video, sidebarQueue: sidebarQueue) }.count
|
|
|
|
}
|
|
|
|
|
2022-11-13 17:52:15 +00:00
|
|
|
var activeToolID: VideoDetailsTool.ID {
|
2022-12-17 23:08:30 +00:00
|
|
|
activeTool?.id ?? "info"
|
2022-11-13 17:52:15 +00:00
|
|
|
}
|
|
|
|
}
|
2022-11-13 22:36:46 +00:00
|
|
|
|
|
|
|
struct VideoDetailsToolbar_Previews: PreviewProvider {
|
|
|
|
static var previews: some View {
|
|
|
|
VideoDetailsToolbar(page: .constant(.queue), sidebarQueue: false)
|
|
|
|
.injectFixtureEnvironmentObjects()
|
|
|
|
}
|
|
|
|
}
|