secant-ios-wallet/modules/Sources/Features/SmartBanner/SmartBannerView.swift

190 lines
6.0 KiB
Swift

//
// SmartBannerView.swift
// Zashi
//
// Created by Lukáš Korba on 2025-03-14.
//
import SwiftUI
import ComposableArchitecture
import Generated
import Generated
import UIComponents
enum SBConstants {
static let fixedHeight: CGFloat = 32
static let fixedHeightWithShadow: CGFloat = 36
static let shadowHeight: CGFloat = 4
}
public struct SmartBannerView: View {
@Environment(\.colorScheme) var colorScheme
@Perception.Bindable var store: StoreOf<SmartBanner>
@State private var realHeight: CGFloat = 100
@State private var isUnhidden = false
@State private var height: CGFloat = 0
let tokenName: String
public init(store: StoreOf<SmartBanner>, tokenName: String) {
self.store = store
self.tokenName = tokenName
}
public var body: some View {
WithPerceptionTracking {
ZStack(alignment: .top) {
BottomRoundedRectangle(radius: SBConstants.fixedHeight)
.frame(height: SBConstants.fixedHeight)
.foregroundColor(Design.screenBackground.color(colorScheme))
.shadow(color: Design.Text.primary.color(colorScheme).opacity(store.isOpen ? 0.25 : 0), radius: 1)
.zIndex(1)
VStack(spacing: 0) {
if store.isOpen {
priorityContent()
.padding(.vertical, 16)
.padding(.top, SBConstants.fixedHeight)
.onTapGesture {
store.send(.smartBannerContentTapped)
}
.screenHorizontalPadding()
}
TopRoundedRectangle(radius: store.isOpen ? SBConstants.fixedHeight : 0)
.frame(height: SBConstants.fixedHeightWithShadow)
.foregroundColor(Design.screenBackground.color(colorScheme))
.shadow(color: Design.Text.primary.color(colorScheme).opacity(0.1), radius: store.isOpen ? 1 : 0, y: -1)
}
.frame(minHeight: SBConstants.fixedHeight + SBConstants.shadowHeight)
shareMessageView()
}
.zashiSheet(isPresented: $store.isSmartBannerSheetPresented) {
helpSheetContent()
.screenHorizontalPadding()
.applyScreenBackground()
}
.onAppear { store.send(.onAppear) }
.onDisappear { store.send(.onDisappear) }
.background {
VStack(spacing: 0) {
Design.screenBackground.color(colorScheme)
.frame(height: 2)
.frame(maxWidth: .infinity)
LinearGradient(
stops: [
Gradient.Stop(color: Design.Utility.Purple._700.color(.light), location: 0.00),
Gradient.Stop(color: Design.Utility.Purple._950.color(.light), location: 1.00)
],
startPoint: UnitPoint(x: 0.5, y: 0.0),
endPoint: UnitPoint(x: 0.5, y: 1.0)
)
Design.screenBackground.color(colorScheme)
.frame(height: 2)
.frame(maxWidth: .infinity)
}
}
.clipShape( Rectangle() )
}
}
}
extension SmartBannerView {
@ViewBuilder func shareMessageView() -> some View {
if let message = store.messageToBeShared {
UIShareDialogView(activityItems: [message]) {
store.send(.shareFinished)
}
// UIShareDialogView only wraps UIActivityViewController presentation
// so frame is set to 0 to not break SwiftUIs layout
.frame(width: 0, height: 0)
} else {
EmptyView()
}
}
}
// MARK: - Store
extension SmartBanner {
public static var initial = StoreOf<SmartBanner>(
initialState: .initial
) {
SmartBanner()
}
}
// MARK: - Placeholders
extension SmartBanner.State {
public static let initial = SmartBanner.State()
}
// MARK: - Helpers
struct BottomRoundedRectangle: Shape {
var radius: CGFloat
func path(in rect: CGRect) -> Path {
let width = rect.width
let height = rect.height
return Path { path in
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: width, y: 0))
path.addLine(to: CGPoint(x: width, y: height - radius))
path.addQuadCurve(
to: CGPoint(x: width - radius, y: height),
control: CGPoint(x: width, y: height)
)
path.addLine(to: CGPoint(x: radius, y: height))
path.addQuadCurve(
to: CGPoint(x: 0, y: height - radius),
control: CGPoint(x: 0, y: height)
)
path.addLine(to: CGPoint(x: 0, y: 0))
}
}
}
struct TopRoundedRectangle: Shape {
var radius: CGFloat
var animatableData: CGFloat {
get { radius }
set { radius = newValue }
}
func path(in rect: CGRect) -> Path {
let width = rect.width
let height = rect.height
return Path { path in
path.move(to: CGPoint(x: 0, y: height))
path.addLine(to: CGPoint(x: width, y: height))
path.addLine(to: CGPoint(x: width, y: radius))
path.addQuadCurve(
to: CGPoint(x: width - radius, y: 0),
control: CGPoint(x: width, y: 0)
)
path.addLine(to: CGPoint(x: radius, y: 0))
path.addQuadCurve(
to: CGPoint(x: 0, y: radius),
control: CGPoint(x: 0, y: 0)
)
path.addLine(to: CGPoint(x: 0, y: 0))
}
}
}