98 lines
2.9 KiB
Swift
98 lines
2.9 KiB
Swift
//
|
|
// Swiftfin is subject to the terms of the Mozilla Public
|
|
// License, v2.0. If a copy of the MPL was not distributed with this
|
|
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
//
|
|
// Copyright (c) 2025 Jellyfin & Jellyfin Contributors
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
// TODO: Better name
|
|
// TODO: don't use pushed to indicate a presented value
|
|
|
|
class UpdateViewProxy: ObservableObject {
|
|
|
|
@Published
|
|
private(set) var systemName: String? = nil
|
|
@Published
|
|
private(set) var iconSize: CGSize = .init(width: 25, height: 25)
|
|
@Published
|
|
private(set) var title: String = ""
|
|
@Published
|
|
private(set) var pushed: Bool = false
|
|
|
|
func present(systemName: String, title: String, iconSize: CGSize = .init(width: 25, height: 25)) {
|
|
self.systemName = systemName
|
|
self.iconSize = iconSize
|
|
self.title = title
|
|
pushed.toggle()
|
|
}
|
|
}
|
|
|
|
struct UpdateView: View {
|
|
|
|
@ObservedObject
|
|
private var proxy: UpdateViewProxy
|
|
|
|
@State
|
|
private var isPresenting: Bool = false
|
|
@State
|
|
private var workItem: DispatchWorkItem?
|
|
|
|
init(proxy: UpdateViewProxy) {
|
|
self.proxy = proxy
|
|
}
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
if isPresenting {
|
|
HStack {
|
|
if let systemName = proxy.systemName {
|
|
Image(systemName: systemName)
|
|
.renderingMode(.template)
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(maxWidth: proxy.iconSize.width, maxHeight: proxy.iconSize.height, alignment: .center)
|
|
}
|
|
|
|
Text(proxy.title)
|
|
.font(.body)
|
|
.fontWeight(.bold)
|
|
.monospacedDigit()
|
|
}
|
|
.padding(.horizontal, 24)
|
|
.padding(.vertical, 8)
|
|
.frame(minHeight: 50)
|
|
.background(BlurView())
|
|
.clipShape(Capsule())
|
|
.overlay(Capsule().stroke(Color.gray.opacity(0.2), lineWidth: 1))
|
|
.shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 6)
|
|
.compositingGroup()
|
|
.transition(.opacity)
|
|
}
|
|
}
|
|
.animation(.linear(duration: 0.1), value: proxy.systemName)
|
|
.animation(.linear(duration: 0.1), value: proxy.iconSize)
|
|
.onChange(of: proxy.pushed) { _ in
|
|
|
|
if !isPresenting {
|
|
withAnimation {
|
|
isPresenting = true
|
|
}
|
|
}
|
|
|
|
workItem?.cancel()
|
|
|
|
let task = DispatchWorkItem {
|
|
withAnimation(.spring()) {
|
|
isPresenting = false
|
|
}
|
|
}
|
|
workItem = task
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: task)
|
|
}
|
|
}
|
|
}
|