mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-07-10 09:58:35 +02:00
Update Ghostgram features
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
import SGStrings
|
||||
import SGSimpleSettings
|
||||
|
||||
import Foundation
|
||||
import SwiftSignalKit
|
||||
import UIKit
|
||||
@@ -156,6 +159,8 @@ public final class AccountContextImpl: AccountContext {
|
||||
return self._countriesConfiguration.get()
|
||||
}
|
||||
|
||||
public var watchManager: WatchManager?
|
||||
|
||||
private var storedPassword: (String, CFAbsoluteTime, SwiftSignalKit.Timer)?
|
||||
private var limitsConfigurationDisposable: Disposable?
|
||||
private var contentSettingsDisposable: Disposable?
|
||||
@@ -830,6 +835,8 @@ public final class AccountContextImpl: AccountContext {
|
||||
}
|
||||
|
||||
public func requestCall(peerId: PeerId, isVideo: Bool, completion: @escaping () -> Void) {
|
||||
// MARK: Swiftgram
|
||||
let makeCall = {
|
||||
guard let callResult = self.sharedContext.callManager?.requestCall(context: self, peerId: peerId, isVideo: isVideo, endCurrentIfAny: false) else {
|
||||
return
|
||||
}
|
||||
@@ -897,6 +904,19 @@ public final class AccountContextImpl: AccountContext {
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
// MARK: Swiftgram
|
||||
}
|
||||
if SGSimpleSettings.shared.confirmCalls {
|
||||
let presentationData = self.sharedContext.currentPresentationData.with { $0 }
|
||||
self.sharedContext.mainWindow?.present(textAlertController(context: self, title: nil, text: isVideo ? i18n("CallConfirmation.Video.Title", presentationData.strings.baseLanguageCode) : i18n("CallConfirmation.Audio.Title", presentationData.strings.baseLanguageCode), actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Yes, action: { [weak self] in
|
||||
guard let _ = self else {
|
||||
return
|
||||
}
|
||||
makeCall()
|
||||
})]), on: .root)
|
||||
} else {
|
||||
makeCall()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
// MARK: Swiftgram
|
||||
import StoreKit
|
||||
import SGIAP
|
||||
import SGAPI
|
||||
import SGDeviceToken
|
||||
import SGAPIToken
|
||||
|
||||
import SGActionRequestHandlerSanitizer
|
||||
import SGGHSettings
|
||||
import SGAPIWebSettings
|
||||
import SGLogging
|
||||
import SGStrings
|
||||
import SGSimpleSettings
|
||||
import SGStatus
|
||||
import SGSwiftSignalKit
|
||||
import UIKit
|
||||
@preconcurrency import SwiftSignalKit
|
||||
import SwiftSignalKit
|
||||
import Display
|
||||
import TelegramCore
|
||||
import UserNotifications
|
||||
@@ -19,6 +34,7 @@ import OverlayStatusController
|
||||
import UndoUI
|
||||
import LegacyUI
|
||||
import PassportUI
|
||||
import WatchBridge
|
||||
import SettingsUI
|
||||
import AppBundle
|
||||
import UrlHandling
|
||||
@@ -41,8 +57,10 @@ import MediaEditor
|
||||
import TelegramUIDeclareEncodables
|
||||
import ContextMenuScreen
|
||||
import MetalEngine
|
||||
import RecaptchaEnterpriseSDK
|
||||
import RecaptchaEnterprise
|
||||
import NavigationBarImpl
|
||||
import ContextUI
|
||||
import ContextControllerImpl
|
||||
|
||||
#if canImport(AppCenter)
|
||||
import AppCenter
|
||||
@@ -150,6 +168,22 @@ private func legacyDocumentsPath() -> String {
|
||||
return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/legacy"
|
||||
}
|
||||
|
||||
private func getContextForUserId(context: AccountContext, userId: Int64) -> Signal<AccountContext, NoError> {
|
||||
return context.sharedContext.activeAccountContexts
|
||||
|> take(1)
|
||||
|> mapToSignal { primary, accounts, _ -> Signal<AccountContext, NoError> in
|
||||
if let primary, primary.account.peerId.id._internalGetInt64Value() == userId {
|
||||
return .single(primary)
|
||||
}
|
||||
if let match = accounts.first(where: { account in
|
||||
account.1.account.peerId.id._internalGetInt64Value() == userId
|
||||
})?.1 {
|
||||
return .single(match)
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
protocol SupportedStartCallIntent {
|
||||
@available(iOS 10.0, *)
|
||||
var contacts: [INPerson]? { get }
|
||||
@@ -228,6 +262,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
let hasActiveAudioSession = Promise<Bool>(false)
|
||||
|
||||
private let sharedContextPromise = Promise<SharedApplicationContext>()
|
||||
private let watchCommunicationManagerPromise = Promise<WatchCommunicationManager?>()
|
||||
|
||||
private var accountManager: AccountManager<TelegramAccountManagerTypes>?
|
||||
private var accountManagerState: AccountManagerState?
|
||||
@@ -589,6 +624,12 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
}
|
||||
|
||||
let rootPath = rootPathForBasePath(appGroupUrl.path)
|
||||
// MARK: Swiftgram
|
||||
if UserDefaults.standard.bool(forKey: "sg_db_hard_reset") {
|
||||
self.window?.makeKeyAndVisible()
|
||||
sgHardReset(dataPath: rootPath, present: self.mainWindow?.presentNative)
|
||||
return true
|
||||
}
|
||||
performAppGroupUpgrades(appGroupPath: appGroupUrl.path, rootPath: rootPath)
|
||||
|
||||
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
|
||||
@@ -882,6 +923,31 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
icons.append(PresentationAppIcon(name: "PremiumTurbo", imageName: "PremiumTurbo", isPremium: true))
|
||||
icons.append(PresentationAppIcon(name: "PremiumBlack", imageName: "PremiumBlack", isPremium: true))
|
||||
|
||||
|
||||
// MARK: Swiftgram
|
||||
icons = [
|
||||
PresentationAppIcon(name: "SGDefault", imageName: "SGDefault", isDefault: true),
|
||||
PresentationAppIcon(name: "SGBlack", imageName: "SGBlack"),
|
||||
PresentationAppIcon(name: "SGLegacy", imageName: "SGLegacy"),
|
||||
PresentationAppIcon(name: "SGInverted", imageName: "SGInverted"),
|
||||
PresentationAppIcon(name: "SGWhite", imageName: "SGWhite"),
|
||||
PresentationAppIcon(name: "SGNight", imageName: "SGNight"),
|
||||
PresentationAppIcon(name: "SGSky", imageName: "SGSky"),
|
||||
PresentationAppIcon(name: "SGTitanium", imageName: "SGTitanium"),
|
||||
PresentationAppIcon(name: "SGPro", imageName: "SGPro", isSGPro: true),
|
||||
PresentationAppIcon(name: "SGDay", imageName: "SGDay", isSGPro: true),
|
||||
PresentationAppIcon(name: "SGGold", imageName: "SGGold", isSGPro: true),
|
||||
SGSimpleSettings.shared.duckyAppIconAvailable ? PresentationAppIcon(name: "SGDucky", imageName: "SGDucky", isSGPro: true) : PresentationAppIcon(name: "", imageName: ""), // Empty
|
||||
PresentationAppIcon(name: "SGNeon", imageName: "SGNeon"),
|
||||
PresentationAppIcon(name: "SGNeonBlue", imageName: "SGNeonBlue"),
|
||||
PresentationAppIcon(name: "SGGlass", imageName: "SGGlass"),
|
||||
PresentationAppIcon(name: "SGSparkling", imageName: "SGSparkling"),
|
||||
]
|
||||
|
||||
if Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" {
|
||||
icons.append(PresentationAppIcon(name: "SGBeta", imageName: "SGBeta"))
|
||||
}
|
||||
|
||||
return icons
|
||||
} else {
|
||||
return []
|
||||
@@ -1052,7 +1118,26 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
|
||||
let watchTasks = self.context.get()
|
||||
|> mapToSignal { context -> Signal<AccountRecordId?, NoError> in
|
||||
if let context = context, let watchManager = context.context.watchManager {
|
||||
let accountId = context.context.account.id
|
||||
let runningTasks: Signal<WatchRunningTasks?, NoError> = .single(nil)
|
||||
|> then(watchManager.runningTasks)
|
||||
return runningTasks
|
||||
|> distinctUntilChanged
|
||||
|> map { value -> AccountRecordId? in
|
||||
if let value = value, value.running {
|
||||
return accountId
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
let wakeupManager = SharedWakeupManager(beginBackgroundTask: { name, expiration in
|
||||
let id = application.beginBackgroundTask(withName: name, expirationHandler: expiration)
|
||||
Logger.shared.log("App \(self.episodeId)", "Begin background task \(name): \(id)")
|
||||
@@ -1064,7 +1149,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
application.endBackgroundTask(id)
|
||||
}, backgroundTimeRemaining: { application.backgroundTimeRemaining }, acquireIdleExtension: {
|
||||
return applicationBindings.pushIdleTimerExtension()
|
||||
}, activeAccounts: sharedContext.activeAccountContexts |> map { ($0.0?.account, $0.1.map { ($0.0, $0.1.account) }) }, liveLocationPolling: liveLocationPolling, watchTasks: .single(nil), inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: self.hasActiveAudioSession.get(), notificationManager: notificationManager, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
|
||||
}, activeAccounts: sharedContext.activeAccountContexts |> map { ($0.0?.account, $0.1.map { ($0.0, $0.1.account) }) }, liveLocationPolling: liveLocationPolling, watchTasks: watchTasks /* MARK: Swiftgram */, inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: self.hasActiveAudioSession.get(), notificationManager: notificationManager, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
|
||||
return sharedContext.accountUserInterfaceInUse(id)
|
||||
})
|
||||
let sharedApplicationContext = SharedApplicationContext(sharedContext: sharedContext, notificationManager: notificationManager, wakeupManager: wakeupManager)
|
||||
@@ -1075,13 +1160,16 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
}
|
||||
}
|
||||
self.sharedContextPromise.set(sharedContextSignal
|
||||
|> mapToSignal { sharedApplicationContext, loggingSettings -> Signal<SharedApplicationContext, NoError> in
|
||||
|> mapToSignal { value -> Signal<SharedApplicationContext, NoError> in
|
||||
let (sharedApplicationContext, loggingSettings) = value
|
||||
Logger.shared.logToFile = loggingSettings.logToFile
|
||||
Logger.shared.logToConsole = loggingSettings.logToConsole
|
||||
Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData
|
||||
|
||||
return .single(sharedApplicationContext)
|
||||
})
|
||||
|
||||
let watchManagerArgumentsPromise = Promise<WatchManagerArguments?>()
|
||||
|
||||
self.context.set(self.sharedContextPromise.get()
|
||||
|> deliverOnMainQueue
|
||||
@@ -1096,7 +1184,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
}
|
||||
return true
|
||||
})
|
||||
|> mapToSignal { context -> Signal<(AccountContext, CallListSettings)?, NoError> in
|
||||
|> mapToSignal { (context: AccountContext?) -> Signal<(AccountContext, CallListSettings)?, NoError> in
|
||||
return sharedApplicationContext.sharedContext.accountManager.transaction { transaction -> CallListSettings? in
|
||||
return transaction.getSharedData(ApplicationSpecificSharedDataKeys.callListSettings)?.get(CallListSettings.self)
|
||||
}
|
||||
@@ -1118,9 +1206,9 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue
|
||||
|> map { accountAndSettings -> AuthorizedApplicationContext? in
|
||||
|> map { (accountAndSettings: (AccountContext, CallListSettings)?) -> AuthorizedApplicationContext? in
|
||||
return accountAndSettings.flatMap { context, callListSettings in
|
||||
return AuthorizedApplicationContext(sharedApplicationContext: sharedApplicationContext, mainWindow: self.mainWindow, context: context as! AccountContextImpl, accountManager: sharedApplicationContext.sharedContext.accountManager, showCallsTab: callListSettings.showTab, reinitializedNotificationSettings: {
|
||||
return AuthorizedApplicationContext(sharedApplicationContext: sharedApplicationContext, mainWindow: self.mainWindow, watchManagerArguments: watchManagerArgumentsPromise.get(), context: context as! AccountContextImpl, accountManager: sharedApplicationContext.sharedContext.accountManager, showContactsTab: callListSettings.showContactsTab, showCallsTab: callListSettings.showTab, reinitializedNotificationSettings: {
|
||||
let _ = (self.context.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { context in
|
||||
@@ -1197,6 +1285,8 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
var network: Network?
|
||||
if let context = context {
|
||||
network = context.context.account.network
|
||||
// MARK: Swiftgram
|
||||
sgDBResetIfNeeded(databasePath: context.context.sharedContext.accountManager.basePath + "/db", present: self.mainWindow?.presentNative)
|
||||
}
|
||||
|
||||
Logger.shared.log("App \(self.episodeId)", "received context \(String(describing: context)) account \(String(describing: context?.context.account.id)) network \(String(describing: network))")
|
||||
@@ -1242,6 +1332,21 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
self.registerForNotifications(context: context.context, authorize: authorizeNotifications)
|
||||
|
||||
self.resetIntentsIfNeeded(context: context.context)
|
||||
|
||||
// MARK: Swiftgram
|
||||
updateSGWebSettingsInteractivelly(context: context.context)
|
||||
updateSGGHSettingsInteractivelly(context: context.context)
|
||||
let _ = (context.context.sharedContext.presentationData.start(next: { presentationData in
|
||||
SGLocalizationManager.shared.downloadLocale(presentationData.strings.baseLanguageCode)
|
||||
}))
|
||||
if #available(iOS 13.0, *) {
|
||||
let _ = Task {
|
||||
let primaryContext = await self.getPrimaryContext(anyContext: context.context)
|
||||
SGLogger.shared.log("SGIAP", "Verifying Status \(primaryContext.sharedContext.immediateSGStatus.status) for: \(primaryContext.account.peerId.id._internalGetInt64Value())")
|
||||
let _ = await self.fetchSGStatus(primaryContext: primaryContext)
|
||||
}
|
||||
}
|
||||
|
||||
}))
|
||||
} else {
|
||||
self.mainWindow.viewController = nil
|
||||
@@ -1311,6 +1416,12 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
authContextReadyDisposable.set(nil)
|
||||
}
|
||||
}))
|
||||
|
||||
|
||||
// MARK: Swiftgram
|
||||
if #available(iOS 13.0, *) {
|
||||
self.setupIAP()
|
||||
}
|
||||
|
||||
|
||||
let logoutDataSignal: Signal<(AccountManager, Set<PeerId>), NoError> = self.sharedContextPromise.get()
|
||||
@@ -1346,6 +1457,20 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
}).start()
|
||||
}))
|
||||
|
||||
self.watchCommunicationManagerPromise.set(watchCommunicationManager(context: self.context.get() |> flatMap { WatchCommunicationManagerContext(context: $0.context) }, allowBackgroundTimeExtension: { timeout in
|
||||
let _ = (self.sharedContextPromise.get()
|
||||
|> take(1)).start(next: { sharedContext in
|
||||
sharedContext.wakeupManager.allowBackgroundTimeExtension(timeout: timeout)
|
||||
})
|
||||
}))
|
||||
let _ = self.watchCommunicationManagerPromise.get().start(next: { manager in
|
||||
if let manager = manager {
|
||||
watchManagerArgumentsPromise.set(.single(manager.arguments))
|
||||
} else {
|
||||
watchManagerArgumentsPromise.set(.single(nil))
|
||||
}
|
||||
})
|
||||
|
||||
self.resetBadge()
|
||||
|
||||
if #available(iOS 9.1, *) {
|
||||
@@ -1392,7 +1517,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
|
||||
if let url = launchOptions?[.url] {
|
||||
if let url = url as? URL, url.scheme == "tg" || url.scheme == buildConfig.appSpecificUrlScheme {
|
||||
self.openUrlWhenReady(url: url)
|
||||
self.openUrlWhenReady(url: sgActionRequestHandlerSanitizer(url))
|
||||
} else if let urlString = url as? String, urlString.lowercased().hasPrefix("tg:") || urlString.lowercased().hasPrefix("\(buildConfig.appSpecificUrlScheme):"), let url = URL(string: urlString) {
|
||||
self.openUrlWhenReady(url: url)
|
||||
}
|
||||
@@ -1892,6 +2017,10 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
}
|
||||
|
||||
func runForegroundTasks() {
|
||||
|
||||
|
||||
var sgTasksLaunched: Bool = false
|
||||
|
||||
let _ = (self.sharedContextPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { sharedApplicationContext in
|
||||
@@ -1899,6 +2028,12 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { activeAccounts in
|
||||
for (_, context, _) in activeAccounts.accounts {
|
||||
// MARK: Swiftgram
|
||||
if !sgTasksLaunched {
|
||||
updateSGWebSettingsInteractivelly(context: context)
|
||||
updateSGGHSettingsInteractivelly(context: context)
|
||||
sgTasksLaunched = true
|
||||
}
|
||||
(context.downloadedMediaStoreManager as? DownloadedMediaStoreManagerImpl)?.runTasks()
|
||||
}
|
||||
})
|
||||
@@ -2414,6 +2549,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { sharedContext, context, authContext in
|
||||
let url = sgActionRequestHandlerSanitizer(url)
|
||||
if let authContext = authContext, let confirmationCode = parseConfirmationCodeUrl(sharedContext: sharedContext, url: url) {
|
||||
authContext.rootController.applyConfirmationCode(confirmationCode)
|
||||
} else if let context = context {
|
||||
@@ -3211,3 +3347,179 @@ final class UpdateSettings: Codable, Equatable {
|
||||
return lhs.url == rhs.url
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Swiftgram
|
||||
@available(iOS 13.0, *)
|
||||
extension AppDelegate {
|
||||
|
||||
func setupIAP() {
|
||||
NotificationCenter.default.addObserver(forName: .SGIAPHelperPurchaseNotification, object: nil, queue: nil) { [weak self] notification in
|
||||
SGLogger.shared.log("SGIAP", "Got SGIAPHelperPurchaseNotification")
|
||||
guard let strongSelf = self else { return }
|
||||
if let transactions = notification.object as? [SKPaymentTransaction] {
|
||||
let _ = (strongSelf.context.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak strongSelf] context in
|
||||
guard let veryStrongSelf = strongSelf else {
|
||||
SGLogger.shared.log("SGIAP", "Finishing transactions \(transactions.map({ $0.transactionIdentifier ?? "nil" }).joined(separator: ", "))")
|
||||
let defaultPaymentQueue = SKPaymentQueue.default()
|
||||
for transaction in transactions {
|
||||
defaultPaymentQueue.finishTransaction(transaction)
|
||||
}
|
||||
return
|
||||
}
|
||||
guard let context = context else {
|
||||
SGLogger.shared.log("SGIAP", "Empty app context (how?)")
|
||||
|
||||
SGLogger.shared.log("SGIAP", "Finishing transactions \(transactions.map({ $0.transactionIdentifier ?? "nil" }).joined(separator: ", "))")
|
||||
let defaultPaymentQueue = SKPaymentQueue.default()
|
||||
for transaction in transactions {
|
||||
defaultPaymentQueue.finishTransaction(transaction)
|
||||
}
|
||||
return
|
||||
}
|
||||
SGLogger.shared.log("SGIAP", "Got context for SGIAPHelperPurchaseNotification")
|
||||
let _ = Task {
|
||||
await veryStrongSelf.sendReceiptForVerification(primaryContext: context.context)
|
||||
await veryStrongSelf.fetchSGStatus(primaryContext: context.context)
|
||||
|
||||
SGLogger.shared.log("SGIAP", "Finishing transactions \(transactions.map({ $0.transactionIdentifier ?? "nil" }).joined(separator: ", "))")
|
||||
let defaultPaymentQueue = SKPaymentQueue.default()
|
||||
for transaction in transactions {
|
||||
defaultPaymentQueue.finishTransaction(transaction)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
SGLogger.shared.log("SGIAP", "Wrong object in SGIAPHelperPurchaseNotification")
|
||||
#if DEBUG
|
||||
preconditionFailure("Wrong object in SGIAPHelperPurchaseNotification")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getPrimaryContext(anyContext context: AccountContext, fallbackToCurrent: Bool = false) async -> AccountContext {
|
||||
var primaryUserId: Int64 = Int64(SGSimpleSettings.shared.primaryUserId) ?? 0
|
||||
if primaryUserId == 0 {
|
||||
primaryUserId = context.account.peerId.id._internalGetInt64Value()
|
||||
}
|
||||
|
||||
var primaryContext = try? await getContextForUserId(context: context, userId: primaryUserId).awaitable()
|
||||
if let primaryContext = primaryContext {
|
||||
SGLogger.shared.log("SGIAP", "Got primary context for user id: \(primaryContext.account.peerId.id._internalGetInt64Value())")
|
||||
return primaryContext
|
||||
} else {
|
||||
primaryContext = context
|
||||
let newPrimaryUserId = context.account.peerId.id._internalGetInt64Value()
|
||||
SGLogger.shared.log("SGIAP", "Primary context for user id \(primaryUserId) is nil! Falling back to current context with user id: \(newPrimaryUserId)")
|
||||
return context
|
||||
}
|
||||
}
|
||||
|
||||
func sendReceiptForVerification(primaryContext: AccountContext) async {
|
||||
guard let receiptData = getPurchaceReceiptData() else {
|
||||
return
|
||||
}
|
||||
|
||||
let encodedReceiptData = receiptData.base64EncodedData(options: [])
|
||||
|
||||
var deviceToken: String?
|
||||
var apiToken: String?
|
||||
do {
|
||||
async let deviceTokenTask = getDeviceToken().awaitable()
|
||||
async let apiTokenTask = getSGApiToken(context: primaryContext).awaitable()
|
||||
|
||||
(deviceToken, apiToken) = try await (deviceTokenTask, apiTokenTask)
|
||||
} catch {
|
||||
SGLogger.shared.log("SGIAP", "Error getting device token or API token: \(error)")
|
||||
return
|
||||
}
|
||||
|
||||
if let deviceToken, let apiToken {
|
||||
do {
|
||||
let _ = try await postSGReceipt(token: apiToken,
|
||||
deviceToken: deviceToken,
|
||||
encodedReceiptData: encodedReceiptData).awaitable()
|
||||
} catch let error as SignalCompleted {
|
||||
let _ = error
|
||||
} catch {
|
||||
SGLogger.shared.log("SGIAP", "Error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fetchSGStatus(primaryContext: AccountContext) async {
|
||||
// TODO(swiftgram): Stuck on getting shouldKeepConnection
|
||||
// Perhaps, we can drop on some timeout?
|
||||
// let currentShouldKeepConnection = await (primaryContext.account.network.shouldKeepConnection.get() |> take(1) |> deliverOnMainQueue).awaitable()
|
||||
guard !primaryContext.account.testingEnvironment else {
|
||||
return
|
||||
}
|
||||
let currentShouldKeepConnection = false
|
||||
let userId = primaryContext.account.peerId.id._internalGetInt64Value()
|
||||
// SGLogger.shared.log("SGIAP", "User id \(userId) currently keeps connection: \(currentShouldKeepConnection)")
|
||||
if !currentShouldKeepConnection {
|
||||
SGLogger.shared.log("SGIAP", "Asking user id \(userId) to keep connection: true")
|
||||
primaryContext.account.network.shouldKeepConnection.set(.single(true))
|
||||
}
|
||||
// MARK: Swiftgram
|
||||
let sgIqtpQueryString = makeIqtpQuery("s")
|
||||
//
|
||||
let iqtpResponse = try? await sgIqtpQuery(engine: primaryContext.engine, query: sgIqtpQueryString).awaitable()
|
||||
guard let iqtpResponse = iqtpResponse else {
|
||||
SGLogger.shared.log("SGIAP", "IQTP response is nil!")
|
||||
// if !currentShouldKeepConnection {
|
||||
// SGLogger.shared.log("SGIAP", "Setting user id \(userId) keep connection back to false")
|
||||
// primaryContext.account.network.shouldKeepConnection.set(.single(false))
|
||||
// }
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(name: .SGIAPHelperValidationErrorNotification, object: nil, userInfo: ["error": "PayWall.ValidationError.TryAgain"])
|
||||
}
|
||||
return
|
||||
}
|
||||
SGLogger.shared.log("SGIAP", "Got IQTP response: \(iqtpResponse)")
|
||||
let _ = try? await updateSGStatusInteractively(accountManager: primaryContext.sharedContext.accountManager, { value in
|
||||
var value = value
|
||||
|
||||
let newStatus: Int64
|
||||
if let status = Int64(iqtpResponse.value) {
|
||||
newStatus = status
|
||||
} else {
|
||||
SGLogger.shared.log("SGIAP", "Can't parse IQTP response into status!")
|
||||
newStatus = value.status // unparseable
|
||||
}
|
||||
|
||||
let userId = primaryContext.account.peerId.id._internalGetInt64Value()
|
||||
if value.status != newStatus {
|
||||
SGLogger.shared.log("SGIAP", "Updating \(userId) status \(value.status) -> \(newStatus)")
|
||||
if newStatus > 1 {
|
||||
let stringUserId = String(userId)
|
||||
if SGSimpleSettings.shared.primaryUserId != stringUserId {
|
||||
SGLogger.shared.log("SGIAP", "Setting new primary user id: \(userId)")
|
||||
SGSimpleSettings.shared.primaryUserId = stringUserId
|
||||
}
|
||||
} else {
|
||||
SGLogger.shared.log("SGIAP", "Status expired")
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(name: .SGIAPHelperValidationErrorNotification, object: nil, userInfo: ["error": "PayWall.ValidationError.Expired"])
|
||||
}
|
||||
}
|
||||
value.status = newStatus
|
||||
} else {
|
||||
SGLogger.shared.log("SGIAP", "Status \(value.status) for \(userId) hasn't changed")
|
||||
if newStatus < 2 {
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(name: .SGIAPHelperValidationErrorNotification, object: nil, userInfo: ["error": "PayWall.ValidationError.TryAgain"])
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
}).awaitable()
|
||||
|
||||
// if !currentShouldKeepConnection {
|
||||
// SGLogger.shared.log("SGIAP", "Setting user id \(userId) keep connection back to false")
|
||||
// primaryContext.account.network.shouldKeepConnection.set(.single(false))
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// MARK: Swiftgram
|
||||
import SGSimpleSettings
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Intents
|
||||
@@ -20,6 +22,7 @@ import TelegramPermissionsUI
|
||||
import PasscodeUI
|
||||
import ImageBlur
|
||||
import FastBlur
|
||||
import WatchBridge
|
||||
import SettingsUI
|
||||
import AppLock
|
||||
import AccountUtils
|
||||
@@ -151,11 +154,12 @@ final class AuthorizedApplicationContext {
|
||||
|
||||
private var applicationInForegroundDisposable: Disposable?
|
||||
|
||||
private var showContactsTab: Bool
|
||||
private var showCallsTab: Bool
|
||||
private var showCallsTabDisposable: Disposable?
|
||||
private var enablePostboxTransactionsDiposable: Disposable?
|
||||
|
||||
init(sharedApplicationContext: SharedApplicationContext, mainWindow: Window1, context: AccountContextImpl, accountManager: AccountManager<TelegramAccountManagerTypes>, showCallsTab: Bool, reinitializedNotificationSettings: @escaping () -> Void) {
|
||||
init(sharedApplicationContext: SharedApplicationContext, mainWindow: Window1, watchManagerArguments: Signal<WatchManagerArguments?, NoError>, context: AccountContextImpl, accountManager: AccountManager<TelegramAccountManagerTypes>, showContactsTab: Bool, showCallsTab: Bool, reinitializedNotificationSettings: @escaping () -> Void) {
|
||||
self.sharedApplicationContext = sharedApplicationContext
|
||||
|
||||
setupLegacyComponents(context: context)
|
||||
@@ -166,6 +170,8 @@ final class AuthorizedApplicationContext {
|
||||
|
||||
self.context = context
|
||||
|
||||
self.showContactsTab = showContactsTab
|
||||
|
||||
self.showCallsTab = showCallsTab
|
||||
|
||||
self.notificationController = NotificationContainerController(context: context)
|
||||
@@ -249,7 +255,7 @@ final class AuthorizedApplicationContext {
|
||||
}
|
||||
|
||||
if self.rootController.rootTabController == nil {
|
||||
self.rootController.addRootControllers(showCallsTab: self.showCallsTab)
|
||||
self.rootController.addRootControllers(hidePhoneInSettings: SGSimpleSettings.shared.hidePhoneInSettings, showContactsTab: self.showContactsTab, showCallsTab: self.showCallsTab)
|
||||
}
|
||||
if let tabsController = self.rootController.viewControllers.first as? TabBarController, !tabsController.controllers.isEmpty, tabsController.selectedIndex >= 0 {
|
||||
let controller = tabsController.controllers[tabsController.selectedIndex]
|
||||
@@ -782,22 +788,81 @@ final class AuthorizedApplicationContext {
|
||||
})
|
||||
|
||||
let showCallsTabSignal = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.callListSettings])
|
||||
|> map { sharedData -> Bool in
|
||||
var value = CallListSettings.defaultSettings.showTab
|
||||
|> map { sharedData -> (Bool, Bool) in
|
||||
var showCallsTabValue = CallListSettings.defaultSettings.showTab
|
||||
var showContactsTabValue = CallListSettings.defaultSettings.showContactsTab
|
||||
if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.callListSettings]?.get(CallListSettings.self) {
|
||||
value = settings.showTab
|
||||
showCallsTabValue = settings.showTab
|
||||
showContactsTabValue = settings.showContactsTab
|
||||
}
|
||||
return value
|
||||
return (showContactsTabValue, showCallsTabValue)
|
||||
}
|
||||
self.showCallsTabDisposable = (showCallsTabSignal |> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
self.showCallsTabDisposable = (showCallsTabSignal |> deliverOnMainQueue).start(next: { [weak self] values in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.showCallsTab != value {
|
||||
strongSelf.showCallsTab = value
|
||||
strongSelf.rootController.updateRootControllers(showCallsTab: value)
|
||||
let (showContactsTabValue, showCallsTabValue) = values
|
||||
var needControllersUpdate = false
|
||||
if strongSelf.showCallsTab != showCallsTabValue {
|
||||
needControllersUpdate = true
|
||||
strongSelf.showCallsTab = showCallsTabValue
|
||||
}
|
||||
if strongSelf.showContactsTab != showContactsTabValue {
|
||||
needControllersUpdate = true
|
||||
strongSelf.showContactsTab = showContactsTabValue
|
||||
}
|
||||
if needControllersUpdate {
|
||||
strongSelf.rootController.updateRootControllers(showContactsTab: showContactsTabValue, showCallsTab: showCallsTabValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let _ = (watchManagerArguments
|
||||
|> deliverOnMainQueue).start(next: { [weak self] arguments in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let watchManager = WatchManagerImpl(arguments: arguments)
|
||||
strongSelf.context.watchManager = watchManager
|
||||
|
||||
strongSelf.watchNavigateToMessageDisposable.set((strongSelf.context.sharedContext.applicationBindings.applicationInForeground |> mapToSignal({ applicationInForeground -> Signal<(Bool, MessageId), NoError> in
|
||||
return watchManager.navigateToMessageRequested
|
||||
|> map { messageId in
|
||||
return (applicationInForeground, messageId)
|
||||
}
|
||||
|> deliverOnMainQueue
|
||||
})).start(next: { [weak self] applicationInForeground, messageId in
|
||||
if let strongSelf = self {
|
||||
if applicationInForeground {
|
||||
var chatIsVisible = false
|
||||
if let controller = strongSelf.rootController.viewControllers.last as? ChatControllerImpl, case .peer(messageId.peerId) = controller.chatLocation {
|
||||
chatIsVisible = true
|
||||
}
|
||||
|
||||
let navigateToMessage = {
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
guard let peer = peer else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(peer), subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false)))
|
||||
})
|
||||
}
|
||||
|
||||
if chatIsVisible {
|
||||
navigateToMessage()
|
||||
} else {
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = textAlertController(context: strongSelf.context, title: presentationData.strings.WatchRemote_AlertTitle, text: presentationData.strings.WatchRemote_AlertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.WatchRemote_AlertOpen, action:navigateToMessage)])
|
||||
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
||||
}
|
||||
} else {
|
||||
//strongSelf.notificationManager.presentWatchContinuityNotification(context: strongSelf.context, messageId: messageId)
|
||||
}
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
self.rootController.setForceInCallStatusBar((self.context.sharedContext as! SharedAccountContextImpl).currentCallStatusBarNode)
|
||||
if let groupCallController = self.context.sharedContext.currentGroupCallController as? VoiceChatController {
|
||||
if let overlayController = groupCallController.currentOverlayController {
|
||||
|
||||
@@ -2008,7 +2008,7 @@ extension ChatControllerImpl {
|
||||
}
|
||||
}
|
||||
}))
|
||||
}, forwardSelectedMessages: { [weak self] in
|
||||
}, forwardSelectedMessages: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.commitPurposefulAction()
|
||||
if let forwardMessageIdsSet = strongSelf.presentationInterfaceState.interfaceState.selectionState?.selectedIds {
|
||||
@@ -2023,7 +2023,7 @@ extension ChatControllerImpl {
|
||||
strongSelf.forwardMessages(messageIds: forwardMessageIds, options: strongSelf.presentationInterfaceState.interfaceState.forwardOptionsState, resetCurrent: true)
|
||||
}
|
||||
}
|
||||
}, forwardMessages: { [weak self] messages in
|
||||
}, forwardMessages: { [weak self] messages, action in
|
||||
if let strongSelf = self, !messages.isEmpty {
|
||||
guard !strongSelf.presentAccountFrozenInfoIfNeeded(delay: true) else {
|
||||
return
|
||||
@@ -2031,7 +2031,16 @@ extension ChatControllerImpl {
|
||||
|
||||
strongSelf.commitPurposefulAction()
|
||||
let forwardMessageIds = messages.map { $0.id }.sorted()
|
||||
strongSelf.forwardMessages(messageIds: forwardMessageIds)
|
||||
switch action {
|
||||
case "forwardMessagesToCloud":
|
||||
strongSelf.forwardMessagesToCloud(messageIds: forwardMessageIds, removeNames: false, openCloud: false)
|
||||
case "forwardMessagesWithNoNames":
|
||||
strongSelf.forwardMessages(messageIds: forwardMessageIds, options: ChatInterfaceForwardOptionsState(hideNames: true, hideCaptions: false, unhideNamesOnCaptionChange: false))
|
||||
case "forwardMessagesToCloudWithNoNamesAndOpen":
|
||||
strongSelf.forwardMessagesToCloud(messageIds: forwardMessageIds, removeNames: true, openCloud: true)
|
||||
default:
|
||||
strongSelf.forwardMessages(messageIds: forwardMessageIds)
|
||||
}
|
||||
}
|
||||
}, updateForwardOptionsState: { [weak self] f in
|
||||
if let strongSelf = self {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// MARK: Swiftgram
|
||||
import SGSimpleSettings
|
||||
import TextFormat
|
||||
import TranslateUI
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
@@ -84,6 +88,47 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no
|
||||
return
|
||||
}
|
||||
|
||||
// MARK: Swiftgram
|
||||
let outgoingMessageTranslateToLang = SGSimpleSettings.shared.outgoingLanguageTranslation[SGSimpleSettings.makeOutgoingLanguageTranslationKey(accountId: selfController.context.account.peerId.id._internalGetInt64Value(), peerId: peer.id.id._internalGetInt64Value())] ?? selfController.contentData?.state.predictedChatLanguage
|
||||
|
||||
let sgTranslationContext: (outgoingMessageTranslateToLang: String?, translate: (() -> Void)?, changeTranslationLanguage: (() -> ())?) = (outgoingMessageTranslateToLang: outgoingMessageTranslateToLang, translate: { [weak selfController] in
|
||||
guard let selfController else { return }
|
||||
let textToTranslate = selfController.presentationInterfaceState.interfaceState.effectiveInputState.inputText.string
|
||||
let textEntities = selfController.presentationInterfaceState.interfaceState.synchronizeableInputState?.entities ?? []
|
||||
if let outgoingMessageTranslateToLang = outgoingMessageTranslateToLang {
|
||||
let _ = (selfController.context.engine.messages.translate(text: textToTranslate, toLang: outgoingMessageTranslateToLang, entities: textEntities) |> deliverOnMainQueue).start(next: { [weak selfController] translatedTextAndEntities in
|
||||
guard let selfController, let translatedTextAndEntities else { return }
|
||||
let newInputText = chatInputStateStringWithAppliedEntities(translatedTextAndEntities.0, entities: translatedTextAndEntities.1)
|
||||
let newTextInputState = ChatTextInputState(inputText: newInputText, selectionRange: 0 ..< newInputText.length)
|
||||
selfController.updateChatPresentationInterfaceState(interactive: true, { state in
|
||||
return state.updatedInterfaceState { interfaceState in
|
||||
return interfaceState.withUpdatedEffectiveInputState(newTextInputState)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}, changeTranslationLanguage: { [weak selfController] in
|
||||
guard let selfController else { return }
|
||||
let controller = languageSelectionController(context: selfController.context, forceTheme: selfController.presentationData.theme, fromLanguage: "", toLanguage: selfController.presentationInterfaceState.translationState?.fromLang ?? "", completion: { _, toLang in
|
||||
guard let peerId = selfController.chatLocation.peerId else {
|
||||
return
|
||||
}
|
||||
var langCode = toLang
|
||||
if langCode == "nb" {
|
||||
langCode = "no"
|
||||
} else if langCode == "pt-br" {
|
||||
langCode = "pt"
|
||||
}
|
||||
|
||||
if !toLang.isEmpty {
|
||||
SGSimpleSettings.shared.outgoingLanguageTranslation[SGSimpleSettings.makeOutgoingLanguageTranslationKey(accountId: selfController.context.account.peerId.id._internalGetInt64Value(), peerId: peerId.id._internalGetInt64Value())] = langCode
|
||||
}
|
||||
chatMessageDisplaySendMessageOptions(selfController: selfController, node: node, gesture: gesture)
|
||||
})
|
||||
controller.navigationPresentation = .modal
|
||||
selfController.push(controller)
|
||||
})
|
||||
|
||||
if let editMessage = selfController.presentationInterfaceState.interfaceState.editMessage {
|
||||
if editMessages.isEmpty {
|
||||
return
|
||||
@@ -122,6 +167,7 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no
|
||||
}
|
||||
|
||||
let controller = makeChatSendMessageActionSheetController(
|
||||
sgTranslationContext: sgTranslationContext,
|
||||
initialData: initialData,
|
||||
context: selfController.context,
|
||||
updatedPresentationData: selfController.updatedPresentationData,
|
||||
@@ -213,6 +259,7 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no
|
||||
}
|
||||
|
||||
let controller = makeChatSendMessageActionSheetController(
|
||||
sgTranslationContext: sgTranslationContext,
|
||||
initialData: initialData,
|
||||
context: selfController.context,
|
||||
updatedPresentationData: selfController.updatedPresentationData,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
import SGSimpleSettings
|
||||
import Foundation
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
@@ -136,6 +137,10 @@ extension ChatControllerImpl {
|
||||
var voiceMessagesAvailable: Bool = true
|
||||
var requestsState: PeerInvitationImportersState?
|
||||
var dismissedInvitationRequests: [Int64]?
|
||||
|
||||
// MARK: Swiftgram
|
||||
var predictedChatLanguage: String?
|
||||
|
||||
var customEmojiAvailable: Bool = false
|
||||
var threadData: ChatPresentationInterfaceState.ThreadData?
|
||||
var forumTopicData: ChatPresentationInterfaceState.ThreadData?
|
||||
@@ -163,6 +168,9 @@ extension ChatControllerImpl {
|
||||
private var premiumGiftSuggestionDisposable: Disposable?
|
||||
private var translationStateDisposable: Disposable?
|
||||
|
||||
// MARK: Swiftgram
|
||||
private var chatLanguagePredictionDisposable: Disposable?
|
||||
|
||||
private let isPeerInfoReady = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
private let isChatLocationInfoReady = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
private let isCachedDataReady = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
@@ -893,8 +901,7 @@ extension ChatControllerImpl {
|
||||
peerVerification = cachedChannelData.verification
|
||||
}
|
||||
}
|
||||
// GHOSTGRAM: Bypass copy protection if enabled in Misc settings
|
||||
copyProtectionEnabled = MiscSettingsManager.shared.shouldBypassCopyProtection ? false : peer.isCopyProtectionEnabled
|
||||
copyProtectionEnabled = peer.isCopyProtectionEnabled
|
||||
if let cachedGroupData = peerView.cachedData as? CachedGroupData {
|
||||
if !cachedGroupData.botInfos.isEmpty {
|
||||
hasBots = true
|
||||
@@ -983,7 +990,9 @@ extension ChatControllerImpl {
|
||||
}
|
||||
|
||||
var adMessage = adMessage
|
||||
if let peer = peerView.peers[peerView.peerId] as? TelegramUser, peer.botInfo != nil {
|
||||
if MiscSettingsManager.shared.shouldBlockAds {
|
||||
adMessage = nil
|
||||
} else if let peer = peerView.peers[peerView.peerId] as? TelegramUser, peer.botInfo != nil {
|
||||
} else {
|
||||
adMessage = nil
|
||||
}
|
||||
@@ -1028,7 +1037,7 @@ extension ChatControllerImpl {
|
||||
} else {
|
||||
isRegularChat = true
|
||||
}
|
||||
if strongSelf.nextChannelToReadDisposable == nil, let peerId = chatLocation.peerId, let customChatNavigationStack {
|
||||
if strongSelf.nextChannelToReadDisposable == nil, let peerId = chatLocation.peerId, let customChatNavigationStack, !SGSimpleSettings.shared.disableScrollToNextChannel {
|
||||
if let index = customChatNavigationStack.firstIndex(of: peerId), index != customChatNavigationStack.count - 1 {
|
||||
let nextPeerId = customChatNavigationStack[index + 1]
|
||||
strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(),
|
||||
@@ -1072,7 +1081,7 @@ extension ChatControllerImpl {
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if isRegularChat, strongSelf.nextChannelToReadDisposable == nil {
|
||||
} else if isRegularChat, strongSelf.nextChannelToReadDisposable == nil, !SGSimpleSettings.shared.disableScrollToNextChannel {
|
||||
//TODO:loc optimize
|
||||
let accountPeerId = context.account.peerId
|
||||
strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(),
|
||||
@@ -1372,8 +1381,7 @@ extension ChatControllerImpl {
|
||||
var alwaysShowGiftButton = false
|
||||
var disallowedGifts: TelegramDisallowedGifts?
|
||||
if let peer = peerView.peers[peerView.peerId] {
|
||||
// GHOSTGRAM: Bypass copy protection if enabled in Misc settings
|
||||
copyProtectionEnabled = MiscSettingsManager.shared.shouldBypassCopyProtection ? false : peer.isCopyProtectionEnabled
|
||||
copyProtectionEnabled = peer.isCopyProtectionEnabled
|
||||
if let cachedData = peerView.cachedData as? CachedUserData {
|
||||
contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: nil, managingBot: managingBot)
|
||||
if case let .known(value) = cachedData.businessIntro {
|
||||
@@ -1701,7 +1709,7 @@ extension ChatControllerImpl {
|
||||
strongSelf.state.alwaysShowGiftButton = alwaysShowGiftButton
|
||||
strongSelf.state.disallowedGifts = disallowedGifts
|
||||
|
||||
if let replyThreadId, let channel = renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, strongSelf.nextChannelToReadDisposable == nil {
|
||||
if let replyThreadId, let channel = renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, strongSelf.nextChannelToReadDisposable == nil, !SGSimpleSettings.shared.disableScrollToNextTopic {
|
||||
strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(),
|
||||
context.engine.peers.getNextUnreadForumTopic(peerId: channel.id, topicId: Int32(clamping: replyThreadId)),
|
||||
ApplicationSpecificNotice.getNextChatSuggestionTip(accountManager: context.sharedContext.accountManager)
|
||||
@@ -2112,7 +2120,8 @@ extension ChatControllerImpl {
|
||||
if counterAndTimestamp.0 >= 3 {
|
||||
maybeSuggestPremium = true
|
||||
}
|
||||
if (isPremium || maybeSuggestPremium || hasAutoTranslate) && !isHidden {
|
||||
let sgTranslationAvailable = SGSimpleSettings.shared.translationBackendEnum != .default
|
||||
if (isPremium || maybeSuggestPremium || hasAutoTranslate || sgTranslationAvailable) && !isHidden {
|
||||
return chatTranslationState(context: context, peerId: peerId, threadId: chatLocation.threadId)
|
||||
|> map { translationState -> ChatPresentationTranslationState? in
|
||||
if let translationState, !translationState.fromLang.isEmpty && (translationState.fromLang != baseLanguageCode || translationState.isEnabled) {
|
||||
@@ -2137,6 +2146,27 @@ extension ChatControllerImpl {
|
||||
|
||||
strongSelf.onUpdated?(previousState)
|
||||
})
|
||||
|
||||
// MARK: Swiftgram
|
||||
self.chatLanguagePredictionDisposable = (
|
||||
chatTranslationState(context: context, peerId: peerId, threadId: chatLocation.threadId)
|
||||
|> map { translationState -> ChatPresentationTranslationState? in
|
||||
if let translationState, !translationState.fromLang.isEmpty {
|
||||
return ChatPresentationTranslationState(isEnabled: translationState.isEnabled, fromLang: translationState.fromLang, toLang: translationState.toLang ?? baseLanguageCode)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] translationState in
|
||||
if let strongSelf = self, let translationState = translationState, strongSelf.state.predictedChatLanguage == nil {
|
||||
let previousState = strongSelf.state
|
||||
|
||||
strongSelf.state.predictedChatLanguage = translationState.fromLang
|
||||
|
||||
strongSelf.onUpdated?(previousState)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let premiumGiftOptions: Signal<[CachedPremiumGiftOption], NoError> = .single([])
|
||||
@@ -2400,6 +2430,8 @@ extension ChatControllerImpl {
|
||||
self.cachedDataDisposable?.dispose()
|
||||
self.premiumGiftSuggestionDisposable?.dispose()
|
||||
self.translationStateDisposable?.dispose()
|
||||
// MARK: Swiftgram
|
||||
self.chatLanguagePredictionDisposable?.dispose()
|
||||
self.inviteRequestsDisposable?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,165 @@ import ReactionSelectionNode
|
||||
import TopMessageReactions
|
||||
import ChatMessagePaymentAlertController
|
||||
|
||||
enum ForwardEnqueueMessagesResult {
|
||||
case messages([EnqueueMessage])
|
||||
case unsupported
|
||||
}
|
||||
|
||||
extension ChatControllerImpl {
|
||||
private func forwardOptionsAttributes(_ options: ChatInterfaceForwardOptionsState?) -> [MessageAttribute] {
|
||||
return [
|
||||
ForwardOptionsMessageAttribute(
|
||||
hideNames: options?.hideNames == true,
|
||||
hideCaptions: options?.hideCaptions == true
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
private func shouldUsePlainCopyForward(for message: Message) -> Bool {
|
||||
return MiscSettingsManager.shared.shouldBypassCopyProtection && message.isCopyProtectedIgnoringBypass()
|
||||
}
|
||||
|
||||
private func forwardMessagesNeedPlainCopy(_ messages: [Message]) -> Bool {
|
||||
return messages.contains(where: self.shouldUsePlainCopyForward(for:))
|
||||
}
|
||||
|
||||
private func plainCopyForwardMediaReference(for message: Message) -> AnyMediaReference? {
|
||||
var supportedMedia: Media?
|
||||
|
||||
for media in message.media {
|
||||
switch media {
|
||||
case is TelegramMediaAction, is TelegramMediaPoll, is TelegramMediaTodo, is TelegramMediaDice, is TelegramMediaPaidContent, is TelegramMediaExpiredContent, is TelegramMediaStory, is TelegramMediaInvoice:
|
||||
return nil
|
||||
case is TelegramMediaWebpage:
|
||||
continue
|
||||
case is TelegramMediaImage, is TelegramMediaFile:
|
||||
if supportedMedia != nil {
|
||||
return nil
|
||||
}
|
||||
supportedMedia = media
|
||||
case is TelegramMediaMap, is TelegramMediaContact:
|
||||
if supportedMedia != nil {
|
||||
return nil
|
||||
}
|
||||
supportedMedia = media
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
guard let supportedMedia else {
|
||||
return nil
|
||||
}
|
||||
if supportedMedia is TelegramMediaImage || supportedMedia is TelegramMediaFile {
|
||||
return .message(message: MessageReference(message), media: supportedMedia)
|
||||
} else {
|
||||
return .standalone(media: supportedMedia)
|
||||
}
|
||||
}
|
||||
|
||||
private func plainCopyForwardMessage(
|
||||
_ message: Message,
|
||||
options: ChatInterfaceForwardOptionsState?,
|
||||
threadId: Int64?,
|
||||
localGroupingKey: Int64?
|
||||
) -> EnqueueMessage? {
|
||||
if message.id.peerId.namespace == Namespaces.Peer.SecretChat || message.containsSecretMedia || message.minAutoremoveOrClearTimeout == viewOnceTimeout {
|
||||
return nil
|
||||
}
|
||||
|
||||
let mediaReference = self.plainCopyForwardMediaReference(for: message)
|
||||
if !message.media.isEmpty && mediaReference == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var text = message.text
|
||||
var entities = message.textEntitiesAttribute?.entities ?? []
|
||||
if options?.hideCaptions == true, mediaReference != nil {
|
||||
text = ""
|
||||
entities = []
|
||||
}
|
||||
|
||||
if mediaReference == nil && text.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
var attributes: [MessageAttribute] = []
|
||||
if !entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
for attribute in message.attributes {
|
||||
if attribute is MediaSpoilerMessageAttribute || attribute is InvertMediaMessageAttribute {
|
||||
attributes.append(attribute)
|
||||
}
|
||||
}
|
||||
|
||||
return .message(
|
||||
text: text,
|
||||
attributes: attributes,
|
||||
inlineStickers: [:],
|
||||
mediaReference: mediaReference,
|
||||
threadId: threadId,
|
||||
replyToMessageId: nil,
|
||||
replyToStoryId: nil,
|
||||
localGroupingKey: localGroupingKey,
|
||||
correlationId: nil,
|
||||
bubbleUpEmojiOrStickersets: []
|
||||
)
|
||||
}
|
||||
|
||||
func buildForwardEnqueueMessages(
|
||||
from messages: [Message],
|
||||
options: ChatInterfaceForwardOptionsState?,
|
||||
threadId: Int64?
|
||||
) -> ForwardEnqueueMessagesResult {
|
||||
let forwardAttributes = self.forwardOptionsAttributes(options)
|
||||
var groupingKeyMap: [Int64: Int64] = [:]
|
||||
var result: [EnqueueMessage] = []
|
||||
|
||||
for message in messages {
|
||||
if self.shouldUsePlainCopyForward(for: message) {
|
||||
let localGroupingKey: Int64?
|
||||
if let groupingKey = message.groupingKey {
|
||||
if let existingKey = groupingKeyMap[groupingKey] {
|
||||
localGroupingKey = existingKey
|
||||
} else {
|
||||
let newKey = Int64.random(in: Int64.min ... Int64.max)
|
||||
groupingKeyMap[groupingKey] = newKey
|
||||
localGroupingKey = newKey
|
||||
}
|
||||
} else {
|
||||
localGroupingKey = nil
|
||||
}
|
||||
|
||||
guard let plainCopyMessage = self.plainCopyForwardMessage(message, options: options, threadId: threadId, localGroupingKey: localGroupingKey) else {
|
||||
return .unsupported
|
||||
}
|
||||
result.append(plainCopyMessage)
|
||||
} else {
|
||||
result.append(.forward(source: message.id, threadId: threadId, grouping: .auto, attributes: forwardAttributes, correlationId: nil))
|
||||
}
|
||||
}
|
||||
|
||||
return .messages(result)
|
||||
}
|
||||
|
||||
func presentUnsupportedProtectedForwardAlert(in controller: ViewController?) {
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let text = "This protected message type can't be copied yet."
|
||||
let alert = textAlertController(
|
||||
context: self.context,
|
||||
title: nil,
|
||||
text: text,
|
||||
actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
|
||||
)
|
||||
if let controller {
|
||||
controller.present(alert, in: .window(.root))
|
||||
} else {
|
||||
self.present(alert, in: .window(.root))
|
||||
}
|
||||
}
|
||||
|
||||
func forwardMessages(messageIds: [MessageId], options: ChatInterfaceForwardOptionsState? = nil, resetCurrent: Bool = false) {
|
||||
let _ = (self.context.engine.data.get(EngineDataMap(
|
||||
messageIds.map(TelegramEngine.EngineData.Item.Messages.Message.init)
|
||||
@@ -53,7 +211,7 @@ extension ChatControllerImpl {
|
||||
var attemptSelectionImpl: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?
|
||||
let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, updatedPresentationData: self.updatedPresentationData, filter: filter, hasFilters: true, attemptSelection: { peer, _, reason in
|
||||
attemptSelectionImpl?(peer, reason)
|
||||
}, multipleSelection: true, forwardedMessageIds: messages.map { $0.id }, selectForumThreads: true))
|
||||
}, multipleSelection: true, forwardedMessageIds: messages.map { $0.id }, initialForwardOptionsState: options, selectForumThreads: true))
|
||||
let context = self.context
|
||||
attemptSelectionImpl = { [weak self, weak controller] peer, reason in
|
||||
guard let strongSelf = self, let controller = controller else {
|
||||
@@ -153,12 +311,13 @@ extension ChatControllerImpl {
|
||||
}
|
||||
}
|
||||
|
||||
var attributes: [MessageAttribute] = []
|
||||
attributes.append(ForwardOptionsMessageAttribute(hideNames: forwardOptions?.hideNames == true, hideCaptions: forwardOptions?.hideCaptions == true))
|
||||
|
||||
result.append(contentsOf: messages.map { message -> EnqueueMessage in
|
||||
return .forward(source: message.id, threadId: nil, grouping: .auto, attributes: attributes, correlationId: nil)
|
||||
})
|
||||
switch strongSelf.buildForwardEnqueueMessages(from: messages, options: forwardOptions, threadId: nil) {
|
||||
case let .messages(forwardMessages):
|
||||
result.append(contentsOf: forwardMessages)
|
||||
case .unsupported:
|
||||
strongSelf.presentUnsupportedProtectedForwardAlert(in: strongController)
|
||||
return
|
||||
}
|
||||
|
||||
let commit: ([EnqueueMessage]) -> Void = { result in
|
||||
guard let strongSelf = self else {
|
||||
@@ -392,10 +551,17 @@ extension ChatControllerImpl {
|
||||
}
|
||||
|
||||
var correlationIds: [Int64] = []
|
||||
let mappedMessages = messages.map { message -> EnqueueMessage in
|
||||
let correlationId = Int64.random(in: Int64.min ... Int64.max)
|
||||
correlationIds.append(correlationId)
|
||||
return .forward(source: message.id, threadId: nil, grouping: .auto, attributes: [], correlationId: correlationId)
|
||||
let mappedMessages: [EnqueueMessage]
|
||||
switch strongSelf.buildForwardEnqueueMessages(from: messages, options: options, threadId: nil) {
|
||||
case let .messages(builtMessages):
|
||||
mappedMessages = builtMessages.map { message in
|
||||
let correlationId = Int64.random(in: Int64.min ... Int64.max)
|
||||
correlationIds.append(correlationId)
|
||||
return message.withUpdatedCorrelationId(correlationId)
|
||||
}
|
||||
case .unsupported:
|
||||
strongSelf.presentUnsupportedProtectedForwardAlert(in: strongController)
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (reactionItems
|
||||
@@ -448,6 +614,33 @@ extension ChatControllerImpl {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) })
|
||||
strongController.dismiss()
|
||||
} else {
|
||||
if strongSelf.forwardMessagesNeedPlainCopy(messages) {
|
||||
let forwardOptions = options ?? ChatInterfaceForwardOptionsState(hideNames: !hasNotOwnMessages, hideCaptions: false, unhideNamesOnCaptionChange: false)
|
||||
let mappedMessages: [EnqueueMessage]
|
||||
switch strongSelf.buildForwardEnqueueMessages(from: messages, options: forwardOptions, threadId: threadId) {
|
||||
case let .messages(builtMessages):
|
||||
mappedMessages = builtMessages
|
||||
case .unsupported:
|
||||
strongSelf.presentUnsupportedProtectedForwardAlert(in: strongController)
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: mappedMessages)
|
||||
|> deliverOnMainQueue).startStandalone()
|
||||
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
peerName = peerName.replacingOccurrences(of: "**", with: "")
|
||||
let text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string
|
||||
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: false, text: text), elevatedLayout: false, position: .bottom, animateInAsReplacement: true, action: { _ in
|
||||
return false
|
||||
}), in: .current)
|
||||
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) })
|
||||
strongController.dismiss()
|
||||
return
|
||||
}
|
||||
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
for controller in navigationController.viewControllers {
|
||||
if let maybeChat = controller as? ChatControllerImpl {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Foundation
|
||||
import SGSimpleSettings
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Postbox
|
||||
@@ -129,7 +130,6 @@ class HistoryNodeContainer: ASDisplayNode {
|
||||
var isSecret: Bool {
|
||||
didSet {
|
||||
if self.isSecret != oldValue {
|
||||
// MISC: Bypass screenshot protection if enabled
|
||||
let shouldDisable = self.isSecret && !MiscSettingsManager.shared.shouldBypassScreenshotProtection
|
||||
setLayerDisableScreenshots(self.layer, shouldDisable)
|
||||
}
|
||||
@@ -146,7 +146,6 @@ class HistoryNodeContainer: ASDisplayNode {
|
||||
super.init()
|
||||
|
||||
if self.isSecret {
|
||||
// MISC: Bypass screenshot protection if enabled
|
||||
let shouldDisable = self.isSecret && !MiscSettingsManager.shared.shouldBypassScreenshotProtection
|
||||
setLayerDisableScreenshots(self.layer, shouldDisable)
|
||||
}
|
||||
@@ -1109,8 +1108,8 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
statusBar.updateStatusBarStyle(.White, animated: true)
|
||||
} else {
|
||||
let statusBarStyle: StatusBarStyle
|
||||
if let isDark = self.backgroundNode.isDark {
|
||||
if isDark {
|
||||
if let contentStats = self.backgroundNode.contentStats {
|
||||
if contentStats.isDark {
|
||||
statusBarStyle = .White
|
||||
} else {
|
||||
statusBarStyle = .Black
|
||||
@@ -1129,7 +1128,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
// GHOSTGRAM: Bypass screenshot protection if enabled in Misc settings
|
||||
let effectiveCopyProtection = MiscSettingsManager.shared.shouldBypassScreenshotProtection ? false : self.chatPresentationInterfaceState.copyProtectionEnabled
|
||||
let isSecret = effectiveCopyProtection || self.chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat || self.chatLocation.peerId?.isVerificationCodes == true
|
||||
if self.historyNodeContainer.isSecret != isSecret {
|
||||
@@ -1644,6 +1642,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
dismissedFloatingTopicsPanel = floatingTopicsPanel
|
||||
}
|
||||
|
||||
if floatingTopicsPanelInsets.top != 0.0 {
|
||||
floatingTopicsPanelInsets.top += 8.0
|
||||
}
|
||||
|
||||
var isSidebarOpen = false
|
||||
if let floatingTopicsPanel = self.floatingTopicsPanel {
|
||||
isSidebarOpen = floatingTopicsPanel.component.location == .side
|
||||
@@ -1704,8 +1706,21 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
var dismissedAccessoryPanelNode: AccessoryPanelNode?
|
||||
var dismissedInputContextPanelNode: ChatInputContextPanelNode?
|
||||
var dismissedOverlayContextPanelNode: ChatInputContextPanelNode?
|
||||
|
||||
let inputPanelNodes = inputPanelForChatPresentationIntefaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.inputPanelNode, currentSecondaryPanel: self.secondaryInputPanelNode, textInputPanelNode: self.textInputPanelNode, chatControllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction)
|
||||
// MARK: Swiftgram
|
||||
var inputPanelNodes = inputPanelForChatPresentationIntefaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.inputPanelNode, currentSecondaryPanel: self.secondaryInputPanelNode, textInputPanelNode: self.textInputPanelNode, chatControllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction)
|
||||
if SGSimpleSettings.shared.hideChannelBottomButton {
|
||||
// We still need the panel for messages multi-select or search. Likely can break in future.
|
||||
if self.chatPresentationInterfaceState.interfaceState.selectionState != nil || self.chatPresentationInterfaceState.search != nil {
|
||||
self.inputPanelBackgroundNode.isHidden = false
|
||||
} else if (inputPanelNodes.primary != nil || inputPanelNodes.secondary != nil) {
|
||||
// So there should be some panel, but user don't want it. Let's check if our logic will hide it
|
||||
inputPanelNodes = inputPanelForChatPresentationIntefaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.inputPanelNode, currentSecondaryPanel: self.secondaryInputPanelNode, textInputPanelNode: self.textInputPanelNode, chatControllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction)
|
||||
if inputPanelNodes.primary == nil && inputPanelNodes.secondary == nil {
|
||||
// Looks like we're eligible to hide the panel, let's remove safe area fill as well
|
||||
self.inputPanelBackgroundNode.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let inputPanelBottomInset = max(insets.bottom, inputPanelBottomInsetTerm)
|
||||
|
||||
@@ -1931,7 +1946,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
|
||||
updateExtraNavigationBarBackgroundHeight(0.0, 0.0, nil, transition)
|
||||
|
||||
var sidePanelTopInset: CGFloat = insets.top + 4.0
|
||||
var sidePanelTopInset: CGFloat = insets.top - 4.0
|
||||
|
||||
let contentBounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width - wrappingInsets.left - wrappingInsets.right, height: layout.size.height - wrappingInsets.top - wrappingInsets.bottom)
|
||||
|
||||
@@ -2241,24 +2256,38 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
var bottomBackgroundEdgeEffectNode: WallpaperEdgeEffectNode?
|
||||
if let current = self.bottomBackgroundEdgeEffectNode {
|
||||
bottomBackgroundEdgeEffectNode = current
|
||||
let edgeEffectAlpha: CGFloat
|
||||
if case .image = self.chatPresentationInterfaceState.chatWallpaper {
|
||||
edgeEffectAlpha = 0.7
|
||||
} else if case .color = self.chatPresentationInterfaceState.chatWallpaper {
|
||||
edgeEffectAlpha = 0.85
|
||||
} else {
|
||||
if let value = self.backgroundNode.makeEdgeEffectNode() {
|
||||
bottomBackgroundEdgeEffectNode = value
|
||||
self.bottomBackgroundEdgeEffectNode = value
|
||||
self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view)
|
||||
edgeEffectAlpha = 0.75
|
||||
}
|
||||
|
||||
|
||||
var bottomBackgroundEdgeEffectNode: WallpaperEdgeEffectNode?
|
||||
if self.historyNode.rotated {
|
||||
if let current = self.bottomBackgroundEdgeEffectNode {
|
||||
bottomBackgroundEdgeEffectNode = current
|
||||
} else {
|
||||
if let value = self.backgroundNode.makeEdgeEffectNode() {
|
||||
bottomBackgroundEdgeEffectNode = value
|
||||
self.bottomBackgroundEdgeEffectNode = value
|
||||
value.isUserInteractionEnabled = false
|
||||
self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let bottomBackgroundEdgeEffectNode {
|
||||
var blurFrame = inputBackgroundFrame
|
||||
blurFrame.origin.y -= 18.0
|
||||
blurFrame.origin.y -= 20.0
|
||||
blurFrame.size.height = max(100.0, layout.size.height - blurFrame.origin.y)
|
||||
transition.updateFrame(node: bottomBackgroundEdgeEffectNode, frame: blurFrame)
|
||||
bottomBackgroundEdgeEffectNode.update(
|
||||
rect: blurFrame,
|
||||
edge: WallpaperEdgeEffectEdge(edge: .bottom, size: 100.0),
|
||||
edge: WallpaperEdgeEffectEdge(edge: .bottom, size: min(60.0, blurFrame.height)),
|
||||
alpha: edgeEffectAlpha,
|
||||
blur: false,
|
||||
containerSize: wallpaperBounds.size,
|
||||
transition: transition
|
||||
@@ -2275,7 +2304,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
var contentBottomInset: CGFloat = inputPanelsHeight + 11.0 + inputPanelsInset
|
||||
var contentBottomInset: CGFloat = inputPanelsHeight + inputPanelsInset
|
||||
if previewing {
|
||||
} else {
|
||||
contentBottomInset += 11.0
|
||||
}
|
||||
|
||||
if let scrollContainerNode = self.scrollContainerNode {
|
||||
transition.updateFrame(node: scrollContainerNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
@@ -2307,7 +2340,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
}
|
||||
|
||||
if let containerNode = self.containerNode {
|
||||
contentBottomInset += 8.0
|
||||
let containerNodeFrame = CGRect(origin: CGPoint(x: wrappingInsets.left, y: wrappingInsets.top), size: CGSize(width: contentBounds.size.width, height: contentBounds.size.height - containerInsets.bottom - inputPanelsHeight - 8.0))
|
||||
transition.updateFrame(node: containerNode, frame: containerNodeFrame)
|
||||
|
||||
@@ -2322,7 +2354,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
overlayNavigationBar.updateLayout(size: barFrame.size, transition: transition)
|
||||
}
|
||||
|
||||
var listInsets = UIEdgeInsets(top: containerInsets.bottom + contentBottomInset, left: containerInsets.right, bottom: containerInsets.top + 6.0, right: containerInsets.left)
|
||||
var listInsets = UIEdgeInsets(top: containerInsets.bottom + contentBottomInset, left: containerInsets.right, bottom: containerInsets.top, right: containerInsets.left)
|
||||
let listScrollIndicatorInsets = UIEdgeInsets(top: containerInsets.bottom + inputPanelsHeight, left: containerInsets.right, bottom: containerInsets.top, right: containerInsets.left)
|
||||
|
||||
var childContentInsets: UIEdgeInsets = containerInsets
|
||||
@@ -2446,23 +2478,28 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
})
|
||||
|
||||
var topBackgroundEdgeEffectNode: WallpaperEdgeEffectNode?
|
||||
if let current = self.topBackgroundEdgeEffectNode {
|
||||
topBackgroundEdgeEffectNode = current
|
||||
} else {
|
||||
if let value = self.backgroundNode.makeEdgeEffectNode() {
|
||||
topBackgroundEdgeEffectNode = value
|
||||
self.topBackgroundEdgeEffectNode = value
|
||||
self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view)
|
||||
if self.historyNode.rotated {
|
||||
if let current = self.topBackgroundEdgeEffectNode {
|
||||
topBackgroundEdgeEffectNode = current
|
||||
} else {
|
||||
if let value = self.backgroundNode.makeEdgeEffectNode() {
|
||||
topBackgroundEdgeEffectNode = value
|
||||
self.topBackgroundEdgeEffectNode = value
|
||||
value.isUserInteractionEnabled = false
|
||||
self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let topBackgroundEdgeEffectNode {
|
||||
var blurFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: max(100.0, listInsets.bottom + 10.0)))
|
||||
blurFrame.origin.y = listInsets.bottom + 10.0 - blurFrame.height
|
||||
let topExtent: CGFloat = 34.0
|
||||
var blurFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: max(100.0, listInsets.bottom + topExtent)))
|
||||
blurFrame.origin.y = listInsets.bottom + topExtent - blurFrame.height
|
||||
transition.updateFrame(node: topBackgroundEdgeEffectNode, frame: blurFrame)
|
||||
topBackgroundEdgeEffectNode.update(
|
||||
rect: blurFrame,
|
||||
edge: WallpaperEdgeEffectEdge(edge: .top, size: 100.0),
|
||||
blur: false,
|
||||
edge: WallpaperEdgeEffectEdge(edge: .top, size: 80.0),
|
||||
alpha: edgeEffectAlpha,
|
||||
blur: true,
|
||||
containerSize: wallpaperBounds.size,
|
||||
transition: transition
|
||||
)
|
||||
@@ -2524,6 +2561,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
transition.updateFrame(node: self.inputPanelBackgroundNode, frame: apparentInputBackgroundFrame, beginWithCurrentState: true)
|
||||
|
||||
if let headerPanelsComponentView = self.headerPanelsView?.view, let headerPanelsSize {
|
||||
sidePanelTopInset += 8.0
|
||||
let headerPanelsFrame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: sidePanelTopInset), size: headerPanelsSize)
|
||||
var headerPanelsTransition = ComponentTransition(transition)
|
||||
if headerPanelsComponentView.superview == nil {
|
||||
@@ -4602,12 +4640,13 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
// GHOSTGRAM: When send delay is active, scheduled messages
|
||||
// don't trigger history view update, so clear input immediately.
|
||||
if SendDelayManager.shared.isEnabled {
|
||||
self.collapseInput()
|
||||
|
||||
self.ignoreUpdateHeight = true
|
||||
textInputPanelNode.text = ""
|
||||
if let textInputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode {
|
||||
textInputPanelNode.text = ""
|
||||
}
|
||||
self.requestUpdateChatInterfaceState(.immediate, overrideThreadId == nil, { state in
|
||||
var state = state
|
||||
state = state.withUpdatedReplyMessageSubject(nil)
|
||||
@@ -4657,7 +4696,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
}
|
||||
|
||||
var targetThreadId: Int64?
|
||||
if self.chatLocation.threadId == nil, let user = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.isForum {
|
||||
if self.chatLocation.threadId == nil, let user = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.hasForum), botInfo.flags.contains(.forumManagedByUser) {
|
||||
if let message = messages.first {
|
||||
switch message {
|
||||
case let .message(_, _, _, _, _, replyToMessageId, _, _, _, _):
|
||||
|
||||
@@ -647,7 +647,7 @@ func chatHistoryEntriesForView(
|
||||
}
|
||||
}
|
||||
|
||||
if !dynamicAdMessages.isEmpty {
|
||||
if !MiscSettingsManager.shared.shouldBlockAds && !dynamicAdMessages.isEmpty {
|
||||
assert(entries.sorted() == entries)
|
||||
for message in dynamicAdMessages {
|
||||
entries.append(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil, displayContinueThreadFooter: false)))
|
||||
@@ -655,7 +655,7 @@ func chatHistoryEntriesForView(
|
||||
entries.sort()
|
||||
}
|
||||
|
||||
if view.laterId == nil && !view.isLoading {
|
||||
if !MiscSettingsManager.shared.shouldBlockAds && view.laterId == nil && !view.isLoading {
|
||||
if !entries.isEmpty, case let .MessageEntry(lastMessage, _, _, _, _, _) = entries[entries.count - 1], let message = adMessage {
|
||||
var nextAdMessageId: Int32 = 10000
|
||||
let updatedMessage = Message(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import SGSimpleSettings
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Postbox
|
||||
@@ -339,6 +340,7 @@ final class ChatHistoryTransactionOpaqueState {
|
||||
}
|
||||
|
||||
private func extractAssociatedData(
|
||||
translationSettings: TranslationSettings,
|
||||
chatLocation: ChatLocation,
|
||||
view: MessageHistoryView,
|
||||
automaticDownloadNetworkType: MediaAutoDownloadNetworkType,
|
||||
@@ -421,7 +423,43 @@ private func extractAssociatedData(
|
||||
automaticDownloadPeerId = message.peerId
|
||||
}
|
||||
|
||||
return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadPeerId: automaticDownloadPeerId, automaticDownloadNetworkType: automaticDownloadNetworkType, preferredStoryHighQuality: preferredStoryHighQuality, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction, areStarReactionsEnabled: areStarReactionsEnabled, isPremium: isPremium, accountPeer: accountPeer, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, topicAuthorId: topicAuthorId, hasBots: hasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers, isInline: isInline, showSensitiveContent: showSensitiveContent, isSuspiciousPeer: isSuspiciousPeer)
|
||||
return ChatMessageItemAssociatedData(
|
||||
automaticDownloadPeerType: automaticMediaDownloadPeerType,
|
||||
automaticDownloadPeerId: automaticDownloadPeerId,
|
||||
automaticDownloadNetworkType: automaticDownloadNetworkType,
|
||||
preferredStoryHighQuality: preferredStoryHighQuality,
|
||||
isRecentActions: false,
|
||||
subject: subject,
|
||||
contactsPeerIds: contactsPeerIds,
|
||||
channelDiscussionGroup: channelDiscussionGroup,
|
||||
animatedEmojiStickers: animatedEmojiStickers,
|
||||
additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers,
|
||||
forcedResourceStatus: nil,
|
||||
currentlyPlayingMessageId: currentlyPlayingMessageId,
|
||||
isCopyProtectionEnabled: isCopyProtectionEnabled,
|
||||
availableReactions: availableReactions,
|
||||
availableMessageEffects: availableMessageEffects,
|
||||
savedMessageTags: savedMessageTags,
|
||||
defaultReaction: defaultReaction,
|
||||
areStarReactionsEnabled: areStarReactionsEnabled,
|
||||
isPremium: isPremium,
|
||||
accountPeer: accountPeer,
|
||||
forceInlineReactions: false,
|
||||
alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton,
|
||||
topicAuthorId: topicAuthorId,
|
||||
hasBots: hasBots,
|
||||
translationSettings: translationSettings,
|
||||
translateToLanguage: translateToLanguage,
|
||||
maxReadStoryId: maxReadStoryId,
|
||||
recommendedChannels: recommendedChannels,
|
||||
audioTranscriptionTrial: audioTranscriptionTrial,
|
||||
chatThemes: chatThemes,
|
||||
deviceContactsNumbers: deviceContactsNumbers,
|
||||
isStandalone: false,
|
||||
isInline: isInline,
|
||||
showSensitiveContent: showSensitiveContent,
|
||||
isSuspiciousPeer: isSuspiciousPeer
|
||||
)
|
||||
}
|
||||
|
||||
private extension ChatHistoryLocationInput {
|
||||
@@ -791,6 +829,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
||||
self.messageTransitionNode = messageTransitionNode
|
||||
self.mode = mode
|
||||
|
||||
if SGSimpleSettings.shared.disableSnapDeletionEffect { self.allowDustEffect = false }
|
||||
if let data = context.currentAppConfiguration.with({ $0 }).data {
|
||||
if let _ = data["ios_killswitch_disable_unread_alignment"] {
|
||||
self.enableUnreadAlignment = false
|
||||
@@ -1816,6 +1855,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
||||
var measure_isFirstTime = true
|
||||
let messageViewQueue = Queue.mainQueue()
|
||||
let historyViewTransitionDisposable = (combineLatest(queue: messageViewQueue,
|
||||
self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings]) |> take(1),
|
||||
historyViewUpdate |> debug_measureTimeToFirstEvent(label: "chatHistoryNode_historyViewUpdate"),
|
||||
self.chatPresentationDataPromise.get() |> debug_measureTimeToFirstEvent(label: "chatHistoryNode_chatPresentationData"),
|
||||
selectedMessages |> debug_measureTimeToFirstEvent(label: "chatHistoryNode_selectedMessages"),
|
||||
@@ -1841,7 +1881,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
||||
chatThemes |> debug_measureTimeToFirstEvent(label: "chatHistoryNode_chatThemes"),
|
||||
deviceContactsNumbers |> debug_measureTimeToFirstEvent(label: "chatHistoryNode_deviceContactsNumbers"),
|
||||
contentSettings |> debug_measureTimeToFirstEvent(label: "chatHistoryNode_contentSettings")
|
||||
) |> debug_measureTimeToFirstEvent(label: "chatHistoryNode_firstChatHistoryTransition")).startStrict(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, preferredStoryHighQuality, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, availableMessageEffects, savedMessageTags, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, translationState, maxReadStoryId, recommendedChannels, audioTranscriptionTrial, chatThemes, deviceContactsNumbers, contentSettings in
|
||||
) |> debug_measureTimeToFirstEvent(label: "chatHistoryNode_firstChatHistoryTransition")).startStrict(next: { [weak self] sharedData, /* MARK: Swiftgram */ update, chatPresentationData, selectedMessages, updatingMedia, networkType, preferredStoryHighQuality, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, availableMessageEffects, savedMessageTags, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, translationState, maxReadStoryId, recommendedChannels, audioTranscriptionTrial, chatThemes, deviceContactsNumbers, contentSettings in
|
||||
let (historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, currentlyPlayingMessageIdAndType, scrollToMessageId, chatHasBots, allAdMessages) = promises
|
||||
|
||||
if measure_isFirstTime {
|
||||
@@ -1852,6 +1892,13 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
||||
#endif
|
||||
}
|
||||
|
||||
let translationSettings: TranslationSettings
|
||||
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) {
|
||||
translationSettings = current
|
||||
} else {
|
||||
translationSettings = TranslationSettings.defaultSettings
|
||||
}
|
||||
|
||||
func applyHole() {
|
||||
Queue.mainQueue().async {
|
||||
if let strongSelf = self {
|
||||
@@ -2056,23 +2103,24 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
||||
}
|
||||
}
|
||||
}
|
||||
// GHOSTGRAM: Bypass copy protection if enabled in Misc settings
|
||||
if MiscSettingsManager.shared.shouldBypassCopyProtection {
|
||||
isCopyProtectionEnabled = false
|
||||
}
|
||||
let alwaysDisplayTranscribeButton = ChatMessageItemAssociatedData.DisplayTranscribeButton(
|
||||
canBeDisplayed: suggestAudioTranscription.0 < 2,
|
||||
displayForNotConsumed: suggestAudioTranscription.1,
|
||||
providedByGroupBoost: audioTranscriptionProvidedByBoost
|
||||
)
|
||||
|
||||
var translateToLanguage: (fromLang: String, toLang: String)?
|
||||
if let translationState, (isPremium || autoTranslate) && translationState.isEnabled {
|
||||
var languageCode = translationState.toLang ?? chatPresentationData.strings.baseLanguageCode
|
||||
|
||||
// MARK: Swiftgram
|
||||
// var translateToLanguage: (fromLang: String, toLang: String)?
|
||||
// if let translationState, (isPremium || autoTranslate) && translationState.isEnabled {
|
||||
var languageCode = translationState?.toLang ?? chatPresentationData.strings.baseLanguageCode
|
||||
let rawSuffix = "-raw"
|
||||
if languageCode.hasSuffix(rawSuffix) {
|
||||
languageCode = String(languageCode.dropLast(rawSuffix.count))
|
||||
}
|
||||
languageCode = normalizeTranslationLanguage(languageCode)
|
||||
var translateToLanguage: (fromLang: String, toLang: String)?
|
||||
let sgTranslationAvailable = SGSimpleSettings.shared.translationBackendEnum != .default
|
||||
if let translationState, (isPremium || autoTranslate || sgTranslationAvailable) && translationState.isEnabled {
|
||||
translateToLanguage = (normalizeTranslationLanguage(translationState.fromLang), normalizeTranslationLanguage(languageCode))
|
||||
}
|
||||
|
||||
@@ -2081,7 +2129,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
||||
isSuspiciousPeer = true
|
||||
}
|
||||
|
||||
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, preferredStoryHighQuality: preferredStoryHighQuality, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction.0, areStarReactionsEnabled: defaultReaction.1, isPremium: isPremium, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, accountPeer: accountPeer, topicAuthorId: topicAuthorId, hasBots: chatHasBots, translateToLanguage: translateToLanguage?.toLang, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers, isInline: !rotated, showSensitiveContent: contentSettings.ignoreContentRestrictionReasons.contains("sensitive"), isSuspiciousPeer: isSuspiciousPeer)
|
||||
let associatedData = extractAssociatedData(translationSettings: translationSettings, /* MARK: Swiftgram */ chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, preferredStoryHighQuality: preferredStoryHighQuality, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction.0, areStarReactionsEnabled: defaultReaction.1, isPremium: isPremium, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, accountPeer: accountPeer, topicAuthorId: topicAuthorId, hasBots: chatHasBots, translateToLanguage: translateToLanguage?.toLang, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers, isInline: !rotated, showSensitiveContent: contentSettings.ignoreContentRestrictionReasons.contains("sensitive"), isSuspiciousPeer: isSuspiciousPeer)
|
||||
|
||||
var includeEmbeddedSavedChatInfo = false
|
||||
if case let .replyThread(message) = chatLocation, message.peerId == context.account.peerId, !rotated {
|
||||
@@ -2106,8 +2154,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
||||
selectedMessages: selectedMessages,
|
||||
presentationData: chatPresentationData,
|
||||
historyAppearsCleared: historyAppearsCleared,
|
||||
// GHOSTGRAM: Keep view-once media visible if bypass is enabled
|
||||
skipViewOnceMedia: MiscSettingsManager.shared.shouldDisableViewOnceAutoDelete ? false : (mode != .bubbles),
|
||||
skipViewOnceMedia: mode != .bubbles,
|
||||
pendingUnpinnedAllMessages: pendingUnpinnedAllMessages,
|
||||
pendingRemovedMessages: pendingRemovedMessages,
|
||||
associatedData: associatedData,
|
||||
@@ -2115,9 +2162,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
||||
customChannelDiscussionReadState: customChannelDiscussionReadState,
|
||||
customThreadOutgoingReadState: customThreadOutgoingReadState,
|
||||
cachedData: data.cachedData,
|
||||
// GHOSTGRAM: Block ads if enabled in Misc settings
|
||||
adMessage: MiscSettingsManager.shared.shouldBlockAds ? nil : allAdMessages.fixed,
|
||||
dynamicAdMessages: MiscSettingsManager.shared.shouldBlockAds ? [] : allAdMessages.opportunistic
|
||||
adMessage: allAdMessages.fixed,
|
||||
dynamicAdMessages: allAdMessages.opportunistic
|
||||
)
|
||||
let lastHeaderId = filteredEntries.last.flatMap { listMessageDateHeaderId(timestamp: $0.index.timestamp) } ?? 0
|
||||
let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id, locationInput: update.2, ignoreMessagesInTimestampRange: update.3, ignoreMessageIds: update.4)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import SGSimpleSettings
|
||||
import Foundation
|
||||
import UIKit
|
||||
import TelegramCore
|
||||
@@ -261,7 +262,7 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
|
||||
}
|
||||
} else {
|
||||
stickersAreEmoji = stickersAreEmoji || hasForward
|
||||
if stickersEnabled {
|
||||
if stickersEnabled, !SGSimpleSettings.shared.forceEmojiTab {
|
||||
accessoryItems.append(.input(isEnabled: true, inputMode: stickersAreEmoji ? .emoji : .stickers))
|
||||
} else {
|
||||
accessoryItems.append(.input(isEnabled: true, inputMode: .emoji))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
import SGSimpleSettings
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
@@ -494,8 +495,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
}
|
||||
|
||||
if currentTranslateToLanguageUpdated || messageUpdated, let message = interfaceState.pinnedMessage?.message {
|
||||
if let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, translation.toLang == translateToLanguage?.toLang {
|
||||
} else if let translateToLanguage {
|
||||
if let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, translation.toLang == translateToLanguage?.toLang || translation.toLang.hasPrefix("\(translateToLanguage?.toLang ?? "")-") /* MARK: Swiftgram */ {
|
||||
} else if let translateToLanguage {
|
||||
self.translationDisposable.set(translateMessageIds(context: self.context, messageIds: [message.id], fromLang: translateToLanguage.fromLang, toLang: translateToLanguage.toLang).startStrict())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,17 @@ import TranslateUI
|
||||
import TelegramUIPreferences
|
||||
import TelegramNotices
|
||||
import PremiumUI
|
||||
import SGSimpleSettings
|
||||
|
||||
private func sgChatTranslationAvailable(isPremium: Bool, peer: Peer?) -> Bool {
|
||||
if isPremium || SGSimpleSettings.shared.translationBackendEnum != .default {
|
||||
return true
|
||||
}
|
||||
if let channel = peer as? TelegramChannel, channel.flags.contains(.autoTranslateEnabled) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
final class ChatTranslationPanelNode: ASDisplayNode {
|
||||
private let context: AccountContext
|
||||
@@ -183,11 +194,7 @@ final class ChatTranslationPanelNode: ASDisplayNode {
|
||||
|
||||
@objc private func closePressed() {
|
||||
let isPremium = self.chatInterfaceState?.isPremium ?? false
|
||||
|
||||
var translationAvailable = isPremium
|
||||
if let channel = self.chatInterfaceState?.renderedPeer?.chatMainPeer as? TelegramChannel, channel.flags.contains(.autoTranslateEnabled) {
|
||||
translationAvailable = true
|
||||
}
|
||||
let translationAvailable = sgChatTranslationAvailable(isPremium: isPremium, peer: self.chatInterfaceState?.renderedPeer?.chatMainPeer)
|
||||
|
||||
if translationAvailable {
|
||||
self.interfaceInteraction?.hideTranslationPanel()
|
||||
@@ -202,11 +209,7 @@ final class ChatTranslationPanelNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
let isPremium = self.chatInterfaceState?.isPremium ?? false
|
||||
|
||||
var translationAvailable = isPremium
|
||||
if let channel = self.chatInterfaceState?.renderedPeer?.chatMainPeer as? TelegramChannel, channel.flags.contains(.autoTranslateEnabled) {
|
||||
translationAvailable = true
|
||||
}
|
||||
let translationAvailable = sgChatTranslationAvailable(isPremium: isPremium, peer: self.chatInterfaceState?.renderedPeer?.chatMainPeer)
|
||||
|
||||
if translationAvailable {
|
||||
self.interfaceInteraction?.toggleTranslation(translationState.isEnabled ? .original : .translated)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Foundation
|
||||
import SGSimpleSettings
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import Postbox
|
||||
|
||||
@@ -757,7 +757,7 @@ func openResolvedUrlImpl(
|
||||
case let .settings(section):
|
||||
dismissInput()
|
||||
switch section {
|
||||
case .theme:
|
||||
case .legacy(.theme):
|
||||
if let navigationController = navigationController {
|
||||
let controller = themeSettingsController(context: context)
|
||||
controller.navigationPresentation = .modal
|
||||
@@ -768,7 +768,7 @@ func openResolvedUrlImpl(
|
||||
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
case .devices:
|
||||
case .legacy(.devices):
|
||||
if let navigationController = navigationController {
|
||||
let activeSessions = deferred { () -> Signal<(ActiveSessionsContext, Int, WebSessionsContext), NoError> in
|
||||
let activeSessionsContext = context.engine.privacy.activeSessions()
|
||||
@@ -798,15 +798,13 @@ func openResolvedUrlImpl(
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
})
|
||||
}
|
||||
case .autoremoveMessages:
|
||||
case .legacy(.autoremoveMessages):
|
||||
let _ = (context.engine.privacy.requestAccountPrivacySettings()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { settings in
|
||||
navigationController?.pushViewController(globalAutoremoveScreen(context: context, initialValue: settings.messageAutoremoveTimeout ?? 0, updated: { _ in }), animated: true)
|
||||
})
|
||||
case .twoStepAuth:
|
||||
break
|
||||
case .enableLog:
|
||||
case .legacy(.enableLog):
|
||||
if let navigationController = navigationController {
|
||||
let _ = updateLoggingSettings(accountManager: context.sharedContext.accountManager, {
|
||||
$0.withUpdatedLogToFile(true)
|
||||
@@ -819,7 +817,7 @@ func openResolvedUrlImpl(
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
}
|
||||
case .phonePrivacy:
|
||||
case .legacy(.phonePrivacy):
|
||||
let privacySignal = context.engine.privacy.requestAccountPrivacySettings()
|
||||
let _ = (privacySignal
|
||||
|> deliverOnMainQueue).start(next: { info in
|
||||
@@ -831,11 +829,13 @@ func openResolvedUrlImpl(
|
||||
navigationController.pushViewController(controller)
|
||||
}
|
||||
})
|
||||
case .loginEmail:
|
||||
case .legacy(.loginEmail):
|
||||
if let navigationController {
|
||||
let controller = loginEmailSetupController(context: context, blocking: false, emailPattern: nil, navigationController: navigationController, completion: {}, dismiss: {})
|
||||
navigationController.pushViewController(controller)
|
||||
}
|
||||
case .path(_):
|
||||
break
|
||||
}
|
||||
case let .premiumOffer(reference):
|
||||
dismissInput()
|
||||
@@ -851,7 +851,7 @@ func openResolvedUrlImpl(
|
||||
}
|
||||
case let .starsTopup(amount, purpose):
|
||||
dismissInput()
|
||||
if let starsContext = context.starsContext {
|
||||
if let starsContext = context.starsContext, let amount {
|
||||
let proceed = {
|
||||
let controller = context.sharedContext.makeStarsPurchaseScreen(context: context, starsContext: starsContext, options: [], purpose: .topUp(requiredStars: amount, purpose: purpose), targetPeerId: nil, customTheme: nil, completion: { _ in })
|
||||
if let navigationController = navigationController {
|
||||
@@ -1480,8 +1480,8 @@ func openResolvedUrlImpl(
|
||||
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
}
|
||||
})
|
||||
case let .collectible(gift):
|
||||
if let gift {
|
||||
case let .collectible(collectible):
|
||||
if case let .gift(gift) = collectible {
|
||||
var dismissedImpl: (() -> Void)?
|
||||
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||
let updateExternalController = storyProgressPauseContext.update
|
||||
@@ -1628,5 +1628,17 @@ func openResolvedUrlImpl(
|
||||
navigationController?.pushViewController(controller)
|
||||
}
|
||||
}
|
||||
case let .unknownDeepLink(path):
|
||||
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: path, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
case let .oauth(url):
|
||||
context.sharedContext.openExternalUrl(context: context, urlContext: urlContext, url: url, forceExternal: true, presentationData: presentationData, navigationController: navigationController, dismissInput: dismissInput)
|
||||
case .chats(_):
|
||||
dismissInput()
|
||||
case .compose(_):
|
||||
dismissInput()
|
||||
case .postStory(_):
|
||||
dismissInput()
|
||||
case .contacts(_):
|
||||
dismissInput()
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,10 @@
|
||||
// MARK: Swiftgram
|
||||
import SGIAP
|
||||
import SGPayWall
|
||||
import SGProUI
|
||||
import SGSimpleSettings
|
||||
import SGStatus
|
||||
//
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
@@ -95,6 +102,7 @@ import PasskeysScreen
|
||||
import GiftDemoScreen
|
||||
import ChatTextLinkEditUI
|
||||
import CocoonInfoScreen
|
||||
import GiftCraftScreen
|
||||
|
||||
private final class AccountUserInterfaceInUseContext {
|
||||
let subscribers = Bag<(Bool) -> Void>()
|
||||
@@ -254,6 +262,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
public let currentMediaDisplaySettings: Atomic<MediaDisplaySettings>
|
||||
private var mediaDisplaySettingsDisposable: Disposable?
|
||||
|
||||
public let currentChatSettings: Atomic<ChatSettings>
|
||||
private var chatSettingsDisposable: Disposable?
|
||||
|
||||
public let currentStickerSettings: Atomic<StickerSettings>
|
||||
private var stickerSettingsDisposable: Disposable?
|
||||
|
||||
@@ -264,6 +275,14 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return self.immediateExperimentalUISettingsValue.with { $0 }
|
||||
}
|
||||
private var experimentalUISettingsDisposable: Disposable?
|
||||
|
||||
// MARK: Swiftgram
|
||||
private var immediateSGStatusValue = Atomic<SGStatus>(value: SGStatus.default)
|
||||
public var immediateSGStatus: SGStatus {
|
||||
return self.immediateSGStatusValue.with { $0 }
|
||||
}
|
||||
private var sgStatusDisposable: Disposable?
|
||||
public var SGIAP: SGIAPManager?
|
||||
|
||||
public var presentGlobalController: (ViewController, Any?) -> Void = { _, _ in }
|
||||
public var presentCrossfadeController: () -> Void = {}
|
||||
@@ -338,6 +357,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
self.currentMediaDisplaySettings = Atomic(value: initialPresentationDataAndSettings.mediaDisplaySettings)
|
||||
self.currentStickerSettings = Atomic(value: initialPresentationDataAndSettings.stickerSettings)
|
||||
self.currentInAppNotificationSettings = Atomic(value: initialPresentationDataAndSettings.inAppNotificationSettings)
|
||||
self.currentChatSettings = Atomic(value: ChatSettings.defaultSettings)
|
||||
|
||||
if automaticEnergyUsageShouldBeOnNow(settings: self.currentAutomaticMediaDownloadSettings) {
|
||||
self.energyUsageSettings = EnergyUsageSettings.powerSavingDefault
|
||||
@@ -484,6 +504,15 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}
|
||||
})
|
||||
|
||||
self.chatSettingsDisposable = (self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.chatSettings])
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
|
||||
if let strongSelf = self {
|
||||
if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.chatSettings]?.get(ChatSettings.self) {
|
||||
let _ = strongSelf.currentChatSettings.swap(settings)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let immediateExperimentalUISettingsValue = self.immediateExperimentalUISettingsValue
|
||||
let _ = immediateExperimentalUISettingsValue.swap(initialPresentationDataAndSettings.experimentalUISettings)
|
||||
|
||||
@@ -498,6 +527,18 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
GlassBackgroundView.useCustomGlassImpl = settings.fakeGlass
|
||||
}
|
||||
})
|
||||
// MARK: Swiftgram
|
||||
let immediateSGStatusValue = self.immediateSGStatusValue
|
||||
self.sgStatusDisposable = (self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.sgStatus])
|
||||
|> deliverOnMainQueue).start(next: { sharedData in
|
||||
if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.sgStatus]?.get(SGStatus.self) {
|
||||
let _ = immediateSGStatusValue.swap(settings)
|
||||
SGSimpleSettings.shared.ephemeralStatus = settings.status
|
||||
SGSimpleSettings.shared.status = settings.status
|
||||
}
|
||||
})
|
||||
self.initSGIAP(isMainApp: applicationBindings.isMainApp)
|
||||
//
|
||||
|
||||
let _ = self.contactDataManager?.personNameDisplayOrder().start(next: { order in
|
||||
let _ = updateContactSettingsInteractively(accountManager: accountManager, { settings in
|
||||
@@ -1077,6 +1118,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
self.inAppNotificationSettingsDisposable?.dispose()
|
||||
self.mediaInputSettingsDisposable?.dispose()
|
||||
self.mediaDisplaySettingsDisposable?.dispose()
|
||||
self.chatSettingsDisposable?.dispose()
|
||||
self.stickerSettingsDisposable?.dispose()
|
||||
self.callDisposable?.dispose()
|
||||
self.groupCallDisposable?.dispose()
|
||||
self.callStateDisposable?.dispose()
|
||||
@@ -1084,8 +1127,20 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
self.callPeerDisposable?.dispose()
|
||||
}
|
||||
|
||||
// MARK: Swiftgram
|
||||
var didPerformSGUISettingsMigration = false
|
||||
//
|
||||
// MARK: Swiftgram
|
||||
func sgPrimaryAccountContextForMigration() -> AccountContext? {
|
||||
return self.activeAccountsValue?.primary
|
||||
}
|
||||
//
|
||||
private var didPerformAccountSettingsImport = false
|
||||
|
||||
private func performAccountSettingsImportIfNecessary() {
|
||||
// MARK: Swiftgram
|
||||
self.performSGUISettingsMigrationIfNecessary()
|
||||
//
|
||||
if self.didPerformAccountSettingsImport {
|
||||
return
|
||||
}
|
||||
@@ -3767,7 +3822,24 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}
|
||||
|
||||
public func makeStarsTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, source: BotPaymentInvoiceSource, extendedMedia: [TelegramExtendedMedia], inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?, EnginePeer?)?, NoError>, completion: @escaping (Bool) -> Void) -> ViewController {
|
||||
return StarsTransferScreen(context: context, starsContext: starsContext, invoice: invoice, source: source, extendedMedia: extendedMedia, inputData: inputData, completion: completion)
|
||||
return StarsTransferScreen(context: context, starsContext: starsContext, invoice: invoice, source: source, extendedMedia: extendedMedia, inputData: inputData, navigateToPeer: { [weak self] peer in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let infoController = self.makePeerInfoController(
|
||||
context: context,
|
||||
updatedPresentationData: nil,
|
||||
peer: peer._asPeer(),
|
||||
mode: .generic,
|
||||
avatarInitiallyExpanded: peer.smallProfileImage != nil,
|
||||
fromChat: false,
|
||||
requestsContext: nil
|
||||
) {
|
||||
if let navigationController = self.mainWindow?.viewController as? NavigationController {
|
||||
navigationController.pushViewController(infoController)
|
||||
}
|
||||
}
|
||||
}, completion: completion)
|
||||
}
|
||||
|
||||
public func makeStarsSubscriptionTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, link: String, inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?, EnginePeer?)?, NoError>, navigateToPeer: @escaping (EnginePeer) -> Void) -> ViewController {
|
||||
@@ -3851,7 +3923,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return GiftViewScreen(context: context, subject: .wearPreview(gift, attributes))
|
||||
}
|
||||
|
||||
public func makeGiftUpgradePreviewScreen(context: AccountContext, attributes: [StarGift.UniqueGift.Attribute], peerName: String) -> ViewController {
|
||||
public func makeGiftUpgradePreviewScreen(context: AccountContext, gift: StarGift.Gift, attributes: [StarGift.UniqueGift.Attribute], peerName: String) -> ViewController {
|
||||
return GiftViewScreen(context: context, subject: .upgradePreview(attributes, peerName))
|
||||
}
|
||||
|
||||
@@ -3875,7 +3947,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return giftOfferAlertController(context: context, updatedPresentationData: updatedPresentationData, gift: gift, peer: peer, amount: amount, commit: commit)
|
||||
}
|
||||
|
||||
public func makeGiftUpgradeVariantsScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute], selectedAttributes: [StarGift.UniqueGift.Attribute]?, focusedAttribute: StarGift.UniqueGift.Attribute?) -> ViewController {
|
||||
public func makeGiftUpgradeVariantsScreen(context: AccountContext, gift: StarGift, crafted: Bool, attributes: [StarGift.UniqueGift.Attribute], selectedAttributes: [StarGift.UniqueGift.Attribute]?, focusedAttribute: StarGift.UniqueGift.Attribute?) -> ViewController {
|
||||
return GiftUpgradeVariantsScreen(context: context, gift: gift, attributes: attributes, selectedAttributes: selectedAttributes, focusedAttribute: focusedAttribute)
|
||||
}
|
||||
|
||||
@@ -3883,6 +3955,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return GiftAuctionWearPreviewScreen(context: context, auctionContext: auctionContext, attributes: attributes, completion: completion)
|
||||
}
|
||||
|
||||
public func makeGiftCraftScreen(context: AccountContext, gift: StarGift.UniqueGift, profileGiftsContext: ProfileGiftsContext?) -> ViewController {
|
||||
return GiftCraftScreen(context: context, gift: gift, profileGiftsContext: profileGiftsContext)
|
||||
}
|
||||
|
||||
public func makeGiftDemoScreen(context: AccountContext) -> ViewController {
|
||||
return GiftDemoScreen(context: context)
|
||||
}
|
||||
@@ -4094,8 +4170,23 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}
|
||||
|
||||
private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? {
|
||||
var switchToMediaTarget: PeerInfoSwitchToMediaTarget?
|
||||
switch mode {
|
||||
case let .media(kind, index):
|
||||
let mappedKind: PeerInfoSwitchToMediaTarget.Kind
|
||||
switch kind {
|
||||
case .photoVideo:
|
||||
mappedKind = .photoVideo
|
||||
case .file:
|
||||
mappedKind = .file
|
||||
}
|
||||
switchToMediaTarget = PeerInfoSwitchToMediaTarget(kind: mappedKind, messageIndex: index)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if let _ = peer as? TelegramGroup {
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [])
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], switchToMediaTarget: switchToMediaTarget)
|
||||
} else if let _ = peer as? TelegramChannel {
|
||||
var forumTopicThread: ChatReplyThreadMessage?
|
||||
var switchToRecommendedChannels = false
|
||||
@@ -4118,7 +4209,7 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
|
||||
default:
|
||||
break
|
||||
}
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], forumTopicThread: forumTopicThread, switchToRecommendedChannels: switchToRecommendedChannels, switchToGiftsTarget: switchToGiftsTarget, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder)
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], forumTopicThread: forumTopicThread, switchToRecommendedChannels: switchToRecommendedChannels, switchToGiftsTarget: switchToGiftsTarget, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder, switchToMediaTarget: switchToMediaTarget)
|
||||
} else if peer is TelegramUser {
|
||||
var nearbyPeerDistance: Int32?
|
||||
var reactionSourceMessageId: MessageId?
|
||||
@@ -4171,9 +4262,9 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
|
||||
default:
|
||||
break
|
||||
}
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, reactionSourceMessageId: reactionSourceMessageId, callMessages: callMessages, isMyProfile: isMyProfile, hintGroupInCommon: hintGroupInCommon, forumTopicThread: forumTopicThread, sharedMediaFromForumTopic: sharedMediaFromForumTopic, switchToGiftsTarget: switchToGiftsTarget, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder)
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, reactionSourceMessageId: reactionSourceMessageId, callMessages: callMessages, isMyProfile: isMyProfile, hintGroupInCommon: hintGroupInCommon, forumTopicThread: forumTopicThread, sharedMediaFromForumTopic: sharedMediaFromForumTopic, switchToGiftsTarget: switchToGiftsTarget, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder, switchToMediaTarget: switchToMediaTarget)
|
||||
} else if peer is TelegramSecretChat {
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [])
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], switchToMediaTarget: switchToMediaTarget)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -4184,3 +4275,64 @@ private func useFlatModalCallsPresentation(context: AccountContext) -> Bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: Swiftgram
|
||||
extension SharedAccountContextImpl {
|
||||
func initSGIAP(isMainApp: Bool) {
|
||||
if isMainApp {
|
||||
self.SGIAP = SGIAPManager()
|
||||
} else {
|
||||
self.SGIAP = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func makeSGProController(context: AccountContext) -> ViewController {
|
||||
let controller = sgProController(context: context)
|
||||
return controller
|
||||
}
|
||||
|
||||
public func makeSGPayWallController(context: AccountContext) -> ViewController? {
|
||||
guard #available(iOS 13.0, *) else {
|
||||
return nil
|
||||
}
|
||||
guard let sgIAP = self.SGIAP else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let statusSignal = self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.sgStatus])
|
||||
|> map { sharedData -> Int64 in
|
||||
let sgStatus = sharedData.entries[ApplicationSpecificSharedDataKeys.sgStatus]?.get(SGStatus.self) ?? SGStatus.default
|
||||
return sgStatus.status
|
||||
}
|
||||
|
||||
let proController = self.makeSGProController(context: context)
|
||||
let sgWebSettings = context.currentAppConfiguration.with { $0 }.sgWebSettings
|
||||
let presentationData = self.currentPresentationData.with { $0 }
|
||||
var payWallController: ViewController? = nil
|
||||
let openUrl: ((String, Bool) -> Void) = { [weak self, weak context] url, forceExternal in
|
||||
guard let strongSelf = self, let strongContext = context, let strongPayWallController = payWallController else {
|
||||
return
|
||||
}
|
||||
let navigationController = strongPayWallController.navigationController as? NavigationController
|
||||
Queue.mainQueue().async {
|
||||
strongSelf.openExternalUrl(context: strongContext, urlContext: .generic, url: url, forceExternal: forceExternal, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||
}
|
||||
}
|
||||
|
||||
var supportUrl: String? = nil
|
||||
if let supportUrlString = sgWebSettings.global.proSupportUrl, !supportUrlString.isEmpty, let data = Data(base64Encoded: supportUrlString), let decodedString = String(data: data, encoding: .utf8) {
|
||||
supportUrl = decodedString
|
||||
}
|
||||
payWallController = sgPayWallController(statusSignal: statusSignal, replacementController: proController, presentationData: presentationData, SGIAPManager: sgIAP, openUrl: openUrl, paymentsEnabled: sgWebSettings.global.paymentsEnabled, canBuyInBeta: sgWebSettings.user.canBuyInBeta, openAppStorePage: self.applicationBindings.openAppStorePage, proSupportUrl: supportUrl)
|
||||
return payWallController
|
||||
}
|
||||
|
||||
public func makeSGUpdateIOSController() -> ViewController {
|
||||
let presentationData = self.currentPresentationData.with { $0 }
|
||||
let controller = textAlertController(sharedContext: self, title: nil, text: "Common.UpdateOS".i18n(presentationData.strings.baseLanguageCode), actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||
})])
|
||||
return controller
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import SGSimpleSettings
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
@@ -41,7 +42,7 @@ private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceh
|
||||
|
||||
init(context: AccountContext) {
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil)
|
||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, preferredGlassType: .default, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil)
|
||||
|
||||
self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: true)
|
||||
self.emptyNode = ChatEmptyNode(context: context, interaction: nil)
|
||||
@@ -54,7 +55,8 @@ private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceh
|
||||
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.presentationInterfaceState.limitsConfiguration, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.presentationInterfaceState.accountPeerId, mode: .standard(.default), chatLocation: self.presentationInterfaceState.chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil)
|
||||
let preferredGlassType = self.presentationInterfaceState.preferredGlassType
|
||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, preferredGlassType: preferredGlassType, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.presentationInterfaceState.limitsConfiguration, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.presentationInterfaceState.accountPeerId, mode: .standard(.default), chatLocation: self.presentationInterfaceState.chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil)
|
||||
|
||||
self.wallpaperBackgroundNode.update(wallpaper: presentationData.chatWallpaper, animated: false)
|
||||
}
|
||||
@@ -156,10 +158,18 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
return self.chatListController
|
||||
}
|
||||
|
||||
public func getSettingsController() -> ViewController? {
|
||||
return self.accountSettingsController
|
||||
}
|
||||
|
||||
public func getPrivacySettings() -> Promise<AccountPrivacySettings?>? {
|
||||
return self.accountSettingsController?.privacySettings
|
||||
}
|
||||
|
||||
public func getTwoStepAuthData() -> Promise<TwoStepAuthData?>? {
|
||||
return self.accountSettingsController?.twoStepAuthData
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
let needsRootWallpaperBackgroundNode: Bool
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
@@ -186,7 +196,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
|
||||
public func addRootControllers(showCallsTab: Bool) {
|
||||
public func addRootControllers(hidePhoneInSettings: Bool, showContactsTab: Bool, showCallsTab: Bool) {
|
||||
let tabBarController = TabBarControllerImpl(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||
tabBarController.navigationPresentation = .master
|
||||
let chatListController = self.context.sharedContext.makeChatListController(context: self.context, location: .chatList(groupId: .root), controlsHistoryPreload: true, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||
@@ -201,7 +211,10 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
contactsController.switchToChatsController = { [weak self] in
|
||||
self?.openChatsController(activateSearch: false)
|
||||
}
|
||||
controllers.append(contactsController)
|
||||
// MARK: Swiftgram
|
||||
if showContactsTab {
|
||||
controllers.append(contactsController)
|
||||
}
|
||||
|
||||
if showCallsTab {
|
||||
controllers.append(callListController)
|
||||
@@ -217,7 +230,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
sharedContext.switchingData = (nil, nil, nil)
|
||||
}
|
||||
|
||||
let accountSettingsController = PeerInfoScreenImpl(context: self.context, updatedPresentationData: nil, peerId: self.context.account.peerId, avatarInitiallyExpanded: false, isOpenedFromChat: false, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], isSettings: true)
|
||||
let accountSettingsController = PeerInfoScreenImpl(hidePhoneInSettings: hidePhoneInSettings, context: self.context, updatedPresentationData: nil, peerId: self.context.account.peerId, avatarInitiallyExpanded: false, isOpenedFromChat: false, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], isSettings: true)
|
||||
accountSettingsController.tabBarItemDebugTapAction = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@@ -237,12 +250,14 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
self.pushViewController(tabBarController, animated: false)
|
||||
}
|
||||
|
||||
public func updateRootControllers(showCallsTab: Bool) {
|
||||
public func updateRootControllers(showContactsTab: Bool, showCallsTab: Bool) {
|
||||
guard let rootTabController = self.rootTabController as? TabBarControllerImpl else {
|
||||
return
|
||||
}
|
||||
var controllers: [ViewController] = []
|
||||
controllers.append(self.contactsController!)
|
||||
if showContactsTab {
|
||||
controllers.append(self.contactsController!)
|
||||
}
|
||||
if showCallsTab {
|
||||
controllers.append(self.callListController!)
|
||||
}
|
||||
@@ -300,7 +315,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func openStoryCamera(customTarget: Stories.PendingTarget?, resumeLiveStream: Bool, transitionIn: StoryCameraTransitionIn?, transitionedIn: @escaping () -> Void, transitionOut: @escaping (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?) -> StoryCameraTransitionInCoordinator? {
|
||||
public func openStoryCamera(mode: StoryCameraMode, customTarget: Stories.PendingTarget?, resumeLiveStream: Bool, transitionIn: StoryCameraTransitionIn?, transitionedIn: @escaping () -> Void, transitionOut: @escaping (Stories.PendingTarget?, Bool) -> StoryCameraTransitionOut?) -> StoryCameraTransitionInCoordinator? {
|
||||
guard let controller = self.viewControllers.last as? ViewController else {
|
||||
return nil
|
||||
}
|
||||
@@ -675,7 +690,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
defer {
|
||||
TempBox.shared.dispose(tempFile)
|
||||
}
|
||||
if let imageData = compressImageToJPEG(image, quality: 0.7, tempFilePath: tempFile.path) {
|
||||
if let imageData = compressImageToJPEG(image, quality: Float(SGSimpleSettings.shared.outgoingPhotoQuality) / 100.0, tempFilePath: tempFile.path) {
|
||||
media = .image(dimensions: dimensions, data: imageData, stickers: result.stickers)
|
||||
}
|
||||
case let .video(content, firstFrameImage, values, duration, dimensions):
|
||||
@@ -698,7 +713,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
defer {
|
||||
TempBox.shared.dispose(tempFile)
|
||||
}
|
||||
let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6, tempFilePath: tempFile.path) }
|
||||
let imageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: Float(SGSimpleSettings.shared.outgoingPhotoQuality) / 100.0, tempFilePath: tempFile.path) }
|
||||
let firstFrameFile = imageData.flatMap { data -> TempBoxFile? in
|
||||
let file = TempBox.shared.tempFile(fileName: "image.jpg")
|
||||
if let _ = try? data.write(to: URL(fileURLWithPath: file.path)) {
|
||||
@@ -758,7 +773,31 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
}
|
||||
}
|
||||
|
||||
public func openSettings() {
|
||||
public func openChats() {
|
||||
guard let rootTabController = self.rootTabController else {
|
||||
return
|
||||
}
|
||||
|
||||
self.popToRoot(animated: false)
|
||||
|
||||
if let index = rootTabController.controllers.firstIndex(where: { $0 is ChatListController }) {
|
||||
rootTabController.selectedIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
public func openContacts() {
|
||||
guard let rootTabController = self.rootTabController else {
|
||||
return
|
||||
}
|
||||
|
||||
self.popToRoot(animated: false)
|
||||
|
||||
if let index = rootTabController.controllers.firstIndex(where: { $0 is ContactsController }) {
|
||||
rootTabController.selectedIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
public func openSettings(edit: Bool) {
|
||||
guard let rootTabController = self.rootTabController else {
|
||||
return
|
||||
}
|
||||
@@ -768,6 +807,10 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
if let index = rootTabController.controllers.firstIndex(where: { $0 is PeerInfoScreenImpl }) {
|
||||
rootTabController.selectedIndex = index
|
||||
}
|
||||
|
||||
if edit {
|
||||
self.accountSettingsController?.activateEdit()
|
||||
}
|
||||
}
|
||||
|
||||
public func openBirthdaySetup() {
|
||||
@@ -784,6 +827,10 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
accountSettingsController.openAvatars()
|
||||
}
|
||||
}
|
||||
|
||||
public func startNewCall() {
|
||||
self.callListController?.tabBarActivateSearch()
|
||||
}
|
||||
}
|
||||
|
||||
#if SWIFT_PACKAGE
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import SGSimpleSettings
|
||||
import Foundation
|
||||
import UIKit
|
||||
import TelegramCore
|
||||
@@ -169,7 +170,8 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me
|
||||
defer {
|
||||
TempBox.shared.dispose(tempFile)
|
||||
}
|
||||
if let fullImage = UIImage(contentsOfFile: data.path), let smallestImage = generateScaledImage(image: fullImage, size: smallestSize, scale: 1.0), let smallestData = compressImageToJPEG(smallestImage, quality: 0.7, tempFilePath: tempFile.path) {
|
||||
// MARK: Swiftgram
|
||||
if let fullImage = UIImage(contentsOfFile: data.path), let smallestImage = generateScaledImage(image: fullImage, size: smallestSize, scale: 1.0), let smallestData = compressImageToJPEG(smallestImage, quality: Float(SGSimpleSettings.shared.outgoingPhotoQuality) / 100.0, tempFilePath: tempFile.path) {
|
||||
var representations = image.representations
|
||||
|
||||
let thumbnailResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import Foundation
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import WatchBridge
|
||||
|
||||
public final class WatchManagerImpl: WatchManager {
|
||||
private let arguments: WatchManagerArguments?
|
||||
|
||||
public init(arguments: WatchManagerArguments?) {
|
||||
self.arguments = arguments
|
||||
}
|
||||
|
||||
public var watchAppInstalled: Signal<Bool, NoError> {
|
||||
return self.arguments?.appInstalled ?? .single(false)
|
||||
}
|
||||
|
||||
public var navigateToMessageRequested: Signal<EngineMessage.Id, NoError> {
|
||||
return self.arguments?.navigateToMessageRequested ?? .never()
|
||||
}
|
||||
|
||||
public var runningTasks: Signal<WatchRunningTasks?, NoError> {
|
||||
return self.arguments?.runningTasks ?? .single(nil)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user