jellyflood/jellypig iOS/Components/UpdateView.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)
}
}
}