diff --git a/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift b/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift index cab717fba..803418378 100644 --- a/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift +++ b/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift @@ -9,8 +9,15 @@ import UIKit import CommonKit struct NotificationModel: Equatable, Hashable { + static func == (lhs: NotificationModel, rhs: NotificationModel) -> Bool { + return lhs.icon == rhs.icon + && lhs.title == rhs.title + && lhs.description == rhs.description + } + let icon: UIImage? let title: String? let description: String? let tapHandler: IDWrapper<() -> Void>? + let cancelAutoDismiss: IDWrapper<() -> Void>? } diff --git a/PopupKit/Sources/PopupKit/Implementation/Views/NotificationPresenterView.swift b/PopupKit/Sources/PopupKit/Implementation/Views/NotificationPresenterView.swift new file mode 100644 index 000000000..f52d85786 --- /dev/null +++ b/PopupKit/Sources/PopupKit/Implementation/Views/NotificationPresenterView.swift @@ -0,0 +1,86 @@ +// +// NotificationPresenterView.swift +// PopupKit +// +// Created by Yana Silosieva on 02.12.2024. +// + +import SwiftUI +import CommonKit + +struct NotificationPresenterView: View { + @State private var dragTranslation: CGFloat = .zero + @State private var horizontalDragTranslation: CGFloat = .zero + @State private var minTranslationYForDismiss: CGFloat = .infinity + @State private var minTranslationXForDismiss: CGFloat = .infinity + @State private var isTextLimited: Bool = true + + let model: NotificationModel + let safeAreaInsets: EdgeInsets + let dismissAction: ((Edge)) -> Void + + var body: some View { + NotificationView( + isTextLimited: $isTextLimited, + model: model + ) + .padding([.leading, .trailing], 15) + .padding([.top, .bottom], 10) + .background(GeometryReader(content: processGeometry)) + .expanded(axes: .horizontal) + .offset(y: dragTranslation < .zero ? dragTranslation : .zero) + .offset(x: horizontalDragTranslation < .zero ? horizontalDragTranslation : .zero) + .gesture(dragGesture) + .onTapGesture(perform: onTap) + .cornerRadius(10) + .padding(.horizontal, 15) + .padding(.top, safeAreaInsets.top) + } +} + +private extension NotificationPresenterView { + var dragGesture: some Gesture { + DragGesture() + .onChanged { + dragTranslation = $0.translation.height + horizontalDragTranslation = $0.translation.width + } + .onEnded { + if $0.velocity.height < -100 || -$0.translation.height > minTranslationYForDismiss { + Task { + dismissAction(.top) + } + } else if $0.velocity.width < -100 || $0.translation.width > minTranslationXForDismiss { + Task { + dismissAction(.leading) + } + } else if $0.velocity.height > -100 || -$0.translation.height < minTranslationYForDismiss { + withAnimation { + horizontalDragTranslation = .zero + isTextLimited = false + } + model.cancelAutoDismiss?.value() + } else { + withAnimation { + dragTranslation = .zero + horizontalDragTranslation = .zero + } + } + } + } + + func processGeometry(_ geometry: GeometryProxy) -> some View { + DispatchQueue.main.async { + minTranslationYForDismiss = geometry.size.height / 2 + minTranslationXForDismiss = geometry.size.width / 2 + } + + return Color.init(uiColor: .adamant.swipeBlockColor) + .cornerRadius(10) + } + + func onTap() { + model.tapHandler?.value() + dismissAction(.top) + } +} diff --git a/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift b/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift index 4defc4893..7b759090f 100644 --- a/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift +++ b/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift @@ -9,12 +9,8 @@ import SwiftUI import CommonKit struct NotificationView: View { - @State private var dragTranslation: CGFloat = .zero - @State private var minTranslationForDismiss: CGFloat = .infinity - + @Binding var isTextLimited: Bool let model: NotificationModel - let safeAreaInsets: EdgeInsets - let dismissAction: () -> Void var body: some View { HStack(alignment: .top, spacing: 8) { @@ -24,15 +20,6 @@ struct NotificationView: View { textStack Spacer(minLength: .zero) } - .padding([.leading, .trailing], 15) - .padding([.top, .bottom], 10) - .background(GeometryReader(content: processGeometry)) - .gesture(dragGesture) - .onTapGesture(perform: onTap) - .background(Color(.adamant.swipeBlockColor)) - .cornerRadius(10) - .padding(.horizontal, 15) - .padding(.top, safeAreaInsets.top) } } @@ -48,7 +35,7 @@ private extension NotificationView { } var textStack: some View { - VStack(alignment: .leading, spacing: .zero) { + VStack(alignment: .leading, spacing: 3) { if let title = model.title { Text(title) .font(.system(size: 15, weight: .bold)) @@ -56,34 +43,9 @@ private extension NotificationView { if let description = model.description { Text(description) .font(.system(size: 13)) - .lineLimit(3) - } - } - } - - var dragGesture: some Gesture { - DragGesture() - .onChanged { dragTranslation = $0.translation.height } - .onEnded { - print(-$0.translation.height, minTranslationForDismiss) - if $0.velocity.height < -100 || -$0.translation.height > minTranslationForDismiss { - dismissAction() - } else { - withAnimation { dragTranslation = .zero } - } + .lineLimit(isTextLimited ? 3 : nil) + } - } - - func processGeometry(_ geometry: GeometryProxy) -> some View { - DispatchQueue.main.async { - minTranslationForDismiss = geometry.size.height / 2 } - - return Color.clear - } - - func onTap() { - model.tapHandler?.value() - dismissAction() } } diff --git a/PopupKit/Sources/PopupKit/Implementation/Views/PopupCoordinatorView.swift b/PopupKit/Sources/PopupKit/Implementation/Views/PopupCoordinatorView.swift index bced879de..52f37a786 100644 --- a/PopupKit/Sources/PopupKit/Implementation/Views/PopupCoordinatorView.swift +++ b/PopupKit/Sources/PopupKit/Implementation/Views/PopupCoordinatorView.swift @@ -10,6 +10,7 @@ import CommonKit struct PopupCoordinatorView: View { @ObservedObject var model: PopupCoordinatorModel + @State var dismissEdge: Edge = .top var body: some View { GeometryReader { geomerty in @@ -33,15 +34,19 @@ private extension PopupCoordinatorView { func makeNotificationView(safeAreaInsets: EdgeInsets) -> some View { VStack { if let notificationModel = model.notification { - NotificationView( + NotificationPresenterView( model: notificationModel, safeAreaInsets: safeAreaInsets, - dismissAction: { [weak model] in - model?.notification = nil + dismissAction: { [weak model] edge in + dismissEdge = edge + Task { + model?.notification = nil + dismissEdge = .top + } } ) .id(model.notification?.hashValue) - .transition(.move(edge: .top)) + .transition(.move(edge: dismissEdge)) } Spacer() } diff --git a/PopupKit/Sources/PopupKit/PopupManager.swift b/PopupKit/Sources/PopupKit/PopupManager.swift index a237dbe5a..341a8ecb8 100644 --- a/PopupKit/Sources/PopupKit/PopupManager.swift +++ b/PopupKit/Sources/PopupKit/PopupManager.swift @@ -83,7 +83,10 @@ public extension PopupManager { icon: icon, title: title, description: description, - tapHandler: tapHandler.map { .init(id: .empty, value: $0) } + tapHandler: tapHandler.map { .init(id: .empty, value: $0) }, + cancelAutoDismiss: .init(id: .empty, value: { [weak self] in + self?.autoDismissManager.notificationDismissSubscription?.cancel() + }) ) if autoDismiss {