Merge branch 'mkirk/upgrade-experience'
This commit is contained in:
commit
79f0c14e29
23
Signal/Images.xcassets/introductory_splash_profile.imageset/Contents.json
vendored
Normal file
23
Signal/Images.xcassets/introductory_splash_profile.imageset/Contents.json
vendored
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
BIN
Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile.png
vendored
Normal file
BIN
Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile@2x.png
vendored
Normal file
BIN
Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
BIN
Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile@3x.png
vendored
Normal file
BIN
Signal/Images.xcassets/introductory_splash_profile.imageset/introductory_splash_profile@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
@ -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"
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -4,6 +4,94 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
private class IntroductingProfilesExperienceUpgradeViewController: ExperienceUpgradeViewController {
|
||||
|
||||
override func loadView() {
|
||||
self.view = UIView()
|
||||
|
||||
/// Create Views
|
||||
|
||||
// Title label
|
||||
let titleLabel = UILabel()
|
||||
view.addSubview(titleLabel)
|
||||
titleLabel.text = header
|
||||
titleLabel.textAlignment = .center
|
||||
titleLabel.font = UIFont.ows_regularFont(withSize: ScaleFromIPhone5(24))
|
||||
titleLabel.textColor = UIColor.white
|
||||
titleLabel.minimumScaleFactor = 0.5
|
||||
titleLabel.adjustsFontSizeToFitWidth = true
|
||||
|
||||
// Body label
|
||||
let bodyLabel = UILabel()
|
||||
self.bodyLabel = bodyLabel
|
||||
view.addSubview(bodyLabel)
|
||||
bodyLabel.text = body
|
||||
bodyLabel.font = UIFont.ows_lightFont(withSize: ScaleFromIPhone5To7Plus(17, 22))
|
||||
bodyLabel.textColor = UIColor.black
|
||||
bodyLabel.numberOfLines = 0
|
||||
bodyLabel.lineBreakMode = .byWordWrapping
|
||||
bodyLabel.textAlignment = .center
|
||||
|
||||
// Image
|
||||
let imageView = UIImageView(image: image)
|
||||
view.addSubview(imageView)
|
||||
imageView.contentMode = .scaleAspectFit
|
||||
|
||||
// Button
|
||||
let button = UIButton()
|
||||
view.addSubview(button)
|
||||
let buttonTitle = NSLocalizedString("UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_BUTTON", comment: "button label shown one time, after user upgrades app")
|
||||
button.setTitle(buttonTitle, for: .normal)
|
||||
button.setTitleColor(UIColor.white, for: .normal)
|
||||
button.backgroundColor = UIColor.ows_materialBlue()
|
||||
|
||||
button.isUserInteractionEnabled = true
|
||||
button.addTarget(self, action:#selector(didTapButton), for: .touchUpInside)
|
||||
button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
|
||||
|
||||
button.titleLabel?.font = UIFont.ows_mediumFont(withSize: ScaleFromIPhone5(18))
|
||||
|
||||
/// Layout Views
|
||||
|
||||
// Image layout
|
||||
imageView.autoAlignAxis(toSuperviewAxis: .vertical)
|
||||
imageView.autoPinToSquareAspectRatio()
|
||||
imageView.autoPinEdge(.top, to: .bottom, of: titleLabel, withOffset: ScaleFromIPhone5To7Plus(36, 40))
|
||||
imageView.autoSetDimension(.height, toSize: ScaleFromIPhone5(225))
|
||||
|
||||
// Title label layout
|
||||
titleLabel.autoSetDimension(.height, toSize: ScaleFromIPhone5(40))
|
||||
titleLabel.autoPinWidthToSuperview(withMargin: ScaleFromIPhone5To7Plus(16, 24))
|
||||
titleLabel.autoPinEdge(toSuperviewEdge: .top)
|
||||
|
||||
// Body label layout
|
||||
bodyLabel.autoPinEdge(.top, to: .bottom, of: imageView, withOffset: ScaleFromIPhone5To7Plus(18, 28))
|
||||
bodyLabel.autoPinWidthToSuperview(withMargin: bodyMargin)
|
||||
bodyLabel.sizeToFit()
|
||||
|
||||
// Button layout
|
||||
button.autoPinEdge(.top, to: .bottom, of: bodyLabel, withOffset: ScaleFromIPhone5(18))
|
||||
button.autoPinWidthToSuperview(withMargin: ScaleFromIPhone5(32))
|
||||
button.autoPinEdge(toSuperviewEdge: .bottom, withInset: ScaleFromIPhone5(16))
|
||||
button.autoSetDimension(.height, toSize: ScaleFromIPhone5(36))
|
||||
}
|
||||
|
||||
// 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 +167,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
|
||||
@ -102,19 +190,22 @@ private class ExperienceUpgradeViewController: OWSViewController {
|
||||
|
||||
/// Layout Views
|
||||
|
||||
// Image layout
|
||||
imageView.autoAlignAxis(toSuperviewAxis: .vertical)
|
||||
imageView.autoPinToSquareAspectRatio()
|
||||
imageView.autoPinEdge(.top, to: .bottom, of: titleLabel, withOffset: ScaleFromIPhone5To7Plus(36, 60))
|
||||
imageView.autoSetDimension(.height, toSize: ScaleFromIPhone5(225))
|
||||
|
||||
// Title label layout
|
||||
titleLabel.autoSetDimension(.height, toSize: ScaleFromIPhone5(40))
|
||||
titleLabel.autoPinWidthToSuperview(withMargin: ScaleFromIPhone5To7Plus(16, 24))
|
||||
titleLabel.autoPinEdge(toSuperviewEdge: .top)
|
||||
|
||||
// Body label layout
|
||||
bodyLabel.autoPinEdge(.top, to: .bottom, of: imageView, withOffset: ScaleFromIPhone5To7Plus(18, 28))
|
||||
bodyLabel.autoPinWidthToSuperview(withMargin: bodyMargin)
|
||||
bodyLabel.sizeToFit()
|
||||
|
||||
// Image layout
|
||||
imageView.autoPinWidthToSuperview()
|
||||
imageView.autoSetDimension(.height, toSize: ScaleFromIPhone5To7Plus(200, 280))
|
||||
imageView.autoPinEdge(.top, to: .bottom, of: titleLabel, withOffset: ScaleFromIPhone5To7Plus(24, 32))
|
||||
imageView.autoPinEdge(.bottom, to: .top, of: bodyLabel, withOffset: -ScaleFromIPhone5To7Plus(18, 28))
|
||||
bodyLabel.autoPinEdge(toSuperviewEdge: .bottom, withInset: ScaleFromIPhone5(16))
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,7 +213,7 @@ func setPageControlAppearance() {
|
||||
if #available(iOS 9.0, *) {
|
||||
let pageControl = UIPageControl.appearance(whenContainedInInstancesOf: [UIPageViewController.self])
|
||||
pageControl.pageIndicatorTintColor = UIColor.lightGray
|
||||
pageControl.currentPageIndicatorTintColor = UIColor.white
|
||||
pageControl.currentPageIndicatorTintColor = UIColor.ows_materialBlue()
|
||||
} else {
|
||||
// iOS8 won't see the page controls =(
|
||||
}
|
||||
@ -185,20 +276,14 @@ class ExperienceUpgradesPageViewController: OWSViewController, UIPageViewControl
|
||||
view.addSubview(headerBackgroundView)
|
||||
headerBackgroundView.backgroundColor = UIColor.ows_materialBlue()
|
||||
|
||||
// Footer Background
|
||||
|
||||
let footerBackgroundView = UIView()
|
||||
view.addSubview(footerBackgroundView)
|
||||
footerBackgroundView.backgroundColor = UIColor.ows_materialBlue()
|
||||
|
||||
// Dismiss button
|
||||
let dismissButton = UIButton()
|
||||
view.addSubview(dismissButton)
|
||||
dismissButton.setTitle(CommonStrings.dismissButton, for: .normal)
|
||||
dismissButton.setTitleColor(UIColor.white, for: .normal)
|
||||
dismissButton.setTitleColor(UIColor.ows_signalBrandBlue(), for: .normal)
|
||||
dismissButton.isUserInteractionEnabled = true
|
||||
dismissButton.addTarget(self, action:#selector(didTapDismissButton), for: .touchUpInside)
|
||||
dismissButton.titleLabel?.font = UIFont.ows_mediumFont(withSize: ScaleFromIPhone5(20))
|
||||
dismissButton.titleLabel?.font = UIFont.ows_mediumFont(withSize: ScaleFromIPhone5(16))
|
||||
let dismissInsetValue: CGFloat = ScaleFromIPhone5(10)
|
||||
dismissButton.contentEdgeInsets = UIEdgeInsets(top: dismissInsetValue, left: dismissInsetValue, bottom: dismissInsetValue, right: dismissInsetValue)
|
||||
|
||||
@ -216,11 +301,6 @@ class ExperienceUpgradesPageViewController: OWSViewController, UIPageViewControl
|
||||
headerBackgroundView.autoPinEdge(toSuperviewEdge: .top)
|
||||
headerBackgroundView.autoSetDimension(.height, toSize: ScaleFromIPhone5(80))
|
||||
|
||||
// Footer Background layout
|
||||
footerBackgroundView.autoPinWidthToSuperview()
|
||||
footerBackgroundView.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
footerBackgroundView.autoSetDimension(.height, toSize: ScaleFromIPhone5(95))
|
||||
|
||||
// Dismiss button layout
|
||||
dismissButton.autoHCenterInSuperview()
|
||||
dismissButton.autoPinEdge(toSuperviewEdge: .bottom, withInset: ScaleFromIPhone5(10))
|
||||
@ -230,7 +310,7 @@ class ExperienceUpgradesPageViewController: OWSViewController, UIPageViewControl
|
||||
// negative inset so as to overlay the header text in the carousel view with the header background which
|
||||
// lives outside of the carousel. We do this so that the user can't bounce past the page view controllers
|
||||
// width limits, exposing the edge of the header.
|
||||
carouselView.autoPinEdge(.top, to: .bottom, of: headerBackgroundView, withOffset: ScaleFromIPhone5(-35))
|
||||
carouselView.autoPin(toTopLayoutGuideOf: self, withInset: ScaleFromIPhone5To7Plus(14, 24))
|
||||
carouselView.autoPinEdge(.bottom, to: .top, of: dismissButton, withOffset: ScaleFromIPhone5(-10))
|
||||
}
|
||||
|
||||
@ -301,6 +381,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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -47,6 +47,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
||||
@property (nonatomic) UISegmentedControl *segmentedControl;
|
||||
@property (nonatomic) id previewingContext;
|
||||
@property (nonatomic) NSSet<NSString *> *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
|
||||
|
||||
@ -6,27 +6,45 @@ import Foundation
|
||||
|
||||
enum ExperienceUpgradeId: String {
|
||||
case videoCalling = "001",
|
||||
callKit = "002"
|
||||
callKit = "002",
|
||||
introducingProfiles = "003"
|
||||
}
|
||||
|
||||
class ExperienceUpgradeFinder: NSObject {
|
||||
public let TAG = "[ExperienceUpgradeFinder]"
|
||||
|
||||
var videoCalling: ExperienceUpgrade {
|
||||
return ExperienceUpgrade(uniqueId: ExperienceUpgradeId.videoCalling.rawValue,
|
||||
title: NSLocalizedString("UPGRADE_EXPERIENCE_VIDEO_TITLE", comment: "Header for upgrade experience"),
|
||||
body: NSLocalizedString("UPGRADE_EXPERIENCE_VIDEO_DESCRIPTION", comment: "Description of video calling to upgrading (existing) users"),
|
||||
image: #imageLiteral(resourceName: "introductory_splash_video_calling"))
|
||||
}
|
||||
|
||||
var callKit: ExperienceUpgrade {
|
||||
return ExperienceUpgrade(uniqueId: ExperienceUpgradeId.callKit.rawValue,
|
||||
title: NSLocalizedString("UPGRADE_EXPERIENCE_CALLKIT_TITLE", comment: "Header for upgrade experience"),
|
||||
body: NSLocalizedString("UPGRADE_EXPERIENCE_CALLKIT_DESCRIPTION", comment: "Description of CallKit to upgrading (existing) users"),
|
||||
image: #imageLiteral(resourceName: "introductory_splash_callkit"))
|
||||
}
|
||||
|
||||
var introducingProfiles: ExperienceUpgrade {
|
||||
return 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"))
|
||||
}
|
||||
|
||||
// Keep these ordered by increasing uniqueId.
|
||||
private var allExperienceUpgrades: [ExperienceUpgrade] {
|
||||
var upgrades = [ExperienceUpgrade(uniqueId: ExperienceUpgradeId.videoCalling.rawValue,
|
||||
title: NSLocalizedString("UPGRADE_EXPERIENCE_VIDEO_TITLE", comment: "Header for upgrade experience"),
|
||||
body: NSLocalizedString("UPGRADE_EXPERIENCE_VIDEO_DESCRIPTION", comment: "Description of video calling to upgrading (existing) users"),
|
||||
image: #imageLiteral(resourceName: "introductory_splash_video_calling"))]
|
||||
|
||||
if UIDevice.current.supportsCallKit {
|
||||
upgrades.append(ExperienceUpgrade(uniqueId: ExperienceUpgradeId.callKit.rawValue,
|
||||
title: NSLocalizedString("UPGRADE_EXPERIENCE_CALLKIT_TITLE", comment: "Header for upgrade experience"),
|
||||
body: NSLocalizedString("UPGRADE_EXPERIENCE_CALLKIT_DESCRIPTION", comment: "Description of CallKit to upgrading (existing) users"),
|
||||
image: #imageLiteral(resourceName: "introductory_splash_callkit")))
|
||||
}
|
||||
|
||||
return upgrades
|
||||
return [
|
||||
// Disable old experience upgrades. Most people have seen them by now, and accomodating multiple makes layout harder.
|
||||
// Note if we ever want to show multiple experience upgrades again
|
||||
// we'll have to update the layout in ExperienceUpgradesPageViewController
|
||||
//
|
||||
// videoCalling,
|
||||
// (UIDevice.current.supportsCallKit ? callKit : nil),
|
||||
introducingProfiles
|
||||
].flatMap { $0 }
|
||||
}
|
||||
|
||||
// MARK: - Instance Methods
|
||||
|
||||
@ -8,7 +8,6 @@ class ExperienceUpgrade: TSYapDatabaseObject {
|
||||
let title: String
|
||||
let body: String
|
||||
let image: UIImage?
|
||||
var seenAt: Date?
|
||||
|
||||
required init(uniqueId: String, title: String, body: String, image: UIImage) {
|
||||
self.title = title
|
||||
@ -61,9 +60,4 @@ class ExperienceUpgrade: TSYapDatabaseObject {
|
||||
return super.storageBehaviorForProperty(withKey: propertyKey)
|
||||
}
|
||||
}
|
||||
|
||||
func markAsSeen(transaction: YapDatabaseReadWriteTransaction) {
|
||||
self.seenAt = Date()
|
||||
super.save(with: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1109,10 +1109,10 @@
|
||||
"PROFILE_VIEW_PROFILE_AVATAR_FIELD" = "Avatar";
|
||||
|
||||
/* Description of the user profile. */
|
||||
"PROFILE_VIEW_PROFILE_DESCRIPTION" = "Your profile affects how you will appear to other users if you are not in their contacts.";
|
||||
"PROFILE_VIEW_PROFILE_DESCRIPTION" = "Your Signal Profile will be visible to your contacts, when you initiate new conversations, and when you share it with other users and groups.";
|
||||
|
||||
/* Link to more information about the user profile. */
|
||||
"PROFILE_VIEW_PROFILE_DESCRIPTION_LINK" = "Click here to learn more.";
|
||||
"PROFILE_VIEW_PROFILE_DESCRIPTION_LINK" = "Tap here to learn more.";
|
||||
|
||||
/* Label for the profile name field of the profile view. */
|
||||
"PROFILE_VIEW_PROFILE_NAME_FIELD" = "Profile Name";
|
||||
@ -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";
|
||||
|
||||
/* Description of new profile feature for upgrading (existing) users */
|
||||
"UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_DESCRIPTION" = "You can now share a profile 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.";
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user