mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
742c4a161f
# Conflicts: # Session.xcodeproj/project.pbxproj # Session/Conversations/ConversationVC+Interaction.swift # Session/Conversations/ConversationViewModel.swift # Session/Media Viewing & Editing/GIFs/GifPickerViewController.swift # Session/Media Viewing & Editing/GIFs/GiphyAPI.swift # Session/Meta/Translations/de.lproj/Localizable.strings # Session/Meta/Translations/en.lproj/Localizable.strings # Session/Meta/Translations/es.lproj/Localizable.strings # Session/Meta/Translations/fa.lproj/Localizable.strings # Session/Meta/Translations/fi.lproj/Localizable.strings # Session/Meta/Translations/fr.lproj/Localizable.strings # Session/Meta/Translations/hi.lproj/Localizable.strings # Session/Meta/Translations/hr.lproj/Localizable.strings # Session/Meta/Translations/id-ID.lproj/Localizable.strings # Session/Meta/Translations/it.lproj/Localizable.strings # Session/Meta/Translations/ja.lproj/Localizable.strings # Session/Meta/Translations/nl.lproj/Localizable.strings # Session/Meta/Translations/pl.lproj/Localizable.strings # Session/Meta/Translations/pt_BR.lproj/Localizable.strings # Session/Meta/Translations/ru.lproj/Localizable.strings # Session/Meta/Translations/si.lproj/Localizable.strings # Session/Meta/Translations/sk.lproj/Localizable.strings # Session/Meta/Translations/sv.lproj/Localizable.strings # Session/Meta/Translations/th.lproj/Localizable.strings # Session/Meta/Translations/vi-VN.lproj/Localizable.strings # Session/Meta/Translations/zh-Hant.lproj/Localizable.strings # Session/Meta/Translations/zh_CN.lproj/Localizable.strings # Session/Notifications/AppNotifications.swift # Session/Notifications/SyncPushTokensJob.swift # Session/Notifications/UserNotificationsAdaptee.swift # SessionMessagingKit/Configuration.swift # SessionMessagingKit/Database/Models/Interaction.swift # SessionMessagingKit/Database/Models/SessionThread.swift # SessionMessagingKit/Jobs/Types/MessageReceiveJob.swift # SessionMessagingKit/Jobs/Types/MessageSendJob.swift # SessionMessagingKit/Jobs/Types/SendReadReceiptsJob.swift # SessionMessagingKit/Messages/Message.swift # SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ReadReceipts.swift # SessionMessagingKit/Sending & Receiving/MessageSender+Convenience.swift # SessionMessagingKit/Sending & Receiving/MessageSender.swift # SessionMessagingKit/Shared Models/MentionInfo.swift
237 lines
11 KiB
Swift
237 lines
11 KiB
Swift
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
|
|
|
import Foundation
|
|
import Combine
|
|
import GRDB
|
|
import LocalAuthentication
|
|
import DifferenceKit
|
|
import SessionUIKit
|
|
import SessionMessagingKit
|
|
import SessionUtilitiesKit
|
|
|
|
class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.NavButton, PrivacySettingsViewModel.Section, PrivacySettingsViewModel.Item> {
|
|
private let shouldShowCloseButton: Bool
|
|
|
|
// MARK: - Initialization
|
|
|
|
init(shouldShowCloseButton: Bool = false) {
|
|
self.shouldShowCloseButton = shouldShowCloseButton
|
|
|
|
super.init()
|
|
}
|
|
|
|
// MARK: - Config
|
|
|
|
enum NavButton: Equatable {
|
|
case close
|
|
}
|
|
|
|
public enum Section: SessionTableSection {
|
|
case screenSecurity
|
|
case readReceipts
|
|
case typingIndicators
|
|
case linkPreviews
|
|
case calls
|
|
|
|
var title: String? {
|
|
switch self {
|
|
case .screenSecurity: return "PRIVACY_SECTION_SCREEN_SECURITY".localized()
|
|
case .readReceipts: return "PRIVACY_SECTION_READ_RECEIPTS".localized()
|
|
case .typingIndicators: return "PRIVACY_SECTION_TYPING_INDICATORS".localized()
|
|
case .linkPreviews: return "PRIVACY_SECTION_LINK_PREVIEWS".localized()
|
|
case .calls: return "PRIVACY_SECTION_CALLS".localized()
|
|
}
|
|
}
|
|
|
|
var style: SessionTableSectionStyle { return .titleRoundedContent }
|
|
}
|
|
|
|
public enum Item: Differentiable {
|
|
case screenLock
|
|
case screenshotNotifications
|
|
case readReceipts
|
|
case typingIndicators
|
|
case linkPreviews
|
|
case calls
|
|
}
|
|
|
|
// MARK: - Navigation
|
|
|
|
override var leftNavItems: AnyPublisher<[NavItem]?, Never> {
|
|
guard self.shouldShowCloseButton else { return Just([]).eraseToAnyPublisher() }
|
|
|
|
return Just([
|
|
NavItem(
|
|
id: .close,
|
|
image: UIImage(named: "X")?
|
|
.withRenderingMode(.alwaysTemplate),
|
|
style: .plain,
|
|
accessibilityIdentifier: "Close Button"
|
|
) { [weak self] in
|
|
self?.dismissScreen()
|
|
}
|
|
]).eraseToAnyPublisher()
|
|
}
|
|
|
|
// MARK: - Content
|
|
|
|
override var title: String { "PRIVACY_TITLE".localized() }
|
|
|
|
public override var observableTableData: ObservableData { _observableTableData }
|
|
|
|
/// This is all the data the screen needs to populate itself, please see the following link for tips to help optimise
|
|
/// performance https://github.com/groue/GRDB.swift#valueobservation-performance
|
|
///
|
|
/// **Note:** This observation will be triggered twice immediately (and be de-duped by the `removeDuplicates`)
|
|
/// this is due to the behaviour of `ValueConcurrentObserver.asyncStartObservation` which triggers it's own
|
|
/// fetch (after the ones in `ValueConcurrentObserver.asyncStart`/`ValueConcurrentObserver.syncStart`)
|
|
/// just in case the database has changed between the two reads - unfortunately it doesn't look like there is a way to prevent this
|
|
private lazy var _observableTableData: ObservableData = ValueObservation
|
|
.trackingConstantRegion { db -> [SectionModel] in
|
|
return [
|
|
SectionModel(
|
|
model: .screenSecurity,
|
|
elements: [
|
|
SessionCell.Info(
|
|
id: .screenLock,
|
|
title: "PRIVACY_SCREEN_SECURITY_LOCK_SESSION_TITLE".localized(),
|
|
subtitle: "PRIVACY_SCREEN_SECURITY_LOCK_SESSION_DESCRIPTION".localized(),
|
|
rightAccessory: .toggle(.settingBool(key: .isScreenLockEnabled)),
|
|
onTap: { [weak self] in
|
|
// Make sure the device has a passcode set before allowing screen lock to
|
|
// be enabled (Note: This will always return true on a simulator)
|
|
guard LAContext().canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) else {
|
|
self?.transitionToScreen(
|
|
ConfirmationModal(
|
|
info: ConfirmationModal.Info(
|
|
title: "SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE".localized(),
|
|
cancelTitle: "BUTTON_OK".localized(),
|
|
cancelStyle: .alert_text
|
|
)
|
|
),
|
|
transitionType: .present
|
|
)
|
|
return
|
|
}
|
|
|
|
Storage.shared.write { db in
|
|
db[.isScreenLockEnabled] = !db[.isScreenLockEnabled]
|
|
}
|
|
}
|
|
)
|
|
]
|
|
),
|
|
SectionModel(
|
|
model: .readReceipts,
|
|
elements: [
|
|
SessionCell.Info(
|
|
id: .readReceipts,
|
|
title: "PRIVACY_READ_RECEIPTS_TITLE".localized(),
|
|
subtitle: "PRIVACY_READ_RECEIPTS_DESCRIPTION".localized(),
|
|
rightAccessory: .toggle(.settingBool(key: .areReadReceiptsEnabled)),
|
|
onTap: {
|
|
Storage.shared.write { db in
|
|
db[.areReadReceiptsEnabled] = !db[.areReadReceiptsEnabled]
|
|
}
|
|
}
|
|
)
|
|
]
|
|
),
|
|
SectionModel(
|
|
model: .typingIndicators,
|
|
elements: [
|
|
SessionCell.Info(
|
|
id: .typingIndicators,
|
|
title: SessionCell.TextInfo(
|
|
"PRIVACY_TYPING_INDICATORS_TITLE".localized(),
|
|
font: .title
|
|
),
|
|
subtitle: SessionCell.TextInfo(
|
|
"PRIVACY_TYPING_INDICATORS_DESCRIPTION".localized(),
|
|
font: .subtitle,
|
|
extraViewGenerator: {
|
|
let targetHeight: CGFloat = 20
|
|
let targetWidth: CGFloat = ceil(20 * (targetHeight / 12))
|
|
let result: UIView = UIView(
|
|
frame: CGRect(x: 0, y: 0, width: targetWidth, height: targetHeight)
|
|
)
|
|
result.set(.width, to: targetWidth)
|
|
result.set(.height, to: targetHeight)
|
|
|
|
// Use a transform scale to reduce the size of the typing indicator to the
|
|
// desired size (this way the animation remains intact)
|
|
let cell: TypingIndicatorCell = TypingIndicatorCell()
|
|
cell.transform = CGAffineTransform.scale(targetHeight / cell.bounds.height)
|
|
cell.typingIndicatorView.startAnimation()
|
|
result.addSubview(cell)
|
|
|
|
// Note: Because we are messing with the transform these values don't work
|
|
// logically so we inset the positioning to make it look visually centered
|
|
// within the layout inspector
|
|
cell.center(.vertical, in: result, withInset: -(targetHeight * 0.15))
|
|
cell.center(.horizontal, in: result, withInset: -(targetWidth * 0.35))
|
|
cell.set(.width, to: .width, of: result)
|
|
cell.set(.height, to: .height, of: result)
|
|
|
|
return result
|
|
}
|
|
),
|
|
rightAccessory: .toggle(.settingBool(key: .typingIndicatorsEnabled)),
|
|
onTap: {
|
|
Storage.shared.write { db in
|
|
db[.typingIndicatorsEnabled] = !db[.typingIndicatorsEnabled]
|
|
}
|
|
}
|
|
)
|
|
]
|
|
),
|
|
SectionModel(
|
|
model: .linkPreviews,
|
|
elements: [
|
|
SessionCell.Info(
|
|
id: .linkPreviews,
|
|
title: "PRIVACY_LINK_PREVIEWS_TITLE".localized(),
|
|
subtitle: "PRIVACY_LINK_PREVIEWS_DESCRIPTION".localized(),
|
|
rightAccessory: .toggle(.settingBool(key: .areLinkPreviewsEnabled)),
|
|
onTap: {
|
|
Storage.shared.write { db in
|
|
db[.areLinkPreviewsEnabled] = !db[.areLinkPreviewsEnabled]
|
|
}
|
|
}
|
|
)
|
|
]
|
|
),
|
|
SectionModel(
|
|
model: .calls,
|
|
elements: [
|
|
SessionCell.Info(
|
|
id: .calls,
|
|
title: "PRIVACY_CALLS_TITLE".localized(),
|
|
subtitle: "PRIVACY_CALLS_DESCRIPTION".localized(),
|
|
rightAccessory: .toggle(.settingBool(key: .areCallsEnabled)),
|
|
accessibility: SessionCell.Accessibility(
|
|
label: "Allow voice and video calls"
|
|
),
|
|
confirmationInfo: ConfirmationModal.Info(
|
|
title: "PRIVACY_CALLS_WARNING_TITLE".localized(),
|
|
explanation: "PRIVACY_CALLS_WARNING_DESCRIPTION".localized(),
|
|
stateToShow: .whenDisabled,
|
|
confirmTitle: "continue_2".localized(),
|
|
confirmAccessibilityLabel: "Enable",
|
|
confirmStyle: .textPrimary,
|
|
onConfirm: { _ in Permissions.requestMicrophonePermissionIfNeeded() }
|
|
),
|
|
onTap: {
|
|
Storage.shared.write { db in
|
|
db[.areCallsEnabled] = !db[.areCallsEnabled]
|
|
}
|
|
}
|
|
)
|
|
]
|
|
)
|
|
]
|
|
}
|
|
.removeDuplicates()
|
|
.publisher(in: Storage.shared)
|
|
.mapToSessionTableViewData(for: self)
|
|
}
|