Swiftgram/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift
2024-03-28 16:02:30 +04:00

243 lines
9.4 KiB
Swift

import Foundation
import UIKit
import Display
import SwiftSignalKit
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
import ItemListUI
import PresentationDataUtils
import AccountContext
import UndoUI
import PremiumUI
private final class IncomingMessagePrivacyScreenArguments {
let context: AccountContext
let updateValue: (Bool) -> Void
let disabledValuePressed: () -> Void
let infoLinkAction: () -> Void
init(
context: AccountContext,
updateValue: @escaping (Bool) -> Void,
disabledValuePressed: @escaping () -> Void,
infoLinkAction: @escaping () -> Void
) {
self.context = context
self.updateValue = updateValue
self.disabledValuePressed = disabledValuePressed
self.infoLinkAction = infoLinkAction
}
}
private enum IncomingMessagePrivacySection: Int32 {
case header
case info
}
private enum GlobalAutoremoveEntry: ItemListNodeEntry {
case header
case optionEverybody(value: Bool)
case optionPremium(value: Bool, isEnabled: Bool)
case footer
case info
var section: ItemListSectionId {
switch self {
case .header, .optionEverybody, .optionPremium, .footer:
return IncomingMessagePrivacySection.header.rawValue
case .info:
return IncomingMessagePrivacySection.info.rawValue
}
}
var stableId: Int {
return self.sortIndex
}
var sortIndex: Int {
switch self {
case .header:
return 0
case .optionEverybody:
return 1
case .optionPremium:
return 2
case .footer:
return 3
case .info:
return 4
}
}
static func <(lhs: GlobalAutoremoveEntry, rhs: GlobalAutoremoveEntry) -> Bool {
return lhs.sortIndex < rhs.sortIndex
}
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
let arguments = arguments as! IncomingMessagePrivacyScreenArguments
switch self {
case .header:
return ItemListSectionHeaderItem(presentationData: presentationData, text: presentationData.strings.Privacy_Messages_SectionTitle, sectionId: self.section)
case let .optionEverybody(value):
return ItemListCheckboxItem(presentationData: presentationData, title: presentationData.strings.Privacy_Messages_ValueEveryone, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.updateValue(false)
})
case let .optionPremium(value, isEnabled):
return ItemListCheckboxItem(presentationData: presentationData, icon: isEnabled ? nil : generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/Lock"), color: presentationData.theme.list.itemSecondaryTextColor), iconPlacement: .check, title: presentationData.strings.Privacy_Messages_ValueContactsAndPremium, style: .left, checked: isEnabled && value, zeroSeparatorInsets: false, sectionId: self.section, action: {
if isEnabled {
arguments.updateValue(true)
} else {
arguments.disabledValuePressed()
}
})
case .footer:
return ItemListTextItem(presentationData: presentationData, text: .plain(presentationData.strings.Privacy_Messages_SectionFooter), sectionId: self.section)
case .info:
return ItemListTextItem(presentationData: presentationData, text: .markdown(presentationData.strings.Privacy_Messages_PremiumInfoFooter), sectionId: self.section, linkAction: { _ in
arguments.infoLinkAction()
})
}
}
}
private struct IncomingMessagePrivacyScreenState: Equatable {
var updatedValue: Bool
}
private func incomingMessagePrivacyScreenEntries(presentationData: PresentationData, state: IncomingMessagePrivacyScreenState, isPremium: Bool) -> [GlobalAutoremoveEntry] {
var entries: [GlobalAutoremoveEntry] = []
entries.append(.header)
entries.append(.optionEverybody(value: !state.updatedValue))
entries.append(.optionPremium(value: state.updatedValue, isEnabled: isPremium))
entries.append(.footer)
entries.append(.info)
return entries
}
public func incomingMessagePrivacyScreen(context: AccountContext, value: Bool, update: @escaping (Bool) -> Void) -> ViewController {
let initialState = IncomingMessagePrivacyScreenState(
updatedValue: value
)
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let stateValue = Atomic(value: initialState)
let updateState: ((IncomingMessagePrivacyScreenState) -> IncomingMessagePrivacyScreenState) -> Void = { f in
statePromise.set(stateValue.modify { f($0) })
}
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
var presentInCurrentControllerImpl: ((ViewController) -> Void)?
var pushControllerImpl: ((ViewController) -> Void)?
var dismissImpl: (() -> Void)?
let _ = dismissImpl
let _ = pushControllerImpl
let _ = presentControllerImpl
let actionsDisposable = DisposableSet()
let updateTimeoutDisposable = MetaDisposable()
actionsDisposable.add(updateTimeoutDisposable)
let arguments = IncomingMessagePrivacyScreenArguments(
context: context,
updateValue: { value in
updateState { state in
var state = state
state.updatedValue = value
return state
}
},
disabledValuePressed: {
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
presentInCurrentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: presentationData.strings.Privacy_Messages_PremiumToast_Title, text: presentationData.strings.Privacy_Messages_PremiumToast_Text, customUndoText: presentationData.strings.Privacy_Messages_PremiumToast_Action, timeout: nil, linkAction: { _ in
}), elevatedLayout: false, action: { action in
if case .undo = action {
let controller = PremiumIntroScreen(context: context, source: .settings)
pushControllerImpl?(controller)
}
return false
}))
},
infoLinkAction: {
let controller = PremiumIntroScreen(context: context, source: .settings)
pushControllerImpl?(controller)
}
)
let enableSetting: Signal<Bool, NoError> = context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId),
TelegramEngine.EngineData.Item.Configuration.App()
)
|> map { accountPeer, appConfig -> Bool in
if let accountPeer, accountPeer.isPremium {
return true
}
if let data = appConfig.data, let setting = data["new_noncontact_peers_require_premium_without_ownpremium"] as? Bool {
if setting {
return true
}
}
return false
}
|> distinctUntilChanged
let signal = combineLatest(queue: .mainQueue(),
context.sharedContext.presentationData,
statePromise.get(),
enableSetting
)
|> map { presentationData, state, enableSetting -> (ItemListControllerState, (ItemListNodeState, Any)) in
let rightNavigationButton: ItemListNavigationButton? = nil
let title: ItemListControllerTitle = .text(presentationData.strings.Privacy_Messages_Title)
let entries: [GlobalAutoremoveEntry] = incomingMessagePrivacyScreenEntries(presentationData: presentationData, state: state, isPremium: enableSetting)
let animateChanges = false
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: title, leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: animateChanges, scrollEnabled: true)
return (controllerState, (listState, arguments))
}
|> afterDisposed {
actionsDisposable.dispose()
}
let controller = ItemListController(context: context, state: signal)
presentControllerImpl = { [weak controller] c, p in
guard let controller else {
return
}
controller.present(c, in: .window(.root), with: p)
}
presentInCurrentControllerImpl = { [weak controller] c in
guard let controller else {
return
}
controller.forEachController { c in
if let c = c as? UndoOverlayController {
c.dismiss()
}
return true
}
controller.present(c, in: .current, with: nil)
}
pushControllerImpl = { [weak controller] c in
controller?.push(c)
}
controller.attemptNavigation = { _ in
update(stateValue.with({ $0 }).updatedValue)
return true
}
dismissImpl = { [weak controller] in
controller?.dismiss()
}
return controller
}