diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index c423f2c19..3758930ff 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -140,6 +140,7 @@ 7B0EFDF0275084AA00FFAAE7 /* CallMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B0EFDEF275084AA00FFAAE7 /* CallMessageCell.swift */; }; 7B0EFDF2275449AA00FFAAE7 /* TSInfoMessage+Calls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B0EFDF1275449AA00FFAAE7 /* TSInfoMessage+Calls.swift */; }; 7B0EFDF4275490EA00FFAAE7 /* ringing.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 7B0EFDF3275490EA00FFAAE7 /* ringing.mp3 */; }; + 7B0EFDF62755CC5400FFAAE7 /* CallMissedTipsModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B0EFDF52755CC5400FFAAE7 /* CallMissedTipsModal.swift */; }; 7B1581E2271E743B00848B49 /* OWSSounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1581E1271E743B00848B49 /* OWSSounds.swift */; }; 7B1581E4271FC59D00848B49 /* CallModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1581E3271FC59C00848B49 /* CallModal.swift */; }; 7B1581E6271FD2A100848B49 /* VideoPreviewVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1581E5271FD2A100848B49 /* VideoPreviewVC.swift */; }; @@ -1133,6 +1134,7 @@ 7B0EFDEF275084AA00FFAAE7 /* CallMessageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallMessageCell.swift; sourceTree = ""; }; 7B0EFDF1275449AA00FFAAE7 /* TSInfoMessage+Calls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSInfoMessage+Calls.swift"; sourceTree = ""; }; 7B0EFDF3275490EA00FFAAE7 /* ringing.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = ringing.mp3; sourceTree = ""; }; + 7B0EFDF52755CC5400FFAAE7 /* CallMissedTipsModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallMissedTipsModal.swift; sourceTree = ""; }; 7B1581E1271E743B00848B49 /* OWSSounds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSSounds.swift; sourceTree = ""; }; 7B1581E3271FC59C00848B49 /* CallModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallModal.swift; sourceTree = ""; }; 7B1581E5271FD2A100848B49 /* VideoPreviewVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPreviewVC.swift; sourceTree = ""; }; @@ -2084,6 +2086,7 @@ 7B7CB18D270D066F0079FF93 /* IncomingCallBanner.swift */, 7B7CB18F270FB2150079FF93 /* MiniCallView.swift */, 7B1581E727210ECC00848B49 /* RenderView.swift */, + 7B0EFDF52755CC5400FFAAE7 /* CallMissedTipsModal.swift */, ); path = "Views & Modals"; sourceTree = ""; @@ -4899,6 +4902,7 @@ B84A89BC25DE328A0040017D /* ProfilePictureVC.swift in Sources */, 34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */, 451166C01FD86B98000739BA /* AccountManager.swift in Sources */, + 7B0EFDF62755CC5400FFAAE7 /* CallMissedTipsModal.swift in Sources */, C374EEF425DB31D40073A857 /* VoiceMessageRecordingView.swift in Sources */, 7B1581E6271FD2A100848B49 /* VideoPreviewVC.swift in Sources */, B83F2B88240CB75A000A54AB /* UIImage+Scaling.swift in Sources */, diff --git a/Session/Calls/Views & Modals/CallMissedTipsModal.swift b/Session/Calls/Views & Modals/CallMissedTipsModal.swift new file mode 100644 index 000000000..9c4227398 --- /dev/null +++ b/Session/Calls/Views & Modals/CallMissedTipsModal.swift @@ -0,0 +1,57 @@ +import UIKit + +@objc +final class CallMissedTipsModal : Modal { + private let caller: String + + // MARK: Lifecycle + @objc + init(caller: String) { + self.caller = caller + super.init(nibName: nil, bundle: nil) + self.modalPresentationStyle = .overFullScreen + self.modalTransitionStyle = .crossDissolve + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(onCallEnabled:) instead.") + } + + override init(nibName: String?, bundle: Bundle?) { + preconditionFailure("Use init(onCallEnabled:) instead.") + } + + override func populateContentView() { + // Tips icon + let tipsIconImageView = UIImageView(image: UIImage(named: "Tips")?.withTint(Colors.text)) + tipsIconImageView.set(.width, to: 19) + tipsIconImageView.set(.height, to: 28) + // Title + let titleLabel = UILabel() + titleLabel.textColor = Colors.text + titleLabel.font = .boldSystemFont(ofSize: Values.largeFontSize) + titleLabel.text = NSLocalizedString("modal_call_missed_tips_title", comment: "") + titleLabel.textAlignment = .center + // Message + let messageLabel = UILabel() + messageLabel.textColor = Colors.text + messageLabel.font = .systemFont(ofSize: Values.smallFontSize) + let message = String(format: NSLocalizedString("modal_call_missed_tips_explanation", comment: ""), caller) + messageLabel.text = message + messageLabel.numberOfLines = 0 + messageLabel.lineBreakMode = .byWordWrapping + messageLabel.textAlignment = .natural + // Cancel Button + cancelButton.setTitle(NSLocalizedString("OK", comment: ""), for: .normal) + // Main stack view + let mainStackView = UIStackView(arrangedSubviews: [ tipsIconImageView, titleLabel, messageLabel, cancelButton ]) + mainStackView.axis = .vertical + mainStackView.alignment = .center + mainStackView.spacing = Values.largeSpacing + contentView.addSubview(mainStackView) + mainStackView.pin(.leading, to: .leading, of: contentView, withInset: Values.largeSpacing) + mainStackView.pin(.top, to: .top, of: contentView, withInset: Values.largeSpacing) + contentView.pin(.trailing, to: .trailing, of: mainStackView, withInset: Values.largeSpacing) + contentView.pin(.bottom, to: .bottom, of: mainStackView, withInset: Values.largeSpacing) + } +} diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index edfa8f2c2..59ec4e3ca 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -55,10 +55,12 @@ extension AppDelegate { return infoMessage } - private func showMissedCallTipsIfNeeded() { + private func showMissedCallTipsIfNeeded(caller: String) { let userDefaults = UserDefaults.standard guard !userDefaults[.hasSeenCallMissedTips] else { return } - + guard let presentingVC = CurrentAppContext().frontmostViewController() else { preconditionFailure() } + let callMissedTipsModal = CallMissedTipsModal(caller: caller) + presentingVC.present(callMissedTipsModal, animated: true, completion: nil) userDefaults[.hasSeenCallMissedTips] = true } @@ -75,7 +77,10 @@ extension AppDelegate { guard SSKPreferences.areCallsEnabled else { let infoMessage = self.insertCallInfoMessage(for: message, using: transaction) infoMessage.updateCallInfoMessage(.missed, using: transaction) - self.showMissedCallTipsIfNeeded() + let contactName = Storage.shared.getContact(with: message.sender!, using: transaction)?.displayName(for: Contact.Context.regular) ?? message.sender! + DispatchQueue.main.async { + self.showMissedCallTipsIfNeeded(caller: contactName) + } return } let callManager = AppEnvironment.shared.callManager diff --git a/Session/Meta/Images.xcassets/Session/Tips.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Tips.imageset/Contents.json new file mode 100644 index 000000000..aba19f854 --- /dev/null +++ b/Session/Meta/Images.xcassets/Session/Tips.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Tips.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Session/Meta/Images.xcassets/Session/Tips.imageset/Tips.pdf b/Session/Meta/Images.xcassets/Session/Tips.imageset/Tips.pdf new file mode 100644 index 000000000..2818f54cb Binary files /dev/null and b/Session/Meta/Images.xcassets/Session/Tips.imageset/Tips.pdf differ diff --git a/Session/Meta/Translations/en.lproj/Localizable.strings b/Session/Meta/Translations/en.lproj/Localizable.strings index 5af272ffc..964cdd237 100644 --- a/Session/Meta/Translations/en.lproj/Localizable.strings +++ b/Session/Meta/Translations/en.lproj/Localizable.strings @@ -596,3 +596,5 @@ "light_mode_theme" = "Light"; "PIN_BUTTON_TEXT" = "Pin"; "UNPIN_BUTTON_TEXT" = "Unpin"; +"modal_call_missed_tips_title" = "Call missed"; +"modal_call_missed_tips_explanation" = "Call missed from '%@' because you needed to enable the 'Voice and video calls' permission in the Privacy Settings.";