mirror of
https://github.com/neon443/NearFuture.git
synced 2026-03-11 06:49:12 +00:00
completeevent button has a custo progress bar to work on ios aswell
extracted the com[lete button
This commit is contained in:
@@ -73,6 +73,8 @@
|
|||||||
A98C20CE2DE7308E0008D61C /* ArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20CD2DE7308E0008D61C /* ArchiveView.swift */; };
|
A98C20CE2DE7308E0008D61C /* ArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20CD2DE7308E0008D61C /* ArchiveView.swift */; };
|
||||||
A98C20D02DE731BD0008D61C /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20CF2DE731BD0008D61C /* HomeView.swift */; };
|
A98C20D02DE731BD0008D61C /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20CF2DE731BD0008D61C /* HomeView.swift */; };
|
||||||
A98C20D42DE7339E0008D61C /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20D32DE7339E0008D61C /* AboutView.swift */; };
|
A98C20D42DE7339E0008D61C /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20D32DE7339E0008D61C /* AboutView.swift */; };
|
||||||
|
A9BAC6882DFF242300EC8E44 /* CompleteEventButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BAC6872DFF238100EC8E44 /* CompleteEventButton.swift */; };
|
||||||
|
A9BAC6892DFF242300EC8E44 /* CompleteEventButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BAC6872DFF238100EC8E44 /* CompleteEventButton.swift */; };
|
||||||
A9D1C34D2DFE10FA00703C2D /* EventListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8402DCAABE00064DCA0 /* EventListView.swift */; };
|
A9D1C34D2DFE10FA00703C2D /* EventListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8402DCAABE00064DCA0 /* EventListView.swift */; };
|
||||||
A9FC7EEA2D2823920020D75B /* NearFutureWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */; };
|
A9FC7EEA2D2823920020D75B /* NearFutureWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@@ -157,6 +159,7 @@
|
|||||||
A98C20CD2DE7308E0008D61C /* ArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveView.swift; sourceTree = "<group>"; };
|
A98C20CD2DE7308E0008D61C /* ArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveView.swift; sourceTree = "<group>"; };
|
||||||
A98C20CF2DE731BD0008D61C /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
A98C20CF2DE731BD0008D61C /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||||
A98C20D32DE7339E0008D61C /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
A98C20D32DE7339E0008D61C /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||||
|
A9BAC6872DFF238100EC8E44 /* CompleteEventButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompleteEventButton.swift; sourceTree = "<group>"; };
|
||||||
A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgets.swift; sourceTree = "<group>"; };
|
A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureWidgets.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@@ -227,6 +230,7 @@
|
|||||||
A90D49512DDE2D0000781124 /* Extensions.swift */,
|
A90D49512DDE2D0000781124 /* Extensions.swift */,
|
||||||
A90D49202DDE0A3B00781124 /* Model */,
|
A90D49202DDE0A3B00781124 /* Model */,
|
||||||
A91EF80A2DFC910000B8463D /* ViewModifiers.swift */,
|
A91EF80A2DFC910000B8463D /* ViewModifiers.swift */,
|
||||||
|
A9BAC6872DFF238100EC8E44 /* CompleteEventButton.swift */,
|
||||||
);
|
);
|
||||||
path = Shared;
|
path = Shared;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -529,6 +533,7 @@
|
|||||||
A91EF80E2DFC9A0C00B8463D /* WhatsNewView.swift in Sources */,
|
A91EF80E2DFC9A0C00B8463D /* WhatsNewView.swift in Sources */,
|
||||||
A91EF8192DFD77BF00B8463D /* SymbolsLoader.swift in Sources */,
|
A91EF8192DFD77BF00B8463D /* SymbolsLoader.swift in Sources */,
|
||||||
A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */,
|
A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */,
|
||||||
|
A9BAC6892DFF242300EC8E44 /* CompleteEventButton.swift in Sources */,
|
||||||
A91EF80C2DFC910000B8463D /* ViewModifiers.swift in Sources */,
|
A91EF80C2DFC910000B8463D /* ViewModifiers.swift in Sources */,
|
||||||
A95E9ED92DFC742B00ED655F /* AccentIcon.swift in Sources */,
|
A95E9ED92DFC742B00ED655F /* AccentIcon.swift in Sources */,
|
||||||
A91EF8102DFCB66C00B8463D /* SettingsView.swift in Sources */,
|
A91EF8102DFCB66C00B8463D /* SettingsView.swift in Sources */,
|
||||||
@@ -575,6 +580,7 @@
|
|||||||
A95E9ED82DFC742B00ED655F /* AccentIcon.swift in Sources */,
|
A95E9ED82DFC742B00ED655F /* AccentIcon.swift in Sources */,
|
||||||
A90D49532DDE2D0000781124 /* Extensions.swift in Sources */,
|
A90D49532DDE2D0000781124 /* Extensions.swift in Sources */,
|
||||||
A949F8522DCAABE00064DCA0 /* iCloudSettingsView.swift in Sources */,
|
A949F8522DCAABE00064DCA0 /* iCloudSettingsView.swift in Sources */,
|
||||||
|
A9BAC6882DFF242300EC8E44 /* CompleteEventButton.swift in Sources */,
|
||||||
A949F8532DCAABE00064DCA0 /* ImportView.swift in Sources */,
|
A949F8532DCAABE00064DCA0 /* ImportView.swift in Sources */,
|
||||||
A949F8542DCAABE00064DCA0 /* SettingsView.swift in Sources */,
|
A949F8542DCAABE00064DCA0 /* SettingsView.swift in Sources */,
|
||||||
A91EF81E2DFD796600B8463D /* SymbolsPicker.swift in Sources */,
|
A91EF81E2DFD796600B8463D /* SymbolsPicker.swift in Sources */,
|
||||||
|
|||||||
@@ -12,43 +12,11 @@ struct EventListView: View {
|
|||||||
@ObservedObject var viewModel: EventViewModel
|
@ObservedObject var viewModel: EventViewModel
|
||||||
@State var event: Event
|
@State var event: Event
|
||||||
|
|
||||||
@State var completeInProgress: Bool = false
|
|
||||||
@State var completeStartTime: Date = .now
|
|
||||||
@State var progress: Double = 0
|
|
||||||
@State var timer: Timer?
|
|
||||||
private let completeDuration: TimeInterval = 3.0
|
|
||||||
@Environment(\.openWindow) var openWindow
|
@Environment(\.openWindow) var openWindow
|
||||||
|
|
||||||
@State var largeTick: Bool = false
|
|
||||||
@State var hovering: Bool = false
|
@State var hovering: Bool = false
|
||||||
|
|
||||||
func startCompleting() {
|
#if canImport(AppKit)
|
||||||
#if canImport(UIKit)
|
|
||||||
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
|
|
||||||
#endif
|
|
||||||
completeInProgress = true
|
|
||||||
progress = 0
|
|
||||||
completeStartTime = .now
|
|
||||||
|
|
||||||
timer = Timer(timeInterval: 0.01, repeats: true) { timer in
|
|
||||||
guard timer.isValid else { return }
|
|
||||||
guard completeInProgress else { return }
|
|
||||||
let elapsed = Date().timeIntervalSince(completeStartTime)
|
|
||||||
progress = min(elapsed, 1.0)
|
|
||||||
|
|
||||||
if progress >= 1.0 {
|
|
||||||
timer.invalidate()
|
|
||||||
viewModel.completeEvent(&event)
|
|
||||||
#if canImport(UIKit)
|
|
||||||
UINotificationFeedbackGenerator().notificationOccurred(.success)
|
|
||||||
#endif
|
|
||||||
completeInProgress = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RunLoop.main.add(timer!, forMode: .common)
|
|
||||||
}
|
|
||||||
|
|
||||||
#if canImport(AppKit)
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
Color.black.opacity(hovering ? 0.5 : 0.0)
|
Color.black.opacity(hovering ? 0.5 : 0.0)
|
||||||
@@ -107,42 +75,9 @@ struct EventListView: View {
|
|||||||
.multilineTextAlignment(.trailing)
|
.multilineTextAlignment(.trailing)
|
||||||
.foregroundStyle(event.date.timeIntervalSinceNow < 0 ? .red : .one)
|
.foregroundStyle(event.date.timeIntervalSinceNow < 0 ? .red : .one)
|
||||||
}
|
}
|
||||||
|
CompleteEventButton(
|
||||||
Group {
|
viewModel: viewModel,
|
||||||
if completeInProgress {
|
event: $event
|
||||||
ZStack {
|
|
||||||
ProgressView(value: progress)
|
|
||||||
.progressViewStyle(.circular)
|
|
||||||
Image(systemName: "xmark")
|
|
||||||
.bold()
|
|
||||||
}
|
|
||||||
.onTapGesture {
|
|
||||||
timer?.invalidate()
|
|
||||||
completeInProgress = false
|
|
||||||
progress = 0
|
|
||||||
}
|
|
||||||
} 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: 20)
|
|
||||||
.shadow(color: .one.opacity(0.2), radius: 2.5)
|
|
||||||
.padding(.trailing, 15)
|
|
||||||
.animation(
|
|
||||||
.spring(response: 0.2, dampingFraction: 0.75, blendDuration: 2),
|
|
||||||
value: largeTick
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
@@ -170,131 +105,94 @@ struct EventListView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationLink() {
|
HStack {
|
||||||
EditEventView(
|
RoundedRectangle(cornerRadius: 5)
|
||||||
viewModel: viewModel,
|
.frame(width: 7)
|
||||||
event: $event
|
.foregroundStyle(
|
||||||
)
|
event.color.color.opacity(
|
||||||
} label: {
|
event.complete ? 0.5 : 1
|
||||||
ZStack {
|
)
|
||||||
|
)
|
||||||
|
VStack(alignment: .leading) {
|
||||||
HStack {
|
HStack {
|
||||||
RoundedRectangle(cornerRadius: 5)
|
Image(systemName: event.symbol)
|
||||||
.frame(width: 7)
|
.resizable()
|
||||||
.foregroundStyle(
|
.scaledToFit()
|
||||||
event.color.color.opacity(
|
.frame(width: 20, height: 20)
|
||||||
event.complete ? 0.5 : 1
|
.shadow(radius: 5)
|
||||||
)
|
|
||||||
)
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
HStack {
|
|
||||||
Image(systemName: event.symbol)
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
.shadow(radius: 5)
|
|
||||||
.foregroundStyle(
|
|
||||||
.one.opacity(
|
|
||||||
event.complete ? 0.5 : 1
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Text("\(event.name)")
|
|
||||||
.font(.headline)
|
|
||||||
.foregroundStyle(.one)
|
|
||||||
.strikethrough(event.complete)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
}
|
|
||||||
if !event.notes.isEmpty {
|
|
||||||
Text(event.notes)
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundStyle(.one.opacity(0.8))
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
}
|
|
||||||
Text(
|
|
||||||
event.date.formatted(
|
|
||||||
date: .long,
|
|
||||||
time: .shortened
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundStyle(
|
.foregroundStyle(
|
||||||
.one.opacity(
|
.one.opacity(
|
||||||
event.complete ? 0.5 : 1
|
event.complete ? 0.5 : 1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if event.recurrence != .none {
|
Text("\(event.name)")
|
||||||
Text("Occurs \(event.recurrence.rawValue)")
|
.font(.headline)
|
||||||
.font(.subheadline)
|
.foregroundStyle(.one)
|
||||||
.foregroundStyle(
|
.strikethrough(event.complete)
|
||||||
.one.opacity(event.complete ? 0.5 : 1))
|
.multilineTextAlignment(.leading)
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
VStack {
|
|
||||||
Text("\(daysUntilEvent(event.date).long)")
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundStyle(event.date.timeIntervalSinceNow < 0 ? .red : .one)
|
|
||||||
.multilineTextAlignment(.trailing)
|
|
||||||
}
|
|
||||||
Button() {
|
|
||||||
startCompleting()
|
|
||||||
} label: {
|
|
||||||
if completeInProgress {
|
|
||||||
ZStack {
|
|
||||||
ProgressView(value: progress)
|
|
||||||
.progressViewStyle(.circular)
|
|
||||||
Image(systemName: "xmark")
|
|
||||||
.bold()
|
|
||||||
}
|
|
||||||
.onTapGesture {
|
|
||||||
timer?.invalidate()
|
|
||||||
completeInProgress = false
|
|
||||||
progress = 0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Image(systemName: event.complete ? "checkmark.circle.fill" : "circle")
|
|
||||||
.resizable().scaledToFit()
|
|
||||||
.foregroundStyle(event.complete ? .green : event.color.color)
|
|
||||||
.bold()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.buttonStyle(.borderless)
|
|
||||||
.frame(maxWidth: 25, maxHeight: 25)
|
|
||||||
.shadow(radius: 5)
|
|
||||||
.padding(.trailing, 5)
|
|
||||||
.modifier(hapticSuccess(trigger: event.complete))
|
|
||||||
}
|
}
|
||||||
.transition(.opacity)
|
if !event.notes.isEmpty {
|
||||||
.padding(.vertical, 5)
|
Text(event.notes)
|
||||||
.overlay(
|
.font(.subheadline)
|
||||||
RoundedRectangle(cornerRadius: 10)
|
.foregroundStyle(.one.opacity(0.8))
|
||||||
.stroke(
|
.multilineTextAlignment(.leading)
|
||||||
.one.opacity(0.5),
|
}
|
||||||
lineWidth: 1
|
Text(
|
||||||
)
|
event.date.formatted(
|
||||||
|
date: .long,
|
||||||
|
time: .shortened
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.clipShape(
|
.font(.subheadline)
|
||||||
RoundedRectangle(cornerRadius: 10)
|
.foregroundStyle(
|
||||||
|
.one.opacity(
|
||||||
|
event.complete ? 0.5 : 1
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
if event.recurrence != .none {
|
||||||
|
Text("Occurs \(event.recurrence.rawValue)")
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundStyle(
|
||||||
|
.one.opacity(event.complete ? 0.5 : 1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.contextMenu() {
|
Spacer()
|
||||||
Button(role: .destructive) {
|
VStack {
|
||||||
let eventToModify = viewModel.events.firstIndex() { currEvent in
|
Text("\(daysUntilEvent(event.date).long)")
|
||||||
currEvent.id == event.id
|
.font(.subheadline)
|
||||||
}
|
.foregroundStyle(event.date.timeIntervalSinceNow < 0 ? .red : .one)
|
||||||
if let eventToModify = eventToModify {
|
.multilineTextAlignment(.trailing)
|
||||||
viewModel.events.remove(at: eventToModify)
|
}
|
||||||
viewModel.saveEvents()
|
CompleteEventButton(
|
||||||
}
|
viewModel: viewModel,
|
||||||
} label: {
|
event: $event
|
||||||
Label("Delete", systemImage: "trash")
|
)
|
||||||
|
}
|
||||||
|
.transition(.opacity)
|
||||||
|
.padding(.vertical, 5)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 15)
|
||||||
|
.stroke(.one.opacity(0.5), lineWidth: 1)
|
||||||
|
)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 15))
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.contextMenu() {
|
||||||
|
Button(role: .destructive) {
|
||||||
|
let eventToModify = viewModel.events.firstIndex() { currEvent in
|
||||||
|
currEvent.id == event.id
|
||||||
}
|
}
|
||||||
|
if let eventToModify = eventToModify {
|
||||||
|
viewModel.events.remove(at: eventToModify)
|
||||||
|
viewModel.saveEvents()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Delete", systemImage: "trash")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview("EventListView") {
|
#Preview("EventListView") {
|
||||||
|
|||||||
@@ -47,9 +47,13 @@ struct HomeView: View {
|
|||||||
ScrollView {
|
ScrollView {
|
||||||
// LazyVStack {
|
// LazyVStack {
|
||||||
ForEach(filteredEvents) { event in
|
ForEach(filteredEvents) { event in
|
||||||
EventListView(viewModel: viewModel, event: event)
|
NavigationLink() {
|
||||||
.transition(.moveAndFade)
|
|
||||||
.id(event.complete)
|
} label: {
|
||||||
|
EventListView(viewModel: viewModel, event: event)
|
||||||
|
.transition(.moveAndFade)
|
||||||
|
.id(event.complete)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
// }
|
// }
|
||||||
|
|||||||
126
Shared/CompleteEventButton.swift
Normal file
126
Shared/CompleteEventButton.swift
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
//
|
||||||
|
// 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 {
|
||||||
|
timer.invalidate()
|
||||||
|
progress = 0
|
||||||
|
withAnimation { completeInProgress = false }
|
||||||
|
viewModel.completeEvent(&event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user