Sync experiment

This commit is contained in:
Peter 2019-10-22 19:08:44 +04:00
parent c83a2364ac
commit 09861c72b7
47 changed files with 4971 additions and 4348 deletions

2
BUCK
View File

@ -345,6 +345,8 @@ apple_binary(
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
"//submodules/Database/MessageHistoryReadStateTable:MessageHistoryReadStateTable",
"//submodules/Database/MessageHistoryMetadataTable:MessageHistoryMetadataTable",
"//submodules/Database/PreferencesTable:PreferencesTable",
"//submodules/Database/PeerTable:PeerTable",
"//submodules/sqlcipher:sqlcipher",
],
frameworks = [

View File

@ -3,6 +3,44 @@ load("//Config:utils.bzl",
"dynamic_library_configs",
)
text_section_items = [
"__text",
]
text_section_rename_linker_flags = []#["-Wl,-rename_section,__TEXT,%s,__MY_TEXT,%s" % (name, name) for name in text_section_items] + ["-Wl,-segprot,__MY_TEXT,rx,rx"]
data_section_items = [
"__nl_symbol_ptr",
"__la_symbol_ptr",
"__mod_init_func",
"__const",
"__cfstring",
"__objc_classlist",
"__objc_nlclslist",
"__objc_catlist",
"__objc_nlcatlist",
"__objc_protolist",
"__objc_imageinfo",
"__objc_const",
"__objc_selrefs",
"__objc_protorefs",
"__objc_classrefs",
"__objc_superrefs",
"__objc_ivar",
"__objc_data",
"__data",
"__thread_vars",
"__thread_ptr",
"__swift_hooks",
"__thread_bss",
"__bss",
"__common",
]
data_section_rename_linker_flags = ["-Wl,-rename_section,__DATA,%s,__MY_DATA,%s" % (name, name) for name in data_section_items] + ["-Wl,-segprot,__MY_DATA,rw,rw"]
section_rename_linker_flags = text_section_rename_linker_flags# + data_section_rename_linker_flags
def apple_lib(
name,
visibility = ["PUBLIC"],
@ -71,6 +109,8 @@ def apple_lib(
for framework in weak_frameworks:
resolved_linker_flags = resolved_linker_flags + ["-Wl,-weak_framework,%s" % framework]
resolved_linker_flags = resolved_linker_flags + section_rename_linker_flags
native.apple_library(
name = name + "",
srcs = srcs,

View File

@ -0,0 +1,16 @@
import Foundation
import ValueBox
private func applicationSpecificSharedDataKey(_ value: Int32) -> ValueBoxKey {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: value + 1000)
return key
}
private enum ApplicationSpecificSharedDataKeyValues: Int32 {
case inAppNotificationSettings = 0
}
public struct ApplicationSpecificSharedDataKeys {
public static let inAppNotificationSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.inAppNotificationSettings.rawValue)
}

View File

@ -0,0 +1,90 @@
import Foundation
import PostboxCoding
import PreferencesTable
import MessageHistoryMetadataTable
import PostboxDataTypes
public enum TotalUnreadCountDisplayStyle: Int32 {
case filtered = 0
var category: ChatListTotalUnreadStateCategory {
switch self {
case .filtered:
return .filtered
}
}
}
public enum TotalUnreadCountDisplayCategory: Int32 {
case chats = 0
case messages = 1
var statsType: ChatListTotalUnreadStateStats {
switch self {
case .chats:
return .chats
case .messages:
return .messages
}
}
}
public struct InAppNotificationSettings: PreferencesEntry, Equatable {
public var playSounds: Bool
public var vibrate: Bool
public var displayPreviews: Bool
public var totalUnreadCountDisplayStyle: TotalUnreadCountDisplayStyle
public var totalUnreadCountDisplayCategory: TotalUnreadCountDisplayCategory
public var totalUnreadCountIncludeTags: PeerSummaryCounterTags
public var displayNameOnLockscreen: Bool
public var displayNotificationsFromAllAccounts: Bool
public static var defaultSettings: InAppNotificationSettings {
return InAppNotificationSettings(playSounds: true, vibrate: false, displayPreviews: true, totalUnreadCountDisplayStyle: .filtered, totalUnreadCountDisplayCategory: .messages, totalUnreadCountIncludeTags: [.regularChatsAndPrivateGroups], displayNameOnLockscreen: true, displayNotificationsFromAllAccounts: true)
}
public init(playSounds: Bool, vibrate: Bool, displayPreviews: Bool, totalUnreadCountDisplayStyle: TotalUnreadCountDisplayStyle, totalUnreadCountDisplayCategory: TotalUnreadCountDisplayCategory, totalUnreadCountIncludeTags: PeerSummaryCounterTags, displayNameOnLockscreen: Bool, displayNotificationsFromAllAccounts: Bool) {
self.playSounds = playSounds
self.vibrate = vibrate
self.displayPreviews = displayPreviews
self.totalUnreadCountDisplayStyle = totalUnreadCountDisplayStyle
self.totalUnreadCountDisplayCategory = totalUnreadCountDisplayCategory
self.totalUnreadCountIncludeTags = totalUnreadCountIncludeTags
self.displayNameOnLockscreen = displayNameOnLockscreen
self.displayNotificationsFromAllAccounts = displayNotificationsFromAllAccounts
}
public init(decoder: PostboxDecoder) {
self.playSounds = decoder.decodeInt32ForKey("s", orElse: 0) != 0
self.vibrate = decoder.decodeInt32ForKey("v", orElse: 0) != 0
self.displayPreviews = decoder.decodeInt32ForKey("p", orElse: 0) != 0
self.totalUnreadCountDisplayStyle = TotalUnreadCountDisplayStyle(rawValue: decoder.decodeInt32ForKey("cds", orElse: 0)) ?? .filtered
self.totalUnreadCountDisplayCategory = TotalUnreadCountDisplayCategory(rawValue: decoder.decodeInt32ForKey("totalUnreadCountDisplayCategory", orElse: 1)) ?? .messages
if let value = decoder.decodeOptionalInt32ForKey("totalUnreadCountIncludeTags") {
self.totalUnreadCountIncludeTags = PeerSummaryCounterTags(rawValue: value)
} else {
self.totalUnreadCountIncludeTags = [.regularChatsAndPrivateGroups]
}
self.displayNameOnLockscreen = decoder.decodeInt32ForKey("displayNameOnLockscreen", orElse: 1) != 0
self.displayNotificationsFromAllAccounts = decoder.decodeInt32ForKey("displayNotificationsFromAllAccounts", orElse: 1) != 0
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.playSounds ? 1 : 0, forKey: "s")
encoder.encodeInt32(self.vibrate ? 1 : 0, forKey: "v")
encoder.encodeInt32(self.displayPreviews ? 1 : 0, forKey: "p")
encoder.encodeInt32(self.totalUnreadCountDisplayStyle.rawValue, forKey: "cds")
encoder.encodeInt32(self.totalUnreadCountDisplayCategory.rawValue, forKey: "totalUnreadCountDisplayCategory")
encoder.encodeInt32(self.totalUnreadCountIncludeTags.rawValue, forKey: "totalUnreadCountIncludeTags")
encoder.encodeInt32(self.displayNameOnLockscreen ? 1 : 0, forKey: "displayNameOnLockscreen")
encoder.encodeInt32(self.displayNotificationsFromAllAccounts ? 1 : 0, forKey: "displayNotificationsFromAllAccounts")
}
public func isEqual(to: PreferencesEntry) -> Bool {
if let to = to as? InAppNotificationSettings {
return self == to
} else {
return false
}
}
}

View File

@ -0,0 +1,20 @@
import PostboxDataTypes
extension PeerSummaryCounterTags {
static let regularChatsAndPrivateGroups = PeerSummaryCounterTags(rawValue: 1 << 0)
static let publicGroups = PeerSummaryCounterTags(rawValue: 1 << 1)
static let channels = PeerSummaryCounterTags(rawValue: 1 << 2)
}
struct Namespaces {
struct Message {
static let Cloud: Int32 = 0
}
struct Peer {
static let CloudUser: Int32 = 0
static let CloudGroup: Int32 = 1
static let CloudChannel: Int32 = 2
static let SecretChat: Int32 = 3
}
}

View File

@ -1,3 +0,0 @@
#import <Foundation/Foundation.h>

View File

@ -1,3 +0,0 @@
#import "Sync.h"
//#import <sqlcipher/sqlcipher.h>

View File

@ -1,9 +1,17 @@
import Foundation
import SwiftSignalKit
import ValueBox
import PostboxDataTypes
import MessageHistoryReadStateTable
import MessageHistoryMetadataTable
import PostboxDataTypes
import PreferencesTable
import PeerTable
import PostboxCoding
private let registeredTypes: Void = {
declareEncodable(InAppNotificationSettings.self, f: InAppNotificationSettings.init(decoder:))
declareEncodable(TelegramChannel.self, f: TelegramChannel.init(decoder:))
}()
private func accountRecordIdPathName(_ id: Int64) -> String {
return "account-\(UInt64(bitPattern: id))"
@ -15,40 +23,49 @@ private final class ValueBoxLoggerImpl: ValueBoxLogger {
}
}
private extension PeerSummaryCounterTags {
static let regularChatsAndPrivateGroups = PeerSummaryCounterTags(rawValue: 1 << 0)
static let publicGroups = PeerSummaryCounterTags(rawValue: 1 << 1)
static let channels = PeerSummaryCounterTags(rawValue: 1 << 2)
}
private struct Namespaces {
struct Message {
static let Cloud: Int32 = 0
}
struct Peer {
static let CloudUser: Int32 = 0
static let CloudGroup: Int32 = 1
static let CloudChannel: Int32 = 2
static let SecretChat: Int32 = 3
}
}
final class SyncProviderImpl {
func addIncomingMessage(withRootPath rootPath: String, accountId: Int64, encryptionParameters: DeviceSpecificEncryptionParameters, peerId: Int64, messageId: Int32, completion: @escaping (Int32) -> Void) {
Queue.mainQueue().async {
let _ = registeredTypes
let sharedBasePath = rootPath + "/accounts-metadata"
let basePath = rootPath + "/" + accountRecordIdPathName(accountId) + "/postbox"
let sharedValueBox = SqliteValueBox(basePath: sharedBasePath + "/db", queue: Queue.mainQueue(), logger: ValueBoxLoggerImpl(), encryptionParameters: nil, disableCache: true, upgradeProgress: { _ in
})
let valueBox = SqliteValueBox(basePath: basePath + "/db", queue: Queue.mainQueue(), logger: ValueBoxLoggerImpl(), encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: encryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: encryptionParameters.salt)!), disableCache: true, upgradeProgress: { _ in
})
let metadataTable = MessageHistoryMetadataTable(valueBox: valueBox, table: MessageHistoryMetadataTable.tableSpec(10))
let readStateTable = MessageHistoryReadStateTable(valueBox: valueBox, table: MessageHistoryReadStateTable.tableSpec(14), defaultMessageNamespaceReadStates: [:])
let peerTable = PeerTable(valueBox: valueBox, table: PeerTable.tableSpec(2), reverseAssociatedTable: nil)
let preferencesTable = PreferencesTable(valueBox: sharedValueBox, table: PreferencesTable.tableSpec(2))
let peerId = PeerId(peerId)
let initialCombinedState = readStateTable.getCombinedState(peerId)
let (combinedState, _) = readStateTable.addIncomingMessages(peerId, indices: Set([MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: messageId), timestamp: 1)]))
let combinedState = initialCombinedState.flatMap { state -> CombinedPeerReadState in
var state = state
for i in 0 ..< state.states.count {
if state.states[i].0 == Namespaces.Message.Cloud {
switch state.states[i].1 {
case .idBased(let maxIncomingReadId, let maxOutgoingReadId, var maxKnownId, var count, let markedUnread):
if messageId > maxIncomingReadId {
count += 1
}
maxKnownId = max(maxKnownId, messageId)
state.states[i] = (state.states[i].0, .idBased(maxIncomingReadId: maxIncomingReadId, maxOutgoingReadId: maxOutgoingReadId, maxKnownId: maxKnownId, count: count, markedUnread: markedUnread))
default:
break
}
}
}
return state
}
if let combinedState = combinedState {
let initialCount = initialCombinedState?.count ?? 0
let updatedCount = combinedState.count
@ -56,7 +73,20 @@ final class SyncProviderImpl {
let tag: PeerSummaryCounterTags
if peerId.namespace == Namespaces.Peer.CloudChannel {
tag = .channels
if let channel = peerTable.get(peerId) as? TelegramChannel {
switch channel.info {
case .broadcast:
tag = .channels
case .group:
if channel.username != nil {
tag = .publicGroups
} else {
tag = .regularChatsAndPrivateGroups
}
}
} else {
tag = .channels
}
} else {
tag = .regularChatsAndPrivateGroups
}
@ -79,7 +109,9 @@ final class SyncProviderImpl {
totalUnreadState.filteredCounters[tag] = counters
}
totalCount = totalUnreadState.count(for: .filtered, in: .messages, with: [.channels, .publicGroups, .regularChatsAndPrivateGroups])
let inAppSettings = preferencesTable.get(key: ApplicationSpecificSharedDataKeys.inAppNotificationSettings) as? InAppNotificationSettings ?? InAppNotificationSettings.defaultSettings
totalCount = totalUnreadState.count(for: inAppSettings.totalUnreadCountDisplayStyle.category, in: inAppSettings.totalUnreadCountDisplayCategory.statsType, with: inAppSettings.totalUnreadCountIncludeTags)
metadataTable.setChatListTotalUnreadState(totalUnreadState)
metadataTable.setShouldReindexUnreadCounts(value: true)

View File

@ -0,0 +1,38 @@
import PostboxDataTypes
import PostboxCoding
public enum TelegramChannelInfo: Int32 {
case broadcast = 0
case group = 1
}
public final class TelegramChannel: Peer {
public let id: PeerId
public let username: String?
public let info: TelegramChannelInfo
public let associatedPeerId: PeerId? = nil
public let notificationSettingsPeerId: PeerId? = nil
public init(decoder: PostboxDecoder) {
self.id = PeerId(decoder.decodeInt64ForKey("i", orElse: 0))
self.username = decoder.decodeOptionalStringForKey("un")
self.info = TelegramChannelInfo(rawValue: decoder.decodeInt32ForKey("i.t", orElse: 0)) ?? .broadcast
}
public func encode(_ encoder: PostboxEncoder) {
preconditionFailure()
}
public func isEqual(_ other: Peer) -> Bool {
guard let other = other as? TelegramChannel else {
return false
}
if self.username != other.username {
return false
}
return true
}
}

View File

@ -4824,6 +4824,8 @@ Any member of this group will be able to see messages in the channel.";
"Wallet.Send.ConfirmationConfirm" = "Confirm";
"Wallet.Send.Send" = "Send";
"Wallet.Settings.Title" = "Wallet Settings";
"Wallet.Settings.Configuration" = "Configuration";
"Wallet.Settings.BackupWallet" = "Backup Wallet";
"Wallet.Settings.DeleteWallet" = "Delete Wallet";
"Wallet.Settings.DeleteWalletInfo" = "This will disconnect the wallet from this app. You will be able to restore your wallet using 24 secret words or import another wallet.\n\nWallets are located in the TON Blockchain, which is not controlled by Telegram. If you want a wallet to be deleted, simply transfer all the grams from it and leave it empty.";
"Wallet.Intro.NotNow" = "Not Now";
@ -4908,11 +4910,15 @@ Any member of this group will be able to see messages in the channel.";
"Wallet.Send.SendAnyway" = "Send Anyway";
"Wallet.Receive.CreateInvoice" = "Create Invoice";
"Wallet.Receive.CreateInvoiceInfo" = "You can specify amount and purpose of the payment to save the sender some time.";
"Conversation.WalletRequiredTitle" = "Gram Wallet Required";
"Conversation.WalletRequiredText" = "This link can be used to send money on the TON Blockchain. To do this, you need to set up a Gram wallet first.";
"Conversation.WalletRequiredNotNow" = "Not Now";
"Conversation.WalletRequiredSetup" = "Set Up";
"Wallet.Configuration.Title" = "Confguration";
"Wallet.Configuration.Apply" = "Save";
"Wallet.CreateInvoice.Title" = "Create Invoice";
"Wallet.Navigation.Close" = "Close";

View File

@ -42,19 +42,6 @@
<string>$(PRODUCT_BUNDLE_SHORT_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).ton</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ton</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>${BUILD_NUMBER}</string>
<key>ITSAppUsesNonExemptEncryption</key>

View File

@ -228,9 +228,11 @@ private let records = Atomic<[WalletStateRecord]>(value: [])
private final class WalletStorageInterfaceImpl: WalletStorageInterface {
private let storage: FileBackedStorage
private let configurationStorage: FileBackedStorage
init(path: String) {
init(path: String, configurationPath: String) {
self.storage = FileBackedStorage(path: path)
self.configurationStorage = FileBackedStorage(path: configurationPath)
}
func watchWalletRecords() -> Signal<[WalletStateRecord], NoError> {
@ -278,6 +280,35 @@ private final class WalletStorageInterfaceImpl: WalletStorageInterface {
}
}
}
func customWalletConfiguration() -> Signal<CustomWalletConfiguration?, NoError> {
return self.configurationStorage.watch()
|> map { data -> CustomWalletConfiguration? in
guard let data = data, !data.isEmpty else {
return nil
}
do {
return try JSONDecoder().decode(CustomWalletConfiguration.self, from: data)
} catch let error {
print("Error deserializing data: \(error)")
return nil
}
}
}
func updateCustomWalletConfiguration(_ value: CustomWalletConfiguration?) {
do {
if let value = value {
let updatedData = try JSONEncoder().encode(value)
let _ = self.configurationStorage.set(data: updatedData).start()
} else {
let _ = self.configurationStorage.set(data: Data()).start()
}
} catch let error {
print("Error serializing data: \(error)")
let _ = self.configurationStorage.set(data: Data()).start()
}
}
}
private final class WalletContextImpl: NSObject, WalletContext, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
@ -287,6 +318,8 @@ private final class WalletContextImpl: NSObject, WalletContext, UIImagePickerCon
let presentationData: WalletPresentationData
let window: Window1
let supportsCustomConfigurations: Bool = true
private var currentImagePickerCompletion: ((UIImage) -> Void)?
var inForeground: Signal<Bool, NoError> {
@ -360,17 +393,17 @@ private final class WalletContextImpl: NSObject, WalletContext, UIImagePickerCon
picker.presentingViewController?.dismiss(animated: true, completion: nil)
}
init(basePath: String, config: String, blockchainName: String, navigationBarTheme: NavigationBarTheme, window: Window1) {
init(basePath: String, storage: WalletStorageInterfaceImpl, config: String, blockchainName: String, navigationBarTheme: NavigationBarTheme, window: Window1) {
let _ = try? FileManager.default.createDirectory(at: URL(fileURLWithPath: basePath + "/keys"), withIntermediateDirectories: true, attributes: nil)
self.storage = storage
self.storage = WalletStorageInterfaceImpl(path: basePath + "/data")
self.window = window
self.tonInstance = TonInstance(
basePath: basePath + "/keys",
config: config,
blockchainName: blockchainName,
proxy: nil /*TonProxyImpl()*/
proxy: nil
)
let baseAppBundleId = Bundle.main.bundleIdentifier!
@ -555,40 +588,74 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
print("Starting with \(documentsPath)")
#endif
let updatedConfigSignal: Signal<String, NoError> = Signal { subscriber in
let downloadTask = URLSession.shared.downloadTask(with: URL(string: "https://test.ton.org/ton-lite-client-test1.config.json")!, completionHandler: { location, _, error in
if let location = location, let data = try? Data(contentsOf: location), let string = String(data: data, encoding: .utf8) {
subscriber.putNext(string)
subscriber.putCompletion()
}
})
downloadTask.resume()
return ActionDisposable {
let storage = WalletStorageInterfaceImpl(path: documentsPath + "/data", configurationPath: documentsPath + "/customConfiguration")
let configSignal = downloadFile(url: URL(string: "https://test.ton.org/ton-lite-client-test1.config.json")!)
|> mapToSignal { data -> Signal<String, DownloadFileError> in
if let string = String(data: data, encoding: .utf8) {
return .single(string)
} else {
return .complete()
}
}
|> retry(retryOnError: { error in
if case .network = error {
return true
} else {
return false
}
}, delayIncrement: 0.2, maxDelay: 5.0, maxRetries: 1000, onQueue: Queue.concurrentDefaultQueue())
|> `catch` { _ -> Signal<String, NoError> in
return .complete()
}
let updatedConfig = Promise<String>()
updatedConfig.set(updatedConfigSignal)
let updatedRemoteConfig = Promise<String>()
updatedRemoteConfig.set(configSignal)
let configPath = documentsPath + "/config"
var initialConfig: Signal<String, NoError> = .complete()
if let data = try? Data(contentsOf: URL(fileURLWithPath: configPath)), let string = String(data: data, encoding: .utf8) {
initialConfig = .single(string)
} else {
initialConfig = updatedConfig.get() |> take(1)
initialConfig = updatedRemoteConfig.get() |> take(1)
}
let _ = (initialConfig
let resolvedInitialConfig = combineLatest(queue: .mainQueue(),
initialConfig,
storage.customWalletConfiguration() |> take(1)
)
|> map { initialConfig, customConfig -> String in
if let customConfig = customConfig {
switch customConfig {
case let .string(string):
return string
}
} else {
return initialConfig
}
}
let _ = (resolvedInitialConfig
|> deliverOnMainQueue).start(next: { initialConfig in
let walletContext = WalletContextImpl(basePath: documentsPath, config: initialConfig, blockchainName: "testnet", navigationBarTheme: navigationBarTheme, window: mainWindow)
let walletContext = WalletContextImpl(basePath: documentsPath, storage: storage, config: initialConfig, blockchainName: "testnet", navigationBarTheme: navigationBarTheme, window: mainWindow)
self.walletContext = walletContext
let _ = (updatedConfig.get()
|> deliverOnMainQueue).start(next: { config in
if config != initialConfig {
let _ = walletContext.tonInstance.updateConfig(config: config, blockchainName: "testnet").start()
let _ = (combineLatest(queue: .mainQueue(),
updatedRemoteConfig.get(),
storage.customWalletConfiguration()
)
|> map { initialConfig, customConfig -> String in
if let customConfig = customConfig {
switch customConfig {
case let .string(string):
return string
}
} else {
return initialConfig
}
}
|> distinctUntilChanged).start(next: { config in
let _ = walletContext.tonInstance.updateConfig(config: config, blockchainName: "testnet").start()
})
let _ = (combineLatest(queue: .mainQueue(),
@ -639,3 +706,29 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
return true
}
}
private enum DownloadFileError {
case network
}
private func downloadFile(url: URL) -> Signal<Data, DownloadFileError> {
return Signal { subscriber in
let completed = Atomic<Bool>(value: false)
let downloadTask = URLSession.shared.downloadTask(with: url, completionHandler: { location, _, error in
let _ = completed.swap(true)
if let location = location, let data = try? Data(contentsOf: location) {
subscriber.putNext(data)
subscriber.putCompletion()
} else {
subscriber.putError(.network)
}
})
downloadTask.resume()
return ActionDisposable {
if !completed.with({ $0 }) {
downloadTask.cancel()
}
}
}
}

View File

@ -28,7 +28,7 @@
"Wallet.Info.TransactionTo" = "to";
"Wallet.Info.TransactionFrom" = "from";
"Wallet.Info.Updating" = "updating";
"Wallet.Info.TransactionBlockchainFee" = "%@ blockchain fee";
"Wallet.Info.TransactionBlockchainFee" = "%@ blockchain fees";
"Wallet.Info.TransactionPendingHeader" = "Pending";
"Wallet.Qr.ScanCode" = "Scan QR Code";
"Wallet.Qr.Title" = "QR Code";
@ -58,6 +58,8 @@
"Wallet.Send.ConfirmationConfirm" = "Confirm";
"Wallet.Send.Send" = "Send";
"Wallet.Settings.Title" = "Wallet Settings";
"Wallet.Settings.Configuration" = "Configuration";
"Wallet.Settings.BackupWallet" = "Backup Wallet";
"Wallet.Settings.DeleteWallet" = "Delete Wallet";
"Wallet.Settings.DeleteWalletInfo" = "This will disconnect the wallet from this app. You will be able to restore your wallet using 24 secret words or import another wallet.\n\nWallets are located in the TON Blockchain, which is not controlled by Telegram. If you want a wallet to be deleted, simply transfer all the grams from it and leave it empty.";
"Wallet.Intro.NotNow" = "Not Now";
@ -142,6 +144,8 @@
"Wallet.Send.SendAnyway" = "Send Anyway";
"Wallet.Receive.CreateInvoice" = "Create Invoice";
"Wallet.Receive.CreateInvoiceInfo" = "You can specify amount and purpose of the payment to save the sender some time.";
"Wallet.Configuration.Title" = "Confguration";
"Wallet.Configuration.Apply" = "Save";
"Wallet.CreateInvoice.Title" = "Create Invoice";
"Wallet.Navigation.Close" = "Close";
"Wallet.Navigation.Back" = "Back";
@ -192,3 +196,4 @@
"Wallet.Time.PreciseDate_m11" = "Nov %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m12" = "Dec %1$@, %2$@ at %3$@";
"Wallet.VoiceOver.Editing.ClearText" = "Clear text";
"Wallet.Receive.ShareInvoiceUrlInfo" = "Share this link with other Gram wallet owners to receive %@ Grams from them.";

View File

@ -1284,8 +1284,6 @@ public final class ChatListNode: ListView {
var filter = true
if let inAppNotificationSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.inAppNotificationSettings) as? InAppNotificationSettings {
switch inAppNotificationSettings.totalUnreadCountDisplayStyle {
case .raw:
filter = false
case .filtered:
filter = true
}

View File

@ -17,16 +17,6 @@ private enum MetadataPrefix: Int8 {
case PeerHistoryInitialized = 9
}
public enum ChatListTotalUnreadStateCategory: Int32 {
case filtered = 0
case raw = 1
}
public enum ChatListTotalUnreadStateStats: Int32 {
case messages = 0
case chats = 1
}
private struct InitializedChatListKey: Hashable {
let groupId: PeerGroupId
}

View File

@ -0,0 +1,17 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "PeerTable",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/Table:Table",
"//submodules/Database/PostboxCoding:PostboxCoding",
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
],
)

View File

@ -0,0 +1,100 @@
import Foundation
import ValueBox
import PostboxDataTypes
import Table
import PostboxCoding
public protocol ReverseAssociatedPeerTable {
func removeReverseAssociation(target: PeerId, from: PeerId)
func addReverseAssociation(target: PeerId, from: PeerId)
}
public final class PeerTable: Table {
public static func tableSpec(_ id: Int32) -> ValueBoxTable {
return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false)
}
private let reverseAssociatedTable: ReverseAssociatedPeerTable?
private let sharedEncoder = PostboxEncoder()
private let sharedKey = ValueBoxKey(length: 8)
private var cachedPeers: [PeerId: Peer] = [:]
private var updatedInitialPeers: [PeerId: Peer?] = [:]
public init(valueBox: ValueBox, table: ValueBoxTable, reverseAssociatedTable: ReverseAssociatedPeerTable?) {
self.reverseAssociatedTable = reverseAssociatedTable
super.init(valueBox: valueBox, table: table)
}
private func key(_ id: PeerId) -> ValueBoxKey {
self.sharedKey.setInt64(0, value: id.toInt64())
return self.sharedKey
}
public func set(_ peer: Peer) {
let previous = self.get(peer.id)
self.cachedPeers[peer.id] = peer
if self.updatedInitialPeers[peer.id] == nil {
self.updatedInitialPeers[peer.id] = previous
}
}
public func get(_ id: PeerId) -> Peer? {
if let peer = self.cachedPeers[id] {
return peer
}
if let value = self.valueBox.get(self.table, key: self.key(id)) {
if let peer = PostboxDecoder(buffer: value).decodeRootObject() as? Peer {
self.cachedPeers[id] = peer
return peer
}
}
return nil
}
override public func clearMemoryCache() {
self.cachedPeers.removeAll()
assert(self.updatedInitialPeers.isEmpty)
}
public func transactionUpdatedPeers() -> [(Peer?, Peer)] {
var result: [(Peer?, Peer)] = []
for (peerId, initialPeer) in self.updatedInitialPeers {
if let peer = self.get(peerId) {
result.append((initialPeer, peer))
} else {
assertionFailure()
}
}
return result
}
override public func beforeCommit() {
if !self.updatedInitialPeers.isEmpty {
for (peerId, previousPeer) in self.updatedInitialPeers {
if let peer = self.cachedPeers[peerId] {
self.sharedEncoder.reset()
self.sharedEncoder.encodeRootObject(peer)
self.valueBox.set(self.table, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy())
let previousAssociation = previousPeer?.associatedPeerId
if previousAssociation != peer.associatedPeerId {
if let previousAssociation = previousAssociation {
self.reverseAssociatedTable?.removeReverseAssociation(target: previousAssociation, from: peerId)
}
if let associatedPeerId = peer.associatedPeerId {
self.reverseAssociatedTable?.addReverseAssociation(target: associatedPeerId, from: peerId)
}
}
} else {
assertionFailure()
}
}
self.updatedInitialPeers.removeAll()
}
}
}

View File

@ -0,0 +1,27 @@
import Foundation
public protocol MediaResourceId {
var uniqueId: String { get }
var hashValue: Int { get }
func isEqual(to: MediaResourceId) -> Bool
}
public struct WrappedMediaResourceId: Hashable {
public let id: MediaResourceId
public init(_ id: MediaResourceId) {
self.id = id
}
public static func ==(lhs: WrappedMediaResourceId, rhs: WrappedMediaResourceId) -> Bool {
return lhs.id.isEqual(to: rhs.id)
}
public var hashValue: Int {
return self.id.hashValue
}
}
public func anyHashableFromMediaResourceId(_ id: MediaResourceId) -> AnyHashable {
return AnyHashable(WrappedMediaResourceId(id))
}

View File

@ -0,0 +1,11 @@
import Foundation
import Buffers
import PostboxCoding
public protocol Peer: class, PostboxCoding {
var id: PeerId { get }
var associatedPeerId: PeerId? { get }
var notificationSettingsPeerId: PeerId? { get }
func isEqual(_ other: Peer) -> Bool
}

View File

@ -0,0 +1,17 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "PreferencesTable",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Database/ValueBox:ValueBox",
"//submodules/Database/Table:Table",
"//submodules/Database/PostboxCoding:PostboxCoding",
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
],
)

View File

@ -0,0 +1,15 @@
import Foundation
import PostboxCoding
import PostboxDataTypes
public protocol PreferencesEntry: PostboxCoding {
var relatedResources: [MediaResourceId] { get }
func isEqual(to: PreferencesEntry) -> Bool
}
public extension PreferencesEntry {
var relatedResources: [MediaResourceId] {
return []
}
}

View File

@ -0,0 +1,73 @@
import Foundation
import ValueBox
import Table
import PostboxCoding
public enum PreferencesOperation {
case update(ValueBoxKey, PreferencesEntry?)
}
private struct CachedEntry {
let entry: PreferencesEntry?
}
public final class PreferencesTable: Table {
private var cachedEntries: [ValueBoxKey: CachedEntry] = [:]
private var updatedEntryKeys = Set<ValueBoxKey>()
public static func tableSpec(_ id: Int32) -> ValueBoxTable {
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false)
}
public func enumerateEntries(_ f: (PreferencesEntry) -> Bool) {
self.valueBox.scan(self.table, values: { _, value in
if let object = PostboxDecoder(buffer: value).decodeRootObject() as? PreferencesEntry {
return f(object)
} else {
return true
}
})
}
public func get(key: ValueBoxKey) -> PreferencesEntry? {
if let cached = self.cachedEntries[key] {
return cached.entry
} else {
if let value = self.valueBox.get(self.table, key: key), let object = PostboxDecoder(buffer: value).decodeRootObject() as? PreferencesEntry {
self.cachedEntries[key] = CachedEntry(entry: object)
return object
} else {
self.cachedEntries[key] = CachedEntry(entry: nil)
return nil
}
}
}
public func set(key: ValueBoxKey, value: PreferencesEntry?, operations: inout [PreferencesOperation]) {
self.cachedEntries[key] = CachedEntry(entry: value)
updatedEntryKeys.insert(key)
operations.append(.update(key, value))
}
override public func clearMemoryCache() {
assert(self.updatedEntryKeys.isEmpty)
}
override public func beforeCommit() {
if !self.updatedEntryKeys.isEmpty {
for key in self.updatedEntryKeys {
if let value = self.cachedEntries[key]?.entry {
let encoder = PostboxEncoder()
encoder.encodeRootObject(value)
withExtendedLifetime(encoder, {
self.valueBox.set(self.table, key: key, value: encoder.readBufferNoCopy())
})
} else {
self.valueBox.remove(self.table, key: key, secure: false)
}
}
self.updatedEntryKeys.removeAll()
}
}
}

View File

@ -4,14 +4,10 @@ import SwiftSignalKit
public protocol StatusBarHost {
var statusBarFrame: CGRect { get }
var statusBarStyle: UIStatusBarStyle { get set }
var statusBarWindow: UIView? { get }
var statusBarView: UIView? { get }
var keyboardWindow: UIWindow? { get }
var keyboardView: UIView? { get }
var handleVolumeControl: Signal<Bool, NoError> { get }
var isApplicationInForeground: Bool { get }
func setStatusBarStyle(_ style: UIStatusBarStyle, animated: Bool)

View File

@ -1,303 +0,0 @@
import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
private struct MappedStatusBar {
let style: StatusBarStyle
let frame: CGRect
let statusBar: StatusBar?
}
private struct MappedStatusBarSurface {
let statusBars: [MappedStatusBar]
let surface: StatusBarSurface
}
private func mapStatusBar(_ statusBar: StatusBar, forceInCall: Bool) -> MappedStatusBar {
let frame = CGRect(origin: statusBar.view.convert(CGPoint(), to: nil), size: statusBar.frame.size)
let resolvedStyle: StatusBarStyle
switch statusBar.statusBarStyle {
case .Black, .White:
if forceInCall {
resolvedStyle = .White
} else {
resolvedStyle = statusBar.statusBarStyle
}
default:
resolvedStyle = statusBar.statusBarStyle
}
return MappedStatusBar(style: resolvedStyle, frame: frame, statusBar: statusBar)
}
private func mappedSurface(_ surface: StatusBarSurface, forceInCall: Bool) -> MappedStatusBarSurface {
var statusBars: [MappedStatusBar] = []
for statusBar in surface.statusBars {
if statusBar.statusBarStyle != .Ignore {
statusBars.append(mapStatusBar(statusBar, forceInCall: forceInCall))
}
}
return MappedStatusBarSurface(statusBars: statusBars, surface: surface)
}
private func optimizeMappedSurface(statusBarSize: CGSize, surface: MappedStatusBarSurface, forceInCall: Bool) -> MappedStatusBarSurface {
if surface.statusBars.count > 1 {
for i in 1 ..< surface.statusBars.count {
if (!forceInCall && surface.statusBars[i].style != surface.statusBars[i - 1].style) || abs(surface.statusBars[i].frame.origin.y - surface.statusBars[i - 1].frame.origin.y) > CGFloat.ulpOfOne {
return surface
}
if let lhsStatusBar = surface.statusBars[i - 1].statusBar, let rhsStatusBar = surface.statusBars[i].statusBar , !lhsStatusBar.alpha.isEqual(to: rhsStatusBar.alpha) {
return surface
}
}
let size = statusBarSize
return MappedStatusBarSurface(statusBars: [MappedStatusBar(style: forceInCall ? .White : surface.statusBars[0].style, frame: CGRect(origin: CGPoint(x: 0.0, y: surface.statusBars[0].frame.origin.y), size: size), statusBar: nil)], surface: surface.surface)
} else {
return surface
}
}
private func displayHiddenAnimation() -> CAAnimation {
let animation = CABasicAnimation(keyPath: "transform.translation.y")
animation.fromValue = NSNumber(value: Float(-40.0))
animation.toValue = NSNumber(value: Float(-40.0))
animation.fillMode = .both
animation.duration = 1.0
animation.speed = 0.0
animation.isAdditive = true
animation.isRemovedOnCompletion = false
return animation
}
class StatusBarManager {
private var host: StatusBarHost
private let volumeControlStatusBar: VolumeControlStatusBar
private let volumeControlStatusBarNode: VolumeControlStatusBarNode
private var surfaces: [StatusBarSurface] = []
private var validParams: (withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool)?
var inCallNavigate: (() -> Void)?
private var volumeTimer: SwiftSignalKit.Timer?
init(host: StatusBarHost, volumeControlStatusBar: VolumeControlStatusBar, volumeControlStatusBarNode: VolumeControlStatusBarNode) {
self.host = host
self.volumeControlStatusBar = volumeControlStatusBar
self.volumeControlStatusBarNode = volumeControlStatusBarNode
self.volumeControlStatusBarNode.isHidden = true
self.volumeControlStatusBar.valueChanged = { [weak self] previous, updated in
if let strongSelf = self {
strongSelf.startVolumeTimer()
strongSelf.volumeControlStatusBarNode.updateValue(from: CGFloat(previous), to: CGFloat(updated))
}
}
}
private func startVolumeTimer() {
self.volumeTimer?.invalidate()
let timer = SwiftSignalKit.Timer(timeout: 2.0, repeat: false, completion: { [weak self] in
self?.endVolumeTimer()
}, queue: Queue.mainQueue())
self.volumeTimer = timer
timer.start()
if self.volumeControlStatusBarNode.isHidden {
self.volumeControlStatusBarNode.isHidden = false
self.volumeControlStatusBarNode.alpha = 1.0
self.volumeControlStatusBarNode.allowsGroupOpacity = true
self.volumeControlStatusBarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.18, completion: { [weak self] _ in
self?.volumeControlStatusBarNode.allowsGroupOpacity = false
})
}
if let (withSafeInsets, forceInCallStatusBarText, forceHiddenBySystemWindows) = self.validParams {
self.updateSurfaces(self.surfaces, withSafeInsets: withSafeInsets, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: false, alphaTransition: .animated(duration: 0.2, curve: .easeInOut))
}
}
private func endVolumeTimer() {
self.volumeControlStatusBarNode.alpha = 0.0
self.volumeControlStatusBarNode.allowsGroupOpacity = true
self.volumeControlStatusBarNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { [weak self] completed in
if let strongSelf = self, completed {
strongSelf.volumeControlStatusBarNode.isHidden = true
strongSelf.volumeControlStatusBarNode.allowsGroupOpacity = false
}
})
self.volumeTimer = nil
if let (withSafeInsets, forceInCallStatusBarText, forceHiddenBySystemWindows) = self.validParams {
self.updateSurfaces(self.surfaces, withSafeInsets: withSafeInsets, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: false, alphaTransition: .animated(duration: 0.2, curve: .easeInOut))
}
}
func updateState(surfaces: [StatusBarSurface], withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool, animated: Bool) {
let previousSurfaces = self.surfaces
self.surfaces = surfaces
self.updateSurfaces(previousSurfaces, withSafeInsets: withSafeInsets, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: animated, alphaTransition: .immediate)
}
private func updateSurfaces(_ previousSurfaces: [StatusBarSurface], withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool, animated: Bool, alphaTransition: ContainedViewLayoutTransition) {
let statusBarFrame = self.host.statusBarFrame
guard let statusBarView = self.host.statusBarView else {
return
}
self.validParams = (withSafeInsets, forceInCallStatusBarText, forceHiddenBySystemWindows)
if self.host.statusBarWindow?.isUserInteractionEnabled != (forceInCallStatusBarText == nil) {
self.host.statusBarWindow?.isUserInteractionEnabled = (forceInCallStatusBarText == nil)
}
var mappedSurfaces: [MappedStatusBarSurface] = []
var mapIndex = 0
var doNotOptimize = false
for surface in self.surfaces {
inner: for statusBar in surface.statusBars {
if statusBar.statusBarStyle == .Hide {
doNotOptimize = true
break inner
}
}
let mapped = mappedSurface(surface, forceInCall: forceInCallStatusBarText != nil)
if doNotOptimize {
mappedSurfaces.append(mapped)
} else {
mappedSurfaces.append(optimizeMappedSurface(statusBarSize: statusBarFrame.size, surface: mapped, forceInCall: forceInCallStatusBarText != nil))
}
mapIndex += 1
}
var reduceSurfaces = true
var reduceSurfacesStatusBarStyleAndAlpha: (StatusBarStyle, CGFloat)?
var reduceIndex = 0
outer: for surface in mappedSurfaces {
for mappedStatusBar in surface.statusBars {
if reduceIndex == 0 && mappedStatusBar.style == .Hide {
reduceSurfaces = false
break outer
}
if mappedStatusBar.frame.origin.equalTo(CGPoint()) {
let statusBarAlpha = mappedStatusBar.statusBar?.alpha ?? 1.0
if let reduceSurfacesStatusBarStyleAndAlpha = reduceSurfacesStatusBarStyleAndAlpha {
if mappedStatusBar.style != reduceSurfacesStatusBarStyleAndAlpha.0 {
reduceSurfaces = false
break outer
}
if !statusBarAlpha.isEqual(to: reduceSurfacesStatusBarStyleAndAlpha.1) {
reduceSurfaces = false
break outer
}
} else {
reduceSurfacesStatusBarStyleAndAlpha = (mappedStatusBar.style, statusBarAlpha)
}
}
}
reduceIndex += 1
}
if reduceSurfaces {
outer: for surface in mappedSurfaces {
for mappedStatusBar in surface.statusBars {
if mappedStatusBar.frame.origin.equalTo(CGPoint()) {
if let statusBar = mappedStatusBar.statusBar , !statusBar.layer.hasPositionOrOpacityAnimations() {
mappedSurfaces = [MappedStatusBarSurface(statusBars: [mappedStatusBar], surface: surface.surface)]
break outer
}
}
}
}
}
var visibleStatusBars: [StatusBar] = []
var globalStatusBar: (StatusBarStyle, CGFloat, CGFloat)?
var coveredIdentity = false
var statusBarIndex = 0
for i in 0 ..< mappedSurfaces.count {
for mappedStatusBar in mappedSurfaces[i].statusBars {
if let statusBar = mappedStatusBar.statusBar {
if mappedStatusBar.frame.origin.equalTo(CGPoint()) && !statusBar.layer.hasPositionOrOpacityAnimations() && !statusBar.offsetNode.layer.hasPositionAnimations() {
if !coveredIdentity {
if statusBar.statusBarStyle != .Hide {
if statusBar.offsetNode.frame.origin.equalTo(CGPoint()) {
coveredIdentity = CGFloat(1.0).isLessThanOrEqualTo(statusBar.alpha)
}
if statusBarIndex == 0 && globalStatusBar == nil {
globalStatusBar = (mappedStatusBar.style, statusBar.alpha, statusBar.offsetNode.frame.origin.y)
} else {
visibleStatusBars.append(statusBar)
}
}
}
} else {
visibleStatusBars.append(statusBar)
}
} else {
if !coveredIdentity {
coveredIdentity = true
if statusBarIndex == 0 && globalStatusBar == nil {
globalStatusBar = (mappedStatusBar.style, 1.0, 0.0)
}
}
}
statusBarIndex += 1
}
}
for surface in previousSurfaces {
for statusBar in surface.statusBars {
if !visibleStatusBars.contains(where: {$0 === statusBar}) {
statusBar.updateState(statusBar: nil, withSafeInsets: withSafeInsets, inCallText: forceInCallStatusBarText, animated: animated)
}
}
}
for surface in self.surfaces {
for statusBar in surface.statusBars {
statusBar.inCallNavigate = self.inCallNavigate
if !visibleStatusBars.contains(where: {$0 === statusBar}) {
statusBar.updateState(statusBar: nil, withSafeInsets: withSafeInsets, inCallText: forceInCallStatusBarText, animated: animated)
}
}
}
for statusBar in visibleStatusBars {
statusBar.updateState(statusBar: statusBarView, withSafeInsets: withSafeInsets, inCallText: forceInCallStatusBarText, animated: animated)
}
if self.volumeTimer != nil {
globalStatusBar?.1 = 0.0
}
var isDark = true
if let globalStatusBar = globalStatusBar {
isDark = globalStatusBar.0.systemStyle == UIStatusBarStyle.lightContent
}
self.volumeControlStatusBarNode.isDark = isDark
/*if let globalStatusBar = globalStatusBar, !forceHiddenBySystemWindows {
let statusBarStyle: UIStatusBarStyle
if forceInCallStatusBarText != nil {
statusBarStyle = .lightContent
} else {
statusBarStyle = globalStatusBar.0 == .Black ? .default : .lightContent
}
if self.host.statusBarStyle != statusBarStyle {
self.host.statusBarStyle = statusBarStyle
}
if let statusBarWindow = self.host.statusBarWindow {
alphaTransition.updateAlpha(layer: statusBarView.layer, alpha: globalStatusBar.1)
var statusBarBounds = statusBarWindow.bounds
if !statusBarBounds.origin.y.isEqual(to: globalStatusBar.2) {
statusBarBounds.origin.y = globalStatusBar.2
statusBarWindow.bounds = statusBarBounds
}
}
} else {
statusBarView.alpha = 0.0
}*/
}
}

View File

@ -332,10 +332,6 @@ public class Window1 {
public init(hostView: WindowHostView, statusBarHost: StatusBarHost?) {
self.hostView = hostView
//self.volumeControlStatusBar = VolumeControlStatusBar(frame: CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: 100.0, height: 20.0)), shouldBeVisible: statusBarHost?.handleVolumeControl ?? .single(false))
//self.volumeControlStatusBarNode = VolumeControlStatusBarNode()
//self.volumeControlStatusBarNode.isHidden = true
let boundsSize = self.hostView.eventView.bounds.size
self.deviceMetrics = DeviceMetrics(screenSize: UIScreen.main.bounds.size, statusBarHeight: statusBarHost?.statusBarFrame.height ?? defaultStatusBarHeight, onScreenNavigationHeight: self.hostView.onScreenNavigationHeight)
@ -343,12 +339,10 @@ public class Window1 {
let statusBarHeight: CGFloat
if let statusBarHost = statusBarHost {
statusBarHeight = statusBarHost.statusBarFrame.size.height
//self.statusBarManager = StatusBarManager(host: statusBarHost, volumeControlStatusBar: self.volumeControlStatusBar, volumeControlStatusBarNode: self.volumeControlStatusBarNode)
self.keyboardManager = KeyboardManager(host: statusBarHost)
self.keyboardViewManager = KeyboardViewManager(host: statusBarHost)
} else {
statusBarHeight = self.deviceMetrics.statusBarHeight
//self.statusBarManager = nil
self.keyboardManager = nil
self.keyboardViewManager = nil
}

View File

@ -78,12 +78,6 @@ typedef struct {
const char bit_build[16];
} bit_info_t;
static bit_info_t hockeyapp_library_info __attribute__((section("__TEXT,__bit_ios,regular,no_dead_strip"))) = {
.info_version = 1,
.bit_version = BITHOCKEY_C_VERSION,
.bit_build = BITHOCKEY_C_BUILD
};
#pragma mark - Helpers
@ -485,7 +479,7 @@ NSString *bit_screenSize(void){
}
NSString *bit_sdkVersion(void){
return [NSString stringWithFormat:@"ios:%@", [NSString stringWithUTF8String:hockeyapp_library_info.bit_version]];
return [NSString stringWithFormat:@"ios:%@", [NSString stringWithUTF8String:BITHOCKEY_C_VERSION]];
}
NSString *bit_appVersion(void){

View File

@ -46,12 +46,6 @@ typedef struct {
const char hockey_build[16];
} bitstadium_info_t;
static bitstadium_info_t bitstadium_library_info __attribute__((section("__TEXT,__bit_hockey,regular,no_dead_strip"))) = {
.info_version = 1,
.hockey_version = BITHOCKEY_C_VERSION,
.hockey_build = BITHOCKEY_C_BUILD
};
#if HOCKEYSDK_FEATURE_CRASH_REPORTER
#import "BITCrashManagerPrivate.h"
#endif /* HOCKEYSDK_FEATURE_CRASH_REPORTER */
@ -528,11 +522,11 @@ static bitstadium_info_t bitstadium_library_info __attribute__((section("__TEXT,
- (NSString *)version {
return (NSString *)[NSString stringWithUTF8String:bitstadium_library_info.hockey_version];
return (NSString *)[NSString stringWithUTF8String:BITHOCKEY_C_VERSION];
}
- (NSString *)build {
return (NSString *)[NSString stringWithUTF8String:bitstadium_library_info.hockey_build];
return (NSString *)[NSString stringWithUTF8String:BITHOCKEY_C_BUILD];
}

View File

@ -43,7 +43,6 @@ private final class NotificationsAndSoundsArguments {
let updateInAppPreviews: (Bool) -> Void
let updateDisplayNameOnLockscreen: (Bool) -> Void
let updateTotalUnreadCountStyle: (Bool) -> Void
let updateIncludeTag: (PeerSummaryCounterTags, Bool) -> Void
let updateTotalUnreadCountCategory: (Bool) -> Void
@ -57,7 +56,7 @@ private final class NotificationsAndSoundsArguments {
let updateNotificationsFromAllAccounts: (Bool) -> Void
init(context: AccountContext, presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping(ViewController)->Void, soundSelectionDisposable: MetaDisposable, authorizeNotifications: @escaping () -> Void, suppressWarning: @escaping () -> Void, updateMessageAlerts: @escaping (Bool) -> Void, updateMessagePreviews: @escaping (Bool) -> Void, updateMessageSound: @escaping (PeerMessageSound) -> Void, updateGroupAlerts: @escaping (Bool) -> Void, updateGroupPreviews: @escaping (Bool) -> Void, updateGroupSound: @escaping (PeerMessageSound) -> Void, updateChannelAlerts: @escaping (Bool) -> Void, updateChannelPreviews: @escaping (Bool) -> Void, updateChannelSound: @escaping (PeerMessageSound) -> Void, updateInAppSounds: @escaping (Bool) -> Void, updateInAppVibration: @escaping (Bool) -> Void, updateInAppPreviews: @escaping (Bool) -> Void, updateDisplayNameOnLockscreen: @escaping (Bool) -> Void, updateTotalUnreadCountStyle: @escaping (Bool) -> Void, updateIncludeTag: @escaping (PeerSummaryCounterTags, Bool) -> Void, updateTotalUnreadCountCategory: @escaping (Bool) -> Void, resetNotifications: @escaping () -> Void, updatedExceptionMode: @escaping(NotificationExceptionMode) -> Void, openAppSettings: @escaping () -> Void, updateJoinedNotifications: @escaping (Bool) -> Void, updateNotificationsFromAllAccounts: @escaping (Bool) -> Void) {
init(context: AccountContext, presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping(ViewController)->Void, soundSelectionDisposable: MetaDisposable, authorizeNotifications: @escaping () -> Void, suppressWarning: @escaping () -> Void, updateMessageAlerts: @escaping (Bool) -> Void, updateMessagePreviews: @escaping (Bool) -> Void, updateMessageSound: @escaping (PeerMessageSound) -> Void, updateGroupAlerts: @escaping (Bool) -> Void, updateGroupPreviews: @escaping (Bool) -> Void, updateGroupSound: @escaping (PeerMessageSound) -> Void, updateChannelAlerts: @escaping (Bool) -> Void, updateChannelPreviews: @escaping (Bool) -> Void, updateChannelSound: @escaping (PeerMessageSound) -> Void, updateInAppSounds: @escaping (Bool) -> Void, updateInAppVibration: @escaping (Bool) -> Void, updateInAppPreviews: @escaping (Bool) -> Void, updateDisplayNameOnLockscreen: @escaping (Bool) -> Void, updateIncludeTag: @escaping (PeerSummaryCounterTags, Bool) -> Void, updateTotalUnreadCountCategory: @escaping (Bool) -> Void, resetNotifications: @escaping () -> Void, updatedExceptionMode: @escaping(NotificationExceptionMode) -> Void, openAppSettings: @escaping () -> Void, updateJoinedNotifications: @escaping (Bool) -> Void, updateNotificationsFromAllAccounts: @escaping (Bool) -> Void) {
self.context = context
self.presentController = presentController
self.pushController = pushController
@ -77,7 +76,6 @@ private final class NotificationsAndSoundsArguments {
self.updateInAppVibration = updateInAppVibration
self.updateInAppPreviews = updateInAppPreviews
self.updateDisplayNameOnLockscreen = updateDisplayNameOnLockscreen
self.updateTotalUnreadCountStyle = updateTotalUnreadCountStyle
self.updateIncludeTag = updateIncludeTag
self.updateTotalUnreadCountCategory = updateTotalUnreadCountCategory
self.resetNotifications = resetNotifications
@ -113,7 +111,6 @@ public enum NotificationsAndSoundsEntryTag: ItemListItemTag {
case inAppVibrate
case inAppPreviews
case displayNamesOnLockscreen
case unreadCountStyle
case includePublicGroups
case includeChannels
case unreadCountCategory
@ -168,7 +165,6 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
case displayNamesOnLockscreenInfo(PresentationTheme, String)
case badgeHeader(PresentationTheme, String)
case unreadCountStyle(PresentationTheme, String, Bool)
case includePublicGroups(PresentationTheme, String, Bool)
case includeChannels(PresentationTheme, String, Bool)
case unreadCountCategory(PresentationTheme, String, Bool)
@ -196,7 +192,7 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
return NotificationsAndSoundsSection.inApp.rawValue
case .displayNamesOnLockscreen, .displayNamesOnLockscreenInfo:
return NotificationsAndSoundsSection.displayNamesOnLockscreen.rawValue
case .badgeHeader, .unreadCountStyle, .includePublicGroups, .includeChannels, .unreadCountCategory, .unreadCountCategoryInfo:
case .badgeHeader, .includePublicGroups, .includeChannels, .unreadCountCategory, .unreadCountCategoryInfo:
return NotificationsAndSoundsSection.badge.rawValue
case .joinedNotifications, .joinedNotificationsInfo:
return NotificationsAndSoundsSection.joinedNotifications.rawValue
@ -267,8 +263,6 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
return 28
case .badgeHeader:
return 29
case .unreadCountStyle:
return 30
case .includePublicGroups:
return 31
case .includeChannels:
@ -312,8 +306,6 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
return NotificationsAndSoundsEntryTag.inAppPreviews
case .displayNamesOnLockscreen:
return NotificationsAndSoundsEntryTag.displayNamesOnLockscreen
case .unreadCountStyle:
return NotificationsAndSoundsEntryTag.unreadCountStyle
case .includePublicGroups:
return NotificationsAndSoundsEntryTag.includePublicGroups
case .includeChannels:
@ -511,12 +503,6 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
} else {
return false
}
case let .unreadCountStyle(lhsTheme, lhsText, lhsValue):
if case let .unreadCountStyle(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .includePublicGroups(lhsTheme, lhsText, lhsValue):
if case let .includePublicGroups(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
@ -690,10 +676,6 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
})
case let .badgeHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .unreadCountStyle(theme, text, value):
return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
arguments.updateTotalUnreadCountStyle(updatedValue)
}, tag: self.tag)
case let .includePublicGroups(theme, text, value):
return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
arguments.updateIncludeTag(.publicGroups, updatedValue)
@ -797,7 +779,6 @@ private func notificationsAndSoundsEntries(authorizationStatus: AccessType, warn
entries.append(.displayNamesOnLockscreenInfo(presentationData.theme, presentationData.strings.Notifications_DisplayNamesOnLockScreenInfoWithLink))
entries.append(.badgeHeader(presentationData.theme, presentationData.strings.Notifications_Badge.uppercased()))
entries.append(.unreadCountStyle(presentationData.theme, presentationData.strings.Notifications_Badge_IncludeMutedChats, inAppSettings.totalUnreadCountDisplayStyle == .raw))
entries.append(.includePublicGroups(presentationData.theme, presentationData.strings.Notifications_Badge_IncludePublicGroups, inAppSettings.totalUnreadCountIncludeTags.contains(.publicGroups)))
entries.append(.includeChannels(presentationData.theme, presentationData.strings.Notifications_Badge_IncludeChannels, inAppSettings.totalUnreadCountIncludeTags.contains(.channels)))
entries.append(.unreadCountCategory(presentationData.theme, presentationData.strings.Notifications_Badge_CountUnreadMessages, inAppSettings.totalUnreadCountDisplayCategory == .messages))
@ -928,12 +909,6 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions
settings.displayNameOnLockscreen = value
return settings
}).start()
}, updateTotalUnreadCountStyle: { value in
let _ = updateInAppNotificationSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
var settings = settings
settings.totalUnreadCountDisplayStyle = value ? .raw : .filtered
return settings
}).start()
}, updateIncludeTag: { tag, value in
let _ = updateInAppNotificationSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
var settings = settings

View File

@ -410,9 +410,6 @@ private func notificationSearchableItems(context: AccountContext, settings: Glob
SettingsSearchableItem(id: .notifications(16), title: strings.Notifications_DisplayNamesOnLockScreen, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_DisplayNamesOnLockScreen), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds], present: { context, _, present in
presentNotificationSettings(context, present, .displayNamesOnLockscreen)
}),
SettingsSearchableItem(id: .notifications(17), title: strings.Notifications_Badge_IncludeMutedChats, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChats), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_Badge], present: { context, _, present in
presentNotificationSettings(context, present, .unreadCountStyle)
}),
SettingsSearchableItem(id: .notifications(18), title: strings.Notifications_Badge_IncludePublicGroups, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedPublicGroups), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_Badge], present: { context, _, present in
presentNotificationSettings(context, present, .includePublicGroups)
}),

View File

@ -221,7 +221,7 @@ public final class PresentationCallImpl: PresentationCall {
private var droppedCall = false
private var dropCallKitCallTimer: SwiftSignalKit.Timer?
init(account: Account, audioSession: ManagedAudioSession, callSessionManager: CallSessionManager, callKitIntegration: CallKitIntegration?, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), internalId: CallSessionInternalId, peerId: PeerId, isOutgoing: Bool, peer: Peer?, proxyServer: ProxyServerSettings?, currentNetworkType: NetworkType, updatedNetworkType: Signal<NetworkType, NoError>) {
init(account: Account, audioSession: ManagedAudioSession, callSessionManager: CallSessionManager, callKitIntegration: CallKitIntegration?, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), initialState: CallSession?, internalId: CallSessionInternalId, peerId: PeerId, isOutgoing: Bool, peer: Peer?, proxyServer: ProxyServerSettings?, currentNetworkType: NetworkType, updatedNetworkType: Signal<NetworkType, NoError>) {
self.account = account
self.audioSession = audioSession
self.callSessionManager = callSessionManager
@ -236,7 +236,15 @@ public final class PresentationCallImpl: PresentationCall {
self.ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: currentNetworkType, updatedNetworkType: updatedNetworkType, serializedData: serializedData, dataSaving: dataSaving, derivedState: derivedState)
var didReceiveAudioOutputs = false
self.sessionStateDisposable = (callSessionManager.callState(internalId: internalId)
var callSessionState: Signal<CallSession, NoError> = .complete()
if let initialState = initialState {
callSessionState = .single(initialState)
}
callSessionState = callSessionState
|> then(callSessionManager.callState(internalId: internalId))
self.sessionStateDisposable = (callSessionState
|> deliverOnMainQueue).start(next: { [weak self] sessionState in
if let strongSelf = self {
strongSelf.updateSessionState(sessionState: sessionState, callContextState: strongSelf.callContextState, reception: strongSelf.reception, audioSessionControl: strongSelf.audioSessionControl)
@ -402,7 +410,7 @@ public final class PresentationCallImpl: PresentationCall {
switch sessionState.state {
case .ringing:
presentationState = .ringing
if let _ = audioSessionControl, previous == nil || previousControl == nil {
if previous == nil || previousControl == nil {
if !self.reportedIncomingCall {
self.reportedIncomingCall = true
self.callKitIntegration?.reportIncomingCall(uuid: self.internalId, handle: "\(self.peerId.id)", displayTitle: self.peer?.debugDisplayTitle ?? "Unknown", completion: { [weak self] error in

View File

@ -223,6 +223,51 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
self.callSettingsDisposable?.dispose()
}
public func injectRingingStateSynchronously(account: Account, ringingState: CallSessionRingingState, callSession: CallSession) {
if self.currentCall != nil {
return
}
let semaphore = DispatchSemaphore(value: 0)
var data: (PreferencesView, AccountSharedDataView, Peer?)?
let _ = combineLatest(
account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState])
|> take(1),
accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings])
|> take(1),
account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(ringingState.peerId)
}
).start(next: { preferences, sharedData, peer in
data = (preferences, sharedData, peer)
semaphore.signal()
})
semaphore.wait()
if let (preferences, sharedData, maybePeer) = data, let peer = maybePeer {
let configuration = preferences.values[PreferencesKeys.voipConfiguration] as? VoipConfiguration ?? .defaultValue
let derivedState = preferences.values[ApplicationSpecificPreferencesKeys.voipDerivedState] as? VoipDerivedState ?? .default
let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings] as? AutodownloadSettings ?? .defaultSettings
let enableCallKit = true
let call = PresentationCallImpl(account: account, audioSession: self.audioSession, callSessionManager: account.callSessionManager, callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(self.callKitIntegration, settings: self.callSettings) : nil, serializedData: configuration.serializedData, dataSaving: effectiveDataSaving(for: self.callSettings, autodownloadSettings: autodownloadSettings), derivedState: derivedState, getDeviceAccessData: self.getDeviceAccessData, initialState: callSession, internalId: ringingState.id, peerId: ringingState.peerId, isOutgoing: false, peer: peer, proxyServer: self.proxyServer, currentNetworkType: .none, updatedNetworkType: account.networkType)
self.currentCall = call
self.currentCallPromise.set(.single(call))
self.hasActiveCallsPromise.set(true)
self.removeCurrentCallDisposable.set((call.canBeRemoved
|> deliverOnMainQueue).start(next: { [weak self, weak call] value in
if value, let strongSelf = self, let call = call {
if strongSelf.currentCall === call {
strongSelf.currentCall = nil
strongSelf.currentCallPromise.set(.single(nil))
strongSelf.hasActiveCallsPromise.set(false)
}
}
}))
}
}
private func ringingStatesUpdated(_ ringingStates: [(Account, Peer, CallSessionRingingState, Bool, NetworkType)], enableCallKit: Bool) {
if let firstState = ringingStates.first {
if self.currentCall == nil {
@ -236,7 +281,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
let derivedState = preferences.values[ApplicationSpecificPreferencesKeys.voipDerivedState] as? VoipDerivedState ?? .default
let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings] as? AutodownloadSettings ?? .defaultSettings
let call = PresentationCallImpl(account: firstState.0, audioSession: strongSelf.audioSession, callSessionManager: firstState.0.callSessionManager, callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings) : nil, serializedData: configuration.serializedData, dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings), derivedState: derivedState, getDeviceAccessData: strongSelf.getDeviceAccessData, internalId: firstState.2.id, peerId: firstState.2.peerId, isOutgoing: false, peer: firstState.1, proxyServer: strongSelf.proxyServer, currentNetworkType: firstState.4, updatedNetworkType: firstState.0.networkType)
let call = PresentationCallImpl(account: firstState.0, audioSession: strongSelf.audioSession, callSessionManager: firstState.0.callSessionManager, callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings) : nil, serializedData: configuration.serializedData, dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings), derivedState: derivedState, getDeviceAccessData: strongSelf.getDeviceAccessData, initialState: nil, internalId: firstState.2.id, peerId: firstState.2.peerId, isOutgoing: false, peer: firstState.1, proxyServer: strongSelf.proxyServer, currentNetworkType: firstState.4, updatedNetworkType: firstState.0.networkType)
strongSelf.currentCall = call
strongSelf.currentCallPromise.set(.single(call))
strongSelf.hasActiveCallsPromise.set(true)
@ -366,7 +411,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
let derivedState = preferences.values[ApplicationSpecificPreferencesKeys.voipDerivedState] as? VoipDerivedState ?? .default
let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings] as? AutodownloadSettings ?? .defaultSettings
let call = PresentationCallImpl(account: account, audioSession: strongSelf.audioSession, callSessionManager: account.callSessionManager, callKitIntegration: callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings), serializedData: configuration.serializedData, dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings), derivedState: derivedState, getDeviceAccessData: strongSelf.getDeviceAccessData, internalId: internalId, peerId: peerId, isOutgoing: true, peer: nil, proxyServer: strongSelf.proxyServer, currentNetworkType: currentNetworkType, updatedNetworkType: account.networkType)
let call = PresentationCallImpl(account: account, audioSession: strongSelf.audioSession, callSessionManager: account.callSessionManager, callKitIntegration: callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings), serializedData: configuration.serializedData, dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings), derivedState: derivedState, getDeviceAccessData: strongSelf.getDeviceAccessData, initialState: nil, internalId: internalId, peerId: peerId, isOutgoing: true, peer: nil, proxyServer: strongSelf.proxyServer, currentNetworkType: currentNetworkType, updatedNetworkType: account.networkType)
strongSelf.currentCall = call
strongSelf.currentCallPromise.set(.single(call))
strongSelf.hasActiveCallsPromise.set(true)

View File

@ -640,7 +640,7 @@ public final class AccountStateManager {
}
if !events.updatedCalls.isEmpty {
for call in events.updatedCalls {
strongSelf.callSessionManager.updateSession(call)
strongSelf.callSessionManager.updateSession(call, completion: { _ in })
}
}
if !events.isContactUpdates.isEmpty {
@ -989,6 +989,38 @@ public final class AccountStateManager {
subscriber(updatedPeersNearby)
}
}
public func processIncomingCallUpdate(data: Data, completion: @escaping ((CallSessionRingingState, CallSession)?) -> Void) {
var rawData = data
let reader = BufferReader(Buffer(data: data))
if let signature = reader.readInt32(), signature == 0x3072cfa1 {
if let compressedData = parseBytes(reader) {
if let decompressedData = MTGzip.decompress(compressedData.makeData()) {
rawData = decompressedData
}
}
}
if let updates = Api.parse(Buffer(data: rawData)) as? Api.Updates {
switch updates {
case let .updates(updates, users, chats, date, seq):
for update in updates {
switch update {
case let .updatePhoneCall(phoneCall):
self.callSessionManager.updateSession(phoneCall, completion: { result in
completion(result)
})
return
default:
break
}
}
default:
break
}
}
completion(nil)
}
}
public func messagesForNotification(transaction: Transaction, id: MessageId, alwaysReturnMessage: Bool) -> (messages: [Message], notify: Bool, sound: PeerMessageSound, displayContents: Bool) {

View File

@ -348,9 +348,9 @@ private final class CallSessionManagerContext {
}
}
private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data) {
private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data) -> CallSessionInternalId? {
if self.contextIdByStableId[stableId] != nil {
return
return nil
}
let bBytes = malloc(256)!
@ -365,6 +365,9 @@ private final class CallSessionManagerContext {
self.contextIdByStableId[stableId] = internalId
self.contextUpdated(internalId: internalId)
self.ringingStatesUpdated()
return internalId
} else {
return nil
}
}
@ -497,7 +500,9 @@ private final class CallSessionManagerContext {
}
}
func updateSession(_ call: Api.PhoneCall) {
func updateSession(_ call: Api.PhoneCall, completion: @escaping ((CallSessionRingingState, CallSession)?) -> Void) {
var resultRingingState: (CallSessionRingingState, CallSession)?
switch call {
case .phoneCallEmpty:
break
@ -532,7 +537,7 @@ private final class CallSessionManagerContext {
context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in
if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state {
if let updatedCall = updatedCall {
strongSelf.updateSession(updatedCall)
strongSelf.updateSession(updatedCall, completion: { _ in })
} else {
strongSelf.drop(internalId: internalId, reason: .disconnect)
}
@ -634,7 +639,22 @@ private final class CallSessionManagerContext {
}
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, _):
if self.contextIdByStableId[id] == nil {
self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData())
let internalId = self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData())
if let internalId = internalId {
var resultRingingStateValue: CallSessionRingingState?
for ringingState in self.ringingStatesValue() {
if ringingState.id == internalId {
resultRingingStateValue = ringingState
break
}
}
if let context = self.contexts[internalId] {
let callSession = CallSession(id: internalId, isOutgoing: context.isOutgoing, state: CallSessionState(context))
if let resultRingingStateValue = resultRingingStateValue {
resultRingingState = (resultRingingStateValue, callSession)
}
}
}
}
case let .phoneCallWaiting(_, id, _, _, _, _, _, receiveDate):
if let internalId = self.contextIdByStableId[id] {
@ -653,6 +673,8 @@ private final class CallSessionManagerContext {
}
}
}
completion(resultRingingState)
}
private func makeSessionEncryptionKey(config: SecretChatEncryptionConfig, gAHash: Data, b: Data, gA: Data) -> (key: Data, keyId: Int64, keyVisualHash: Data)? {
@ -744,9 +766,9 @@ public final class CallSessionManager {
}
}
func updateSession(_ call: Api.PhoneCall) {
func updateSession(_ call: Api.PhoneCall, completion: @escaping ((CallSessionRingingState, CallSession)?) -> Void) {
self.withContext { context in
context.updateSession(call)
context.updateSession(call, completion: completion)
}
}
@ -981,38 +1003,38 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
mappedReason = .phoneCallDiscardReasonMissed
}
return network.request(Api.functions.phone.discardCall(flags: 0, peer: Api.InputPhoneCall.inputPhoneCall(id: stableId, accessHash: accessHash), duration: duration, reason: mappedReason, connectionId: 0))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
return .single(nil)
}
|> mapToSignal { updates -> Signal<(Bool, Bool), NoError> in
var reportRating: Bool = false
var sendDebugLogs: Bool = false
if let updates = updates {
switch updates {
case .updates(let updates, _, _, _, _):
for update in updates {
switch update {
case .updatePhoneCall(let phoneCall):
switch phoneCall {
case.phoneCallDiscarded(let values):
reportRating = (values.flags & (1 << 2)) != 0
sendDebugLogs = (values.flags & (1 << 3)) != 0
default:
break
}
break
default:
break
}
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
return .single(nil)
}
|> mapToSignal { updates -> Signal<(Bool, Bool), NoError> in
var reportRating: Bool = false
var sendDebugLogs: Bool = false
if let updates = updates {
switch updates {
case .updates(let updates, _, _, _, _):
for update in updates {
switch update {
case .updatePhoneCall(let phoneCall):
switch phoneCall {
case.phoneCallDiscarded(let values):
reportRating = (values.flags & (1 << 2)) != 0
sendDebugLogs = (values.flags & (1 << 3)) != 0
default:
break
}
break
default:
break
}
default:
break
}
addUpdates(updates)
}
default:
break
}
return .single((reportRating, sendDebugLogs))
addUpdates(updates)
}
return .single((reportRating, sendDebugLogs))
}
}

View File

@ -48,9 +48,6 @@ private func encodeText(_ string: String, _ key: Int) -> String {
return result
}
private let statusBarRootViewClass: AnyClass = NSClassFromString("UIStatusBar")!
private let statusBarPlaceholderClass: AnyClass? = NSClassFromString("UIStatusBar_Placeholder")
private let cutoutStatusBarForegroundClass: AnyClass? = NSClassFromString("_UIStatusBar")
private let keyboardViewClass: AnyClass? = NSClassFromString(encodeText("VJJoqvuTfuIptuWjfx", -1))!
private let keyboardViewContainerClass: AnyClass? = NSClassFromString(encodeText("VJJoqvuTfuDpoubjofsWjfx", -1))!
@ -93,33 +90,6 @@ private class ApplicationStatusBarHost: StatusBarHost {
self.application.setStatusBarHidden(value, with: animated ? .fade : .none)
}
var statusBarWindow: UIView? {
return self.application.value(forKey: "statusBarWindow") as? UIView
}
var statusBarView: UIView? {
guard let containerView = self.statusBarWindow?.subviews.first else {
return nil
}
if containerView.isKind(of: statusBarRootViewClass) {
return containerView
}
if let statusBarPlaceholderClass = statusBarPlaceholderClass {
if containerView.isKind(of: statusBarPlaceholderClass) {
return containerView
}
}
for subview in containerView.subviews {
if let cutoutStatusBarForegroundClass = cutoutStatusBarForegroundClass, subview.isKind(of: cutoutStatusBarForegroundClass) {
return subview
}
}
return nil
}
var keyboardWindow: UIWindow? {
guard let keyboardWindowClass = keyboardWindowClass else {
return nil
@ -149,10 +119,6 @@ private class ApplicationStatusBarHost: StatusBarHost {
}
return nil
}
var handleVolumeControl: Signal<Bool, NoError> {
return MediaManagerImpl.globalAudioSession.isPlaybackActive()
}
}
private func legacyDocumentsPath() -> String {
@ -1460,16 +1426,84 @@ final class SharedApplicationContext {
public func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
if #available(iOS 9.0, *) {
let _ = (self.sharedContextPromise.get()
guard var encryptedPayload = payload.dictionaryPayload["p"] as? String else {
return
}
encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+")
encryptedPayload = encryptedPayload.replacingOccurrences(of: "_", with: "/")
while encryptedPayload.count % 4 != 0 {
encryptedPayload.append("=")
}
guard let data = Data(base64Encoded: encryptedPayload) else {
return
}
let semaphore = DispatchSemaphore(value: 0)
var accountAndDecryptedPayload: (Account, Data)?
var sharedApplicationContextValue: SharedApplicationContext?
let accountsAndKeys = self.sharedContextPromise.get()
|> take(1)
|> deliverOnMainQueue).start(next: { sharedApplicationContext in
|> mapToSignal { sharedApplicationContext -> Signal<[(Account, MasterNotificationKey)], NoError> in
sharedApplicationContextValue = sharedApplicationContext
return sharedApplicationContext.sharedContext.activeAccounts
|> take(1)
|> mapToSignal { activeAccounts -> Signal<[(Account, MasterNotificationKey)], NoError> in
return combineLatest(activeAccounts.accounts.map { account -> Signal<(Account, MasterNotificationKey), NoError> in
return masterNotificationsKey(account: account.1, ignoreDisabled: true)
|> map { key -> (Account, MasterNotificationKey) in
return (account.1, key)
}
})
}
}
let _ = accountsAndKeys.start(next: { accountsAndKeys in
for (account, key) in accountsAndKeys {
if let decryptedData = decryptedNotificationPayload(key: key, data: data) {
accountAndDecryptedPayload = (account, decryptedData)
break
}
}
semaphore.signal()
})
semaphore.wait()
if let sharedApplicationContextValue = sharedApplicationContextValue, let (account, decryptedData) = accountAndDecryptedPayload {
if let decryptedDict = (try? JSONSerialization.jsonObject(with: decryptedData, options: [])) as? [AnyHashable: Any] {
if var updateString = decryptedDict["updates"] as? String {
updateString = updateString.replacingOccurrences(of: "-", with: "+")
updateString = updateString.replacingOccurrences(of: "_", with: "/")
while updateString.count % 4 != 0 {
updateString.append("=")
}
if let updateData = Data(base64Encoded: updateString) {
var result: (CallSessionRingingState, CallSession)?
let semaphore = DispatchSemaphore(value: 0)
account.stateManager.processIncomingCallUpdate(data: updateData, completion: { ringingState in
result = ringingState
semaphore.signal()
})
semaphore.wait()
if let (ringingState, callSession) = result {
(sharedApplicationContextValue.sharedContext.callManager as? PresentationCallManagerImpl)?.injectRingingStateSynchronously(account: account, ringingState: ringingState, callSession: callSession)
}
}
}
}
}
/*.start(next: { sharedApplicationContext in
sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 4.0)
if case PKPushType.voIP = type {
Logger.shared.log("App \(self.episodeId)", "pushRegistry payload: \(payload.dictionaryPayload)")
sharedApplicationContext.notificationManager.addNotification(payload.dictionaryPayload)
}
})
})*/
}
}

View File

@ -662,7 +662,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
let settings = self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings])
|> map { sharedData -> (allAccounts: Bool, includeMuted: Bool) in
let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings] as? InAppNotificationSettings ?? InAppNotificationSettings.defaultSettings
return (settings.displayNotificationsFromAllAccounts, settings.totalUnreadCountDisplayStyle == .raw)
return (settings.displayNotificationsFromAllAccounts, false)
}
|> distinctUntilChanged(isEqual: { lhs, rhs in
if lhs.allAccounts != rhs.allAccounts {

View File

@ -4,14 +4,11 @@ import SwiftSignalKit
public enum TotalUnreadCountDisplayStyle: Int32 {
case filtered = 0
case raw = 1
var category: ChatListTotalUnreadStateCategory {
switch self {
case .filtered:
return .filtered
case .raw:
return .raw
}
}
}
@ -41,7 +38,7 @@ public struct InAppNotificationSettings: PreferencesEntry, Equatable {
public var displayNotificationsFromAllAccounts: Bool
public static var defaultSettings: InAppNotificationSettings {
return InAppNotificationSettings(playSounds: true, vibrate: false, displayPreviews: true, totalUnreadCountDisplayStyle: .raw, totalUnreadCountDisplayCategory: .messages, totalUnreadCountIncludeTags: [.regularChatsAndPrivateGroups], displayNameOnLockscreen: true, displayNotificationsFromAllAccounts: true)
return InAppNotificationSettings(playSounds: true, vibrate: false, displayPreviews: true, totalUnreadCountDisplayStyle: .filtered, totalUnreadCountDisplayCategory: .messages, totalUnreadCountIncludeTags: [.regularChatsAndPrivateGroups], displayNameOnLockscreen: true, displayNotificationsFromAllAccounts: true)
}
public init(playSounds: Bool, vibrate: Bool, displayPreviews: Bool, totalUnreadCountDisplayStyle: TotalUnreadCountDisplayStyle, totalUnreadCountDisplayCategory: TotalUnreadCountDisplayCategory, totalUnreadCountIncludeTags: PeerSummaryCounterTags, displayNameOnLockscreen: Bool, displayNotificationsFromAllAccounts: Bool) {
@ -59,7 +56,7 @@ public struct InAppNotificationSettings: PreferencesEntry, Equatable {
self.playSounds = decoder.decodeInt32ForKey("s", orElse: 0) != 0
self.vibrate = decoder.decodeInt32ForKey("v", orElse: 0) != 0
self.displayPreviews = decoder.decodeInt32ForKey("p", orElse: 0) != 0
self.totalUnreadCountDisplayStyle = TotalUnreadCountDisplayStyle(rawValue: decoder.decodeInt32ForKey("tds", orElse: 1)) ?? .raw
self.totalUnreadCountDisplayStyle = TotalUnreadCountDisplayStyle(rawValue: decoder.decodeInt32ForKey("cds", orElse: 0)) ?? .filtered
self.totalUnreadCountDisplayCategory = TotalUnreadCountDisplayCategory(rawValue: decoder.decodeInt32ForKey("totalUnreadCountDisplayCategory", orElse: 1)) ?? .messages
if let value = decoder.decodeOptionalInt32ForKey("totalUnreadCountIncludeTags") {
self.totalUnreadCountIncludeTags = PeerSummaryCounterTags(rawValue: value)
@ -74,7 +71,7 @@ public struct InAppNotificationSettings: PreferencesEntry, Equatable {
encoder.encodeInt32(self.playSounds ? 1 : 0, forKey: "s")
encoder.encodeInt32(self.vibrate ? 1 : 0, forKey: "v")
encoder.encodeInt32(self.displayPreviews ? 1 : 0, forKey: "p")
encoder.encodeInt32(self.totalUnreadCountDisplayStyle.rawValue, forKey: "tds")
encoder.encodeInt32(self.totalUnreadCountDisplayStyle.rawValue, forKey: "cds")
encoder.encodeInt32(self.totalUnreadCountDisplayCategory.rawValue, forKey: "totalUnreadCountDisplayCategory")
encoder.encodeInt32(self.totalUnreadCountIncludeTags.rawValue, forKey: "totalUnreadCountIncludeTags")
encoder.encodeInt32(self.displayNameOnLockscreen ? 1 : 0, forKey: "displayNameOnLockscreen")

View File

@ -15,8 +15,6 @@ public func renderedTotalUnreadCount(inAppNotificationSettings: InAppNotificatio
public func renderedTotalUnreadCount(inAppSettings: InAppNotificationSettings, totalUnreadState: ChatListTotalUnreadState) -> (Int32, RenderedTotalUnreadCountType) {
let type: RenderedTotalUnreadCountType
switch inAppSettings.totalUnreadCountDisplayStyle {
case .raw:
type = .raw
case .filtered:
type = .filtered
}
@ -42,8 +40,6 @@ public func renderedTotalUnreadCount(accountManager: AccountManager, postbox: Po
}
let type: RenderedTotalUnreadCountType
switch inAppSettings.totalUnreadCountDisplayStyle {
case .raw:
type = .raw
case .filtered:
type = .filtered
}

View File

@ -85,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface TON : NSObject
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest enableExternalRequests:(bool)enableExternalRequests;
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest enableExternalRequests:(bool)enableExternalRequests syncStateUpdated:(void (^)(float))syncStateUpdated;
- (SSignal *)updateConfig:(NSString *)config blockchainName:(NSString *)blockchainName;

View File

@ -242,7 +242,7 @@ typedef enum {
}
}
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest enableExternalRequests:(bool)enableExternalRequests {
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest enableExternalRequests:(bool)enableExternalRequests syncStateUpdated:(void (^)(float))syncStateUpdated {
self = [super init];
if (self != nil) {
_queue = [SQueue mainQueue];
@ -291,7 +291,23 @@ typedef enum {
}]);
}
return;
}
}/* else if (response.object->get_id() == tonlib_api::updateSyncState::ID) {
auto result = tonlib_api::move_object_as<tonlib_api::updateSyncState>(response.object);
if (result.sync_state_->get_id() == tonlib_api::syncStateInProgress::ID) {
auto syncStateInProgress = tonlib_api::move_object_as<tonlib_api::syncStateInProgress>(result.sync_state_);
int32_t currentDelta = syncStateInProgress->current_seqno_ - syncStateInProgress->from_seqno_;
int32_t fullDelta = syncStateInProgress->to_seqno_ - syncStateInProgress->from_seqno_;
if (currentDelta > 0 && fullDelta > 0) {
float progress = ((float)currentDelta) / ((float)fullDelta);
syncStateUpdated(progress);
} else {
syncStateUpdated(0.0f);
}
} else if (result.sync_state_->get_id() == tonlib_api::syncStateDone::ID) {
syncStateUpdated(1.0f);
}
return
}*/
NSNumber *requestId = @(response.id);
[requestHandlersLock lock];
TONRequestHandler *handler = requestHandlers[requestId];

View File

@ -55,6 +55,7 @@ private final class TonInstanceImpl {
private let blockchainName: String
private let proxy: TonNetworkProxy?
private var instance: TON?
private let syncStateProgress = ValuePromise<Float>(0.0)
init(queue: Queue, basePath: String, config: String, blockchainName: String, proxy: TonNetworkProxy?) {
self.queue = queue
@ -70,6 +71,7 @@ private final class TonInstanceImpl {
instance = current
} else {
let proxy = self.proxy
let syncStateProgress = self.syncStateProgress
instance = TON(keystoreDirectory: self.basePath + "/ton-keystore", config: self.config, blockchainName: self.blockchainName, performExternalRequest: { request in
if let proxy = proxy {
let _ = proxy.request(data: request.data, timeout: 20.0, completion: { result in
@ -83,7 +85,9 @@ private final class TonInstanceImpl {
} else {
request.onResult(nil, "NETWORK_DISABLED")
}
}, enableExternalRequests: proxy != nil)
}, enableExternalRequests: proxy != nil, syncStateUpdated: { progress in
syncStateProgress.set(progress)
})
self.instance = instance
}
f(instance)
@ -1019,8 +1023,39 @@ private func getWalletTransactionsOnce(address: String, previousId: WalletTransa
}
}
public enum CustomWalletConfigurationDecodingError: Error {
case generic
}
public enum CustomWalletConfiguration: Codable {
enum Key: CodingKey {
case string
}
case string(String)
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Key.self)
if let string = try? container.decode(String.self, forKey: .string) {
self = .string(string)
} else {
throw CustomWalletConfigurationDecodingError.generic
}
}
public func encode(to encoder: Encoder) throws {
var container = try encoder.container(keyedBy: Key.self)
switch self {
case let .string(string):
try container.encode(string, forKey: .string)
}
}
}
public protocol WalletStorageInterface {
func watchWalletRecords() -> Signal<[WalletStateRecord], NoError>
func getWalletRecords() -> Signal<[WalletStateRecord], NoError>
func updateWalletRecords(_ f: @escaping ([WalletStateRecord]) -> [WalletStateRecord]) -> Signal<[WalletStateRecord], NoError>
func customWalletConfiguration() -> Signal<CustomWalletConfiguration?, NoError>
func updateCustomWalletConfiguration(_ value: CustomWalletConfiguration?)
}

View File

@ -0,0 +1,187 @@
import Foundation
import UIKit
import AppBundle
import AsyncDisplayKit
import Display
import SwiftSignalKit
import OverlayStatusController
import WalletCore
private final class WalletConfigurationScreenArguments {
let updateState: ((WalletConfigurationScreenState) -> WalletConfigurationScreenState) -> Void
let dismissInput: () -> Void
init(updateState: @escaping ((WalletConfigurationScreenState) -> WalletConfigurationScreenState) -> Void, dismissInput: @escaping () -> Void) {
self.updateState = updateState
self.dismissInput = dismissInput
}
}
private enum WalletConfigurationScreenSection: Int32 {
case configString
}
private enum WalletConfigurationScreenEntryTag: ItemListItemTag {
case configStringText
func isEqual(to other: ItemListItemTag) -> Bool {
if let other = other as? WalletConfigurationScreenEntryTag {
return self == other
} else {
return false
}
}
}
private enum WalletConfigurationScreenEntry: ItemListNodeEntry, Equatable {
case configString(WalletTheme, String, String)
var section: ItemListSectionId {
switch self {
case .configString:
return WalletConfigurationScreenSection.configString.rawValue
}
}
var stableId: Int32 {
switch self {
case .configString:
return 0
}
}
static func <(lhs: WalletConfigurationScreenEntry, rhs: WalletConfigurationScreenEntry) -> Bool {
return lhs.stableId < rhs.stableId
}
func item(_ arguments: Any) -> ListViewItem {
let arguments = arguments as! WalletConfigurationScreenArguments
switch self {
case let .configString(theme, placeholder, text):
return ItemListMultilineInputItem(theme: theme, text: text, placeholder: placeholder, maxLength: nil, sectionId: self.section, style: .blocks, capitalization: false, autocorrection: false, returnKeyType: .done, minimalHeight: nil, textUpdated: { value in
arguments.updateState { state in
var state = state
state.configString = value
return state
}
}, shouldUpdateText: { _ in
return true
}, processPaste: nil, updatedFocus: nil, tag: WalletConfigurationScreenEntryTag.configStringText, action: nil, inlineAction: nil)
}
}
}
private struct WalletConfigurationScreenState: Equatable {
var configString: String
var isEmpty: Bool {
return self.configString.isEmpty
}
}
private func walletConfigurationScreenEntries(presentationData: WalletPresentationData, state: WalletConfigurationScreenState) -> [WalletConfigurationScreenEntry] {
var entries: [WalletConfigurationScreenEntry] = []
entries.append(.configString(presentationData.theme, "", state.configString))
return entries
}
protocol WalletConfigurationScreen {
}
private final class WalletConfigurationScreenImpl: ItemListController, WalletConfigurationScreen {
override func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? {
return CGSize(width: layout.size.width, height: layout.size.height - 174.0)
}
}
func walletConfigurationScreen(context: WalletContext, currentConfiguration: CustomWalletConfiguration?) -> ViewController {
var configString = ""
if let currentConfiguration = currentConfiguration {
switch currentConfiguration {
case let .string(string):
configString = string
}
}
let initialState = WalletConfigurationScreenState(configString: configString)
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let stateValue = Atomic(value: initialState)
let updateState: ((WalletConfigurationScreenState) -> WalletConfigurationScreenState) -> Void = { f in
statePromise.set(stateValue.modify { f($0) })
}
var presentControllerImpl: ((ViewController, Any?) -> Void)?
var pushImpl: ((ViewController) -> Void)?
var dismissImpl: (() -> Void)?
var dismissInputImpl: (() -> Void)?
var ensureItemVisibleImpl: ((WalletConfigurationScreenEntryTag, Bool) -> Void)?
weak var currentStatusController: ViewController?
let arguments = WalletConfigurationScreenArguments(updateState: { f in
updateState(f)
}, dismissInput: {
dismissInputImpl?()
})
let signal = combineLatest(queue: .mainQueue(), .single(context.presentationData), statePromise.get())
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Wallet_Configuration_Apply), style: .bold, enabled: !state.isEmpty, action: {
let state = stateValue.with { $0 }
let configuration: CustomWalletConfiguration?
if state.configString.isEmpty {
configuration = nil
} else {
configuration = .string(state.configString)
}
context.storage.updateCustomWalletConfiguration(configuration)
dismissImpl?()
})
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Wallet_Configuration_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Wallet_Navigation_Back), animateChanges: false)
let listState = ItemListNodeState(entries: walletConfigurationScreenEntries(presentationData: presentationData, state: state), style: .blocks, focusItemTag: nil, ensureVisibleItemTag: nil, animateChanges: false)
return (controllerState, (listState, arguments))
}
let controller = WalletConfigurationScreenImpl(theme: context.presentationData.theme, strings: context.presentationData.strings, updatedPresentationData: .single((context.presentationData.theme, context.presentationData.strings)), state: signal, tabBarItem: nil)
controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
controller.experimentalSnapScrollToItem = true
controller.didAppear = { _ in
}
presentControllerImpl = { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a)
}
pushImpl = { [weak controller] c in
controller?.push(c)
}
dismissImpl = { [weak controller] in
controller?.view.endEditing(true)
let _ = controller?.dismiss()
}
dismissInputImpl = { [weak controller] in
controller?.view.endEditing(true)
}
ensureItemVisibleImpl = { [weak controller] targetTag, animated in
controller?.afterLayout({
guard let controller = controller else {
return
}
var resultItemNode: ListViewItemNode?
let state = stateValue.with({ $0 })
let _ = controller.frameForItemNode({ itemNode in
if let itemNode = itemNode as? ItemListItemNode {
if let tag = itemNode.tag, tag.isEqual(to: targetTag) {
resultItemNode = itemNode as? ListViewItemNode
return true
}
}
return false
})
if let resultItemNode = resultItemNode {
controller.ensureItemNodeVisible(resultItemNode, animated: animated)
}
})
}
return controller
}

View File

@ -13,6 +13,8 @@ public protocol WalletContext {
var keychain: TonKeychain { get }
var presentationData: WalletPresentationData { get }
var supportsCustomConfigurations: Bool { get }
var inForeground: Signal<Bool, NoError> { get }
func getServerSalt() -> Signal<Data, WalletContextGetServerSaltError>

View File

@ -9,27 +9,33 @@ import OverlayStatusController
import WalletCore
private final class WalletSettingsControllerArguments {
let openConfiguration: () -> Void
let exportWallet: () -> Void
let deleteWallet: () -> Void
init(exportWallet: @escaping () -> Void, deleteWallet: @escaping () -> Void) {
init(openConfiguration: @escaping () -> Void, exportWallet: @escaping () -> Void, deleteWallet: @escaping () -> Void) {
self.openConfiguration = openConfiguration
self.exportWallet = exportWallet
self.deleteWallet = deleteWallet
}
}
private enum WalletSettingsSection: Int32 {
case configuration
case exportWallet
case deleteWallet
}
private enum WalletSettingsEntry: ItemListNodeEntry {
case configuration(WalletTheme, String)
case exportWallet(WalletTheme, String)
case deleteWallet(WalletTheme, String)
case deleteWalletInfo(WalletTheme, String)
var section: ItemListSectionId {
switch self {
case .configuration:
return WalletSettingsSection.configuration.rawValue
case .exportWallet:
return WalletSettingsSection.exportWallet.rawValue
case .deleteWallet, .deleteWalletInfo:
@ -39,12 +45,14 @@ private enum WalletSettingsEntry: ItemListNodeEntry {
var stableId: Int32 {
switch self {
case .exportWallet:
case .configuration:
return 0
case .deleteWallet:
case .exportWallet:
return 1
case .deleteWalletInfo:
case .deleteWallet:
return 2
case .deleteWalletInfo:
return 3
}
}
@ -55,6 +63,10 @@ private enum WalletSettingsEntry: ItemListNodeEntry {
func item(_ arguments: Any) -> ListViewItem {
let arguments = arguments as! WalletSettingsControllerArguments
switch self {
case let .configuration(theme, text):
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
arguments.openConfiguration()
})
case let .exportWallet(theme, text):
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
arguments.exportWallet()
@ -72,14 +84,16 @@ private enum WalletSettingsEntry: ItemListNodeEntry {
private struct WalletSettingsControllerState: Equatable {
}
private func walletSettingsControllerEntries(presentationData: WalletPresentationData, state: WalletSettingsControllerState) -> [WalletSettingsEntry] {
private func walletSettingsControllerEntries(presentationData: WalletPresentationData, state: WalletSettingsControllerState, supportsCustomConfigurations: Bool) -> [WalletSettingsEntry] {
var entries: [WalletSettingsEntry] = []
entries.append(.exportWallet(presentationData.theme, "Export Wallet"))
if supportsCustomConfigurations {
entries.append(.configuration(presentationData.theme, presentationData.strings.Wallet_Settings_Configuration))
}
entries.append(.exportWallet(presentationData.theme, presentationData.strings.Wallet_Settings_BackupWallet))
entries.append(.deleteWallet(presentationData.theme, presentationData.strings.Wallet_Settings_DeleteWallet))
entries.append(.deleteWalletInfo(presentationData.theme, presentationData.strings.Wallet_Settings_DeleteWalletInfo))
return entries
}
@ -96,7 +110,13 @@ public func walletSettingsController(context: WalletContext, walletInfo: WalletI
var replaceAllWalletControllersImpl: ((ViewController) -> Void)?
let arguments = WalletSettingsControllerArguments(exportWallet: {
let arguments = WalletSettingsControllerArguments(openConfiguration: {
let _ = (context.storage.customWalletConfiguration()
|> take(1)
|> deliverOnMainQueue).start(next: { configuration in
pushControllerImpl?(walletConfigurationScreen(context: context, currentConfiguration: configuration))
})
}, exportWallet: {
let presentationData = context.presentationData
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
presentControllerImpl?(controller, nil)
@ -145,7 +165,7 @@ public func walletSettingsController(context: WalletContext, walletInfo: WalletI
let signal = combineLatest(queue: .mainQueue(), .single(context.presentationData), statePromise.get())
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Wallet_Settings_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Wallet_Navigation_Back), animateChanges: false)
let listState = ItemListNodeState(entries: walletSettingsControllerEntries(presentationData: presentationData, state: state), style: .blocks, animateChanges: false)
let listState = ItemListNodeState(entries: walletSettingsControllerEntries(presentationData: presentationData, state: state, supportsCustomConfigurations: context.supportsCustomConfigurations), style: .blocks, animateChanges: false)
return (controllerState, (listState, arguments))
}

View File

@ -199,228 +199,235 @@ public final class WalletStrings: Equatable {
public var Wallet_Receive_ShareUrlInfo: String { return self._s[7]! }
public var Wallet_RestoreFailed_Title: String { return self._s[8]! }
public var Wallet_TransactionInfo_CopyAddress: String { return self._s[10]! }
public var Wallet_Send_NetworkErrorTitle: String { return self._s[11]! }
public var Wallet_Month_ShortJune: String { return self._s[12]! }
public var Wallet_TransactionInfo_StorageFeeInfo: String { return self._s[13]! }
public var Wallet_Created_Title: String { return self._s[14]! }
public var Wallet_Info_YourBalance: String { return self._s[15]! }
public var Wallet_TransactionInfo_CommentHeader: String { return self._s[16]! }
public var Wallet_TransactionInfo_OtherFeeHeader: String { return self._s[17]! }
public var Wallet_Settings_BackupWallet: String { return self._s[11]! }
public var Wallet_Send_NetworkErrorTitle: String { return self._s[12]! }
public var Wallet_Month_ShortJune: String { return self._s[13]! }
public var Wallet_TransactionInfo_StorageFeeInfo: String { return self._s[14]! }
public var Wallet_Created_Title: String { return self._s[15]! }
public var Wallet_Info_YourBalance: String { return self._s[16]! }
public var Wallet_TransactionInfo_CommentHeader: String { return self._s[17]! }
public var Wallet_TransactionInfo_OtherFeeHeader: String { return self._s[18]! }
public func Wallet_Time_PreciseDate_m3(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[18]!, self._r[18]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[19]!, self._r[19]!, [_1, _2, _3])
}
public var Wallet_WordImport_IncorrectText: String { return self._s[19]! }
public var Wallet_Month_GenJanuary: String { return self._s[20]! }
public var Wallet_Receive_ShareAddress: String { return self._s[21]! }
public var Wallet_WordImport_Title: String { return self._s[22]! }
public var Wallet_TransactionInfo_Title: String { return self._s[23]! }
public var Wallet_Words_NotDoneText: String { return self._s[25]! }
public var Wallet_RestoreFailed_EnterWords: String { return self._s[26]! }
public var Wallet_WordImport_Text: String { return self._s[27]! }
public var Wallet_RestoreFailed_Text: String { return self._s[29]! }
public var Wallet_TransactionInfo_NoAddress: String { return self._s[30]! }
public var Wallet_Navigation_Back: String { return self._s[31]! }
public var Wallet_Intro_Terms: String { return self._s[32]! }
public var Wallet_WordImport_IncorrectText: String { return self._s[20]! }
public var Wallet_Month_GenJanuary: String { return self._s[21]! }
public var Wallet_Receive_ShareAddress: String { return self._s[22]! }
public var Wallet_WordImport_Title: String { return self._s[23]! }
public var Wallet_TransactionInfo_Title: String { return self._s[24]! }
public var Wallet_Words_NotDoneText: String { return self._s[26]! }
public var Wallet_RestoreFailed_EnterWords: String { return self._s[27]! }
public var Wallet_WordImport_Text: String { return self._s[28]! }
public var Wallet_RestoreFailed_Text: String { return self._s[30]! }
public var Wallet_TransactionInfo_NoAddress: String { return self._s[31]! }
public var Wallet_Navigation_Back: String { return self._s[32]! }
public var Wallet_Intro_Terms: String { return self._s[33]! }
public func Wallet_Send_Balance(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[33]!, self._r[33]!, [_0])
return formatWithArgumentRanges(self._s[34]!, self._r[34]!, [_0])
}
public func Wallet_Time_PreciseDate_m8(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[34]!, self._r[34]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[35]!, self._r[35]!, [_1, _2, _3])
}
public var Wallet_TransactionInfo_AddressCopied: String { return self._s[35]! }
public var Wallet_TransactionInfo_AddressCopied: String { return self._s[36]! }
public func Wallet_Info_TransactionDateHeaderYear(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[36]!, self._r[36]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[37]!, self._r[37]!, [_1, _2, _3])
}
public var Wallet_Send_NetworkErrorText: String { return self._s[37]! }
public var Wallet_VoiceOver_Editing_ClearText: String { return self._s[38]! }
public var Wallet_Intro_ImportExisting: String { return self._s[39]! }
public var Wallet_Receive_CommentInfo: String { return self._s[40]! }
public var Wallet_WordCheck_Continue: String { return self._s[41]! }
public var Wallet_Receive_InvoiceUrlCopied: String { return self._s[42]! }
public var Wallet_Completed_Text: String { return self._s[43]! }
public var Wallet_WordCheck_IncorrectHeader: String { return self._s[45]! }
public var Wallet_Receive_Title: String { return self._s[46]! }
public var Wallet_Info_WalletCreated: String { return self._s[47]! }
public var Wallet_Navigation_Cancel: String { return self._s[48]! }
public var Wallet_CreateInvoice_Title: String { return self._s[49]! }
public var Wallet_Send_NetworkErrorText: String { return self._s[38]! }
public var Wallet_VoiceOver_Editing_ClearText: String { return self._s[39]! }
public var Wallet_Intro_ImportExisting: String { return self._s[40]! }
public var Wallet_Receive_CommentInfo: String { return self._s[41]! }
public var Wallet_WordCheck_Continue: String { return self._s[42]! }
public var Wallet_Receive_InvoiceUrlCopied: String { return self._s[43]! }
public var Wallet_Completed_Text: String { return self._s[44]! }
public var Wallet_WordCheck_IncorrectHeader: String { return self._s[46]! }
public var Wallet_Receive_Title: String { return self._s[47]! }
public var Wallet_Info_WalletCreated: String { return self._s[48]! }
public var Wallet_Navigation_Cancel: String { return self._s[49]! }
public var Wallet_CreateInvoice_Title: String { return self._s[50]! }
public func Wallet_WordCheck_Text(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[50]!, self._r[50]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[51]!, self._r[51]!, [_1, _2, _3])
}
public var Wallet_TransactionInfo_SenderHeader: String { return self._s[51]! }
public var Wallet_TransactionInfo_SenderHeader: String { return self._s[52]! }
public func Wallet_Time_PreciseDate_m4(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[52]!, self._r[52]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[53]!, self._r[53]!, [_1, _2, _3])
}
public var Wallet_Month_GenAugust: String { return self._s[53]! }
public var Wallet_Info_UnknownTransaction: String { return self._s[54]! }
public var Wallet_Receive_CreateInvoice: String { return self._s[55]! }
public var Wallet_Month_GenSeptember: String { return self._s[56]! }
public var Wallet_Month_GenJuly: String { return self._s[57]! }
public var Wallet_Receive_AddressHeader: String { return self._s[58]! }
public var Wallet_Send_AmountText: String { return self._s[59]! }
public var Wallet_SecureStorageNotAvailable_Text: String { return self._s[60]! }
public var Wallet_Month_GenAugust: String { return self._s[54]! }
public var Wallet_Info_UnknownTransaction: String { return self._s[55]! }
public var Wallet_Receive_CreateInvoice: String { return self._s[56]! }
public var Wallet_Month_GenSeptember: String { return self._s[57]! }
public var Wallet_Month_GenJuly: String { return self._s[58]! }
public var Wallet_Receive_AddressHeader: String { return self._s[59]! }
public var Wallet_Send_AmountText: String { return self._s[60]! }
public var Wallet_SecureStorageNotAvailable_Text: String { return self._s[61]! }
public func Wallet_Time_PreciseDate_m12(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[61]!, self._r[61]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[62]!, self._r[62]!, [_1, _2, _3])
}
public func Wallet_Updated_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[62]!, self._r[62]!, [_0])
return formatWithArgumentRanges(self._s[63]!, self._r[63]!, [_0])
}
public var Wallet_Words_Title: String { return self._s[64]! }
public var Wallet_Month_ShortMay: String { return self._s[65]! }
public var Wallet_WordCheck_Title: String { return self._s[66]! }
public var Wallet_Words_NotDoneResponse: String { return self._s[67]! }
public var Wallet_Send_ErrorNotEnoughFundsText: String { return self._s[68]! }
public var Wallet_Receive_CreateInvoiceInfo: String { return self._s[69]! }
public var Wallet_Configuration_Title: String { return self._s[65]! }
public var Wallet_Words_Title: String { return self._s[66]! }
public var Wallet_Month_ShortMay: String { return self._s[67]! }
public var Wallet_WordCheck_Title: String { return self._s[68]! }
public var Wallet_Words_NotDoneResponse: String { return self._s[69]! }
public var Wallet_Send_ErrorNotEnoughFundsText: String { return self._s[70]! }
public var Wallet_Receive_CreateInvoiceInfo: String { return self._s[71]! }
public func Wallet_Time_PreciseDate_m9(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[70]!, self._r[70]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[72]!, self._r[72]!, [_1, _2, _3])
}
public var Wallet_Info_Address: String { return self._s[71]! }
public var Wallet_Intro_CreateWallet: String { return self._s[72]! }
public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[73]! }
public var Wallet_Info_Address: String { return self._s[73]! }
public var Wallet_Intro_CreateWallet: String { return self._s[74]! }
public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[75]! }
public func Wallet_SecureStorageReset_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[74]!, self._r[74]!, [_0])
return formatWithArgumentRanges(self._s[76]!, self._r[76]!, [_0])
}
public var Wallet_Send_SendAnyway: String { return self._s[75]! }
public var Wallet_UnknownError: String { return self._s[76]! }
public var Wallet_SecureStorageChanged_ImportWallet: String { return self._s[77]! }
public var Wallet_SecureStorageChanged_CreateWallet: String { return self._s[79]! }
public var Wallet_Words_NotDoneOk: String { return self._s[80]! }
public var Wallet_Intro_Title: String { return self._s[81]! }
public var Wallet_Info_Receive: String { return self._s[82]! }
public var Wallet_Completed_ViewWallet: String { return self._s[83]! }
public var Wallet_Month_ShortJuly: String { return self._s[84]! }
public var Wallet_Month_ShortApril: String { return self._s[85]! }
public var Wallet_Send_SendAnyway: String { return self._s[77]! }
public var Wallet_UnknownError: String { return self._s[78]! }
public var Wallet_SecureStorageChanged_ImportWallet: String { return self._s[79]! }
public var Wallet_SecureStorageChanged_CreateWallet: String { return self._s[81]! }
public var Wallet_Words_NotDoneOk: String { return self._s[82]! }
public var Wallet_Intro_Title: String { return self._s[83]! }
public var Wallet_Info_Receive: String { return self._s[84]! }
public var Wallet_Completed_ViewWallet: String { return self._s[85]! }
public var Wallet_Month_ShortJuly: String { return self._s[86]! }
public var Wallet_Month_ShortApril: String { return self._s[87]! }
public func Wallet_Info_TransactionDateHeader(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[86]!, self._r[86]!, [_1, _2])
return formatWithArgumentRanges(self._s[88]!, self._r[88]!, [_1, _2])
}
public var Wallet_Receive_ShareInvoiceUrl: String { return self._s[87]! }
public var Wallet_Receive_ShareInvoiceUrl: String { return self._s[89]! }
public func Wallet_Time_PreciseDate_m10(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[88]!, self._r[88]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[90]!, self._r[90]!, [_1, _2, _3])
}
public var Wallet_Send_UninitializedText: String { return self._s[90]! }
public var Wallet_Send_UninitializedText: String { return self._s[92]! }
public func Wallet_Sent_Text(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[91]!, self._r[91]!, [_0])
return formatWithArgumentRanges(self._s[93]!, self._r[93]!, [_0])
}
public var Wallet_Month_GenNovember: String { return self._s[92]! }
public var Wallet_Month_GenNovember: String { return self._s[94]! }
public func Wallet_Time_PreciseDate_m5(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[93]!, self._r[93]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[95]!, self._r[95]!, [_1, _2, _3])
}
public var Wallet_Month_GenApril: String { return self._s[94]! }
public var Wallet_Month_ShortMarch: String { return self._s[95]! }
public var Wallet_Month_GenFebruary: String { return self._s[96]! }
public var Wallet_Qr_ScanCode: String { return self._s[97]! }
public var Wallet_Receive_AddressCopied: String { return self._s[98]! }
public var Wallet_Send_UninitializedTitle: String { return self._s[99]! }
public var Wallet_Send_Send: String { return self._s[100]! }
public var Wallet_Info_RefreshErrorTitle: String { return self._s[101]! }
public var Wallet_Month_GenJune: String { return self._s[102]! }
public var Wallet_Send_AddressHeader: String { return self._s[103]! }
public var Wallet_SecureStorageReset_BiometryTouchId: String { return self._s[104]! }
public var Wallet_Send_Confirmation: String { return self._s[105]! }
public var Wallet_Completed_Title: String { return self._s[106]! }
public var Wallet_Alert_OK: String { return self._s[107]! }
public var Wallet_Settings_DeleteWallet: String { return self._s[108]! }
public var Wallet_SecureStorageReset_PasscodeText: String { return self._s[109]! }
public var Wallet_Month_ShortSeptember: String { return self._s[110]! }
public var Wallet_Info_TransactionTo: String { return self._s[111]! }
public var Wallet_Send_ConfirmationConfirm: String { return self._s[112]! }
public var Wallet_TransactionInfo_OtherFeeInfo: String { return self._s[113]! }
public var Wallet_Receive_AmountText: String { return self._s[114]! }
public var Wallet_Receive_CopyAddress: String { return self._s[115]! }
public var Wallet_Intro_Text: String { return self._s[117]! }
public var Wallet_Month_GenApril: String { return self._s[96]! }
public var Wallet_Month_ShortMarch: String { return self._s[97]! }
public var Wallet_Month_GenFebruary: String { return self._s[98]! }
public var Wallet_Qr_ScanCode: String { return self._s[99]! }
public var Wallet_Receive_AddressCopied: String { return self._s[100]! }
public var Wallet_Send_UninitializedTitle: String { return self._s[101]! }
public var Wallet_Send_Send: String { return self._s[102]! }
public var Wallet_Info_RefreshErrorTitle: String { return self._s[103]! }
public var Wallet_Month_GenJune: String { return self._s[104]! }
public var Wallet_Send_AddressHeader: String { return self._s[105]! }
public var Wallet_SecureStorageReset_BiometryTouchId: String { return self._s[106]! }
public var Wallet_Send_Confirmation: String { return self._s[107]! }
public var Wallet_Completed_Title: String { return self._s[108]! }
public var Wallet_Alert_OK: String { return self._s[109]! }
public var Wallet_Settings_DeleteWallet: String { return self._s[110]! }
public var Wallet_SecureStorageReset_PasscodeText: String { return self._s[111]! }
public var Wallet_Month_ShortSeptember: String { return self._s[112]! }
public var Wallet_Info_TransactionTo: String { return self._s[113]! }
public var Wallet_Send_ConfirmationConfirm: String { return self._s[114]! }
public var Wallet_TransactionInfo_OtherFeeInfo: String { return self._s[115]! }
public var Wallet_Receive_AmountText: String { return self._s[116]! }
public var Wallet_Receive_CopyAddress: String { return self._s[117]! }
public var Wallet_Intro_Text: String { return self._s[119]! }
public var Wallet_Configuration_Apply: String { return self._s[120]! }
public func Wallet_SecureStorageChanged_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[118]!, self._r[118]!, [_0])
return formatWithArgumentRanges(self._s[121]!, self._r[121]!, [_0])
}
public var Wallet_TransactionInfo_FeeInfoURL: String { return self._s[119]! }
public var Wallet_TransactionInfo_FeeInfoURL: String { return self._s[122]! }
public func Wallet_Time_PreciseDate_m1(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[120]!, self._r[120]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[123]!, self._r[123]!, [_1, _2, _3])
}
public var Wallet_RestoreFailed_CreateWallet: String { return self._s[121]! }
public var Wallet_Weekday_Yesterday: String { return self._s[122]! }
public var Wallet_Receive_AmountHeader: String { return self._s[123]! }
public var Wallet_Month_ShortFebruary: String { return self._s[124]! }
public var Wallet_Alert_Cancel: String { return self._s[125]! }
public var Wallet_TransactionInfo_RecipientHeader: String { return self._s[126]! }
public var Wallet_Info_TransactionFrom: String { return self._s[127]! }
public var Wallet_Send_ErrorDecryptionFailed: String { return self._s[128]! }
public var Wallet_Words_NotDoneTitle: String { return self._s[129]! }
public var Wallet_Month_ShortOctober: String { return self._s[130]! }
public var Wallet_Month_GenMay: String { return self._s[131]! }
public var Wallet_Intro_CreateErrorTitle: String { return self._s[132]! }
public var Wallet_SecureStorageReset_BiometryFaceId: String { return self._s[133]! }
public var Wallet_Month_ShortJanuary: String { return self._s[134]! }
public var Wallet_Month_GenMarch: String { return self._s[135]! }
public var Wallet_Sending_Text: String { return self._s[136]! }
public var Wallet_Month_GenOctober: String { return self._s[137]! }
public var Wallet_Receive_CopyInvoiceUrl: String { return self._s[138]! }
public var Wallet_ContextMenuCopy: String { return self._s[139]! }
public var Wallet_RestoreFailed_CreateWallet: String { return self._s[124]! }
public var Wallet_Weekday_Yesterday: String { return self._s[125]! }
public var Wallet_Receive_AmountHeader: String { return self._s[126]! }
public var Wallet_Month_ShortFebruary: String { return self._s[127]! }
public var Wallet_Alert_Cancel: String { return self._s[128]! }
public var Wallet_TransactionInfo_RecipientHeader: String { return self._s[129]! }
public var Wallet_Info_TransactionFrom: String { return self._s[130]! }
public var Wallet_Send_ErrorDecryptionFailed: String { return self._s[131]! }
public var Wallet_Words_NotDoneTitle: String { return self._s[132]! }
public var Wallet_Month_ShortOctober: String { return self._s[133]! }
public var Wallet_Month_GenMay: String { return self._s[134]! }
public var Wallet_Intro_CreateErrorTitle: String { return self._s[135]! }
public var Wallet_SecureStorageReset_BiometryFaceId: String { return self._s[136]! }
public var Wallet_Month_ShortJanuary: String { return self._s[137]! }
public var Wallet_Month_GenMarch: String { return self._s[138]! }
public var Wallet_Sending_Text: String { return self._s[139]! }
public var Wallet_Month_GenOctober: String { return self._s[140]! }
public var Wallet_Receive_CopyInvoiceUrl: String { return self._s[141]! }
public var Wallet_ContextMenuCopy: String { return self._s[142]! }
public func Wallet_Time_PreciseDate_m6(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[140]!, self._r[140]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[143]!, self._r[143]!, [_1, _2, _3])
}
public var Wallet_Info_Updating: String { return self._s[142]! }
public var Wallet_Intro_TermsUrl: String { return self._s[143]! }
public var Wallet_Created_ExportErrorTitle: String { return self._s[144]! }
public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[145]! }
public var Wallet_Sending_Title: String { return self._s[146]! }
public var Wallet_Navigation_Done: String { return self._s[147]! }
public var Wallet_Settings_Title: String { return self._s[148]! }
public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[149]! }
public var Wallet_Weekday_Today: String { return self._s[151]! }
public var Wallet_Month_ShortDecember: String { return self._s[152]! }
public var Wallet_Words_Text: String { return self._s[153]! }
public var Wallet_WordCheck_ViewWords: String { return self._s[154]! }
public var Wallet_Send_AddressInfo: String { return self._s[155]! }
public var Wallet_Info_Updating: String { return self._s[145]! }
public var Wallet_Intro_TermsUrl: String { return self._s[146]! }
public var Wallet_Created_ExportErrorTitle: String { return self._s[147]! }
public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[148]! }
public var Wallet_Sending_Title: String { return self._s[149]! }
public var Wallet_Navigation_Done: String { return self._s[150]! }
public var Wallet_Settings_Title: String { return self._s[151]! }
public func Wallet_Receive_ShareInvoiceUrlInfo(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[152]!, self._r[152]!, [_0])
}
public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[153]! }
public var Wallet_Weekday_Today: String { return self._s[155]! }
public var Wallet_Month_ShortDecember: String { return self._s[156]! }
public var Wallet_Words_Text: String { return self._s[157]! }
public var Wallet_WordCheck_ViewWords: String { return self._s[158]! }
public var Wallet_Send_AddressInfo: String { return self._s[159]! }
public func Wallet_Updated_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[156]!, self._r[156]!, [_0])
return formatWithArgumentRanges(self._s[160]!, self._r[160]!, [_0])
}
public var Wallet_Intro_NotNow: String { return self._s[157]! }
public var Wallet_Navigation_Close: String { return self._s[158]! }
public var Wallet_Month_GenDecember: String { return self._s[160]! }
public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[161]! }
public var Wallet_WordImport_IncorrectTitle: String { return self._s[162]! }
public var Wallet_Send_AddressText: String { return self._s[163]! }
public var Wallet_Receive_AmountInfo: String { return self._s[164]! }
public var Wallet_Intro_NotNow: String { return self._s[161]! }
public var Wallet_Navigation_Close: String { return self._s[162]! }
public var Wallet_Month_GenDecember: String { return self._s[164]! }
public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[165]! }
public var Wallet_WordImport_IncorrectTitle: String { return self._s[166]! }
public var Wallet_Send_AddressText: String { return self._s[167]! }
public var Wallet_Receive_AmountInfo: String { return self._s[168]! }
public func Wallet_Time_PreciseDate_m2(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[165]!, self._r[165]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[169]!, self._r[169]!, [_1, _2, _3])
}
public var Wallet_Month_ShortAugust: String { return self._s[166]! }
public var Wallet_Qr_Title: String { return self._s[167]! }
public var Wallet_WordCheck_TryAgain: String { return self._s[168]! }
public var Wallet_Info_TransactionPendingHeader: String { return self._s[169]! }
public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[170]! }
public var Wallet_Created_Text: String { return self._s[171]! }
public var Wallet_Created_Proceed: String { return self._s[172]! }
public var Wallet_Words_Done: String { return self._s[173]! }
public var Wallet_WordImport_Continue: String { return self._s[174]! }
public var Wallet_TransactionInfo_StorageFeeHeader: String { return self._s[175]! }
public var Wallet_WordImport_CanNotRemember: String { return self._s[176]! }
public var Wallet_Month_ShortAugust: String { return self._s[170]! }
public var Wallet_Qr_Title: String { return self._s[171]! }
public var Wallet_Settings_Configuration: String { return self._s[172]! }
public var Wallet_WordCheck_TryAgain: String { return self._s[173]! }
public var Wallet_Info_TransactionPendingHeader: String { return self._s[174]! }
public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[175]! }
public var Wallet_Created_Text: String { return self._s[176]! }
public var Wallet_Created_Proceed: String { return self._s[177]! }
public var Wallet_Words_Done: String { return self._s[178]! }
public var Wallet_WordImport_Continue: String { return self._s[179]! }
public var Wallet_TransactionInfo_StorageFeeHeader: String { return self._s[180]! }
public var Wallet_WordImport_CanNotRemember: String { return self._s[181]! }
public func Wallet_Time_PreciseDate_m11(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[177]!, self._r[177]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[182]!, self._r[182]!, [_1, _2, _3])
}
public func Wallet_Send_ConfirmationText(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[178]!, self._r[178]!, [_1, _2])
return formatWithArgumentRanges(self._s[183]!, self._r[183]!, [_1, _2])
}
public var Wallet_Created_ExportErrorText: String { return self._s[180]! }
public var Wallet_Created_ExportErrorText: String { return self._s[185]! }
public func Wallet_Updated_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[181]!, self._r[181]!, [_0])
return formatWithArgumentRanges(self._s[186]!, self._r[186]!, [_0])
}
public var Wallet_Settings_DeleteWalletInfo: String { return self._s[182]! }
public var Wallet_Intro_CreateErrorText: String { return self._s[183]! }
public var Wallet_Sent_ViewWallet: String { return self._s[184]! }
public var Wallet_Send_ErrorInvalidAddress: String { return self._s[185]! }
public var Wallet_Settings_DeleteWalletInfo: String { return self._s[187]! }
public var Wallet_Intro_CreateErrorText: String { return self._s[188]! }
public var Wallet_Sent_ViewWallet: String { return self._s[189]! }
public var Wallet_Send_ErrorInvalidAddress: String { return self._s[190]! }
public func Wallet_Time_PreciseDate_m7(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[186]!, self._r[186]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[191]!, self._r[191]!, [_1, _2, _3])
}
public var Wallet_Send_Title: String { return self._s[187]! }
public var Wallet_Info_RefreshErrorText: String { return self._s[188]! }
public var Wallet_SecureStorageReset_Title: String { return self._s[189]! }
public var Wallet_Receive_CommentHeader: String { return self._s[190]! }
public var Wallet_Info_ReceiveGrams: String { return self._s[191]! }
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
public var Wallet_Send_Title: String { return self._s[192]! }
public var Wallet_Info_RefreshErrorText: String { return self._s[193]! }
public var Wallet_SecureStorageReset_Title: String { return self._s[194]! }
public var Wallet_Receive_CommentHeader: String { return self._s[195]! }
public var Wallet_Info_ReceiveGrams: String { return self._s[196]! }
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
}
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)