mirror of
https://github.com/neon443/NearFuture.git
synced 2026-03-11 06:49:12 +00:00
toolbar label()s instead of buton("label")s
fix event add/delete/tick animation
fix event complete progress on ios and mac and that it just spazzed out
browsing true for symbols browsing (not picking)
127 lines
2.7 KiB
Swift
127 lines
2.7 KiB
Swift
//
|
|
// CompleteEventButton.swift
|
|
// NearFuture
|
|
//
|
|
// Created by neon443 on 15/06/2025.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct CompleteEventButton: View {
|
|
@ObservedObject var viewModel: EventViewModel
|
|
@Binding var event: Event
|
|
|
|
@State var timer: Timer?
|
|
@State var largeTick: Bool = false
|
|
@State var completeInProgress: Bool = false
|
|
@State var completeStartTime: Date = .now
|
|
@State var progress: Double = 0
|
|
private let completeDuration: TimeInterval = 3.0
|
|
|
|
var isMac: Bool {
|
|
#if canImport(AppKit)
|
|
return true
|
|
#else
|
|
return false
|
|
#endif
|
|
}
|
|
|
|
func startCompleting() {
|
|
#if canImport(UIKit)
|
|
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
|
|
#endif
|
|
withAnimation { completeInProgress = true }
|
|
completeStartTime = .now
|
|
progress = 0
|
|
|
|
timer = Timer(timeInterval: 0.01, repeats: true) { timer in
|
|
guard completeInProgress else { return }
|
|
guard timer.isValid else { return }
|
|
let elapsed = Date().timeIntervalSince(completeStartTime)
|
|
progress = min(1, elapsed)
|
|
|
|
if progress >= 1 {
|
|
withAnimation { completeInProgress = false }
|
|
viewModel.completeEvent(&event)
|
|
timer.invalidate()
|
|
progress = 0
|
|
}
|
|
}
|
|
RunLoop.main.add(timer!, forMode: .common)
|
|
}
|
|
|
|
var body: some View {
|
|
Group {
|
|
if completeInProgress {
|
|
ZStack {
|
|
CircularProgressView(progress: $progress)
|
|
Image(systemName: "xmark")
|
|
.bold()
|
|
}
|
|
.onTapGesture {
|
|
withAnimation { completeInProgress = false }
|
|
}
|
|
} else {
|
|
Image(systemName: event.complete ? "checkmark.circle.fill" : "circle")
|
|
.resizable().scaledToFit()
|
|
.foregroundStyle(event.complete ? .green : event.color.color)
|
|
.bold()
|
|
.onTapGesture {
|
|
startCompleting()
|
|
}
|
|
.onHover() { hovering in
|
|
withAnimation {
|
|
largeTick.toggle()
|
|
}
|
|
}
|
|
.scaleEffect(largeTick ? 1.5 : 1)
|
|
}
|
|
}
|
|
.frame(maxWidth: isMac ? 20 : 30)
|
|
.shadow(color: .one.opacity(0.2), radius: 2.5)
|
|
.padding(.trailing, isMac ? 15 : 5)
|
|
.transition(.scale)
|
|
.animation(.spring, value: completeInProgress)
|
|
.animation(
|
|
.spring(response: 0.2, dampingFraction: 0.75, blendDuration: 2),
|
|
value: largeTick
|
|
)
|
|
}
|
|
}
|
|
|
|
struct CircularProgressView: View {
|
|
@Binding var progress: Double
|
|
var body: some View {
|
|
ZStack {
|
|
Circle()
|
|
.stroke(
|
|
.two,
|
|
lineWidth: 5
|
|
)
|
|
Circle()
|
|
.trim(from: 0, to: progress)
|
|
.stroke(
|
|
.one,
|
|
lineWidth: 5
|
|
// style: StrokeStyle(
|
|
// lineWidth: 5,
|
|
// lineCap: .round
|
|
// )
|
|
)
|
|
.rotationEffect(.degrees(-90))
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
CompleteEventButton(
|
|
viewModel: dummyEventViewModel(),
|
|
event: .constant(dummyEventViewModel().example)
|
|
)
|
|
.scaleEffect(5)
|
|
}
|
|
|
|
#Preview {
|
|
CircularProgressView(progress: .constant(0.5))
|
|
}
|