Restore missing content during iPad permissions onboarding

This commit is contained in:
Adam Sharp 2024-09-16 13:59:32 -04:00
parent c63c0302aa
commit 79112ddcb0
No known key found for this signature in database
6 changed files with 93 additions and 112 deletions

View File

@ -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 */,

View File

@ -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)
}
}

View File

@ -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.

View File

@ -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

View File

@ -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";

View 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
}
}
}