diff --git a/Signal/ConversationView/Components/CVComponentDelegate.swift b/Signal/ConversationView/Components/CVComponentDelegate.swift index 83672689ef..f5bba9c98a 100644 --- a/Signal/ConversationView/Components/CVComponentDelegate.swift +++ b/Signal/ConversationView/Components/CVComponentDelegate.swift @@ -279,6 +279,8 @@ public protocol CVComponentDelegate: AnyObject, AudioMessageViewDelegate, CVPoll func didTapViewPoll(pollInteractionUniqueId: String) func didTapViewPinnedMessage(pinnedMessageUniqueId: String) + + func didTapSafetyTips() } // MARK: - diff --git a/Signal/ConversationView/Components/CVComponentThreadDetails.swift b/Signal/ConversationView/Components/CVComponentThreadDetails.swift index 613064b249..1e7ea3bc7b 100644 --- a/Signal/ConversationView/Components/CVComponentThreadDetails.swift +++ b/Signal/ConversationView/Components/CVComponentThreadDetails.swift @@ -234,8 +234,8 @@ public class CVComponentThreadDetails: CVComponentBase, CVRootComponent { safetyButtonLabelConfig.applyForRendering(button: showTipsButton) showTipsButton.ows_contentEdgeInsets = .init(hMargin: hPaddingSafetyButton, vMargin: vPaddingSafetyButton) showTipsButton.dimsWhenHighlighted = true - showTipsButton.block = { [weak self] in - self?.didShowTips(type: safetySection.threadType) + showTipsButton.block = { [weak componentDelegate] in + componentDelegate?.didTapSafetyTips() } if conversationStyle.hasWallpaper { @@ -704,7 +704,7 @@ public class CVComponentThreadDetails: CVComponentBase, CVRootComponent { safetySection.shouldShowSafetyTipsButton, componentView.showTipsButton.bounds.contains(sender.location(in: componentView.showTipsButton)) { - didShowTips(type: safetySection.threadType) + componentDelegate.didTapSafetyTips() return true } @@ -808,12 +808,6 @@ public class CVComponentThreadDetails: CVComponentBase, CVRootComponent { } extension CVComponentThreadDetails { - - private func didShowTips(type: SafetyTipsType) { - let viewController = SafetyTipsViewController(type: type) - UIApplication.shared.frontmostViewController?.present(viewController, animated: true) - } - private static func buildGroupsSafetySection( from groupThread: TSGroupThread, tx: DBReadTransaction, diff --git a/Signal/ConversationView/ConversationViewController+CVComponentDelegate.swift b/Signal/ConversationView/ConversationViewController+CVComponentDelegate.swift index edca612561..793d1e5ff8 100644 --- a/Signal/ConversationView/ConversationViewController+CVComponentDelegate.swift +++ b/Signal/ConversationView/ConversationViewController+CVComponentDelegate.swift @@ -1449,6 +1449,21 @@ extension ConversationViewController: CVComponentDelegate { isAnimated: true, ) } + + public func didTapSafetyTips() { + let viewController = SafetyTipsViewController() + viewController.delegate = self + present(viewController, animated: true) + } +} + +// MARK: - SafetyTipsViewControllerDelegate + +extension ConversationViewController: SafetyTipsViewControllerDelegate { + public func didTapViewMoreSafetyTips() { + let viewController = MoreSafetyTipsViewController() + present(viewController, animated: true) + } } // MARK: - OWSNavigationChildController diff --git a/Signal/Images.xcassets/safety-tips/safety-tip-1.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safety-tip-1.imageset/Contents.json deleted file mode 100644 index 3c0a21c3e3..0000000000 --- a/Signal/Images.xcassets/safety-tips/safety-tip-1.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "safety_tip1.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Signal/Images.xcassets/safety-tips/safety-tip-1.imageset/safety_tip1.png b/Signal/Images.xcassets/safety-tips/safety-tip-1.imageset/safety_tip1.png deleted file mode 100644 index fec1c8ba41..0000000000 Binary files a/Signal/Images.xcassets/safety-tips/safety-tip-1.imageset/safety_tip1.png and /dev/null differ diff --git a/Signal/Images.xcassets/safety-tips/safety-tip-2.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safety-tip-2.imageset/Contents.json deleted file mode 100644 index dfa159e841..0000000000 --- a/Signal/Images.xcassets/safety-tips/safety-tip-2.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "safety_tip2.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Signal/Images.xcassets/safety-tips/safety-tip-2.imageset/safety_tip2.png b/Signal/Images.xcassets/safety-tips/safety-tip-2.imageset/safety_tip2.png deleted file mode 100644 index 0635ba0105..0000000000 Binary files a/Signal/Images.xcassets/safety-tips/safety-tip-2.imageset/safety_tip2.png and /dev/null differ diff --git a/Signal/Images.xcassets/safety-tips/safety-tip-3.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safety-tip-3.imageset/Contents.json deleted file mode 100644 index 86bd02b188..0000000000 --- a/Signal/Images.xcassets/safety-tips/safety-tip-3.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "safety_tip3.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Signal/Images.xcassets/safety-tips/safety-tip-3.imageset/safety_tip3.png b/Signal/Images.xcassets/safety-tips/safety-tip-3.imageset/safety_tip3.png deleted file mode 100644 index 77dc27a7a3..0000000000 Binary files a/Signal/Images.xcassets/safety-tips/safety-tip-3.imageset/safety_tip3.png and /dev/null differ diff --git a/Signal/Images.xcassets/safety-tips/safety-tip-4.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safety-tip-4.imageset/Contents.json deleted file mode 100644 index efb8c24dd9..0000000000 --- a/Signal/Images.xcassets/safety-tips/safety-tip-4.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "safety_tip4.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Signal/Images.xcassets/safety-tips/safety-tip-4.imageset/safety_tip4.png b/Signal/Images.xcassets/safety-tips/safety-tip-4.imageset/safety_tip4.png deleted file mode 100644 index bdc0e51bf5..0000000000 Binary files a/Signal/Images.xcassets/safety-tips/safety-tip-4.imageset/safety_tip4.png and /dev/null differ diff --git a/Signal/Images.xcassets/safety-tips/safety-tip-5.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safety-tip-5.imageset/Contents.json deleted file mode 100644 index 2a402d9c9f..0000000000 --- a/Signal/Images.xcassets/safety-tips/safety-tip-5.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "safety_tip5.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Signal/Images.xcassets/safety-tips/safety-tip-5.imageset/safety_tip5.png b/Signal/Images.xcassets/safety-tips/safety-tip-5.imageset/safety_tip5.png deleted file mode 100644 index da123c8892..0000000000 Binary files a/Signal/Images.xcassets/safety-tips/safety-tip-5.imageset/safety_tip5.png and /dev/null differ diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_01.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safetytip_240_01.imageset/Contents.json new file mode 100644 index 0000000000..c8bab6eff1 --- /dev/null +++ b/Signal/Images.xcassets/safety-tips/safetytip_240_01.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "safetytip_240_01.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_01.imageset/safetytip_240_01.pdf b/Signal/Images.xcassets/safety-tips/safetytip_240_01.imageset/safetytip_240_01.pdf new file mode 100644 index 0000000000..5c979c90a0 Binary files /dev/null and b/Signal/Images.xcassets/safety-tips/safetytip_240_01.imageset/safetytip_240_01.pdf differ diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_02.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safetytip_240_02.imageset/Contents.json new file mode 100644 index 0000000000..f94614e80c --- /dev/null +++ b/Signal/Images.xcassets/safety-tips/safetytip_240_02.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "safetytip_240_02.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_02.imageset/safetytip_240_02.pdf b/Signal/Images.xcassets/safety-tips/safetytip_240_02.imageset/safetytip_240_02.pdf new file mode 100644 index 0000000000..95a295ace5 Binary files /dev/null and b/Signal/Images.xcassets/safety-tips/safetytip_240_02.imageset/safetytip_240_02.pdf differ diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_03.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safetytip_240_03.imageset/Contents.json new file mode 100644 index 0000000000..4478845b9a --- /dev/null +++ b/Signal/Images.xcassets/safety-tips/safetytip_240_03.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "safetytip_240_03.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_03.imageset/safetytip_240_03.pdf b/Signal/Images.xcassets/safety-tips/safetytip_240_03.imageset/safetytip_240_03.pdf new file mode 100644 index 0000000000..4c2bbf0845 Binary files /dev/null and b/Signal/Images.xcassets/safety-tips/safetytip_240_03.imageset/safetytip_240_03.pdf differ diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_04.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safetytip_240_04.imageset/Contents.json new file mode 100644 index 0000000000..666443566e --- /dev/null +++ b/Signal/Images.xcassets/safety-tips/safetytip_240_04.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "safetytip_240_04.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_04.imageset/safetytip_240_04.pdf b/Signal/Images.xcassets/safety-tips/safetytip_240_04.imageset/safetytip_240_04.pdf new file mode 100644 index 0000000000..f8a00133b9 Binary files /dev/null and b/Signal/Images.xcassets/safety-tips/safetytip_240_04.imageset/safetytip_240_04.pdf differ diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_05.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safetytip_240_05.imageset/Contents.json new file mode 100644 index 0000000000..fd4d3e1a6a --- /dev/null +++ b/Signal/Images.xcassets/safety-tips/safetytip_240_05.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "safetytip_240_05.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_05.imageset/safetytip_240_05.pdf b/Signal/Images.xcassets/safety-tips/safetytip_240_05.imageset/safetytip_240_05.pdf new file mode 100644 index 0000000000..059efba941 Binary files /dev/null and b/Signal/Images.xcassets/safety-tips/safetytip_240_05.imageset/safetytip_240_05.pdf differ diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_06.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safetytip_240_06.imageset/Contents.json new file mode 100644 index 0000000000..9003bf11f3 --- /dev/null +++ b/Signal/Images.xcassets/safety-tips/safetytip_240_06.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "safetytip_240_06.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/safety-tips/safetytip_240_06.imageset/safetytip_240_06.pdf b/Signal/Images.xcassets/safety-tips/safetytip_240_06.imageset/safetytip_240_06.pdf new file mode 100644 index 0000000000..dd5ed5845f Binary files /dev/null and b/Signal/Images.xcassets/safety-tips/safetytip_240_06.imageset/safetytip_240_06.pdf differ diff --git a/Signal/Images.xcassets/safety-tips/safetytip_48_01.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safetytip_48_01.imageset/Contents.json new file mode 100644 index 0000000000..af0a5dd193 --- /dev/null +++ b/Signal/Images.xcassets/safety-tips/safetytip_48_01.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "safetytip_48_01.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/safety-tips/safetytip_48_01.imageset/safetytip_48_01.pdf b/Signal/Images.xcassets/safety-tips/safetytip_48_01.imageset/safetytip_48_01.pdf new file mode 100644 index 0000000000..c0be10dddf Binary files /dev/null and b/Signal/Images.xcassets/safety-tips/safetytip_48_01.imageset/safetytip_48_01.pdf differ diff --git a/Signal/Images.xcassets/safety-tips/safetytip_48_02.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safetytip_48_02.imageset/Contents.json new file mode 100644 index 0000000000..a8f87ad89d --- /dev/null +++ b/Signal/Images.xcassets/safety-tips/safetytip_48_02.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "safetytip_48_02.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/safety-tips/safetytip_48_02.imageset/safetytip_48_02.pdf b/Signal/Images.xcassets/safety-tips/safetytip_48_02.imageset/safetytip_48_02.pdf new file mode 100644 index 0000000000..3ef5db8a66 Binary files /dev/null and b/Signal/Images.xcassets/safety-tips/safetytip_48_02.imageset/safetytip_48_02.pdf differ diff --git a/Signal/Images.xcassets/safety-tips/safetytip_48_03.imageset/Contents.json b/Signal/Images.xcassets/safety-tips/safetytip_48_03.imageset/Contents.json new file mode 100644 index 0000000000..df3f81e060 --- /dev/null +++ b/Signal/Images.xcassets/safety-tips/safetytip_48_03.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "safetytip_48_03.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/safety-tips/safetytip_48_03.imageset/safetytip_48_03.pdf b/Signal/Images.xcassets/safety-tips/safetytip_48_03.imageset/safetytip_48_03.pdf new file mode 100644 index 0000000000..a64c29bc9b Binary files /dev/null and b/Signal/Images.xcassets/safety-tips/safetytip_48_03.imageset/safetytip_48_03.pdf differ diff --git a/Signal/Symbols.xcassets/chevron/chevron-left-26.imageset/Contents.json b/Signal/Symbols.xcassets/chevron/chevron-left-26.imageset/Contents.json new file mode 100644 index 0000000000..226502b803 --- /dev/null +++ b/Signal/Symbols.xcassets/chevron/chevron-left-26.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "chevron-left-26.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Signal/Symbols.xcassets/chevron/chevron-left-26.imageset/chevron-left-26.pdf b/Signal/Symbols.xcassets/chevron/chevron-left-26.imageset/chevron-left-26.pdf new file mode 100644 index 0000000000..954e92c7a8 Binary files /dev/null and b/Signal/Symbols.xcassets/chevron/chevron-left-26.imageset/chevron-left-26.pdf differ diff --git a/Signal/Symbols.xcassets/chevron/chevron-right-26.imageset/Contents.json b/Signal/Symbols.xcassets/chevron/chevron-right-26.imageset/Contents.json new file mode 100644 index 0000000000..415c234628 --- /dev/null +++ b/Signal/Symbols.xcassets/chevron/chevron-right-26.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "chevron-right-26.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Signal/Symbols.xcassets/chevron/chevron-right-26.imageset/chevron-right-26.pdf b/Signal/Symbols.xcassets/chevron/chevron-right-26.imageset/chevron-right-26.pdf new file mode 100644 index 0000000000..fa89b142e8 Binary files /dev/null and b/Signal/Symbols.xcassets/chevron/chevron-right-26.imageset/chevron-right-26.pdf differ diff --git a/Signal/src/ViewControllers/EditHistoryTableSheetViewController.swift b/Signal/src/ViewControllers/EditHistoryTableSheetViewController.swift index c77900489e..ea6e745818 100644 --- a/Signal/src/ViewControllers/EditHistoryTableSheetViewController.swift +++ b/Signal/src/ViewControllers/EditHistoryTableSheetViewController.swift @@ -532,6 +532,8 @@ extension EditHistoryTableSheetViewController: CVComponentDelegate { func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {} func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {} + + func didTapSafetyTips() {} } extension EditHistoryTableSheetViewController: LongTextViewDelegate { diff --git a/Signal/src/ViewControllers/MediaGallery/Cells/MediaGalleryFileCell.swift b/Signal/src/ViewControllers/MediaGallery/Cells/MediaGalleryFileCell.swift index 90c6b0bd54..55b9c0b59e 100644 --- a/Signal/src/ViewControllers/MediaGallery/Cells/MediaGalleryFileCell.swift +++ b/Signal/src/ViewControllers/MediaGallery/Cells/MediaGalleryFileCell.swift @@ -545,4 +545,6 @@ extension MediaGalleryFileCell: CVComponentDelegate { func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {} func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {} + + func didTapSafetyTips() {} } diff --git a/Signal/src/ViewControllers/MemberLabels/MemberLabelViewController.swift b/Signal/src/ViewControllers/MemberLabels/MemberLabelViewController.swift index 7434c7ebe0..af7c004719 100644 --- a/Signal/src/ViewControllers/MemberLabels/MemberLabelViewController.swift +++ b/Signal/src/ViewControllers/MemberLabels/MemberLabelViewController.swift @@ -780,4 +780,6 @@ extension MemberLabelViewController: CVComponentDelegate { func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {} func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {} + + func didTapSafetyTips() {} } diff --git a/Signal/src/ViewControllers/MessageDetailViewController.swift b/Signal/src/ViewControllers/MessageDetailViewController.swift index 7a25702e03..0feabe7c20 100644 --- a/Signal/src/ViewControllers/MessageDetailViewController.swift +++ b/Signal/src/ViewControllers/MessageDetailViewController.swift @@ -1346,6 +1346,8 @@ extension MessageDetailViewController: CVComponentDelegate { func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {} func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {} + + func didTapSafetyTips() {} } extension MessageDetailViewController: UINavigationControllerDelegate { diff --git a/Signal/src/ViewControllers/PinnedMessages/PinnedMessagesDetailsViewController.swift b/Signal/src/ViewControllers/PinnedMessages/PinnedMessagesDetailsViewController.swift index e8c6e56fb7..5aa3ad91cf 100644 --- a/Signal/src/ViewControllers/PinnedMessages/PinnedMessagesDetailsViewController.swift +++ b/Signal/src/ViewControllers/PinnedMessages/PinnedMessagesDetailsViewController.swift @@ -658,4 +658,6 @@ extension PinnedMessagesDetailsViewController: CVComponentDelegate { func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {} func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {} + + func didTapSafetyTips() {} } diff --git a/Signal/src/ViewControllers/SafetyTipsViewController.swift b/Signal/src/ViewControllers/SafetyTipsViewController.swift index 25311b0993..054ca6ff67 100644 --- a/Signal/src/ViewControllers/SafetyTipsViewController.swift +++ b/Signal/src/ViewControllers/SafetyTipsViewController.swift @@ -12,9 +12,194 @@ public enum SafetyTipsType { case group } +public protocol SafetyTipsViewControllerDelegate: AnyObject { + func didTapViewMoreSafetyTips() +} + public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollViewDelegate { + private enum SafetyTips: CaseIterable { + case chatsFromSignal + case reviewNames + case scams + + var image: UIImage? { + switch self { + case .chatsFromSignal: + return UIImage(resource: .safetytip4801) + case .reviewNames: + return UIImage(resource: .safetytip4802) + case .scams: + return UIImage(resource: .safetytip4803) + } + } + + var title: String { + switch self { + case .chatsFromSignal: + return OWSLocalizedString( + "SAFETY_TIPS_SIGNAL_CHATS_TITLE", + comment: "Message title describing the signal chats tip.", + ) + case .reviewNames: + return OWSLocalizedString( + "SAFETY_TIPS_REVIEW_NAMES_TITLE", + comment: "Message title describing the review names safety tip.", + ) + case .scams: + return OWSLocalizedString( + "SAFETY_TIPS_LOOK_OUT_FOR_SCAMS_TITLE", + comment: "Message title describing the scams safety tip.", + ) + } + } + + var body: String { + switch self { + case .chatsFromSignal: + return OWSLocalizedString( + "SAFETY_TIPS_SIGNAL_CHATS_BODY", + comment: "Message body describing the signal chats tip.", + ) + case .reviewNames: + return OWSLocalizedString( + "SAFETY_TIPS_REVIEW_NAMES_BODY", + comment: "Message body describing the review names safety tip.", + ) + case .scams: + return OWSLocalizedString( + "SAFETY_TIPS_LOOK_OUT_FOR_SCAMS_BODY", + comment: "Message body describing the scams safety tip.", + ) + } + } + } + let contentScrollView = UIScrollView() let stackView = UIStackView() + + public weak var delegate: SafetyTipsViewControllerDelegate? + + override public func viewDidLoad() { + super.viewDidLoad() + + minimizedHeight = min(612, CurrentAppContext().frame.height) + super.allowsExpansion = false + + let header = UILabel() + header.text = OWSLocalizedString( + "SAFETY_TIPS_HEADER_TITLE", + comment: "Title for Safety Tips education screen.", + ) + header.font = .dynamicTypeHeadline + header.textAlignment = .center + header.isAccessibilityElement = true + header.accessibilityTraits.insert(.header) + contentView.addSubview(header) + header.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + header.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), + header.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor, constant: 16), + ]) + + contentView.addSubview(contentScrollView) + contentScrollView.addSubview(stackView) + + stackView.axis = .vertical + stackView.spacing = 20 + + contentScrollView.translatesAutoresizingMaskIntoConstraints = false + stackView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + contentScrollView.topAnchor.constraint(equalTo: header.bottomAnchor, constant: 24), + contentScrollView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -90), + contentScrollView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + contentScrollView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + + stackView.topAnchor.constraint(equalTo: contentScrollView.contentLayoutGuide.topAnchor), + stackView.bottomAnchor.constraint(equalTo: contentScrollView.contentLayoutGuide.bottomAnchor), + stackView.leadingAnchor.constraint(equalTo: contentScrollView.contentLayoutGuide.leadingAnchor, constant: 24), + stackView.trailingAnchor.constraint(equalTo: contentScrollView.contentLayoutGuide.trailingAnchor, constant: 24), + stackView.widthAnchor.constraint(equalTo: contentScrollView.frameLayoutGuide.widthAnchor, constant: -48), + ]) + + for bullet in SafetyTips.allCases { + let bulletView = SafetyBulletView(bullet) + stackView.addArrangedSubview(bulletView) + } + + var config = UIButton.Configuration.filled() + config.baseBackgroundColor = UIColor.Signal.secondaryFill + config.cornerStyle = .capsule + var attrString = AttributedString(CommonStrings.viewMoreButton) + attrString.font = .dynamicTypeBodyClamped.medium() + config.attributedTitle = attrString + config.baseForegroundColor = UIColor.Signal.label + config.contentInsets = .init(margin: 14) + let viewMoreButton = UIButton( + configuration: config, + primaryAction: .init(handler: { [weak self] _ in + self?.dismiss(animated: true) + self?.delegate?.didTapViewMoreSafetyTips() + }), + ) + + viewMoreButton.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(viewMoreButton) + NSLayoutConstraint.activate([ + viewMoreButton.bottomAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.bottomAnchor), + viewMoreButton.leadingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.leadingAnchor, constant: 20), + viewMoreButton.trailingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.trailingAnchor, constant: -20), + viewMoreButton.heightAnchor.constraint(equalToConstant: 52), + ]) + } + + private class SafetyBulletView: UIStackView { + init(_ bullet: SafetyTips) { + super.init(frame: .zero) + + self.axis = .horizontal + self.alignment = .firstBaseline + self.spacing = 24 + self.isLayoutMarginsRelativeArrangement = true + self.layoutMargins = .zero + + let textStack = UIStackView() + textStack.axis = .vertical + textStack.spacing = 8 + + let headerLabel = UILabel() + headerLabel.text = bullet.title + headerLabel.numberOfLines = 0 + headerLabel.textColor = UIColor.Signal.label + headerLabel.font = .dynamicTypeBody.semibold() + textStack.addArrangedSubview(headerLabel) + + let bodyLabel = UILabel() + bodyLabel.text = bullet.body + bodyLabel.numberOfLines = 0 + bodyLabel.textColor = UIColor.Signal.secondaryLabel + bodyLabel.font = .dynamicTypeBody + textStack.addArrangedSubview(bodyLabel) + + let bulletPoint = UIImageView(image: bullet.image) + bulletPoint.contentMode = .scaleAspectFit + bulletPoint.translatesAutoresizingMaskIntoConstraints = false + bulletPoint.widthAnchor.constraint(equalToConstant: 48).isActive = true + bulletPoint.heightAnchor.constraint(equalToConstant: 48).isActive = true + + addArrangedSubview(bulletPoint) + addArrangedSubview(textStack) + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + } +} + +public class MoreSafetyTipsViewController: InteractiveSheetViewController, UIScrollViewDelegate { + let contentScrollView = UIScrollView() override public var interactiveScrollViews: [UIScrollView] { [contentScrollView] } override public var sheetBackgroundColor: UIColor { Theme.tableView2PresentedBackgroundColor } @@ -25,59 +210,47 @@ public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollV static let outerMargins: UIEdgeInsets = .init(hMargin: 24.0, vMargin: 0.0) static let footerSpacing: CGFloat = 16.0 - static let footerMargins: UIEdgeInsets = .init( - top: 0.0, - left: 24.0, - bottom: 42.0, - right: 24.0, - ) - static let buttonInsets: UIEdgeInsets = .init( - top: 16.0, - leading: 36.0, - bottom: 12.0, - trailing: 36.0, - ) - - static let buttonEdgeInsets: UIEdgeInsets = .init( - hMargin: 0.0, - vMargin: 14.0, - ) + static let buttonDiameter: CGFloat = 52.0 + static let buttonMargin: CGFloat = 24.0 } - fileprivate enum SafetyTips: CaseIterable { - case fakeNames - case crypto + fileprivate enum MoreSafetyTips: CaseIterable { + case chatsFromSignal + case reviewNames case vagueMessages case messagesWithLinks + case crypto case fakeBusiness var image: UIImage? { switch self { - case .fakeNames: - return UIImage(named: "safety-tip-5") - case .crypto: - return UIImage(named: "safety-tip-1") + case .chatsFromSignal: + return UIImage(resource: .safetytip24001) + case .reviewNames: + return UIImage(resource: .safetytip24002) case .vagueMessages: - return UIImage(named: "safety-tip-2") + return UIImage(resource: .safetytip24003) case .messagesWithLinks: - return UIImage(named: "safety-tip-3") + return UIImage(resource: .safetytip24004) + case .crypto: + return UIImage(resource: .safetytip24005) case .fakeBusiness: - return UIImage(named: "safety-tip-4") + return UIImage(resource: .safetytip24006) } } var title: String { switch self { - case .fakeNames: + case .chatsFromSignal: return OWSLocalizedString( - "SAFETY_TIPS_FAKE_NAMES_TITLE", - comment: "Message title describing the fake names safety tip.", + "SAFETY_TIPS_SIGNAL_CHATS_TITLE", + comment: "Message title describing the signal chats tip.", ) - case .crypto: + case .reviewNames: return OWSLocalizedString( - "SAFETY_TIPS_CRYPTO_TITLE", - comment: "Message title describing the crypto safety tip.", + "SAFETY_TIPS_REVIEW_NAMES_TITLE", + comment: "Message title describing the review names safety tip.", ) case .vagueMessages: return OWSLocalizedString( @@ -89,6 +262,11 @@ public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollV "SAFETY_TIPS_MESSAGE_LINKS_TITLE", comment: "Message title describing the safety tip about unknown links in messages.", ) + case .crypto: + return OWSLocalizedString( + "SAFETY_TIPS_CRYPTO_TITLE", + comment: "Message title describing the crypto safety tip.", + ) case .fakeBusiness: return OWSLocalizedString( "SAFETY_TIPS_FAKE_BUSINESS_TITLE", @@ -99,15 +277,15 @@ public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollV var body: String { switch self { - case .fakeNames: + case .chatsFromSignal: return OWSLocalizedString( - "SAFETY_TIPS_FAKE_NAMES_BODY", - comment: "Message contents for the fake names safety tip.", + "SAFETY_TIPS_SIGNAL_CHATS_BODY_VIEW_MORE", + comment: "Message body describing the signal chats tip in the 'view more' flow.", ) - case .crypto: + case .reviewNames: return OWSLocalizedString( - "SAFETY_TIPS_CRYPTO_BODY", - comment: "Message contents for the crypto safety tip.", + "SAFETY_TIPS_REVIEW_NAMES_BODY_VIEW_MORE", + comment: "Message body describing the review names safety tip in the 'view more' flow.", ) case .vagueMessages: return OWSLocalizedString( @@ -119,6 +297,11 @@ public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollV "SAFETY_TIPS_MESSAGE_LINKS_BODY", comment: "Message contents for the unknown links in messages safety tip.", ) + case .crypto: + return OWSLocalizedString( + "SAFETY_TIPS_CRYPTO_BODY", + comment: "Message contents for the crypto safety tip.", + ) case .fakeBusiness: return OWSLocalizedString( "SAFETY_TIPS_FAKE_BUSINESS_BODY", @@ -128,34 +311,34 @@ public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollV } } - public var prefersNavigationBarHidden: Bool { true } - - private let type: SafetyTipsType - - init(type: SafetyTipsType) { - self.type = type - super.init() - } + var prefersNavigationBarHidden: Bool { true } override public func viewDidLoad() { super.viewDidLoad() - minimizedHeight = min(725, CurrentAppContext().frame.height) + minimizedHeight = min(510, CurrentAppContext().frame.height) super.allowsExpansion = false contentView.addSubview(contentScrollView) - contentScrollView.addSubview(stackView) + contentScrollView.addSubview(tipScrollView) - stackView.axis = .vertical - stackView.spacing = Constants.outerSpacing - stackView.isLayoutMarginsRelativeArrangement = true - stackView.autoPinEdgesToSuperviewEdges() - stackView.autoPinWidth(toWidthOf: contentScrollView) - stackView.translatesAutoresizingMaskIntoConstraints = false - stackView.autoPinEdge(.bottom, to: .bottom, of: contentView, withOffset: 0.0, relation: .greaterThanOrEqual) + tipScrollView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + tipScrollView.topAnchor.constraint(equalTo: contentScrollView.topAnchor), + tipScrollView.bottomAnchor.constraint(equalTo: contentScrollView.bottomAnchor), + tipScrollView.leadingAnchor.constraint(equalTo: contentScrollView.leadingAnchor), + tipScrollView.trailingAnchor.constraint(equalTo: contentScrollView.trailingAnchor), + tipScrollView.widthAnchor.constraint(equalTo: contentScrollView.frameLayoutGuide.widthAnchor), + ]) contentScrollView.translatesAutoresizingMaskIntoConstraints = false - contentScrollView.autoPinEdgesToSuperviewEdges() + NSLayoutConstraint.activate([ + contentScrollView.topAnchor.constraint(equalTo: contentView.topAnchor), + contentScrollView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -84), + contentScrollView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + contentScrollView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + contentScrollView.widthAnchor.constraint(equalTo: contentView.widthAnchor), + ]) buildContents() updateButtonState() @@ -181,62 +364,71 @@ public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollV private lazy var pageControl: UIPageControl = { let pageControl = UIPageControl() - pageControl.numberOfPages = SafetyTips.allCases.count + pageControl.numberOfPages = MoreSafetyTips.allCases.count pageControl.currentPage = 0 pageControl.addTarget(self, action: #selector(self.changePage), for: .valueChanged) return pageControl }() - private lazy var previousTipButton: OWSFlatButton = { - let previousButton = OWSFlatButton.insetButton( - title: OWSLocalizedString( - "SAFETY_TIPS_PREVIOUS_TIP_BUTTON", - comment: "Button that will show the previous safety tip.", - ), - font: .dynamicTypeHeadlineClamped, - titleColor: .white, - backgroundColor: .ows_accentBlue, - target: self, - selector: #selector(didTapPrevious), - ) - previousButton.button.setBackgroundImage(UIImage.image(color: .clear), for: .disabled) - previousButton.button.setTitleColor(.ows_accentBlue, for: .disabled) - previousButton.contentEdgeInsets = Constants.buttonEdgeInsets + private lazy var previousTipButton: UIButton = { + let previousButton = UIButton(type: .system) + var config = UIButton.Configuration.filled() + config.baseForegroundColor = UIColor.Signal.label + config.baseBackgroundColor = UIColor.Signal.primaryFill + config.image = UIImage(resource: .chevronLeft26) + config.cornerStyle = .capsule + previousButton.accessibilityLabel = CommonStrings.backButton + previousButton.configuration = config + previousButton.addTarget(self, action: #selector(didTapPrevious), for: .touchUpInside) + return previousButton + }() - private lazy var nextTipButton: OWSFlatButton = { - let nextButton = OWSFlatButton.insetButton( - title: OWSLocalizedString( - "SAFETY_TIPS_NEXT_TIP_BUTTON", - comment: "Button that will show the next safety tip.", - ), - font: .dynamicTypeHeadlineClamped, - titleColor: .white, - backgroundColor: .ows_accentBlue, - target: self, - selector: #selector(didTapNext), - ) - nextButton.button.setBackgroundImage(UIImage.image(color: .clear), for: .disabled) - nextButton.button.setTitleColor(.ows_accentBlue, for: .disabled) - nextButton.contentEdgeInsets = Constants.buttonEdgeInsets + private lazy var nextTipButton: UIButton = { + let nextButton = UIButton(type: .system) + var config = UIButton.Configuration.filled() + config.baseForegroundColor = UIColor.Signal.label + config.baseBackgroundColor = UIColor.Signal.primaryFill + config.image = UIImage(resource: .chevronRight26) + config.cornerStyle = .capsule + nextButton.configuration = config + nextButton.accessibilityLabel = CommonStrings.nextButton + nextButton.addTarget(self, action: #selector(didTapNext), for: .touchUpInside) + return nextButton }() private lazy var footerView: UIView = { let stackView = UIStackView(arrangedSubviews: [ previousTipButton, + pageControl, nextTipButton, ]) + + nextTipButton.translatesAutoresizingMaskIntoConstraints = false + previousTipButton.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + nextTipButton.widthAnchor.constraint(equalToConstant: Constants.buttonDiameter), + nextTipButton.heightAnchor.constraint(equalToConstant: Constants.buttonDiameter), + previousTipButton.widthAnchor.constraint(equalToConstant: Constants.buttonDiameter), + previousTipButton.heightAnchor.constraint(equalToConstant: Constants.buttonDiameter), + ]) + let container = UIView() container.addSubview(stackView) - container.layoutMargins = Constants.footerMargins - container.setContentHuggingHigh() + container.backgroundColor = sheetBackgroundColor + container.tintColor = sheetBackgroundColor stackView.axis = .horizontal stackView.spacing = Constants.footerSpacing - stackView.distribution = .fillEqually - stackView.autoPinEdgesToSuperviewMargins() + stackView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + stackView.centerXAnchor.constraint(equalTo: container.centerXAnchor), + stackView.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -Constants.buttonMargin), + stackView.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: Constants.buttonMargin), + stackView.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -Constants.buttonMargin), + ]) return container }() @@ -245,20 +437,21 @@ public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollV private func buildContents() { prepareTipsScrollView() - stackView.removeAllSubviews() - stackView.addArrangedSubview(Self.HeaderView(type: type)) - stackView.addArrangedSubview(tipScrollView) - stackView.setCustomSpacing(8.0, after: tipScrollView) - stackView.addArrangedSubview(pageControl) - stackView.setCustomSpacing(0.0, after: pageControl) - stackView.addArrangedSubview(UIView.transparentSpacer()) - stackView.addArrangedSubview(footerView) + + contentView.addSubview(footerView) + footerView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + footerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + footerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + footerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + footerView.heightAnchor.constraint(equalToConstant: 84), + ]) } private func prepareTipsScrollView() { var priorView: UIView? tipScrollView.removeAllSubviews() - SafetyTips.allCases.forEach { tip in + MoreSafetyTips.allCases.forEach { tip in let view = SafetyTipView(safetyTip: tip) tipScrollView.addSubview(view) @@ -289,6 +482,11 @@ public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollV let x = CGFloat(pageControl.currentPage) * tipScrollView.frame.size.width tipScrollView.setContentOffset(CGPoint(x: x, y: 0), animated: true) updateButtonState() + + let currentPageView = tipScrollView.subviews[pageControl.currentPage] + DispatchQueue.main.async { + UIAccessibility.post(notification: .layoutChanged, argument: currentPageView) + } } @objc @@ -306,14 +504,23 @@ public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollV private func updateButtonState() { switch pageControl.currentPage { case 0: - previousTipButton.setEnabled(false) - nextTipButton.setEnabled(true) + // hide previous, show next + previousTipButton.alpha = 0 + previousTipButton.isUserInteractionEnabled = false + nextTipButton.alpha = 1 + nextTipButton.isUserInteractionEnabled = true case pageControl.numberOfPages - 1: - previousTipButton.setEnabled(true) - nextTipButton.setEnabled(false) + // show previous, hide next + previousTipButton.alpha = 1 + previousTipButton.isUserInteractionEnabled = true + nextTipButton.alpha = 0 + nextTipButton.isUserInteractionEnabled = false default: - previousTipButton.setEnabled(true) - nextTipButton.setEnabled(true) + // show previous, show next + previousTipButton.alpha = 1 + previousTipButton.isUserInteractionEnabled = true + nextTipButton.alpha = 1 + nextTipButton.isUserInteractionEnabled = true } } @@ -323,136 +530,22 @@ public class SafetyTipsViewController: InteractiveSheetViewController, UIScrollV } } -extension SafetyTipsViewController { - class HeaderView: UIView { - - private let type: SafetyTipsType - - // MARK: Init - - init(type: SafetyTipsType) { - self.type = type - super.init(frame: .zero) - layoutMargins = Constants.outerMargins - - let stackView = UIStackView() - self.addSubview(stackView) - - stackView.axis = .vertical - stackView.alignment = .center - stackView.spacing = Constants.stackSpacing - stackView.autoPinEdgesToSuperviewMargins() - stackView.addArrangedSubviews([ - titleLabel, - subtitleLabel, - ]) - - self.setContentHuggingHigh() - updateFontsForCurrentPreferredContentSize() - setColorsForCurrentTheme() - } - - @available(*, unavailable, message: "Use other constructor") - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Views - - private lazy var titleLabel: UILabel = { - let label = UILabel() - label.text = OWSLocalizedString( - "SAFETY_TIPS_HEADER_TITLE", - comment: "Title for Safety Tips education screen.", - ) - label.numberOfLines = 0 - label.textAlignment = .center - label.lineBreakMode = .byWordWrapping - label.setContentHuggingVerticalHigh() - return label - }() - - private lazy var subtitleLabel: UILabel = { - let label = UILabel() - let message = { - switch type { - case .contact: - return OWSLocalizedString( - "SAFETY_TIPS_INDIVIDUAL_HEADER_MESSAGE", - comment: "Message describing safety tips for 1:1 conversations.", - ) - case .group: - return OWSLocalizedString( - "SAFETY_TIPS_GROUPS_HEADER_MESSAGE", - comment: "Message describing safety tips for group conversations.", - ) - } - }() - label.text = message - label.numberOfLines = 0 - label.textAlignment = .center - label.lineBreakMode = .byWordWrapping - label.setContentHuggingVerticalHigh() - return label - }() - - // MARK: - Style views - - func updateFontsForCurrentPreferredContentSize() { - titleLabel.font = .dynamicTypeTitle2Clamped.bold() - subtitleLabel.font = .dynamicTypeSubheadlineClamped - } - - func setColorsForCurrentTheme() { - titleLabel.textColor = Theme.primaryTextColor - subtitleLabel.textColor = Theme.primaryTextColor - } - } -} - -extension SafetyTipsViewController { +extension MoreSafetyTipsViewController { class SafetyTipView: UIView { - private enum Constants { - static let cornerRadius: CGFloat = 20.0 - static let layoutMargin: CGFloat = 12.0 - static let imageMargin: CGFloat = 24.0 - static let containerMargins: UIEdgeInsets = .init( - top: layoutMargin, - left: layoutMargin, - bottom: 24.0, - right: layoutMargin, - ) - } - - fileprivate init(safetyTip: SafetyTips) { + fileprivate init(safetyTip: MoreSafetyTips) { super.init(frame: .zero) layoutMargins = .init(hMargin: 24.0, vMargin: 0.0) - let containerView = UIView() - containerView.layer.cornerRadius = Constants.cornerRadius - containerView.backgroundColor = Theme.isDarkThemeEnabled ? .ows_gray75 : .ows_white - self.addSubview(containerView) - containerView.layoutMargins = Constants.containerMargins - containerView.autoPinEdgesToSuperviewMargins() - let stackView = UIStackView() stackView.axis = .vertical - containerView.addSubview(stackView) - stackView.spacing = Constants.layoutMargin + self.addSubview(stackView) + stackView.spacing = 12.0 stackView.autoPinEdgesToSuperviewMargins() - let imageContainerView = UIView() - imageContainerView.backgroundColor = Theme.isDarkThemeEnabled ? .ows_gray60 : .ows_gray02 - imageContainerView.layer.cornerRadius = Constants.cornerRadius - imageContainerView.layoutMargins = .init(margin: Constants.imageMargin) - stackView.addArrangedSubview(imageContainerView) - let imageView = UIImageView(image: safetyTip.image) imageView.contentMode = .scaleAspectFit - imageContainerView.addSubview(imageView) - imageView.autoPinEdgesToSuperviewMargins() - imageView.autoPinToAspectRatio(withSize: safetyTip.image?.size ?? .init(square: 1.0)) + stackView.addArrangedSubview(imageView) let titleLabel = UILabel() titleLabel.text = safetyTip.title diff --git a/Signal/src/views/MockConversationView.swift b/Signal/src/views/MockConversationView.swift index 36a62027a8..a7e8a84140 100644 --- a/Signal/src/views/MockConversationView.swift +++ b/Signal/src/views/MockConversationView.swift @@ -560,4 +560,6 @@ extension MockConversationView: CVComponentDelegate { func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {} func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {} + + func didTapSafetyTips() {} } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index cbaf3d8cf1..06d42497a3 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -7925,34 +7925,40 @@ "SAFETY_TIPS_FAKE_BUSINESS_BODY" = "Be careful of businesses or government agencies contacting you. Messages involving tax agencies, couriers, and more can be spam."; /* Message title describing the safety tip about unknown or fake businesses. */ -"SAFETY_TIPS_FAKE_BUSINESS_TITLE" = "Fake businesses and institutions"; - -/* Message contents for the fake names safety tip. */ -"SAFETY_TIPS_FAKE_NAMES_BODY" = "Signal will never contact you for your registration code or PIN. Be cautious of requests that impersonate others. Profile names are chosen by their account holder and aren't verified."; - -/* Message title describing the fake names safety tip. */ -"SAFETY_TIPS_FAKE_NAMES_TITLE" = "Fake names and accounts"; - -/* Message describing safety tips for group conversations. */ -"SAFETY_TIPS_GROUPS_HEADER_MESSAGE" = "Review this request carefully. None of your contacts or people you chat with are in this group. Here are a few things to watch out for:"; +"SAFETY_TIPS_FAKE_BUSINESS_TITLE" = "Fake businesses"; /* Title for Safety Tips education screen. */ "SAFETY_TIPS_HEADER_TITLE" = "Safety Tips"; -/* Message describing safety tips for 1:1 conversations. */ -"SAFETY_TIPS_INDIVIDUAL_HEADER_MESSAGE" = "Be careful when accepting message requests from people you don’t know. Watch out for:"; +/* Message body describing the scams safety tip. */ +"SAFETY_TIPS_LOOK_OUT_FOR_SCAMS_BODY" = "Avoid vague messages that try to get you to reply. Be aware of financial tips and suspicious web links."; + +/* Message title describing the scams safety tip. */ +"SAFETY_TIPS_LOOK_OUT_FOR_SCAMS_TITLE" = "Look out for scams"; /* Message contents for the unknown links in messages safety tip. */ "SAFETY_TIPS_MESSAGE_LINKS_BODY" = "Be careful of messages from people you don’t know that have links to websites. Never visit links from people you don’t trust."; /* Message title describing the safety tip about unknown links in messages. */ -"SAFETY_TIPS_MESSAGE_LINKS_TITLE" = "Messages with links"; +"SAFETY_TIPS_MESSAGE_LINKS_TITLE" = "Messages with web links"; -/* Button that will show the next safety tip. */ -"SAFETY_TIPS_NEXT_TIP_BUTTON" = "Next"; +/* Message body describing the review names safety tip. */ +"SAFETY_TIPS_REVIEW_NAMES_BODY" = "Look out for the “Name not verified” notice. Everyone sets their own profile name in Signal."; -/* Button that will show the previous safety tip. */ -"SAFETY_TIPS_PREVIOUS_TIP_BUTTON" = "Previous"; +/* Message body describing the review names safety tip in the 'view more' flow. */ +"SAFETY_TIPS_REVIEW_NAMES_BODY_VIEW_MORE" = "Look out for the “Name not verified” notice. Everyone sets their own profile name and photo in Signal. If you’re unsure who a new request is from, it’s safer to ignore it."; + +/* Message title describing the review names safety tip. */ +"SAFETY_TIPS_REVIEW_NAMES_TITLE" = "Review names and photos"; + +/* Message body describing the signal chats tip. */ +"SAFETY_TIPS_SIGNAL_CHATS_BODY" = "Signal will never message you for your registration code, PIN, or recovery key. Never respond to a chat pretending to be Signal."; + +/* Message body describing the signal chats tip in the 'view more' flow. */ +"SAFETY_TIPS_SIGNAL_CHATS_BODY_VIEW_MORE" = "Signal will never message you for your registration code, PIN, or recovery key. Don’t reply to chats pretending to be Signal or Signal Support. Bad actors set up fake names to try to take over your account."; + +/* Message title describing the signal chats tip. */ +"SAFETY_TIPS_SIGNAL_CHATS_TITLE" = "Don’t respond to chats from Signal"; /* Message contents for the vague message safety tip. */ "SAFETY_TIPS_VAGUE_MESSAGE_BODY" = "Spammers often start with a simple message like “Hi” to draw you in. If you respond they may engage you further."; @@ -10102,6 +10108,9 @@ /* Button label for the 'View Contact' button */ "VIEW_CONTACT_BUTTON" = "View Contact"; +/* Label for the 'view more' button */ +"VIEW_MORE_BUTTON" = "View More"; + /* Toast alert text shown when tapping on a view-once message that has already been viewed. */ "VIEW_ONCE_ALREADY_VIEWED_TOAST" = "You already viewed this message."; diff --git a/SignalServiceKit/Util/CommonStrings.swift b/SignalServiceKit/Util/CommonStrings.swift index d40fe6e82f..e767de29f9 100644 --- a/SignalServiceKit/Util/CommonStrings.swift +++ b/SignalServiceKit/Util/CommonStrings.swift @@ -234,6 +234,10 @@ public enum CommonStrings { OWSLocalizedString("BUTTON_VIEW", comment: "Label for the 'view' button.") } + public static var viewMoreButton: String { + OWSLocalizedString("VIEW_MORE_BUTTON", comment: "Label for the 'view more' button") + } + public static var seeAllButton: String { OWSLocalizedString("SEE_ALL_BUTTON", comment: "Label for the 'see all' button.") }