diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 35c09f8df..fd2e204a1 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 2400888E239F30A600305217 /* SessionRestoreBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2400888D239F30A600305217 /* SessionRestoreBannerView.swift */; }; + 2400888E239F30A600305217 /* SessionRestorationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2400888D239F30A600305217 /* SessionRestorationView.swift */; }; 241C6314231F64C000B4198E /* JazzIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C630E231F5AAC00B4198E /* JazzIcon.swift */; }; 241C6315231F64CE00B4198E /* CGFloat+Rounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */; }; 241C6316231F64CE00B4198E /* UIColor+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C6310231F5C4400B4198E /* UIColor+Helper.swift */; }; @@ -710,7 +710,7 @@ 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1C93CF3971B64E8B6C1F9AC1 /* Pods-SignalShareExtension.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.test.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.test.xcconfig"; sourceTree = ""; }; 1CE3CD5C23334683BDD3D78C /* Pods-Signal.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Signal.test.xcconfig"; path = "Pods/Target Support Files/Pods-Signal/Pods-Signal.test.xcconfig"; sourceTree = ""; }; - 2400888D239F30A600305217 /* SessionRestoreBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionRestoreBannerView.swift; sourceTree = ""; }; + 2400888D239F30A600305217 /* SessionRestorationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionRestorationView.swift; sourceTree = ""; }; 241C630E231F5AAC00B4198E /* JazzIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JazzIcon.swift; sourceTree = ""; }; 241C6310231F5C4400B4198E /* UIColor+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Helper.swift"; sourceTree = ""; }; 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+Rounding.swift"; sourceTree = ""; }; @@ -2785,7 +2785,7 @@ B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */, B85357C023A1B81900AAF6CD /* SeedReminderViewDelegate.swift */, B8BB82B82394911B00BA5194 /* Separator.swift */, - 2400888D239F30A600305217 /* SessionRestoreBannerView.swift */, + 2400888D239F30A600305217 /* SessionRestorationView.swift */, B8CCF638239721E20091D419 /* TabBar.swift */, B8BB82B423947F2D00BA5194 /* TextField.swift */, ); @@ -3983,7 +3983,7 @@ B8162F0322891AD600D46544 /* FriendRequestView.swift in Sources */, 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */, 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */, - 2400888E239F30A600305217 /* SessionRestoreBannerView.swift in Sources */, + 2400888E239F30A600305217 /* SessionRestorationView.swift in Sources */, B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */, 4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */, 34EA69402194933900702471 /* MediaDownloadView.swift in Sources */, diff --git a/Signal/src/Loki/Components/FriendRequestView.swift b/Signal/src/Loki/Components/FriendRequestView.swift index f4a79c8f5..905bd6a4c 100644 --- a/Signal/src/Loki/Components/FriendRequestView.swift +++ b/Signal/src/Loki/Components/FriendRequestView.swift @@ -114,10 +114,10 @@ final class FriendRequestView : UIView { let format: String = { switch (message.friendRequestStatus) { case .none, .sendingOrFailed: preconditionFailure() - case .pending: return NSLocalizedString("%@ sent you a message request", comment: "") - case .accepted: return NSLocalizedString("You've accepted %@'s message request", comment: "") - case .declined: return NSLocalizedString("You've declined %@'s message request", comment: "") - case .expired: return NSLocalizedString("%@'s message request has expired", comment: "") + case .pending: return NSLocalizedString("%@ sent you a session request", comment: "") + case .accepted: return NSLocalizedString("You've accepted %@'s session request", comment: "") + case .declined: return NSLocalizedString("You've declined %@'s session request", comment: "") + case .expired: return NSLocalizedString("%@'s session request has expired", comment: "") default: preconditionFailure() } }() @@ -130,10 +130,10 @@ final class FriendRequestView : UIView { switch (message.friendRequestStatus) { case .none: preconditionFailure() case .sendingOrFailed: return nil - case .pending: return NSLocalizedString("You've sent %@ a message request", comment: "") - case .accepted: return NSLocalizedString("%@ accepted your message request", comment: "") + case .pending: return NSLocalizedString("You've sent %@ a session request", comment: "") + case .accepted: return NSLocalizedString("%@ accepted your session request", comment: "") case .declined: preconditionFailure() - case .expired: return NSLocalizedString("Your message request to %@ has expired", comment: "") + case .expired: return NSLocalizedString("Your session request to %@ has expired", comment: "") default: preconditionFailure() } }() diff --git a/Signal/src/Loki/Components/SessionRestorationView.swift b/Signal/src/Loki/Components/SessionRestorationView.swift new file mode 100644 index 000000000..bac9d4737 --- /dev/null +++ b/Signal/src/Loki/Components/SessionRestorationView.swift @@ -0,0 +1,84 @@ + +@objc(LKSessionRestorationView) +final class SessionRestorationView : UIView { + private let thread: TSThread + @objc public var onRestore: (() -> Void)? + @objc public var onDismiss: (() -> Void)? + + // MARK: Lifecycle + @objc init(thread: TSThread) { + self.thread = thread; + super.init(frame: CGRect.zero) + initialize() + } + + required init?(coder: NSCoder) { fatalError("Using SessionRestorationView.init(coder:) isn't allowed. Use SessionRestorationView.init(thread:) instead.") } + override init(frame: CGRect) { fatalError("Using SessionRestorationView.init(frame:) isn't allowed. Use SessionRestorationView.init(thread:) instead.") } + + private func initialize() { + // Set up background + backgroundColor = Colors.modalBackground + layer.cornerRadius = Values.modalCornerRadius + layer.masksToBounds = false + layer.borderColor = Colors.modalBorder.cgColor + layer.borderWidth = Values.borderThickness + layer.shadowColor = UIColor.black.cgColor + layer.shadowRadius = 8 + layer.shadowOpacity = 0.64 + // Set up title label + let titleLabel = UILabel() + titleLabel.textColor = Colors.text + titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize) + titleLabel.text = NSLocalizedString("Session Out of Sync", comment: "") + titleLabel.numberOfLines = 0 + titleLabel.lineBreakMode = .byWordWrapping + titleLabel.textAlignment = .center + // Set up explanation label + let explanationLabel = UILabel() + explanationLabel.textColor = Colors.text + explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) + explanationLabel.text = NSLocalizedString("Would you like to restore your session?", comment: "") + explanationLabel.numberOfLines = 0 + explanationLabel.textAlignment = .center + explanationLabel.lineBreakMode = .byWordWrapping + // Set up restore button + let restoreButton = UIButton() + restoreButton.set(.height, to: Values.mediumButtonHeight) + restoreButton.layer.cornerRadius = Values.modalButtonCornerRadius + restoreButton.backgroundColor = Colors.accent + restoreButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) + restoreButton.setTitleColor(Colors.text, for: UIControl.State.normal) + restoreButton.setTitle(NSLocalizedString("Restore", comment: ""), for: UIControl.State.normal) + restoreButton.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside) + // Set up dismiss button + let dismissButton = UIButton() + dismissButton.set(.height, to: Values.mediumButtonHeight) + dismissButton.layer.cornerRadius = Values.modalButtonCornerRadius + dismissButton.backgroundColor = Colors.buttonBackground + dismissButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) + dismissButton.setTitleColor(Colors.text, for: UIControl.State.normal) + dismissButton.setTitle(NSLocalizedString("Dismiss", comment: ""), for: UIControl.State.normal) + dismissButton.addTarget(self, action: #selector(dismiss), for: UIControl.Event.touchUpInside) + // Set up button stack view + let buttonStackView = UIStackView(arrangedSubviews: [ dismissButton, restoreButton ]) + buttonStackView.axis = .horizontal + buttonStackView.spacing = Values.mediumSpacing + buttonStackView.distribution = .fillEqually + // Set up main stack view + let mainStackView = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel, buttonStackView ]) + mainStackView.axis = .vertical + mainStackView.spacing = Values.smallSpacing + addSubview(mainStackView) + mainStackView.pin(to: self, withInset: Values.mediumSpacing) + // Update explanation label if possible + if let contactID = thread.contactIdentifier() { + let displayName = DisplayNameUtilities.getPrivateChatDisplayName(for: contactID) ?? contactID + explanationLabel.text = String(format: NSLocalizedString("Would you like to restore your session with %@?", comment: ""), displayName) + } + } + + // MARK: Interaction + @objc private func restore() { onRestore?() } + + @objc private func dismiss() { onDismiss?() } +} diff --git a/Signal/src/Loki/Components/SessionRestoreBannerView.swift b/Signal/src/Loki/Components/SessionRestoreBannerView.swift deleted file mode 100644 index fb9d683a9..000000000 --- a/Signal/src/Loki/Components/SessionRestoreBannerView.swift +++ /dev/null @@ -1,83 +0,0 @@ -@objc(LKSessionRestoreBannerView) -final class SessionRestoreBannerView : UIView { - private let thread: TSThread - @objc public var onRestore: (() -> Void)? - @objc public var onDismiss: (() -> Void)? - - private lazy var bannerView: UIView = { - let bannerView = UIView.container() - bannerView.backgroundColor = UIColor.lokiGray() - bannerView.layer.cornerRadius = 2.5; - - // Use a shadow to "pop" the indicator above the other views. - bannerView.layer.shadowColor = UIColor.black.cgColor - bannerView.layer.shadowOffset = CGSize(width: 2, height: 3) - bannerView.layer.shadowRadius = 2 - bannerView.layer.shadowOpacity = 0.35 - return bannerView - }() - - private lazy var label: UILabel = { - let result = UILabel() - result.textColor = UIColor.white - result.font = UIFont.ows_dynamicTypeSubheadlineClamped - result.numberOfLines = 0 - result.textAlignment = .center - result.lineBreakMode = .byWordWrapping - return result - }() - - private lazy var buttonStackView: UIStackView = { - let result = UIStackView() - result.axis = .horizontal - result.distribution = .fillEqually - return result - }() - - private lazy var buttonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight() - private lazy var buttonHeight = buttonFont.pointSize * 48 / 17 - - // MARK: Lifecycle - @objc init(thread: TSThread) { - self.thread = thread; - super.init(frame: CGRect.zero) - initialize() - } - - required init?(coder: NSCoder) { fatalError("Using SessionRestoreBannerView.init(coder:) isn't allowed. Use SessionRestoreBannerView.init(thread:) instead.") } - override init(frame: CGRect) { fatalError("Using SessionRestoreBannerView.init(frame:) isn't allowed. Use SessionRestoreBannerView.init(thread:) instead.") } - - private func initialize() { - // Set up UI - let mainStackView = UIStackView() - mainStackView.axis = .vertical - mainStackView.distribution = .fill - mainStackView.addArrangedSubview(label) - mainStackView.addArrangedSubview(buttonStackView) - - let restoreButton = OWSFlatButton.button(title: NSLocalizedString("Restore session", comment: ""), font: buttonFont, titleColor: .ows_materialBlue, backgroundColor: .white, target: self, selector:#selector(restore)) - restoreButton.setBackgroundColors(upColor: .clear, downColor: .clear) - restoreButton.autoSetDimension(.height, toSize: buttonHeight) - buttonStackView.addArrangedSubview(restoreButton) - - let dismissButton = OWSFlatButton.button(title: NSLocalizedString("DISMISS_BUTTON_TEXT", comment: ""), font: buttonFont, titleColor: .ows_white, backgroundColor: .white, target: self, selector:#selector(dismiss)) - dismissButton.setBackgroundColors(upColor: .clear, downColor: .clear) - dismissButton.autoSetDimension(.height, toSize: buttonHeight) - buttonStackView.addArrangedSubview(dismissButton) - - bannerView.addSubview(mainStackView) - mainStackView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 16, left: 16, bottom: 8, right: 16)) - - addSubview(bannerView) - bannerView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)) - - if let contactID = thread.contactIdentifier() { - let displayName = Environment.shared.contactsManager.profileName(forRecipientId: contactID) ?? contactID - label.text = String(format: NSLocalizedString("Would you like to start a new session with %@?", comment: ""), displayName) - } - } - - @objc private func restore() { onRestore?() } - - @objc private func dismiss() { onDismiss?() } -} diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index f559c10e5..c8ee5809f 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -1015,12 +1015,12 @@ typedef enum : NSUInteger { if (isContactThread) { TSContactThread *thread = (TSContactThread *)self.thread; if (thread.sessionRestoreDevices.count > 0) { - if (!self.restoreSessionBannerView) { - LKSessionRestoreBannerView *bannerView = [[LKSessionRestoreBannerView alloc] initWithThread:thread]; + if (self.restoreSessionBannerView == nil) { + LKSessionRestorationView *bannerView = [[LKSessionRestorationView alloc] initWithThread:thread]; [self.view addSubview:bannerView]; - [bannerView autoPinEdgeToSuperviewEdge:ALEdgeTop]; - [bannerView autoPinEdgeToSuperviewEdge:ALEdgeLeft]; - [bannerView autoPinEdgeToSuperviewEdge:ALEdgeRight]; + [bannerView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:LKValues.mediumSpacing]; + [bannerView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:LKValues.largeSpacing]; + [bannerView autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:LKValues.mediumSpacing]; [self.view layoutSubviews]; self.restoreSessionBannerView = bannerView; [bannerView setOnRestore:^{ @@ -1674,7 +1674,7 @@ typedef enum : NSUInteger { isAttachmentButtonHidden = false; } [self.inputToolbar setUserInteractionEnabled:isEnabled]; - NSString *placeholderText = isEnabled ? NSLocalizedString(@"Message", "") : NSLocalizedString(@"Pending message request", ""); + NSString *placeholderText = isEnabled ? NSLocalizedString(@"Message", "") : NSLocalizedString(@"Pending session request", ""); [self.inputToolbar setPlaceholderText:placeholderText]; [self.inputToolbar setAttachmentButtonHidden:isAttachmentButtonHidden]; } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index b874c7f0e..37dcdc3b0 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2573,20 +2573,20 @@ "Calculating proof of work" = "Calculating proof of work"; "Failed to calculate proof of work." = "Failed to calculate proof of work."; "Share Public Key" = "Share Public Key"; -"%@ sent you a message request" = "%@ sent you a message request"; +"%@ sent you a session request" = "%@ sent you a session request"; "Accept" = "Accept"; "Decline" = "Decline"; -"Pending message request" = "Pending message request"; +"Pending session request" = "Pending session request"; "New Message" = "New Message"; "Secure session reset in progress" = "Secure session reset in progress"; "Secure session reset done" = "Secure session reset done"; "Session" = "Session"; -"You've sent %@ a message request" = "You've sent %@ a message request"; -"You've declined %@'s message request" = "You've declined %@'s message request"; -"You've accepted %@'s message request" = "You've accepted %@'s message request"; -"%@ accepted your message request" = "%@ accepted your message request"; -"%@'s message request has expired" = "%@'s message request has expired"; -"Your message request to %@ has expired" = "Your message request to %@ has expired"; +"You've sent %@ a session request" = "You've sent %@ a session request"; +"You've declined %@'s session request" = "You've declined %@'s session request"; +"You've accepted %@'s session request" = "You've accepted %@'s session request"; +"%@ accepted your session request" = "%@ accepted your session request"; +"%@'s session request has expired" = "%@'s session request has expired"; +"Your session request to %@ has expired" = "Your session request to %@ has expired"; "Show Seed" = "Show Seed"; "Your Seed" = "Your Seed"; "Require Touch ID, Face ID or your device passcode to unlock Session’s screen. You can still receive notifications when Screen Lock is enabled. Use Session’s notification settings to customise the information displayed in notifications." = "Require Touch ID, Face ID or your device passcode to unlock Session’s screen. You can still receive notifications when Screen Lock is enabled. Use Session’s notification settings to customise the information displayed in notifications."; @@ -2774,3 +2774,8 @@ "Are you sure? This cannot be undone." = "Are you sure? This cannot be undone."; "When enabled, messages between you and %@ will disappear after they have been seen." = "When enabled, messages between you and %@ will disappear after they have been seen."; "This will be your name when you use Session." = "This will be your name when you use Session."; +"Session Out of Sync" = "Session Out of Sync"; +"Would you like to restore your session?" = "Would you like to restore your session?"; +"Would you like to restore your session with %@?" = "Would you like to restore your session with %@?"; +"Restore" = "Restore"; +"Dismiss" = "Dismiss";