Restore missing content during iPad permissions onboarding
This commit is contained in:
parent
c63c0302aa
commit
79112ddcb0
@ -26,6 +26,7 @@
|
||||
0550A5E22C4035170072CC02 /* CLVViewInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0550A5E12C4035170072CC02 /* CLVViewInfo.swift */; };
|
||||
0550A5E42C4048CF0072CC02 /* ChatListFilterFooterCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0550A5E32C4048CF0072CC02 /* ChatListFilterFooterCell.swift */; };
|
||||
05572BC42BFC0094006A72F1 /* DoubleTapToEditOnboardingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05572BC32BFC0094006A72F1 /* DoubleTapToEditOnboardingController.swift */; };
|
||||
05594CCE2C989F1900CCBFF6 /* ScrollableWhenCompact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05594CCD2C989F1900CCBFF6 /* ScrollableWhenCompact.swift */; };
|
||||
058B49932C66805500307D38 /* AVAssetExportSession+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 058B49922C66804B00307D38 /* AVAssetExportSession+Async.swift */; };
|
||||
059982642C6D0C5200C87533 /* ChatListPinInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 059982632C6D0C4F00C87533 /* ChatListPinInfo.swift */; };
|
||||
05B411252C62845000A1EDBC /* ChatListInboxFilterSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05B411242C62845000A1EDBC /* ChatListInboxFilterSection.swift */; };
|
||||
@ -3422,6 +3423,7 @@
|
||||
0550A5E12C4035170072CC02 /* CLVViewInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLVViewInfo.swift; sourceTree = "<group>"; };
|
||||
0550A5E32C4048CF0072CC02 /* ChatListFilterFooterCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListFilterFooterCell.swift; sourceTree = "<group>"; };
|
||||
05572BC32BFC0094006A72F1 /* DoubleTapToEditOnboardingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleTapToEditOnboardingController.swift; sourceTree = "<group>"; };
|
||||
05594CCD2C989F1900CCBFF6 /* ScrollableWhenCompact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollableWhenCompact.swift; sourceTree = "<group>"; };
|
||||
058B49922C66804B00307D38 /* AVAssetExportSession+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAssetExportSession+Async.swift"; sourceTree = "<group>"; };
|
||||
059982632C6D0C4F00C87533 /* ChatListPinInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListPinInfo.swift; sourceTree = "<group>"; };
|
||||
05B411242C62845000A1EDBC /* ChatListInboxFilterSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListInboxFilterSection.swift; sourceTree = "<group>"; };
|
||||
@ -10555,6 +10557,7 @@
|
||||
children = (
|
||||
05104E392C8B540C00F8851F /* AccessibleLayoutMetric.swift */,
|
||||
05104D172C8A151100F8851F /* AsyncViewTask.swift */,
|
||||
05594CCD2C989F1900CCBFF6 /* ScrollableWhenCompact.swift */,
|
||||
0510F69D2C91EB2800FA3FDE /* ScrollBounceBehaviorIfAvailable.swift */,
|
||||
B9D721752C87B8EB007EDA85 /* SwiftUI+Animations.swift */,
|
||||
);
|
||||
@ -15175,6 +15178,7 @@
|
||||
88B987022880890800F8C74D /* SafetyNumberConfirmationSheet.swift in Sources */,
|
||||
88B9870928808A8A00F8C74D /* ScanQRCodeViewController.swift in Sources */,
|
||||
7677E41129F7A60500AC6A75 /* ScreenLockViewController.swift in Sources */,
|
||||
05594CCE2C989F1900CCBFF6 /* ScrollableWhenCompact.swift in Sources */,
|
||||
0510F69E2C91EB3000FA3FDE /* ScrollBounceBehaviorIfAvailable.swift in Sources */,
|
||||
50597BBF2B97D629004681E1 /* SearchableNameFinder.swift in Sources */,
|
||||
66FC638E29EDABAC00F00DAC /* SearchDisplayConfigurations.swift in Sources */,
|
||||
|
||||
@ -3,104 +3,39 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Contacts
|
||||
import Lottie
|
||||
import SignalServiceKit
|
||||
import SignalUI
|
||||
|
||||
public class ProvisioningPermissionsViewController: ProvisioningBaseViewController {
|
||||
|
||||
private let animationView = LottieAnimationView(name: "notificationPermission")
|
||||
|
||||
override public func loadView() {
|
||||
class ProvisioningPermissionsViewController: ProvisioningBaseViewController {
|
||||
override func loadView() {
|
||||
view = UIView()
|
||||
view.backgroundColor = Theme.backgroundColor
|
||||
view.addSubview(primaryView)
|
||||
primaryView.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
view.backgroundColor = Theme.backgroundColor
|
||||
|
||||
let titleText: String
|
||||
let explanationText: String
|
||||
let giveAccessText: String
|
||||
titleText = OWSLocalizedString(
|
||||
"LINKED_ONBOARDING_PERMISSIONS_TITLE",
|
||||
comment: "Title of the 'onboarding permissions' view."
|
||||
)
|
||||
explanationText = OWSLocalizedString(
|
||||
"LINKED_ONBOARDING_PERMISSIONS_EXPLANATION",
|
||||
comment: "Explanation in the 'onboarding permissions' view."
|
||||
)
|
||||
giveAccessText = OWSLocalizedString(
|
||||
"LINKED_ONBOARDING_PERMISSIONS_ENABLE_PERMISSIONS_BUTTON",
|
||||
comment: "Label for the 'give access' button in the 'onboarding permissions' view."
|
||||
)
|
||||
|
||||
let titleLabel = self.createTitleLabel(text: titleText)
|
||||
titleLabel.accessibilityIdentifier = "onboarding.permissions." + "titleLabel"
|
||||
titleLabel.setCompressionResistanceVerticalHigh()
|
||||
|
||||
let explanationLabel = self.createExplanationLabel(explanationText: explanationText)
|
||||
explanationLabel.accessibilityIdentifier = "onboarding.permissions." + "explanationLabel"
|
||||
explanationLabel.setCompressionResistanceVerticalHigh()
|
||||
|
||||
let giveAccessButton = self.primaryButton(title: giveAccessText, selector: #selector(giveAccessPressed))
|
||||
giveAccessButton.accessibilityIdentifier = "onboarding.permissions." + "giveAccessButton"
|
||||
giveAccessButton.setCompressionResistanceVerticalHigh()
|
||||
|
||||
animationView.loopMode = .playOnce
|
||||
animationView.backgroundBehavior = .pauseAndRestore
|
||||
animationView.contentMode = .scaleAspectFit
|
||||
animationView.setContentHuggingHigh()
|
||||
|
||||
let stackView = UIStackView(arrangedSubviews: [
|
||||
titleLabel,
|
||||
UIView.spacer(withHeight: 20),
|
||||
explanationLabel,
|
||||
UIView.spacer(withHeight: 60),
|
||||
animationView,
|
||||
UIView.vStretchingSpacer(minHeight: 80),
|
||||
ProvisioningBaseViewController.horizontallyWrap(primaryButton: giveAccessButton)
|
||||
])
|
||||
stackView.axis = .vertical
|
||||
stackView.alignment = .fill
|
||||
stackView.spacing = 0
|
||||
primaryView.addSubview(stackView)
|
||||
|
||||
stackView.autoPinEdgesToSuperviewMargins()
|
||||
let content = RegistrationPermissionsViewController(requestingContactsAuthorization: false, presenter: self)
|
||||
addChild(content)
|
||||
primaryView.addSubview(content.view)
|
||||
content.view.autoPinEdgesToSuperviewMargins()
|
||||
content.didMove(toParent: self)
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
self.animationView.play()
|
||||
}
|
||||
|
||||
// MARK: Requesting permissions
|
||||
|
||||
public func needsToAskForAnyPermissions() -> Guarantee<Bool> {
|
||||
return Guarantee<Bool> { resolve in
|
||||
func needsToAskForAnyPermissions() -> Guarantee<Bool> {
|
||||
Guarantee { resolve in
|
||||
UNUserNotificationCenter.current().getNotificationSettings { settings in
|
||||
resolve(settings.authorizationStatus == .notDetermined)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func requestPermissions() {
|
||||
extension ProvisioningPermissionsViewController: RegistrationPermissionsPresenter {
|
||||
func requestPermissions() async {
|
||||
Logger.info("")
|
||||
|
||||
// If you request any additional permissions, make sure to add them to
|
||||
// `needsToAskForAnyPermissions`.
|
||||
Task { @MainActor in
|
||||
await PushRegistrationManager.shared.registerUserNotificationSettings()
|
||||
self.provisioningController.provisioningPermissionsDidComplete(viewController: self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Events
|
||||
|
||||
@objc
|
||||
private func giveAccessPressed() {
|
||||
Logger.info("")
|
||||
|
||||
requestPermissions()
|
||||
await PushRegistrationManager.shared.registerUserNotificationSettings()
|
||||
provisioningController.provisioningPermissionsDidComplete(viewController: self)
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +147,9 @@ public class RegistrationNavigationController: OWSNavigationController {
|
||||
case .permissions:
|
||||
return Controller(
|
||||
type: RegistrationPermissionsViewController.self,
|
||||
make: RegistrationPermissionsViewController.init(presenter:),
|
||||
make: { presenter in
|
||||
RegistrationPermissionsViewController(requestingContactsAuthorization: true, presenter: presenter)
|
||||
},
|
||||
// The state never changes here. In theory we would build
|
||||
// state update support in the permissions controller,
|
||||
// but its overkill so we have not.
|
||||
|
||||
@ -12,26 +12,29 @@ protocol RegistrationPermissionsPresenter {
|
||||
func requestPermissions() async
|
||||
}
|
||||
|
||||
final class RegistrationPermissionsViewController: UIHostingController<RegistrationPermissionsView> {
|
||||
init(presenter: any RegistrationPermissionsPresenter) {
|
||||
super.init(rootView: RegistrationPermissionsView(presenter: presenter))
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
struct RegistrationPermissionsView: View {
|
||||
var requestingContactsAuthorization: Bool
|
||||
var presenter: any RegistrationPermissionsPresenter
|
||||
@State private var requestPermissions: RequestPermissionsTask?
|
||||
|
||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||
@AccessibleLayoutMetric private var headerPadding = 16
|
||||
@AccessibleLayoutMetric private var headerSpacing = 12
|
||||
@AccessibleLayoutMetric(scale: 0.5) private var sectionSpacing = 64
|
||||
|
||||
private var isCompactLayout: Bool {
|
||||
horizontalSizeClass == .compact || verticalSizeClass == .compact
|
||||
}
|
||||
|
||||
private var layoutMargins: EdgeInsets {
|
||||
if isCompactLayout {
|
||||
EdgeInsets(.layoutMarginsForRegistration(UIUserInterfaceSizeClass(horizontalSizeClass)))
|
||||
} else {
|
||||
EdgeInsets()
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
VStack(spacing: headerSpacing) {
|
||||
@ -44,7 +47,7 @@ struct RegistrationPermissionsView: View {
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, headerPadding)
|
||||
|
||||
ScrollView {
|
||||
ScrollableWhenCompact {
|
||||
VStack {
|
||||
Spacer(minLength: sectionSpacing)
|
||||
.frame(maxHeight: $sectionSpacing.rawValue)
|
||||
@ -59,14 +62,18 @@ struct RegistrationPermissionsView: View {
|
||||
PermissionIcon(.bellRing)
|
||||
}
|
||||
|
||||
PermissionDescription {
|
||||
Text(OWSLocalizedString("ONBOARDING_PERMISSIONS_CONTACTS_TITLE", comment: "Title introducing the 'Contacts' permission in the 'onboarding permissions' view."))
|
||||
} description: {
|
||||
Text(OWSLocalizedString("ONBOARDING_PERMISSIONS_CONTACTS_DESCRIPTION", comment: "Description of the 'Contacts' permission in the 'onboarding permissions' view."))
|
||||
} icon: {
|
||||
PermissionIcon(.personCircleLarge)
|
||||
if requestingContactsAuthorization {
|
||||
PermissionDescription {
|
||||
Text(OWSLocalizedString("ONBOARDING_PERMISSIONS_CONTACTS_TITLE", comment: "Title introducing the 'Contacts' permission in the 'onboarding permissions' view."))
|
||||
} description: {
|
||||
Text(OWSLocalizedString("ONBOARDING_PERMISSIONS_CONTACTS_DESCRIPTION", comment: "Description of the 'Contacts' permission in the 'onboarding permissions' view."))
|
||||
} icon: {
|
||||
PermissionIcon(.personCircleLarge)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Expand to available width when compact, otherwise horizontally center.
|
||||
.frame(maxWidth: isCompactLayout ? .infinity : nil, alignment: .leading)
|
||||
|
||||
Spacer(minLength: sectionSpacing)
|
||||
.layoutPriority(-1)
|
||||
@ -78,11 +85,16 @@ struct RegistrationPermissionsView: View {
|
||||
.dynamicTypeSize(...DynamicTypeSize.accessibility2)
|
||||
.frame(maxWidth: 400)
|
||||
}
|
||||
.padding(EdgeInsets(.layoutMarginsForRegistration(UIUserInterfaceSizeClass(horizontalSizeClass))))
|
||||
.padding(layoutMargins)
|
||||
}
|
||||
.scrollBounceBehaviorIfAvailable(.basedOnSize)
|
||||
}
|
||||
.foregroundStyle(Color.Signal.label, Color.Signal.secondaryLabel, Color.Signal.tertiaryLabel)
|
||||
// Use larger text size on iPad.
|
||||
.transformEnvironment(\.dynamicTypeSize) { dynamicTypeSize in
|
||||
if !isCompactLayout, dynamicTypeSize < .xLarge {
|
||||
dynamicTypeSize = .xLarge
|
||||
}
|
||||
}
|
||||
.dynamicTypeSize(...DynamicTypeSize.accessibility3)
|
||||
.minimumScaleFactor(0.9)
|
||||
.navigationBarBackButtonHidden()
|
||||
@ -150,7 +162,6 @@ private extension RegistrationPermissionsView {
|
||||
.foregroundStyle(.secondary)
|
||||
.layoutPriority(1)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,6 +192,17 @@ private extension RegistrationPermissionsView {
|
||||
}
|
||||
}
|
||||
|
||||
final class RegistrationPermissionsViewController: UIHostingController<RegistrationPermissionsView> {
|
||||
init(requestingContactsAuthorization: Bool, presenter: any RegistrationPermissionsPresenter) {
|
||||
super.init(rootView: RegistrationPermissionsView(requestingContactsAuthorization: requestingContactsAuthorization, presenter: presenter))
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private struct PreviewPermissionsPresenter: RegistrationPermissionsPresenter {
|
||||
func requestPermissions() async {
|
||||
@ -191,7 +213,7 @@ private struct PreviewPermissionsPresenter: RegistrationPermissionsPresenter {
|
||||
#Preview {
|
||||
VStack {
|
||||
Color.clear.frame(height: 44)
|
||||
RegistrationPermissionsView(presenter: PreviewPermissionsPresenter())
|
||||
RegistrationPermissionsView(requestingContactsAuthorization: true, presenter: PreviewPermissionsPresenter())
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -3937,15 +3937,6 @@
|
||||
/* Menu item and navbar title for the device manager */
|
||||
"LINKED_DEVICES_TITLE" = "Linked Devices";
|
||||
|
||||
/* Label for the 'give access' button in the 'onboarding permissions' view. */
|
||||
"LINKED_ONBOARDING_PERMISSIONS_ENABLE_PERMISSIONS_BUTTON" = "Allow Notifications";
|
||||
|
||||
/* Explanation in the 'onboarding permissions' view. */
|
||||
"LINKED_ONBOARDING_PERMISSIONS_EXPLANATION" = "Allowing notifications lets you know when you receive messages, calls or updates about new conversation activity.";
|
||||
|
||||
/* Title of the 'onboarding permissions' view. */
|
||||
"LINKED_ONBOARDING_PERMISSIONS_TITLE" = "Allow Notifications";
|
||||
|
||||
/* Alert Title */
|
||||
"LINKING_DEVICE_FAILED_TITLE" = "Linking Device Failed";
|
||||
|
||||
|
||||
27
SignalUI/SwiftUIExtensions/ScrollableWhenCompact.swift
Normal file
27
SignalUI/SwiftUIExtensions/ScrollableWhenCompact.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
public struct ScrollableWhenCompact<Content: View>: View {
|
||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||
|
||||
private var content: Content
|
||||
|
||||
public init(@ViewBuilder content: () -> Content) {
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
if verticalSizeClass == .compact {
|
||||
ScrollView {
|
||||
content
|
||||
}
|
||||
.scrollBounceBehaviorIfAvailable(.basedOnSize)
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user