Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions DevLog/Presentation/ViewModel/PushNotificationViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,11 @@ private extension PushNotificationViewModel {
state.nextCursor = nil
return [.fetchNotifications(state.query, cursor: nil)]
case .loadNextPage:
guard state.hasMore, !state.isLoading else { return [] }
guard state.hasMore, !state.isLoading, state.pendingTask == nil else { return [] }
return [.fetchNotifications(state.query, cursor: state.nextCursor)]
case .confirmDelete:
guard let (item, _ ) = state.pendingTask else { return [] }
guard let (item, _) = state.pendingTask else { return [] }
state.pendingTask = nil
return [.delete(item)]
case .setToast(let isPresented, let type):
setToast(&state, isPresented: isPresented, for: type)
Expand All @@ -241,7 +242,13 @@ private extension PushNotificationViewModel {
state.notifications = []
state.nextCursor = nil
case .appendNotifications(let notifications, let nextCursor):
state.notifications.append(contentsOf: notifications)
let filteredNotifications: [PushNotification]
if let (pendingItem, _) = state.pendingTask {
filteredNotifications = notifications.filter { $0.id != pendingItem.id }
} else {
filteredNotifications = notifications
}
state.notifications.append(contentsOf: filteredNotifications)
state.nextCursor = nextCursor
default:
break
Expand Down
54 changes: 40 additions & 14 deletions DevLog/UI/Common/Componeent/Toast.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ private struct ToastOverlayView<Label: View>: View {
@State private var opacityValue: Double = 0
@State private var dismissWorkItem: DispatchWorkItem?
@State private var isTapped: Bool = false
@State private var isScheduled: Bool = false

var body: some View {
if isPresented {
Expand All @@ -50,19 +51,18 @@ private struct ToastOverlayView<Label: View>: View {
)
.offset(y: yOffset)
.opacity(opacityValue)
.onChange(of: isPresented) { newValue in
if newValue {
resetForNewPresentation()
presentAnimated()
scheduleDismissIfNeeded()
} else {
cleanupPresentation()
}
}
.onAppear {
presentAnimated()
scheduleDismiss()
}
.onDisappear {
dismissWorkItem?.cancel()
dismissWorkItem = nil
isPresented = false

// 토스트를 탭하지 않고 자동으로 사라진 경우에만 onDismiss 호출
if !isTapped {
onDismiss?()
}
scheduleDismissIfNeeded()
}
.onTapGesture {
isTapped = true
Expand All @@ -74,16 +74,36 @@ private struct ToastOverlayView<Label: View>: View {
}

private func presentAnimated() {
dismissWorkItem?.cancel()
dismissWorkItem = nil
guard opacityValue == 0 else { return }

withAnimation(.spring(response: 0.5, dampingFraction: 1, blendDuration: 0.0)) {
yOffset = -100
opacityValue = 1
}
}

private func scheduleDismiss() {
private func resetForNewPresentation() {
dismissWorkItem?.cancel()
dismissWorkItem = nil
isScheduled = false
isTapped = false
yOffset = 0
opacityValue = 0
}

private func cleanupPresentation() {
dismissWorkItem?.cancel()
dismissWorkItem = nil
isScheduled = false
isTapped = false
yOffset = 0
opacityValue = 0
}

private func scheduleDismissIfNeeded() {
guard !isScheduled else { return }
isScheduled = true

let workItem = DispatchWorkItem {
dismissAnimated()
}
Expand All @@ -102,6 +122,12 @@ private struct ToastOverlayView<Label: View>: View {

DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
isPresented = false
isScheduled = false

if !isTapped {
onDismiss?()
}
isTapped = false
}
}
}
Expand Down