diff --git a/Signal/Images.xcassets/introductory_splash_profile.imageset/Contents.json b/Signal/Images.xcassets/introductory_splash_profile.imageset/Contents.json new file mode 100644 index 000000000..e6a3d1e58 --- /dev/null +++ b/Signal/Images.xcassets/introductory_splash_profile.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "introductory_splash_profile.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "introductory_splash_profile@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "introductory_splash_profile@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile.png b/Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile.png new file mode 100644 index 000000000..76492d660 Binary files /dev/null and b/Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile.png differ diff --git a/Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile@2x.png b/Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile@2x.png new file mode 100644 index 000000000..7bb3e0075 Binary files /dev/null and b/Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile@2x.png differ diff --git a/Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile@3x.png b/Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile@3x.png new file mode 100644 index 000000000..05cded095 Binary files /dev/null and b/Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile@3x.png differ diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index 4eecc6337..61b979666 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -24,6 +24,7 @@ #import "OWSViewController.h" #import "OWSWebRTCDataProtos.pb.h" #import "PrivacySettingsTableViewController.h" +#import "ProfileViewController.h" #import "PropertyListPreferences.h" #import "PushManager.h" #import "Release.h" diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m b/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m index db6296fc0..ec66c4a93 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m @@ -53,6 +53,10 @@ NS_ASSUME_NONNULL_BEGIN actionBlock:^{ [DebugUIMisc setManualCensorshipCircumventionEnabled:NO]; }]]; + [items addObject:[OWSTableItem itemWithTitle:@"Clear experience upgrades (works once per launch)" + actionBlock:^{ + [ExperienceUpgrade removeAllObjectsInCollection]; + }]]; [items addObject:[OWSTableItem itemWithTitle:@"Clear hasDismissedOffers" actionBlock:^{ [DebugUIMisc clearHasDismissedOffers]; diff --git a/Signal/src/ViewControllers/ExperienceUpgradesPageViewController.swift b/Signal/src/ViewControllers/ExperienceUpgradesPageViewController.swift index 309d38cde..eebb54827 100644 --- a/Signal/src/ViewControllers/ExperienceUpgradesPageViewController.swift +++ b/Signal/src/ViewControllers/ExperienceUpgradesPageViewController.swift @@ -4,6 +4,45 @@ import Foundation +private class IntroductingProfilesExperienceUpgradeViewController: ExperienceUpgradeViewController { + + override func loadView() { + super.loadView() + assert(view != nil) + assert(bodyLabel != nil) + + // Privacy Settings Button + let button = UIButton() + view.addSubview(button) + let privacyTitle = NSLocalizedString("UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_BUTTON", comment: "button label shown one time, after user upgrades app") + button.setTitle(privacyTitle, for: .normal) + button.setTitleColor(UIColor.ows_signalBrandBlue(), for: .normal) + button.isUserInteractionEnabled = true + button.addTarget(self, action:#selector(didTapButton), for: .touchUpInside) + button.titleLabel?.font = bodyLabel.font + + // Privacy Settings Button layout + button.autoPinWidthToSuperview(withMargin: bodyMargin) + button.autoPinEdge(.top, to: .bottom, of: bodyLabel, withOffset: ScaleFromIPhone5(12)) + button.sizeToFit() + } + + // MARK: - Actions + + func didTapButton(sender: UIButton) { + Logger.debug("\(TAG) in \(#function)") + + // dismiss the modally presented view controller, then proceed. + experienceUpgradesPageViewController.dismiss(animated: true) { + guard let fromViewController = UIApplication.shared.frontmostViewController as? SignalsViewController else { + owsFail("unexpected frontmostViewController: \(String(describing: UIApplication.shared.frontmostViewController))") + return + } + ProfileViewController.presentForUpgradeOrNag(from: fromViewController) + } + } +} + private class CallKitExperienceUpgradeViewController: ExperienceUpgradeViewController { override func loadView() { @@ -79,7 +118,7 @@ private class ExperienceUpgradeViewController: OWSViewController { view.addSubview(titleLabel) titleLabel.text = header titleLabel.textAlignment = .center - titleLabel.font = UIFont.ows_regularFont(withSize: ScaleFromIPhone5To7Plus(26, 32)) + titleLabel.font = UIFont.ows_regularFont(withSize: ScaleFromIPhone5(24)) titleLabel.textColor = UIColor.white titleLabel.minimumScaleFactor = 0.5 titleLabel.adjustsFontSizeToFitWidth = true @@ -301,6 +340,8 @@ class ExperienceUpgradesPageViewController: OWSViewController, UIPageViewControl switch identifier { case .callKit: return CallKitExperienceUpgradeViewController(experienceUpgrade: experienceUpgrade, experienceUpgradesPageViewController: self) + case .introducingProfiles: + return IntroductingProfilesExperienceUpgradeViewController(experienceUpgrade: experienceUpgrade, experienceUpgradesPageViewController: self) default: return ExperienceUpgradeViewController(experienceUpgrade: experienceUpgrade, experienceUpgradesPageViewController: self) } diff --git a/Signal/src/ViewControllers/ProfileViewController.h b/Signal/src/ViewControllers/ProfileViewController.h index 88764363a..ca6f3b462 100644 --- a/Signal/src/ViewControllers/ProfileViewController.h +++ b/Signal/src/ViewControllers/ProfileViewController.h @@ -14,7 +14,8 @@ NS_ASSUME_NONNULL_BEGIN + (void)presentForAppSettings:(UINavigationController *)navigationController; + (void)presentForRegistration:(UINavigationController *)navigationController; -+ (void)presentForUpgradeOrNag:(SignalsViewController *)presentingController; ++ (void)presentForUpgradeOrNag:(SignalsViewController *)presentingController + NS_SWIFT_NAME(presentForUpgradeOrNag(from:)); @end diff --git a/Signal/src/ViewControllers/SignalsViewController.m b/Signal/src/ViewControllers/SignalsViewController.m index 57f6be088..7433a0b53 100644 --- a/Signal/src/ViewControllers/SignalsViewController.m +++ b/Signal/src/ViewControllers/SignalsViewController.m @@ -47,6 +47,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; @property (nonatomic) UISegmentedControl *segmentedControl; @property (nonatomic) id previewingContext; @property (nonatomic) NSSet *blockedPhoneNumberSet; +@property (nonatomic) BOOL viewHasEverAppeared; @property (nonatomic) BOOL isViewVisible; @property (nonatomic) BOOL isAppInBackground; @@ -491,9 +492,11 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; // Start running the disappearing messages job in case the newly registered user // enables this feature [[OWSDisappearingMessagesJob sharedJob] startIfNecessary]; - } else { + } else if (!self.viewHasEverAppeared) { [self displayAnyUnseenUpgradeExperience]; } + + self.viewHasEverAppeared = YES; } #pragma mark - startup diff --git a/Signal/src/environment/ExperienceUpgradeFinder.swift b/Signal/src/environment/ExperienceUpgradeFinder.swift index 053677961..5d0c268a6 100644 --- a/Signal/src/environment/ExperienceUpgradeFinder.swift +++ b/Signal/src/environment/ExperienceUpgradeFinder.swift @@ -6,7 +6,8 @@ import Foundation enum ExperienceUpgradeId: String { case videoCalling = "001", - callKit = "002" + callKit = "002", + introducingProfiles = "003" } class ExperienceUpgradeFinder: NSObject { @@ -26,6 +27,11 @@ class ExperienceUpgradeFinder: NSObject { image: #imageLiteral(resourceName: "introductory_splash_callkit"))) } + upgrades.append(ExperienceUpgrade(uniqueId: ExperienceUpgradeId.introducingProfiles.rawValue, + title: NSLocalizedString("UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_TITLE", comment: "Header for upgrade experience"), + body: NSLocalizedString("UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_DESCRIPTION", comment: "Description of new profile feature for upgrading (existing) users"), + image:#imageLiteral(resourceName: "introductory_splash_profile"))) + return upgrades } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 8cdef9d5b..da25399a0 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1516,6 +1516,15 @@ /* Header for upgrade experience */ "UPGRADE_EXPERIENCE_CALLKIT_TITLE" = "Just Swipe to Answer"; +/* button label shown one time, after user upgrades app */ +"UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_BUTTON" = "Set up your profile now."; + +/* Description of new profile feature for upgrading (existing) users */ +"UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_DESCRIPTION" = "You can now share a photo and name with your friends on Signal."; + +/* Header for upgrade experience */ +"UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_TITLE" = "Ready for Your Closeup?"; + /* Description of video calling to upgrading (existing) users */ "UPGRADE_EXPERIENCE_VIDEO_DESCRIPTION" = "Signal now supports secure video calling. Just start a call like normal, tap the camera button, and wave hello.";