safety tips update
This commit is contained in:
parent
89e5757323
commit
d08c0ab160
@ -279,6 +279,8 @@ public protocol CVComponentDelegate: AnyObject, AudioMessageViewDelegate, CVPoll
|
||||
func didTapViewPoll(pollInteractionUniqueId: String)
|
||||
|
||||
func didTapViewPinnedMessage(pinnedMessageUniqueId: String)
|
||||
|
||||
func didTapSafetyTips()
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
@ -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
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.4 KiB |
@ -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
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
@ -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
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
@ -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
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB |
12
Signal/Images.xcassets/safety-tips/safetytip_240_01.imageset/Contents.json
vendored
Normal file
12
Signal/Images.xcassets/safety-tips/safetytip_240_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "safetytip_240_01.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_01.imageset/safetytip_240_01.pdf
vendored
Normal file
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_01.imageset/safetytip_240_01.pdf
vendored
Normal file
Binary file not shown.
12
Signal/Images.xcassets/safety-tips/safetytip_240_02.imageset/Contents.json
vendored
Normal file
12
Signal/Images.xcassets/safety-tips/safetytip_240_02.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "safetytip_240_02.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_02.imageset/safetytip_240_02.pdf
vendored
Normal file
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_02.imageset/safetytip_240_02.pdf
vendored
Normal file
Binary file not shown.
12
Signal/Images.xcassets/safety-tips/safetytip_240_03.imageset/Contents.json
vendored
Normal file
12
Signal/Images.xcassets/safety-tips/safetytip_240_03.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "safetytip_240_03.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_03.imageset/safetytip_240_03.pdf
vendored
Normal file
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_03.imageset/safetytip_240_03.pdf
vendored
Normal file
Binary file not shown.
12
Signal/Images.xcassets/safety-tips/safetytip_240_04.imageset/Contents.json
vendored
Normal file
12
Signal/Images.xcassets/safety-tips/safetytip_240_04.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "safetytip_240_04.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_04.imageset/safetytip_240_04.pdf
vendored
Normal file
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_04.imageset/safetytip_240_04.pdf
vendored
Normal file
Binary file not shown.
12
Signal/Images.xcassets/safety-tips/safetytip_240_05.imageset/Contents.json
vendored
Normal file
12
Signal/Images.xcassets/safety-tips/safetytip_240_05.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "safetytip_240_05.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_05.imageset/safetytip_240_05.pdf
vendored
Normal file
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_05.imageset/safetytip_240_05.pdf
vendored
Normal file
Binary file not shown.
12
Signal/Images.xcassets/safety-tips/safetytip_240_06.imageset/Contents.json
vendored
Normal file
12
Signal/Images.xcassets/safety-tips/safetytip_240_06.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "safetytip_240_06.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_06.imageset/safetytip_240_06.pdf
vendored
Normal file
BIN
Signal/Images.xcassets/safety-tips/safetytip_240_06.imageset/safetytip_240_06.pdf
vendored
Normal file
Binary file not shown.
12
Signal/Images.xcassets/safety-tips/safetytip_48_01.imageset/Contents.json
vendored
Normal file
12
Signal/Images.xcassets/safety-tips/safetytip_48_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "safetytip_48_01.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Signal/Images.xcassets/safety-tips/safetytip_48_01.imageset/safetytip_48_01.pdf
vendored
Normal file
BIN
Signal/Images.xcassets/safety-tips/safetytip_48_01.imageset/safetytip_48_01.pdf
vendored
Normal file
Binary file not shown.
12
Signal/Images.xcassets/safety-tips/safetytip_48_02.imageset/Contents.json
vendored
Normal file
12
Signal/Images.xcassets/safety-tips/safetytip_48_02.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "safetytip_48_02.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Signal/Images.xcassets/safety-tips/safetytip_48_02.imageset/safetytip_48_02.pdf
vendored
Normal file
BIN
Signal/Images.xcassets/safety-tips/safetytip_48_02.imageset/safetytip_48_02.pdf
vendored
Normal file
Binary file not shown.
12
Signal/Images.xcassets/safety-tips/safetytip_48_03.imageset/Contents.json
vendored
Normal file
12
Signal/Images.xcassets/safety-tips/safetytip_48_03.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "safetytip_48_03.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Signal/Images.xcassets/safety-tips/safetytip_48_03.imageset/safetytip_48_03.pdf
vendored
Normal file
BIN
Signal/Images.xcassets/safety-tips/safetytip_48_03.imageset/safetytip_48_03.pdf
vendored
Normal file
Binary file not shown.
15
Signal/Symbols.xcassets/chevron/chevron-left-26.imageset/Contents.json
vendored
Normal file
15
Signal/Symbols.xcassets/chevron/chevron-left-26.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "chevron-left-26.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
BIN
Signal/Symbols.xcassets/chevron/chevron-left-26.imageset/chevron-left-26.pdf
vendored
Normal file
BIN
Signal/Symbols.xcassets/chevron/chevron-left-26.imageset/chevron-left-26.pdf
vendored
Normal file
Binary file not shown.
15
Signal/Symbols.xcassets/chevron/chevron-right-26.imageset/Contents.json
vendored
Normal file
15
Signal/Symbols.xcassets/chevron/chevron-right-26.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "chevron-right-26.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
BIN
Signal/Symbols.xcassets/chevron/chevron-right-26.imageset/chevron-right-26.pdf
vendored
Normal file
BIN
Signal/Symbols.xcassets/chevron/chevron-right-26.imageset/chevron-right-26.pdf
vendored
Normal file
Binary file not shown.
@ -532,6 +532,8 @@ extension EditHistoryTableSheetViewController: CVComponentDelegate {
|
||||
func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {}
|
||||
|
||||
func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {}
|
||||
|
||||
func didTapSafetyTips() {}
|
||||
}
|
||||
|
||||
extension EditHistoryTableSheetViewController: LongTextViewDelegate {
|
||||
|
||||
@ -545,4 +545,6 @@ extension MediaGalleryFileCell: CVComponentDelegate {
|
||||
func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {}
|
||||
|
||||
func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {}
|
||||
|
||||
func didTapSafetyTips() {}
|
||||
}
|
||||
|
||||
@ -780,4 +780,6 @@ extension MemberLabelViewController: CVComponentDelegate {
|
||||
func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {}
|
||||
|
||||
func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {}
|
||||
|
||||
func didTapSafetyTips() {}
|
||||
}
|
||||
|
||||
@ -1346,6 +1346,8 @@ extension MessageDetailViewController: CVComponentDelegate {
|
||||
func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {}
|
||||
|
||||
func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {}
|
||||
|
||||
func didTapSafetyTips() {}
|
||||
}
|
||||
|
||||
extension MessageDetailViewController: UINavigationControllerDelegate {
|
||||
|
||||
@ -658,4 +658,6 @@ extension PinnedMessagesDetailsViewController: CVComponentDelegate {
|
||||
func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {}
|
||||
|
||||
func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {}
|
||||
|
||||
func didTapSafetyTips() {}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -560,4 +560,6 @@ extension MockConversationView: CVComponentDelegate {
|
||||
func didTapVoteOnPoll(poll: OWSPoll, optionIndex: UInt32, isUnvote: Bool) {}
|
||||
|
||||
func didTapViewPinnedMessage(pinnedMessageUniqueId: String) {}
|
||||
|
||||
func didTapSafetyTips() {}
|
||||
}
|
||||
|
||||
@ -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.";
|
||||
|
||||
|
||||
@ -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.")
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user