mirror of
https://github.com/GLEGram/GLEGram-iOS.git
synced 2026-04-23 19:36:26 +02:00
4647310322
Based on Swiftgram 12.5 (Telegram iOS 12.5). All GLEGram features ported and organized in GLEGram/ folder. Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass, Font Replacement, Fake Profile, Chat Export, Plugin System, and more. See CHANGELOG_12.5.md for full details.
211 lines
8.6 KiB
Swift
211 lines
8.6 KiB
Swift
// MARK: Swiftgram - Double Bottom (full logic from Nicegram NGDoubleBottom/DoubleBottomListController)
|
|
// Ref: https://github.com/nicegram/Nicegram-iOS/blob/master/Nicegram/NGDoubleBottom/Sources/DoubleBottomListController.swift
|
|
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import SwiftSignalKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import TelegramPresentationData
|
|
import ItemListUI
|
|
import PresentationDataUtils
|
|
import AccountContext
|
|
import PasscodeUI
|
|
import DoubleBottom
|
|
import SGSimpleSettings
|
|
import TelegramStringFormatting
|
|
|
|
// MARK: - GLEGram
|
|
|
|
// MARK: - Section (Nicegram: DoubleBottomControllerSection)
|
|
|
|
private enum DoubleBottomControllerSection: Int32 {
|
|
case isOn = 0
|
|
}
|
|
|
|
// MARK: - Entry (Nicegram: isOn + info)
|
|
|
|
private enum DoubleBottomEntry: ItemListNodeEntry {
|
|
case isOn(String, Bool, Bool) // title, value, enabled
|
|
case info(String)
|
|
|
|
var section: ItemListSectionId { DoubleBottomControllerSection.isOn.rawValue }
|
|
|
|
var stableId: Int32 {
|
|
switch self {
|
|
case .isOn: return 1000
|
|
case .info: return 1100
|
|
}
|
|
}
|
|
|
|
static func < (lhs: DoubleBottomEntry, rhs: DoubleBottomEntry) -> Bool {
|
|
lhs.stableId < rhs.stableId
|
|
}
|
|
|
|
static func == (lhs: DoubleBottomEntry, rhs: DoubleBottomEntry) -> Bool {
|
|
switch (lhs, rhs) {
|
|
case let (.isOn(lhsText, lhsBool, _), .isOn(rhsText, rhsBool, _)):
|
|
return lhsText == rhsText && lhsBool == rhsBool
|
|
case let (.info(lhsText), .info(rhsText)):
|
|
return lhsText == rhsText
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
|
let args = arguments as! DoubleBottomArguments
|
|
switch self {
|
|
case let .isOn(text, value, enabled):
|
|
return ItemListSwitchItem(
|
|
presentationData: presentationData,
|
|
title: text,
|
|
value: value,
|
|
enabled: enabled,
|
|
sectionId: section,
|
|
style: .blocks,
|
|
updated: { value in
|
|
args.updated(value)
|
|
}
|
|
)
|
|
case let .info(text):
|
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: section)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Arguments (Nicegram: DoubleBottomControllerArguments)
|
|
|
|
private final class DoubleBottomArguments {
|
|
let context: AccountContext
|
|
let updated: (Bool) -> Void
|
|
init(context: AccountContext, updated: @escaping (Bool) -> Void) {
|
|
self.context = context
|
|
self.updated = updated
|
|
}
|
|
}
|
|
|
|
// MARK: - Controller (logic from Nicegram DoubleBottomListController)
|
|
|
|
public func doubleBottomSettingsController(context: AccountContext) -> ViewController {
|
|
let lang = context.sharedContext.currentPresentationData.with { $0 }.strings.baseLanguageCode
|
|
let title = lang == "ru" ? "Двойное дно" : "Double Bottom"
|
|
let toggleTitle = lang == "ru" ? "Двойное дно" : "Double Bottom"
|
|
let noticeText = lang == "ru"
|
|
? "Скрытые аккаунты и вход по паролю. Разные пароли открывают разные профили."
|
|
: "Hidden accounts and passcode access. Different passwords open different profiles."
|
|
|
|
let arguments = DoubleBottomArguments(context: context, updated: { value in
|
|
if value {
|
|
SGSimpleSettings.shared.doubleBottomEnabled = true
|
|
let setupController = PasscodeSetupController(context: context, mode: .setup(change: false, .digits6))
|
|
setupController.complete = { passcode, _ in
|
|
DoubleBottomPasscodeStore.setSecretPasscode(passcode)
|
|
setupController.dismiss()
|
|
}
|
|
context.sharedContext.presentGlobalController(setupController, nil)
|
|
} else {
|
|
SGSimpleSettings.shared.doubleBottomEnabled = false
|
|
DoubleBottomPasscodeStore.removeSecretPasscode()
|
|
DoubleBottomViewingSecretStore.setViewingWithSecretPasscode(false)
|
|
let accountManager = context.sharedContext.accountManager
|
|
// Remove secret passcodes from Keychain for previously hidden accounts
|
|
let _ = (accountManager.accountRecords()
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { view in
|
|
for record in view.records where record.attributes.contains(where: { $0.isHiddenAccountAttribute }) {
|
|
DoubleBottomPasscodeStore.removePasscode(forAccountId: record.id.int64)
|
|
}
|
|
})
|
|
// Nicegram: single transaction - keep device passcode, remove HiddenAccount from all records
|
|
let _ = accountManager.transaction { transaction in
|
|
let challengeData = transaction.getAccessChallengeData()
|
|
let challenge: PostboxAccessChallengeData
|
|
switch challengeData {
|
|
case .numericalPassword(let value):
|
|
challenge = .numericalPassword(value: value)
|
|
case .plaintextPassword(let value):
|
|
challenge = .plaintextPassword(value: value)
|
|
case .none:
|
|
challenge = .none
|
|
}
|
|
transaction.setAccessChallengeData(challenge)
|
|
for record in transaction.getRecords() {
|
|
transaction.updateRecord(record.id) { current in
|
|
guard let current = current else { return nil }
|
|
var attributes = current.attributes
|
|
attributes.removeAll { $0.isHiddenAccountAttribute }
|
|
return AccountRecord(id: current.id, attributes: attributes, temporarySessionId: current.temporarySessionId)
|
|
}
|
|
}
|
|
}.start()
|
|
}
|
|
})
|
|
|
|
let transactionStatus = context.sharedContext.accountManager.transaction { transaction -> (Bool, Bool) in
|
|
let records = transaction.getRecords()
|
|
let publicCount = records.filter { record in
|
|
let attrs = record.attributes
|
|
let hiddenOrLoggedOut = attrs.contains(where: { $0.isHiddenAccountAttribute || $0.isLoggedOutAccountAttribute })
|
|
return !hiddenOrLoggedOut
|
|
}.count
|
|
let hasMoreThanOnePublic = publicCount > 1
|
|
let hasMainPasscode = transaction.getAccessChallengeData() != .none
|
|
return (hasMoreThanOnePublic, hasMainPasscode)
|
|
}
|
|
|
|
let signal: Signal<(ItemListControllerState, (ItemListNodeState, DoubleBottomArguments)), NoError> = combineLatest(context.sharedContext.presentationData, transactionStatus)
|
|
|> map { presentationData, contextStatus -> (ItemListControllerState, (ItemListNodeState, DoubleBottomArguments)) in
|
|
let isOn = SGSimpleSettings.shared.doubleBottomEnabled
|
|
let enabled = isOn || (contextStatus.0 && contextStatus.1)
|
|
let entries: [DoubleBottomEntry] = [
|
|
.isOn(toggleTitle, isOn, enabled),
|
|
.info(noticeText)
|
|
]
|
|
let controllerState = ItemListControllerState(
|
|
presentationData: ItemListPresentationData(presentationData),
|
|
title: .text(title),
|
|
leftNavigationButton: nil,
|
|
rightNavigationButton: nil,
|
|
backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)
|
|
)
|
|
let listState = ItemListNodeState(
|
|
presentationData: ItemListPresentationData(presentationData),
|
|
entries: entries,
|
|
style: .blocks,
|
|
ensureVisibleItemTag: nil,
|
|
footerItem: nil,
|
|
initialScrollToItem: nil
|
|
)
|
|
return (controllerState, (listState, arguments))
|
|
}
|
|
|
|
return ItemListController(context: context, state: signal)
|
|
}
|
|
|
|
// MARK: - Passcode check (Nicegram: check(passcode:challengeData:) for device passcode validation)
|
|
|
|
public func doubleBottomCheckPasscode(_ passcode: String, challengeData: PostboxAccessChallengeData) -> Bool {
|
|
let passcodeType: PasscodeEntryFieldType
|
|
switch challengeData {
|
|
case let .numericalPassword(value):
|
|
passcodeType = value.count == 6 ? .digits6 : .digits4
|
|
default:
|
|
passcodeType = .alphanumeric
|
|
}
|
|
switch challengeData {
|
|
case .none:
|
|
return true
|
|
case let .numericalPassword(code):
|
|
if passcodeType == .alphanumeric {
|
|
return false
|
|
}
|
|
return passcode == normalizeArabicNumeralString(code, type: .western)
|
|
case let .plaintextPassword(code):
|
|
if passcodeType != .alphanumeric {
|
|
return false
|
|
}
|
|
return passcode == code
|
|
}
|
|
}
|