Add "Enable Optimize Storage" toggle to "Welcome to Backups" sheet
This commit is contained in:
parent
39780d4bc7
commit
0206e8c487
@ -147,7 +147,7 @@ class BackupSettingsViewController:
|
||||
),
|
||||
hasBackupFailed: backupFailureStateManager.hasFailedBackup(tx: tx),
|
||||
isBackgroundAppRefreshDisabled: Self.isBackgroundAppRefreshDisabled(),
|
||||
isOptimizeStorageEnabled: remoteConfig.currentConfig().isOptimizeStorageEnabled,
|
||||
isOptimizeStorageRemoteConfigEnabled: remoteConfig.currentConfig().isOptimizeStorageEnabled,
|
||||
)
|
||||
|
||||
return viewModel
|
||||
@ -621,28 +621,88 @@ class BackupSettingsViewController:
|
||||
final class WelcomeToBackupsSheet: HeroSheetViewController {
|
||||
override var canBeDismissed: Bool { false }
|
||||
|
||||
init(onConfirm: @escaping () -> Void) {
|
||||
init(
|
||||
optimizeLocalStorage: (isOn: Bool, onValueChanged: (Bool) -> Void)?,
|
||||
onConfirm: @escaping (HeroSheetViewController) -> Void,
|
||||
) {
|
||||
let toggle: HeroSheetViewController.Body.Toggle?
|
||||
if let (isOn, onValueChanged) = optimizeLocalStorage {
|
||||
toggle = HeroSheetViewController.Body.Toggle(
|
||||
title: OWSLocalizedString(
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_OPTIMIZE_MEDIA_TOGGLE_TITLE",
|
||||
comment: "Title for a toggle shown after the user enables backups, letting them enable the Optimize Storage feature.",
|
||||
),
|
||||
footer: OWSLocalizedString(
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_OPTIMIZE_MEDIA_TOGGLE_FOOTER",
|
||||
comment: "Footer for a toggle shown after the user enables backups, letting them enable the Optimize Storage feature.",
|
||||
),
|
||||
isOn: isOn,
|
||||
onValueChanged: onValueChanged,
|
||||
)
|
||||
} else {
|
||||
toggle = nil
|
||||
}
|
||||
|
||||
super.init(
|
||||
hero: .image(.backupsSubscribed),
|
||||
title: OWSLocalizedString(
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_TITLE",
|
||||
comment: "Title for a sheet shown after the user enables backups.",
|
||||
),
|
||||
body: OWSLocalizedString(
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_MESSAGE",
|
||||
comment: "Message for a sheet shown after the user enables backups.",
|
||||
),
|
||||
primaryButton: HeroSheetViewController.Button(
|
||||
title: CommonStrings.okButton,
|
||||
action: { _ in onConfirm() },
|
||||
body: HeroSheetViewController.Body(
|
||||
textContent: .plain(OWSLocalizedString(
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_MESSAGE",
|
||||
comment: "Message for a sheet shown after the user enables backups.",
|
||||
)),
|
||||
toggle: toggle,
|
||||
),
|
||||
primary: .button(HeroSheetViewController.Button(
|
||||
title: OWSLocalizedString(
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_BUTTON_TITLE",
|
||||
comment: "Title for a button in a sheet shown after the user enables backups.",
|
||||
),
|
||||
action: { onConfirm($0) },
|
||||
)),
|
||||
secondary: nil,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let welcomeToBackupsSheet = WelcomeToBackupsSheet { [self] in
|
||||
viewModel.performManualBackup()
|
||||
dismiss(animated: true)
|
||||
let backupPlan = db.read { tx in
|
||||
backupPlanManager.backupPlan(tx: tx)
|
||||
}
|
||||
|
||||
let welcomeToBackupsSheet: WelcomeToBackupsSheet
|
||||
switch backupPlan {
|
||||
case _ where !viewModel.isOptimizeStorageRemoteConfigEnabled,
|
||||
.disabled,
|
||||
.disabling,
|
||||
.free:
|
||||
welcomeToBackupsSheet = WelcomeToBackupsSheet(
|
||||
optimizeLocalStorage: nil,
|
||||
onConfirm: { sheet in
|
||||
sheet.dismiss(animated: true) { [self] in
|
||||
viewModel.performManualBackup()
|
||||
}
|
||||
},
|
||||
)
|
||||
case .paid,
|
||||
.paidAsTester,
|
||||
.paidExpiringSoon:
|
||||
var isOptimizeStorageEnabled = true
|
||||
|
||||
welcomeToBackupsSheet = WelcomeToBackupsSheet(
|
||||
optimizeLocalStorage: (
|
||||
isOn: isOptimizeStorageEnabled,
|
||||
onValueChanged: { isOptimizeStorageEnabled = $0 },
|
||||
),
|
||||
onConfirm: { sheet in
|
||||
sheet.dismiss(animated: true) { [self] in
|
||||
setOptimizeLocalStorage(isOptimizeStorageEnabled)
|
||||
viewModel.performManualBackup()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
present(welcomeToBackupsSheet, animated: true)
|
||||
@ -1021,35 +1081,33 @@ class BackupSettingsViewController:
|
||||
// MARK: -
|
||||
|
||||
fileprivate func setOptimizeLocalStorage(_ newOptimizeLocalStorage: Bool) {
|
||||
let isPaidPlanTester: Bool = db.write { tx in
|
||||
let hasMadeAtLeastOneBackup: Bool = db.write { tx in
|
||||
let currentBackupPlan = backupPlanManager.backupPlan(tx: tx)
|
||||
let newBackupPlan: BackupPlan
|
||||
let isPaidPlanTester: Bool
|
||||
let lastBackupDetails = backupSettingsStore.lastBackupDetails(tx: tx)
|
||||
|
||||
let newBackupPlan: BackupPlan
|
||||
switch currentBackupPlan {
|
||||
case .disabled, .disabling, .free:
|
||||
owsFailDebug("Shouldn't be setting Optimize Local Storage: \(currentBackupPlan)")
|
||||
return false
|
||||
owsFail("Shouldn't be setting Optimize Local Storage: \(currentBackupPlan)")
|
||||
case .paid:
|
||||
newBackupPlan = .paid(optimizeLocalStorage: newOptimizeLocalStorage)
|
||||
isPaidPlanTester = false
|
||||
case .paidExpiringSoon:
|
||||
newBackupPlan = .paidExpiringSoon(optimizeLocalStorage: newOptimizeLocalStorage)
|
||||
isPaidPlanTester = false
|
||||
case .paidAsTester:
|
||||
newBackupPlan = .paidAsTester(optimizeLocalStorage: newOptimizeLocalStorage)
|
||||
isPaidPlanTester = true
|
||||
}
|
||||
|
||||
backupPlanManager.setBackupPlan(newBackupPlan, tx: tx)
|
||||
return isPaidPlanTester
|
||||
return lastBackupDetails != nil
|
||||
}
|
||||
|
||||
// If disabling Optimize Local Storage, offer to start downloads now.
|
||||
if !newOptimizeLocalStorage {
|
||||
if
|
||||
hasMadeAtLeastOneBackup,
|
||||
!newOptimizeLocalStorage
|
||||
{
|
||||
// If disabling Optimize Local Storage with media potentially
|
||||
// offloaded, offer to start downloads now.
|
||||
showDownloadOffloadedMediaSheet()
|
||||
} else if isPaidPlanTester {
|
||||
showOffloadedMediaForTestersWarningSheet(onAcknowledge: {})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1088,44 +1146,15 @@ class BackupSettingsViewController:
|
||||
presentActionSheet(actionSheet)
|
||||
}
|
||||
|
||||
private func showOffloadedMediaForTestersWarningSheet(
|
||||
onAcknowledge: @escaping () -> Void,
|
||||
) {
|
||||
let actionSheet = ActionSheetController(
|
||||
title: OWSLocalizedString(
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TESTER_WARNING_SHEET_TITLE",
|
||||
comment: "Title for an action sheet warning users who are testers about the Optimize Local Storage feature.",
|
||||
),
|
||||
message: OWSLocalizedString(
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TESTER_WARNING_SHEET_MESSAGE",
|
||||
comment: "Message for an action sheet warning users who are testers about the Optimize Local Storage feature.",
|
||||
),
|
||||
)
|
||||
actionSheet.addAction(ActionSheetAction(
|
||||
title: CommonStrings.okButton,
|
||||
handler: { _ in
|
||||
onAcknowledge()
|
||||
},
|
||||
))
|
||||
|
||||
presentActionSheet(actionSheet)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
fileprivate func setIsBackupDownloadQueueSuspended(_ isSuspended: Bool, backupPlan: BackupPlan) {
|
||||
if isSuspended {
|
||||
switch backupPlan {
|
||||
case .disabled, .disabling, .free, .paid:
|
||||
case .disabled, .disabling, .free, .paid, .paidAsTester:
|
||||
db.write { tx in
|
||||
backupSettingsStore.setIsBackupDownloadQueueSuspended(true, tx: tx)
|
||||
}
|
||||
case .paidAsTester:
|
||||
showOffloadedMediaForTestersWarningSheet(onAcknowledge: { [self] in
|
||||
db.write { tx in
|
||||
backupSettingsStore.setIsBackupDownloadQueueSuspended(true, tx: tx)
|
||||
}
|
||||
})
|
||||
case .paidExpiringSoon:
|
||||
let warningSheet = ActionSheetController(
|
||||
title: OWSLocalizedString(
|
||||
@ -1542,7 +1571,9 @@ private class BackupSettingsViewModel: ObservableObject {
|
||||
/// from running.)
|
||||
@Published var isBackgroundAppRefreshDisabled: Bool
|
||||
|
||||
@Published var isOptimizeStorageEnabled: Bool
|
||||
/// Whether the "Optimze Storage" feature is available to this user, per
|
||||
/// remote config. Not to be confused with `isOptimizeLocalStorageAvailable`.
|
||||
@Published var isOptimizeStorageRemoteConfigEnabled: Bool
|
||||
|
||||
weak var actionsDelegate: ActionsDelegate?
|
||||
|
||||
@ -1560,7 +1591,7 @@ private class BackupSettingsViewModel: ObservableObject {
|
||||
mediaTierCapacityOverflow: UInt64?,
|
||||
hasBackupFailed: Bool,
|
||||
isBackgroundAppRefreshDisabled: Bool,
|
||||
isOptimizeStorageEnabled: Bool,
|
||||
isOptimizeStorageRemoteConfigEnabled: Bool,
|
||||
) {
|
||||
self.backupSubscriptionConfiguration = backupSubscriptionConfiguration
|
||||
|
||||
@ -1581,7 +1612,7 @@ private class BackupSettingsViewModel: ObservableObject {
|
||||
self.hasBackupFailed = hasBackupFailed
|
||||
self.isBackgroundAppRefreshDisabled = isBackgroundAppRefreshDisabled
|
||||
|
||||
self.isOptimizeStorageEnabled = isOptimizeStorageEnabled
|
||||
self.isOptimizeStorageRemoteConfigEnabled = isOptimizeStorageRemoteConfigEnabled
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
@ -1640,7 +1671,9 @@ private class BackupSettingsViewModel: ObservableObject {
|
||||
|
||||
// MARK: -
|
||||
|
||||
var optimizeLocalStorageAvailable: Bool {
|
||||
/// Whether the "Optimze Storage" feature is available, per the current
|
||||
/// `BackupPlan`. Not to be confused with `isOptimizeStorageRemoteConfigEnabled`.
|
||||
var isOptimizeLocalStorageAvailable: Bool {
|
||||
switch backupPlan {
|
||||
case .disabled, .disabling, .free:
|
||||
false
|
||||
@ -1649,7 +1682,7 @@ private class BackupSettingsViewModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
var optimizeLocalStorage: Bool {
|
||||
var isOptimizeLocalStorageEnabled: Bool {
|
||||
switch backupPlan {
|
||||
case .disabled, .disabling, .free:
|
||||
false
|
||||
@ -1922,29 +1955,21 @@ struct BackupSettingsView: View {
|
||||
viewModel: viewModel,
|
||||
)
|
||||
|
||||
if viewModel.isOptimizeStorageEnabled {
|
||||
if viewModel.isOptimizeStorageRemoteConfigEnabled {
|
||||
Toggle(
|
||||
OWSLocalizedString(
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TOGGLE_TITLE",
|
||||
comment: "Title for a toggle allowing users to change the Optimize Local Storage setting.",
|
||||
),
|
||||
isOn: Binding(
|
||||
get: { viewModel.optimizeLocalStorage },
|
||||
get: { viewModel.isOptimizeLocalStorageEnabled },
|
||||
set: { viewModel.setOptimizeLocalStorage($0) },
|
||||
),
|
||||
).disabled(!viewModel.optimizeLocalStorageAvailable)
|
||||
).disabled(!viewModel.isOptimizeLocalStorageAvailable)
|
||||
}
|
||||
} footer: {
|
||||
if viewModel.isOptimizeStorageEnabled {
|
||||
let footerText: String = if
|
||||
viewModel.optimizeLocalStorageAvailable,
|
||||
viewModel.isPaidPlanTester
|
||||
{
|
||||
OWSLocalizedString(
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TOGGLE_FOOTER_AVAILABLE_FOR_TESTERS",
|
||||
comment: "Footer for a toggle allowing users to change the Optimize Local Storage setting, if the toggle is available and they are a tester.",
|
||||
)
|
||||
} else if viewModel.optimizeLocalStorageAvailable {
|
||||
if viewModel.isOptimizeStorageRemoteConfigEnabled {
|
||||
let footerText: String = if viewModel.isOptimizeLocalStorageAvailable {
|
||||
OWSLocalizedString(
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TOGGLE_FOOTER_AVAILABLE",
|
||||
comment: "Footer for a toggle allowing users to change the Optimize Local Storage setting, if the toggle is available.",
|
||||
@ -3178,7 +3203,7 @@ private extension BackupSettingsViewModel {
|
||||
mediaTierCapacityOverflow: mediaTierCapacityOverflow,
|
||||
hasBackupFailed: hasBackupFailed,
|
||||
isBackgroundAppRefreshDisabled: isBackgroundAppRefreshDisabled,
|
||||
isOptimizeStorageEnabled: false,
|
||||
isOptimizeStorageRemoteConfigEnabled: true,
|
||||
)
|
||||
let actionsDelegate = PreviewActionsDelegate()
|
||||
viewModel.actionsDelegate = actionsDelegate
|
||||
|
||||
@ -988,23 +988,14 @@
|
||||
/* Title for an action sheet allowing users to download their offloaded media. */
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_DOWNLOAD_SHEET_TITLE" = "Download Offloaded Media?";
|
||||
|
||||
/* Message for an action sheet warning users who are testers about the Optimize Local Storage feature. */
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TESTER_WARNING_SHEET_MESSAGE" = "Media that is offloaded will be available as long as you are using TestFlight. Make sure to download all your media before leaving the test program.";
|
||||
|
||||
/* Title for an action sheet warning users who are testers about the Optimize Local Storage feature. */
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TESTER_WARNING_SHEET_TITLE" = "Testing Offloaded Media";
|
||||
|
||||
/* Footer for a toggle allowing users to change the Optimize Local Storage setting, if the toggle is available. */
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TOGGLE_FOOTER_AVAILABLE" = "Unused media will be offloaded, but can be downloaded from your backup anytime.";
|
||||
|
||||
/* Footer for a toggle allowing users to change the Optimize Local Storage setting, if the toggle is available and they are a tester. */
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TOGGLE_FOOTER_AVAILABLE_FOR_TESTERS" = "Unused media will be offloaded, but can be downloaded from your backup anytime while you are using TestFlight.";
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TOGGLE_FOOTER_AVAILABLE" = "Older media will be offloaded, but can be downloaded from your backup anytime.";
|
||||
|
||||
/* Footer for a toggle allowing users to change the Optimize Local Storage setting, if the toggle is unavailable. */
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TOGGLE_FOOTER_UNAVAILABLE" = "Storage optimization can only be used with the paid tier of Signal Backups. Upgrade your backup plan to start using this feature.";
|
||||
|
||||
/* Title for a toggle allowing users to change the Optimize Local Storage setting. */
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TOGGLE_TITLE" = "Optimize On-Device Storage";
|
||||
"BACKUP_SETTINGS_OPTIMIZE_LOCAL_STORAGE_TOGGLE_TITLE" = "Optimize Signal Storage";
|
||||
|
||||
/* Subtitle for a notification telling the user they are out of remote storage space. */
|
||||
"BACKUP_SETTINGS_OUT_OF_STORAGE_SPACE_NOTIFICATION_SUBTITLE" = "You’ve reached your backup storage limit. Free up space in Signal to continue backing up chats and media.";
|
||||
@ -1060,11 +1051,20 @@
|
||||
/* Subtitle for a progress bar tracking active uploading. */
|
||||
"BACKUP_SETTINGS_UPLOAD_PROGRESS_SUBTITLE_RUNNING_GENERIC" = "Uploading…";
|
||||
|
||||
/* Title for a button in a sheet shown after the user enables backups. */
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_BUTTON_TITLE" = "Back Up Now";
|
||||
|
||||
/* Message for a sheet shown after the user enables backups. */
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_MESSAGE" = "Depending on the size of your backup, this could take a long time. You can use Signal as you normally do while the backup takes place.";
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_MESSAGE" = "This could take a while. You can use Signal normally while backing up.";
|
||||
|
||||
/* Footer for a toggle shown after the user enables backups, letting them enable the Optimize Storage feature. */
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_OPTIMIZE_MEDIA_TOGGLE_FOOTER" = "Older media will be offloaded, but can be downloaded from your backup anytime.";
|
||||
|
||||
/* Title for a toggle shown after the user enables backups, letting them enable the Optimize Storage feature. */
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_OPTIMIZE_MEDIA_TOGGLE_TITLE" = "Optimize Signal Storage";
|
||||
|
||||
/* Title for a sheet shown after the user enables backups. */
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_TITLE" = "Welcome to Signal Secure Backups. Start your backup now.";
|
||||
"BACKUP_SETTINGS_WELCOME_TO_BACKUPS_SHEET_TITLE" = "You're all set. Start your backup now.";
|
||||
|
||||
/* Message for a sheet shown when your Backup subscription fails to renew. */
|
||||
"BACKUP_SUBSCRIPTION_FAILED_TO_RENEW_SHEET_MESSAGE" = "Check to make sure your payment method is up to date. Tap Manage Subscription > Signal > Update Payment Method.";
|
||||
|
||||
@ -114,7 +114,6 @@ class BackupPlanManagerImpl: BackupPlanManager {
|
||||
let oldBackupPlan = backupPlan(tx: tx)
|
||||
|
||||
guard oldBackupPlan != newBackupPlan else {
|
||||
logger.warn("Attempting to set BackupPlan to existing value: aborting. \(oldBackupPlan)")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -38,16 +38,19 @@ open class HeroSheetViewController: StackSheetViewController {
|
||||
}
|
||||
|
||||
public struct Toggle {
|
||||
public let text: String
|
||||
public let title: String
|
||||
public let footer: String?
|
||||
public let isOn: Bool
|
||||
public let onValueChanged: (_ isEnabled: Bool) -> Void
|
||||
|
||||
public init(
|
||||
text: String,
|
||||
title: String,
|
||||
footer: String?,
|
||||
isOn: Bool,
|
||||
onValueChanged: @escaping (_ isEnabled: Bool) -> Void,
|
||||
) {
|
||||
self.text = text
|
||||
self.title = title
|
||||
self.footer = footer
|
||||
self.isOn = isOn
|
||||
self.onValueChanged = onValueChanged
|
||||
}
|
||||
@ -305,14 +308,15 @@ open class HeroSheetViewController: StackSheetViewController {
|
||||
}
|
||||
|
||||
private func viewForToggle(_ toggle: Body.Toggle) -> UIView {
|
||||
let label = UILabel()
|
||||
label.text = toggle.text
|
||||
label.font = .dynamicTypeSubheadline
|
||||
label.textAlignment = .natural
|
||||
label.numberOfLines = 0
|
||||
label.textColor = .Signal.label
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.text = toggle.title
|
||||
titleLabel.font = .dynamicTypeSubheadline
|
||||
titleLabel.textAlignment = .natural
|
||||
titleLabel.numberOfLines = 0
|
||||
titleLabel.textColor = .Signal.label
|
||||
|
||||
let toggleSwitch = UISwitch()
|
||||
toggleSwitch.setCompressionResistanceHigh()
|
||||
toggleSwitch.isOn = toggle.isOn
|
||||
toggleSwitch.addAction(
|
||||
UIAction { [weak self] action in
|
||||
@ -327,19 +331,41 @@ open class HeroSheetViewController: StackSheetViewController {
|
||||
for: .valueChanged,
|
||||
)
|
||||
|
||||
let containerView = PillView()
|
||||
containerView.backgroundColor = .Signal.tertiaryBackground
|
||||
containerView.layoutMargins = UIEdgeInsets(hMargin: 20, vMargin: 16)
|
||||
containerView.addSubview(label)
|
||||
containerView.addSubview(toggleSwitch)
|
||||
let pillView = PillView()
|
||||
pillView.backgroundColor = .Signal.tertiaryBackground
|
||||
pillView.layoutMargins = UIEdgeInsets(hMargin: 20, vMargin: 16)
|
||||
pillView.addSubview(titleLabel)
|
||||
pillView.addSubview(toggleSwitch)
|
||||
|
||||
label.autoPinEdges(toSuperviewMarginsExcludingEdge: .trailing)
|
||||
titleLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .trailing)
|
||||
|
||||
toggleSwitch.autoPinEdge(.leading, to: .trailing, of: label, withOffset: 16, relation: .greaterThanOrEqual)
|
||||
toggleSwitch.autoPinEdge(.leading, to: .trailing, of: titleLabel, withOffset: 16, relation: .greaterThanOrEqual)
|
||||
toggleSwitch.autoPinEdge(toSuperviewMargin: .trailing)
|
||||
toggleSwitch.autoVCenterInSuperview()
|
||||
|
||||
return containerView
|
||||
if let footer = toggle.footer {
|
||||
let footerLabel = UILabel()
|
||||
footerLabel.text = footer
|
||||
footerLabel.font = .dynamicTypeFootnote
|
||||
footerLabel.textAlignment = .natural
|
||||
footerLabel.numberOfLines = 0
|
||||
footerLabel.textColor = .Signal.secondaryLabel
|
||||
|
||||
let pillAndFooterContainer = UIView()
|
||||
pillAndFooterContainer.addSubview(pillView)
|
||||
pillAndFooterContainer.addSubview(footerLabel)
|
||||
|
||||
pillView.autoPinEdges(toSuperviewEdgesExcludingEdge: .bottom)
|
||||
footerLabel.autoPinEdge(.top, to: .bottom, of: pillView, withOffset: 8)
|
||||
footerLabel.autoPinEdgesToSuperviewEdges(
|
||||
with: UIEdgeInsets(hMargin: 20, vMargin: 0),
|
||||
excludingEdge: .top,
|
||||
)
|
||||
|
||||
return pillAndFooterContainer
|
||||
} else {
|
||||
return pillView
|
||||
}
|
||||
}
|
||||
|
||||
private func viewForElement(_ element: Element) -> UIView {
|
||||
@ -413,14 +439,36 @@ open class HeroSheetViewController: StackSheetViewController {
|
||||
}
|
||||
|
||||
@available(iOS 17, *)
|
||||
#Preview("Body w/toggle") {
|
||||
#Preview("Body w/toggle-and-footer") {
|
||||
SheetPreviewViewController(sheet: HeroSheetViewController(
|
||||
hero: .image(UIImage(named: "toggle-32")!),
|
||||
title: nil,
|
||||
title: "Feeding Boots the cat",
|
||||
body: HeroSheetViewController.Body(
|
||||
textContent: .plain(#"Give Boots extra dinner? He'd like you to know he's "extra hungry" tonight."#),
|
||||
toggle: HeroSheetViewController.Body.Toggle(
|
||||
text: "Extra Food?",
|
||||
title: "Extra dinner?",
|
||||
footer: "Side effects may include sleepiness and increased insistence that he receive extra food in the future.",
|
||||
isOn: true,
|
||||
onValueChanged: { enabled in
|
||||
print(enabled ? "😸" : "😾")
|
||||
},
|
||||
),
|
||||
),
|
||||
primary: .button(.dismissing(title: "Order Up")),
|
||||
secondary: nil,
|
||||
))
|
||||
}
|
||||
|
||||
@available(iOS 17, *)
|
||||
#Preview("Body w/long-text toggle") {
|
||||
SheetPreviewViewController(sheet: HeroSheetViewController(
|
||||
hero: .image(UIImage(named: "toggle-32")!),
|
||||
title: "Feeding Boots the Cat",
|
||||
body: HeroSheetViewController.Body(
|
||||
textContent: .plain(#"Give Boots extra dinner? He'd like you to know he's "extra hungry" tonight."#),
|
||||
toggle: HeroSheetViewController.Body.Toggle(
|
||||
title: "Give Boots extra dinner? Side effects may include sleepiness and increased insistence that he receive extra food in the future.",
|
||||
footer: nil,
|
||||
isOn: true,
|
||||
onValueChanged: { enabled in
|
||||
print(enabled ? "😸" : "😾")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user