mirror of
https://github.com/lux-tel/session-ios.git
synced 2026-10-30 20:38:32 +00:00
# Conflicts: # Session.xcodeproj/project.pbxproj # Session/Conversations/ConversationVC+Interaction.swift # Session/Conversations/ConversationVC.swift # Session/Conversations/Settings/ThreadSettingsViewModel.swift # Session/Home/App Review/AppReviewPromptModel.swift # Session/Home/HomeViewModel.swift # Session/Media Viewing & Editing/MessageInfoScreen.swift # Session/Meta/AppDelegate.swift # Session/Settings/DeveloperSettings/DeveloperSettingsProViewModel.swift # Session/Settings/SettingsViewModel.swift # Session/Shared/BaseVC.swift # Session/Utilities/UIContextualAction+Utilities.swift # SessionMessagingKit/Database/Models/SessionThread.swift # SessionMessagingKit/SessionPro/SessionProPaymentScreenContent.swift # SessionMessagingKit/SessionPro/SessionProSettingsViewModel.swift # SessionMessagingKit/SessionPro/SessionProState.swift # SessionMessagingKit/Shared Models/SessionThreadViewModel.swift # SessionNotificationServiceExtension/NotificationServiceExtension.swift # SessionUIKit/Components/Input View/InputView.swift # SessionUIKit/Components/SwiftUI/ProCTAModal.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+CancelPlan.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+Models.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+Purchase.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+Renew.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+RequestRefund.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+UpdatePlan.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPlanUpdatedScreen.swift # SessionUIKit/Style Guide/Constants+Apple.swift # SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentApprovalViewController.swift # SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentTextToolbar.swift
127 lines
4.2 KiB
Swift
127 lines
4.2 KiB
Swift
// Copyright © 2025 Rangeproof Pty Ltd. All rights reserved.
|
|
|
|
import SwiftUI
|
|
|
|
public struct Modal_SwiftUI<Content>: View where Content: View {
|
|
let host: HostWrapper
|
|
let dismissType: Modal.DismissType
|
|
let afterClosed: (() -> Void)?
|
|
let content: (@escaping @MainActor ((() -> Void)?) -> Void) -> Content
|
|
|
|
let cornerRadius: CGFloat = 11
|
|
let shadowRadius: CGFloat = 10
|
|
let shadowOpacity: Double = 0.4
|
|
|
|
@State private var show: Bool = false
|
|
|
|
public var body: some View {
|
|
ZStack {
|
|
// Background
|
|
Rectangle()
|
|
.fill(.ultraThinMaterial)
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
.ignoresSafeArea()
|
|
.onTapGesture { close() }
|
|
|
|
if show {
|
|
// Modal
|
|
VStack {
|
|
Spacer()
|
|
|
|
VStack(spacing: 0) {
|
|
content { internalAfterClosed in
|
|
close(internalAfterClosed)
|
|
}
|
|
}
|
|
.backgroundColor(themeColor: .alert_background)
|
|
.clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
|
|
.shadow(color: Color.black.opacity(shadowOpacity), radius: shadowRadius)
|
|
.frame(
|
|
maxWidth: UIDevice.current.isIPad ? Values.iPadModalWidth : .infinity
|
|
)
|
|
.padding(.horizontal, UIDevice.current.isIPad ? 0 : Values.veryLargeSpacing)
|
|
|
|
Spacer()
|
|
}
|
|
.transition(.move(edge: .bottom).combined(with: .opacity))
|
|
}
|
|
}
|
|
.frame(
|
|
maxWidth: .infinity,
|
|
maxHeight: .infinity
|
|
)
|
|
.onAppear {
|
|
withAnimation {
|
|
show.toggle()
|
|
}
|
|
}
|
|
.gesture(
|
|
DragGesture(minimumDistance: 20, coordinateSpace: .global)
|
|
.onEnded { value in
|
|
if value.translation.height > 40 {
|
|
close()
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
// MARK: - Dismiss Logic
|
|
|
|
@MainActor private func close(_ internalAfterClosed: (() -> Void)? = nil) {
|
|
// Recursively dismiss all modals (ie. find the first modal presented by a non-modal
|
|
// and get that to dismiss it's presented view controller)
|
|
var targetViewController: UIViewController? = host.controller
|
|
|
|
switch dismissType {
|
|
case .single: break
|
|
case .recursive:
|
|
while targetViewController?.presentingViewController is ModalHostIdentifiable {
|
|
targetViewController = targetViewController?.presentingViewController
|
|
}
|
|
}
|
|
|
|
withAnimation {
|
|
show.toggle()
|
|
}
|
|
|
|
targetViewController?.presentingViewController?.dismiss(
|
|
animated: true,
|
|
completion: {
|
|
afterClosed?()
|
|
internalAfterClosed?()
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
// MARK: - ModalHostIdentifiable
|
|
|
|
protocol ModalHostIdentifiable {}
|
|
|
|
// MARK: - ModalHostingViewController
|
|
|
|
open class ModalHostingViewController<Content>: UIHostingController<ModifiedContent<Content, _EnvironmentKeyWritingModifier<HostWrapper?>>>, ModalHostIdentifiable where Content: View {
|
|
@MainActor public init(modal: Content) {
|
|
let container = HostWrapper()
|
|
let modified = modal.environmentObject(container) as! ModifiedContent<Content, _EnvironmentKeyWritingModifier<HostWrapper?>>
|
|
super.init(rootView: modified)
|
|
container.controller = self
|
|
self.modalTransitionStyle = .crossDissolve
|
|
self.modalPresentationStyle = .overFullScreen
|
|
}
|
|
|
|
required public init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
public override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
navigationItem.backButtonTitle = ""
|
|
view.themeBackgroundColor = .clear
|
|
ThemeManager.applyNavigationStylingIfNeeded(to: self)
|
|
|
|
setNeedsStatusBarAppearanceUpdate()
|
|
}
|
|
}
|