mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Sync experiment
This commit is contained in:
parent
c83a2364ac
commit
09861c72b7
2
BUCK
2
BUCK
@ -345,6 +345,8 @@ apple_binary(
|
|||||||
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
|
"//submodules/Database/PostboxDataTypes:PostboxDataTypes",
|
||||||
"//submodules/Database/MessageHistoryReadStateTable:MessageHistoryReadStateTable",
|
"//submodules/Database/MessageHistoryReadStateTable:MessageHistoryReadStateTable",
|
||||||
"//submodules/Database/MessageHistoryMetadataTable:MessageHistoryMetadataTable",
|
"//submodules/Database/MessageHistoryMetadataTable:MessageHistoryMetadataTable",
|
||||||
|
"//submodules/Database/PreferencesTable:PreferencesTable",
|
||||||
|
"//submodules/Database/PeerTable:PeerTable",
|
||||||
"//submodules/sqlcipher:sqlcipher",
|
"//submodules/sqlcipher:sqlcipher",
|
||||||
],
|
],
|
||||||
frameworks = [
|
frameworks = [
|
||||||
|
@ -3,6 +3,44 @@ load("//Config:utils.bzl",
|
|||||||
"dynamic_library_configs",
|
"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(
|
def apple_lib(
|
||||||
name,
|
name,
|
||||||
visibility = ["PUBLIC"],
|
visibility = ["PUBLIC"],
|
||||||
@ -71,6 +109,8 @@ def apple_lib(
|
|||||||
for framework in weak_frameworks:
|
for framework in weak_frameworks:
|
||||||
resolved_linker_flags = resolved_linker_flags + ["-Wl,-weak_framework,%s" % framework]
|
resolved_linker_flags = resolved_linker_flags + ["-Wl,-weak_framework,%s" % framework]
|
||||||
|
|
||||||
|
resolved_linker_flags = resolved_linker_flags + section_rename_linker_flags
|
||||||
|
|
||||||
native.apple_library(
|
native.apple_library(
|
||||||
name = name + "",
|
name = name + "",
|
||||||
srcs = srcs,
|
srcs = srcs,
|
||||||
|
16
NotificationService/ApplicationSpecificSharedDataKeys.swift
Normal file
16
NotificationService/ApplicationSpecificSharedDataKeys.swift
Normal 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)
|
||||||
|
}
|
90
NotificationService/InAppNotificationSettings.swift
Normal file
90
NotificationService/InAppNotificationSettings.swift
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
NotificationService/Namespaces.swift
Normal file
20
NotificationService/Namespaces.swift
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
#import "Sync.h"
|
|
||||||
//#import <sqlcipher/sqlcipher.h>
|
|
||||||
|
|
@ -1,9 +1,17 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import ValueBox
|
import ValueBox
|
||||||
|
import PostboxDataTypes
|
||||||
import MessageHistoryReadStateTable
|
import MessageHistoryReadStateTable
|
||||||
import MessageHistoryMetadataTable
|
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 {
|
private func accountRecordIdPathName(_ id: Int64) -> String {
|
||||||
return "account-\(UInt64(bitPattern: id))"
|
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 {
|
final class SyncProviderImpl {
|
||||||
func addIncomingMessage(withRootPath rootPath: String, accountId: Int64, encryptionParameters: DeviceSpecificEncryptionParameters, peerId: Int64, messageId: Int32, completion: @escaping (Int32) -> Void) {
|
func addIncomingMessage(withRootPath rootPath: String, accountId: Int64, encryptionParameters: DeviceSpecificEncryptionParameters, peerId: Int64, messageId: Int32, completion: @escaping (Int32) -> Void) {
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
|
let _ = registeredTypes
|
||||||
|
|
||||||
|
let sharedBasePath = rootPath + "/accounts-metadata"
|
||||||
let basePath = rootPath + "/" + accountRecordIdPathName(accountId) + "/postbox"
|
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 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 metadataTable = MessageHistoryMetadataTable(valueBox: valueBox, table: MessageHistoryMetadataTable.tableSpec(10))
|
||||||
let readStateTable = MessageHistoryReadStateTable(valueBox: valueBox, table: MessageHistoryReadStateTable.tableSpec(14), defaultMessageNamespaceReadStates: [:])
|
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 peerId = PeerId(peerId)
|
||||||
|
|
||||||
let initialCombinedState = readStateTable.getCombinedState(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 {
|
if let combinedState = combinedState {
|
||||||
let initialCount = initialCombinedState?.count ?? 0
|
let initialCount = initialCombinedState?.count ?? 0
|
||||||
let updatedCount = combinedState.count
|
let updatedCount = combinedState.count
|
||||||
@ -56,7 +73,20 @@ final class SyncProviderImpl {
|
|||||||
|
|
||||||
let tag: PeerSummaryCounterTags
|
let tag: PeerSummaryCounterTags
|
||||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
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 {
|
} else {
|
||||||
tag = .regularChatsAndPrivateGroups
|
tag = .regularChatsAndPrivateGroups
|
||||||
}
|
}
|
||||||
@ -79,7 +109,9 @@ final class SyncProviderImpl {
|
|||||||
totalUnreadState.filteredCounters[tag] = counters
|
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.setChatListTotalUnreadState(totalUnreadState)
|
||||||
metadataTable.setShouldReindexUnreadCounts(value: true)
|
metadataTable.setShouldReindexUnreadCounts(value: true)
|
||||||
|
|
||||||
|
38
NotificationService/TelegramChannel.swift
Normal file
38
NotificationService/TelegramChannel.swift
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -4824,6 +4824,8 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
"Wallet.Send.ConfirmationConfirm" = "Confirm";
|
"Wallet.Send.ConfirmationConfirm" = "Confirm";
|
||||||
"Wallet.Send.Send" = "Send";
|
"Wallet.Send.Send" = "Send";
|
||||||
"Wallet.Settings.Title" = "Wallet Settings";
|
"Wallet.Settings.Title" = "Wallet Settings";
|
||||||
|
"Wallet.Settings.Configuration" = "Configuration";
|
||||||
|
"Wallet.Settings.BackupWallet" = "Backup Wallet";
|
||||||
"Wallet.Settings.DeleteWallet" = "Delete 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.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";
|
"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.Send.SendAnyway" = "Send Anyway";
|
||||||
"Wallet.Receive.CreateInvoice" = "Create Invoice";
|
"Wallet.Receive.CreateInvoice" = "Create Invoice";
|
||||||
"Wallet.Receive.CreateInvoiceInfo" = "You can specify amount and purpose of the payment to save the sender some time.";
|
"Wallet.Receive.CreateInvoiceInfo" = "You can specify amount and purpose of the payment to save the sender some time.";
|
||||||
|
|
||||||
"Conversation.WalletRequiredTitle" = "Gram Wallet Required";
|
"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.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.WalletRequiredNotNow" = "Not Now";
|
||||||
"Conversation.WalletRequiredSetup" = "Set Up";
|
"Conversation.WalletRequiredSetup" = "Set Up";
|
||||||
|
|
||||||
|
"Wallet.Configuration.Title" = "Confguration";
|
||||||
|
"Wallet.Configuration.Apply" = "Save";
|
||||||
|
|
||||||
"Wallet.CreateInvoice.Title" = "Create Invoice";
|
"Wallet.CreateInvoice.Title" = "Create Invoice";
|
||||||
|
|
||||||
"Wallet.Navigation.Close" = "Close";
|
"Wallet.Navigation.Close" = "Close";
|
||||||
|
@ -42,19 +42,6 @@
|
|||||||
<string>$(PRODUCT_BUNDLE_SHORT_VERSION)</string>
|
<string>$(PRODUCT_BUNDLE_SHORT_VERSION)</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<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>
|
<key>CFBundleVersion</key>
|
||||||
<string>${BUILD_NUMBER}</string>
|
<string>${BUILD_NUMBER}</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
@ -228,9 +228,11 @@ private let records = Atomic<[WalletStateRecord]>(value: [])
|
|||||||
|
|
||||||
private final class WalletStorageInterfaceImpl: WalletStorageInterface {
|
private final class WalletStorageInterfaceImpl: WalletStorageInterface {
|
||||||
private let storage: FileBackedStorage
|
private let storage: FileBackedStorage
|
||||||
|
private let configurationStorage: FileBackedStorage
|
||||||
|
|
||||||
init(path: String) {
|
init(path: String, configurationPath: String) {
|
||||||
self.storage = FileBackedStorage(path: path)
|
self.storage = FileBackedStorage(path: path)
|
||||||
|
self.configurationStorage = FileBackedStorage(path: configurationPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func watchWalletRecords() -> Signal<[WalletStateRecord], NoError> {
|
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 {
|
private final class WalletContextImpl: NSObject, WalletContext, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
||||||
@ -287,6 +318,8 @@ private final class WalletContextImpl: NSObject, WalletContext, UIImagePickerCon
|
|||||||
let presentationData: WalletPresentationData
|
let presentationData: WalletPresentationData
|
||||||
let window: Window1
|
let window: Window1
|
||||||
|
|
||||||
|
let supportsCustomConfigurations: Bool = true
|
||||||
|
|
||||||
private var currentImagePickerCompletion: ((UIImage) -> Void)?
|
private var currentImagePickerCompletion: ((UIImage) -> Void)?
|
||||||
|
|
||||||
var inForeground: Signal<Bool, NoError> {
|
var inForeground: Signal<Bool, NoError> {
|
||||||
@ -360,17 +393,17 @@ private final class WalletContextImpl: NSObject, WalletContext, UIImagePickerCon
|
|||||||
picker.presentingViewController?.dismiss(animated: true, completion: nil)
|
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)
|
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.window = window
|
||||||
|
|
||||||
self.tonInstance = TonInstance(
|
self.tonInstance = TonInstance(
|
||||||
basePath: basePath + "/keys",
|
basePath: basePath + "/keys",
|
||||||
config: config,
|
config: config,
|
||||||
blockchainName: blockchainName,
|
blockchainName: blockchainName,
|
||||||
proxy: nil /*TonProxyImpl()*/
|
proxy: nil
|
||||||
)
|
)
|
||||||
|
|
||||||
let baseAppBundleId = Bundle.main.bundleIdentifier!
|
let baseAppBundleId = Bundle.main.bundleIdentifier!
|
||||||
@ -555,40 +588,74 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
print("Starting with \(documentsPath)")
|
print("Starting with \(documentsPath)")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let updatedConfigSignal: Signal<String, NoError> = Signal { subscriber in
|
let storage = WalletStorageInterfaceImpl(path: documentsPath + "/data", configurationPath: documentsPath + "/customConfiguration")
|
||||||
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) {
|
let configSignal = downloadFile(url: URL(string: "https://test.ton.org/ton-lite-client-test1.config.json")!)
|
||||||
subscriber.putNext(string)
|
|> mapToSignal { data -> Signal<String, DownloadFileError> in
|
||||||
subscriber.putCompletion()
|
if let string = String(data: data, encoding: .utf8) {
|
||||||
}
|
return .single(string)
|
||||||
})
|
} else {
|
||||||
downloadTask.resume()
|
return .complete()
|
||||||
|
|
||||||
return ActionDisposable {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|> 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>()
|
let updatedRemoteConfig = Promise<String>()
|
||||||
updatedConfig.set(updatedConfigSignal)
|
updatedRemoteConfig.set(configSignal)
|
||||||
|
|
||||||
let configPath = documentsPath + "/config"
|
let configPath = documentsPath + "/config"
|
||||||
var initialConfig: Signal<String, NoError> = .complete()
|
var initialConfig: Signal<String, NoError> = .complete()
|
||||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: configPath)), let string = String(data: data, encoding: .utf8) {
|
if let data = try? Data(contentsOf: URL(fileURLWithPath: configPath)), let string = String(data: data, encoding: .utf8) {
|
||||||
initialConfig = .single(string)
|
initialConfig = .single(string)
|
||||||
} else {
|
} 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
|
|> 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
|
self.walletContext = walletContext
|
||||||
|
|
||||||
let _ = (updatedConfig.get()
|
let _ = (combineLatest(queue: .mainQueue(),
|
||||||
|> deliverOnMainQueue).start(next: { config in
|
updatedRemoteConfig.get(),
|
||||||
if config != initialConfig {
|
storage.customWalletConfiguration()
|
||||||
let _ = walletContext.tonInstance.updateConfig(config: config, blockchainName: "testnet").start()
|
)
|
||||||
|
|> 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(),
|
let _ = (combineLatest(queue: .mainQueue(),
|
||||||
@ -639,3 +706,29 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
return true
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
"Wallet.Info.TransactionTo" = "to";
|
"Wallet.Info.TransactionTo" = "to";
|
||||||
"Wallet.Info.TransactionFrom" = "from";
|
"Wallet.Info.TransactionFrom" = "from";
|
||||||
"Wallet.Info.Updating" = "updating";
|
"Wallet.Info.Updating" = "updating";
|
||||||
"Wallet.Info.TransactionBlockchainFee" = "%@ blockchain fee";
|
"Wallet.Info.TransactionBlockchainFee" = "%@ blockchain fees";
|
||||||
"Wallet.Info.TransactionPendingHeader" = "Pending";
|
"Wallet.Info.TransactionPendingHeader" = "Pending";
|
||||||
"Wallet.Qr.ScanCode" = "Scan QR Code";
|
"Wallet.Qr.ScanCode" = "Scan QR Code";
|
||||||
"Wallet.Qr.Title" = "QR Code";
|
"Wallet.Qr.Title" = "QR Code";
|
||||||
@ -58,6 +58,8 @@
|
|||||||
"Wallet.Send.ConfirmationConfirm" = "Confirm";
|
"Wallet.Send.ConfirmationConfirm" = "Confirm";
|
||||||
"Wallet.Send.Send" = "Send";
|
"Wallet.Send.Send" = "Send";
|
||||||
"Wallet.Settings.Title" = "Wallet Settings";
|
"Wallet.Settings.Title" = "Wallet Settings";
|
||||||
|
"Wallet.Settings.Configuration" = "Configuration";
|
||||||
|
"Wallet.Settings.BackupWallet" = "Backup Wallet";
|
||||||
"Wallet.Settings.DeleteWallet" = "Delete 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.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";
|
"Wallet.Intro.NotNow" = "Not Now";
|
||||||
@ -142,6 +144,8 @@
|
|||||||
"Wallet.Send.SendAnyway" = "Send Anyway";
|
"Wallet.Send.SendAnyway" = "Send Anyway";
|
||||||
"Wallet.Receive.CreateInvoice" = "Create Invoice";
|
"Wallet.Receive.CreateInvoice" = "Create Invoice";
|
||||||
"Wallet.Receive.CreateInvoiceInfo" = "You can specify amount and purpose of the payment to save the sender some time.";
|
"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.CreateInvoice.Title" = "Create Invoice";
|
||||||
"Wallet.Navigation.Close" = "Close";
|
"Wallet.Navigation.Close" = "Close";
|
||||||
"Wallet.Navigation.Back" = "Back";
|
"Wallet.Navigation.Back" = "Back";
|
||||||
@ -192,3 +196,4 @@
|
|||||||
"Wallet.Time.PreciseDate_m11" = "Nov %1$@, %2$@ at %3$@";
|
"Wallet.Time.PreciseDate_m11" = "Nov %1$@, %2$@ at %3$@";
|
||||||
"Wallet.Time.PreciseDate_m12" = "Dec %1$@, %2$@ at %3$@";
|
"Wallet.Time.PreciseDate_m12" = "Dec %1$@, %2$@ at %3$@";
|
||||||
"Wallet.VoiceOver.Editing.ClearText" = "Clear text";
|
"Wallet.VoiceOver.Editing.ClearText" = "Clear text";
|
||||||
|
"Wallet.Receive.ShareInvoiceUrlInfo" = "Share this link with other Gram wallet owners to receive %@ Grams from them.";
|
||||||
|
@ -1284,8 +1284,6 @@ public final class ChatListNode: ListView {
|
|||||||
var filter = true
|
var filter = true
|
||||||
if let inAppNotificationSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.inAppNotificationSettings) as? InAppNotificationSettings {
|
if let inAppNotificationSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.inAppNotificationSettings) as? InAppNotificationSettings {
|
||||||
switch inAppNotificationSettings.totalUnreadCountDisplayStyle {
|
switch inAppNotificationSettings.totalUnreadCountDisplayStyle {
|
||||||
case .raw:
|
|
||||||
filter = false
|
|
||||||
case .filtered:
|
case .filtered:
|
||||||
filter = true
|
filter = true
|
||||||
}
|
}
|
||||||
|
@ -17,16 +17,6 @@ private enum MetadataPrefix: Int8 {
|
|||||||
case PeerHistoryInitialized = 9
|
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 {
|
private struct InitializedChatListKey: Hashable {
|
||||||
let groupId: PeerGroupId
|
let groupId: PeerGroupId
|
||||||
}
|
}
|
||||||
|
17
submodules/Database/PeerTable/BUCK
Normal file
17
submodules/Database/PeerTable/BUCK
Normal 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",
|
||||||
|
],
|
||||||
|
)
|
100
submodules/Database/PeerTable/Sources/PeerTable.swift
Normal file
100
submodules/Database/PeerTable/Sources/PeerTable.swift
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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))
|
||||||
|
}
|
11
submodules/Database/PostboxDataTypes/Sources/Peer.swift
Normal file
11
submodules/Database/PostboxDataTypes/Sources/Peer.swift
Normal 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
|
||||||
|
}
|
17
submodules/Database/PreferencesTable/BUCK
Normal file
17
submodules/Database/PreferencesTable/BUCK
Normal 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",
|
||||||
|
],
|
||||||
|
)
|
@ -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 []
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,14 +4,10 @@ import SwiftSignalKit
|
|||||||
public protocol StatusBarHost {
|
public protocol StatusBarHost {
|
||||||
var statusBarFrame: CGRect { get }
|
var statusBarFrame: CGRect { get }
|
||||||
var statusBarStyle: UIStatusBarStyle { get set }
|
var statusBarStyle: UIStatusBarStyle { get set }
|
||||||
var statusBarWindow: UIView? { get }
|
|
||||||
var statusBarView: UIView? { get }
|
|
||||||
|
|
||||||
var keyboardWindow: UIWindow? { get }
|
var keyboardWindow: UIWindow? { get }
|
||||||
var keyboardView: UIView? { get }
|
var keyboardView: UIView? { get }
|
||||||
|
|
||||||
var handleVolumeControl: Signal<Bool, NoError> { get }
|
|
||||||
|
|
||||||
var isApplicationInForeground: Bool { get }
|
var isApplicationInForeground: Bool { get }
|
||||||
|
|
||||||
func setStatusBarStyle(_ style: UIStatusBarStyle, animated: Bool)
|
func setStatusBarStyle(_ style: UIStatusBarStyle, animated: Bool)
|
||||||
|
@ -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
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
}
|
|
@ -332,10 +332,6 @@ public class Window1 {
|
|||||||
public init(hostView: WindowHostView, statusBarHost: StatusBarHost?) {
|
public init(hostView: WindowHostView, statusBarHost: StatusBarHost?) {
|
||||||
self.hostView = hostView
|
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
|
let boundsSize = self.hostView.eventView.bounds.size
|
||||||
self.deviceMetrics = DeviceMetrics(screenSize: UIScreen.main.bounds.size, statusBarHeight: statusBarHost?.statusBarFrame.height ?? defaultStatusBarHeight, onScreenNavigationHeight: self.hostView.onScreenNavigationHeight)
|
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
|
let statusBarHeight: CGFloat
|
||||||
if let statusBarHost = statusBarHost {
|
if let statusBarHost = statusBarHost {
|
||||||
statusBarHeight = statusBarHost.statusBarFrame.size.height
|
statusBarHeight = statusBarHost.statusBarFrame.size.height
|
||||||
//self.statusBarManager = StatusBarManager(host: statusBarHost, volumeControlStatusBar: self.volumeControlStatusBar, volumeControlStatusBarNode: self.volumeControlStatusBarNode)
|
|
||||||
self.keyboardManager = KeyboardManager(host: statusBarHost)
|
self.keyboardManager = KeyboardManager(host: statusBarHost)
|
||||||
self.keyboardViewManager = KeyboardViewManager(host: statusBarHost)
|
self.keyboardViewManager = KeyboardViewManager(host: statusBarHost)
|
||||||
} else {
|
} else {
|
||||||
statusBarHeight = self.deviceMetrics.statusBarHeight
|
statusBarHeight = self.deviceMetrics.statusBarHeight
|
||||||
//self.statusBarManager = nil
|
|
||||||
self.keyboardManager = nil
|
self.keyboardManager = nil
|
||||||
self.keyboardViewManager = nil
|
self.keyboardViewManager = nil
|
||||||
}
|
}
|
||||||
|
@ -78,12 +78,6 @@ typedef struct {
|
|||||||
const char bit_build[16];
|
const char bit_build[16];
|
||||||
} bit_info_t;
|
} 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
|
#pragma mark - Helpers
|
||||||
|
|
||||||
@ -485,7 +479,7 @@ NSString *bit_screenSize(void){
|
|||||||
}
|
}
|
||||||
|
|
||||||
NSString *bit_sdkVersion(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){
|
NSString *bit_appVersion(void){
|
||||||
|
@ -46,12 +46,6 @@ typedef struct {
|
|||||||
const char hockey_build[16];
|
const char hockey_build[16];
|
||||||
} bitstadium_info_t;
|
} 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
|
#if HOCKEYSDK_FEATURE_CRASH_REPORTER
|
||||||
#import "BITCrashManagerPrivate.h"
|
#import "BITCrashManagerPrivate.h"
|
||||||
#endif /* HOCKEYSDK_FEATURE_CRASH_REPORTER */
|
#endif /* HOCKEYSDK_FEATURE_CRASH_REPORTER */
|
||||||
@ -528,11 +522,11 @@ static bitstadium_info_t bitstadium_library_info __attribute__((section("__TEXT,
|
|||||||
|
|
||||||
|
|
||||||
- (NSString *)version {
|
- (NSString *)version {
|
||||||
return (NSString *)[NSString stringWithUTF8String:bitstadium_library_info.hockey_version];
|
return (NSString *)[NSString stringWithUTF8String:BITHOCKEY_C_VERSION];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)build {
|
- (NSString *)build {
|
||||||
return (NSString *)[NSString stringWithUTF8String:bitstadium_library_info.hockey_build];
|
return (NSString *)[NSString stringWithUTF8String:BITHOCKEY_C_BUILD];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,7 +43,6 @@ private final class NotificationsAndSoundsArguments {
|
|||||||
let updateInAppPreviews: (Bool) -> Void
|
let updateInAppPreviews: (Bool) -> Void
|
||||||
|
|
||||||
let updateDisplayNameOnLockscreen: (Bool) -> Void
|
let updateDisplayNameOnLockscreen: (Bool) -> Void
|
||||||
let updateTotalUnreadCountStyle: (Bool) -> Void
|
|
||||||
let updateIncludeTag: (PeerSummaryCounterTags, Bool) -> Void
|
let updateIncludeTag: (PeerSummaryCounterTags, Bool) -> Void
|
||||||
let updateTotalUnreadCountCategory: (Bool) -> Void
|
let updateTotalUnreadCountCategory: (Bool) -> Void
|
||||||
|
|
||||||
@ -57,7 +56,7 @@ private final class NotificationsAndSoundsArguments {
|
|||||||
|
|
||||||
let updateNotificationsFromAllAccounts: (Bool) -> Void
|
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.context = context
|
||||||
self.presentController = presentController
|
self.presentController = presentController
|
||||||
self.pushController = pushController
|
self.pushController = pushController
|
||||||
@ -77,7 +76,6 @@ private final class NotificationsAndSoundsArguments {
|
|||||||
self.updateInAppVibration = updateInAppVibration
|
self.updateInAppVibration = updateInAppVibration
|
||||||
self.updateInAppPreviews = updateInAppPreviews
|
self.updateInAppPreviews = updateInAppPreviews
|
||||||
self.updateDisplayNameOnLockscreen = updateDisplayNameOnLockscreen
|
self.updateDisplayNameOnLockscreen = updateDisplayNameOnLockscreen
|
||||||
self.updateTotalUnreadCountStyle = updateTotalUnreadCountStyle
|
|
||||||
self.updateIncludeTag = updateIncludeTag
|
self.updateIncludeTag = updateIncludeTag
|
||||||
self.updateTotalUnreadCountCategory = updateTotalUnreadCountCategory
|
self.updateTotalUnreadCountCategory = updateTotalUnreadCountCategory
|
||||||
self.resetNotifications = resetNotifications
|
self.resetNotifications = resetNotifications
|
||||||
@ -113,7 +111,6 @@ public enum NotificationsAndSoundsEntryTag: ItemListItemTag {
|
|||||||
case inAppVibrate
|
case inAppVibrate
|
||||||
case inAppPreviews
|
case inAppPreviews
|
||||||
case displayNamesOnLockscreen
|
case displayNamesOnLockscreen
|
||||||
case unreadCountStyle
|
|
||||||
case includePublicGroups
|
case includePublicGroups
|
||||||
case includeChannels
|
case includeChannels
|
||||||
case unreadCountCategory
|
case unreadCountCategory
|
||||||
@ -168,7 +165,6 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
|||||||
case displayNamesOnLockscreenInfo(PresentationTheme, String)
|
case displayNamesOnLockscreenInfo(PresentationTheme, String)
|
||||||
|
|
||||||
case badgeHeader(PresentationTheme, String)
|
case badgeHeader(PresentationTheme, String)
|
||||||
case unreadCountStyle(PresentationTheme, String, Bool)
|
|
||||||
case includePublicGroups(PresentationTheme, String, Bool)
|
case includePublicGroups(PresentationTheme, String, Bool)
|
||||||
case includeChannels(PresentationTheme, String, Bool)
|
case includeChannels(PresentationTheme, String, Bool)
|
||||||
case unreadCountCategory(PresentationTheme, String, Bool)
|
case unreadCountCategory(PresentationTheme, String, Bool)
|
||||||
@ -196,7 +192,7 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
|||||||
return NotificationsAndSoundsSection.inApp.rawValue
|
return NotificationsAndSoundsSection.inApp.rawValue
|
||||||
case .displayNamesOnLockscreen, .displayNamesOnLockscreenInfo:
|
case .displayNamesOnLockscreen, .displayNamesOnLockscreenInfo:
|
||||||
return NotificationsAndSoundsSection.displayNamesOnLockscreen.rawValue
|
return NotificationsAndSoundsSection.displayNamesOnLockscreen.rawValue
|
||||||
case .badgeHeader, .unreadCountStyle, .includePublicGroups, .includeChannels, .unreadCountCategory, .unreadCountCategoryInfo:
|
case .badgeHeader, .includePublicGroups, .includeChannels, .unreadCountCategory, .unreadCountCategoryInfo:
|
||||||
return NotificationsAndSoundsSection.badge.rawValue
|
return NotificationsAndSoundsSection.badge.rawValue
|
||||||
case .joinedNotifications, .joinedNotificationsInfo:
|
case .joinedNotifications, .joinedNotificationsInfo:
|
||||||
return NotificationsAndSoundsSection.joinedNotifications.rawValue
|
return NotificationsAndSoundsSection.joinedNotifications.rawValue
|
||||||
@ -267,8 +263,6 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
|||||||
return 28
|
return 28
|
||||||
case .badgeHeader:
|
case .badgeHeader:
|
||||||
return 29
|
return 29
|
||||||
case .unreadCountStyle:
|
|
||||||
return 30
|
|
||||||
case .includePublicGroups:
|
case .includePublicGroups:
|
||||||
return 31
|
return 31
|
||||||
case .includeChannels:
|
case .includeChannels:
|
||||||
@ -312,8 +306,6 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
|||||||
return NotificationsAndSoundsEntryTag.inAppPreviews
|
return NotificationsAndSoundsEntryTag.inAppPreviews
|
||||||
case .displayNamesOnLockscreen:
|
case .displayNamesOnLockscreen:
|
||||||
return NotificationsAndSoundsEntryTag.displayNamesOnLockscreen
|
return NotificationsAndSoundsEntryTag.displayNamesOnLockscreen
|
||||||
case .unreadCountStyle:
|
|
||||||
return NotificationsAndSoundsEntryTag.unreadCountStyle
|
|
||||||
case .includePublicGroups:
|
case .includePublicGroups:
|
||||||
return NotificationsAndSoundsEntryTag.includePublicGroups
|
return NotificationsAndSoundsEntryTag.includePublicGroups
|
||||||
case .includeChannels:
|
case .includeChannels:
|
||||||
@ -511,12 +503,6 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
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):
|
case let .includePublicGroups(lhsTheme, lhsText, lhsValue):
|
||||||
if case let .includePublicGroups(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
if case let .includePublicGroups(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||||
return true
|
return true
|
||||||
@ -690,10 +676,6 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
case let .badgeHeader(theme, text):
|
case let .badgeHeader(theme, text):
|
||||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
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):
|
case let .includePublicGroups(theme, text, value):
|
||||||
return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
||||||
arguments.updateIncludeTag(.publicGroups, updatedValue)
|
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(.displayNamesOnLockscreenInfo(presentationData.theme, presentationData.strings.Notifications_DisplayNamesOnLockScreenInfoWithLink))
|
||||||
|
|
||||||
entries.append(.badgeHeader(presentationData.theme, presentationData.strings.Notifications_Badge.uppercased()))
|
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(.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(.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))
|
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
|
settings.displayNameOnLockscreen = value
|
||||||
return settings
|
return settings
|
||||||
}).start()
|
}).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
|
}, updateIncludeTag: { tag, value in
|
||||||
let _ = updateInAppNotificationSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
|
let _ = updateInAppNotificationSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
|
||||||
var settings = settings
|
var settings = settings
|
||||||
|
@ -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
|
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)
|
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
|
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)
|
presentNotificationSettings(context, present, .includePublicGroups)
|
||||||
}),
|
}),
|
||||||
|
@ -221,7 +221,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
private var droppedCall = false
|
private var droppedCall = false
|
||||||
private var dropCallKitCallTimer: SwiftSignalKit.Timer?
|
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.account = account
|
||||||
self.audioSession = audioSession
|
self.audioSession = audioSession
|
||||||
self.callSessionManager = callSessionManager
|
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)
|
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
|
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
|
|> deliverOnMainQueue).start(next: { [weak self] sessionState in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateSessionState(sessionState: sessionState, callContextState: strongSelf.callContextState, reception: strongSelf.reception, audioSessionControl: strongSelf.audioSessionControl)
|
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 {
|
switch sessionState.state {
|
||||||
case .ringing:
|
case .ringing:
|
||||||
presentationState = .ringing
|
presentationState = .ringing
|
||||||
if let _ = audioSessionControl, previous == nil || previousControl == nil {
|
if previous == nil || previousControl == nil {
|
||||||
if !self.reportedIncomingCall {
|
if !self.reportedIncomingCall {
|
||||||
self.reportedIncomingCall = true
|
self.reportedIncomingCall = true
|
||||||
self.callKitIntegration?.reportIncomingCall(uuid: self.internalId, handle: "\(self.peerId.id)", displayTitle: self.peer?.debugDisplayTitle ?? "Unknown", completion: { [weak self] error in
|
self.callKitIntegration?.reportIncomingCall(uuid: self.internalId, handle: "\(self.peerId.id)", displayTitle: self.peer?.debugDisplayTitle ?? "Unknown", completion: { [weak self] error in
|
||||||
|
@ -223,6 +223,51 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
self.callSettingsDisposable?.dispose()
|
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) {
|
private func ringingStatesUpdated(_ ringingStates: [(Account, Peer, CallSessionRingingState, Bool, NetworkType)], enableCallKit: Bool) {
|
||||||
if let firstState = ringingStates.first {
|
if let firstState = ringingStates.first {
|
||||||
if self.currentCall == nil {
|
if self.currentCall == nil {
|
||||||
@ -236,7 +281,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
let derivedState = preferences.values[ApplicationSpecificPreferencesKeys.voipDerivedState] as? VoipDerivedState ?? .default
|
let derivedState = preferences.values[ApplicationSpecificPreferencesKeys.voipDerivedState] as? VoipDerivedState ?? .default
|
||||||
let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings] as? AutodownloadSettings ?? .defaultSettings
|
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.currentCall = call
|
||||||
strongSelf.currentCallPromise.set(.single(call))
|
strongSelf.currentCallPromise.set(.single(call))
|
||||||
strongSelf.hasActiveCallsPromise.set(true)
|
strongSelf.hasActiveCallsPromise.set(true)
|
||||||
@ -366,7 +411,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
let derivedState = preferences.values[ApplicationSpecificPreferencesKeys.voipDerivedState] as? VoipDerivedState ?? .default
|
let derivedState = preferences.values[ApplicationSpecificPreferencesKeys.voipDerivedState] as? VoipDerivedState ?? .default
|
||||||
let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings] as? AutodownloadSettings ?? .defaultSettings
|
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.currentCall = call
|
||||||
strongSelf.currentCallPromise.set(.single(call))
|
strongSelf.currentCallPromise.set(.single(call))
|
||||||
strongSelf.hasActiveCallsPromise.set(true)
|
strongSelf.hasActiveCallsPromise.set(true)
|
||||||
|
@ -640,7 +640,7 @@ public final class AccountStateManager {
|
|||||||
}
|
}
|
||||||
if !events.updatedCalls.isEmpty {
|
if !events.updatedCalls.isEmpty {
|
||||||
for call in events.updatedCalls {
|
for call in events.updatedCalls {
|
||||||
strongSelf.callSessionManager.updateSession(call)
|
strongSelf.callSessionManager.updateSession(call, completion: { _ in })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !events.isContactUpdates.isEmpty {
|
if !events.isContactUpdates.isEmpty {
|
||||||
@ -989,6 +989,38 @@ public final class AccountStateManager {
|
|||||||
subscriber(updatedPeersNearby)
|
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) {
|
public func messagesForNotification(transaction: Transaction, id: MessageId, alwaysReturnMessage: Bool) -> (messages: [Message], notify: Bool, sound: PeerMessageSound, displayContents: Bool) {
|
||||||
|
@ -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 {
|
if self.contextIdByStableId[stableId] != nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let bBytes = malloc(256)!
|
let bBytes = malloc(256)!
|
||||||
@ -365,6 +365,9 @@ private final class CallSessionManagerContext {
|
|||||||
self.contextIdByStableId[stableId] = internalId
|
self.contextIdByStableId[stableId] = internalId
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
self.ringingStatesUpdated()
|
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 {
|
switch call {
|
||||||
case .phoneCallEmpty:
|
case .phoneCallEmpty:
|
||||||
break
|
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
|
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 strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state {
|
||||||
if let updatedCall = updatedCall {
|
if let updatedCall = updatedCall {
|
||||||
strongSelf.updateSession(updatedCall)
|
strongSelf.updateSession(updatedCall, completion: { _ in })
|
||||||
} else {
|
} else {
|
||||||
strongSelf.drop(internalId: internalId, reason: .disconnect)
|
strongSelf.drop(internalId: internalId, reason: .disconnect)
|
||||||
}
|
}
|
||||||
@ -634,7 +639,22 @@ private final class CallSessionManagerContext {
|
|||||||
}
|
}
|
||||||
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, _):
|
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, _):
|
||||||
if self.contextIdByStableId[id] == nil {
|
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):
|
case let .phoneCallWaiting(_, id, _, _, _, _, _, receiveDate):
|
||||||
if let internalId = self.contextIdByStableId[id] {
|
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)? {
|
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
|
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
|
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))
|
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)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|> mapToSignal { updates -> Signal<(Bool, Bool), NoError> in
|
|> mapToSignal { updates -> Signal<(Bool, Bool), NoError> in
|
||||||
var reportRating: Bool = false
|
var reportRating: Bool = false
|
||||||
var sendDebugLogs: Bool = false
|
var sendDebugLogs: Bool = false
|
||||||
if let updates = updates {
|
if let updates = updates {
|
||||||
switch updates {
|
switch updates {
|
||||||
case .updates(let updates, _, _, _, _):
|
case .updates(let updates, _, _, _, _):
|
||||||
for update in updates {
|
for update in updates {
|
||||||
switch update {
|
switch update {
|
||||||
case .updatePhoneCall(let phoneCall):
|
case .updatePhoneCall(let phoneCall):
|
||||||
switch phoneCall {
|
switch phoneCall {
|
||||||
case.phoneCallDiscarded(let values):
|
case.phoneCallDiscarded(let values):
|
||||||
reportRating = (values.flags & (1 << 2)) != 0
|
reportRating = (values.flags & (1 << 2)) != 0
|
||||||
sendDebugLogs = (values.flags & (1 << 3)) != 0
|
sendDebugLogs = (values.flags & (1 << 3)) != 0
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
|
||||||
}
|
}
|
||||||
default:
|
}
|
||||||
break
|
default:
|
||||||
}
|
break
|
||||||
|
|
||||||
addUpdates(updates)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return .single((reportRating, sendDebugLogs))
|
|
||||||
|
addUpdates(updates)
|
||||||
|
|
||||||
|
}
|
||||||
|
return .single((reportRating, sendDebugLogs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -48,9 +48,6 @@ private func encodeText(_ string: String, _ key: Int) -> String {
|
|||||||
return result
|
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 keyboardViewClass: AnyClass? = NSClassFromString(encodeText("VJJoqvuTfuIptuWjfx", -1))!
|
||||||
private let keyboardViewContainerClass: AnyClass? = NSClassFromString(encodeText("VJJoqvuTfuDpoubjofsWjfx", -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)
|
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? {
|
var keyboardWindow: UIWindow? {
|
||||||
guard let keyboardWindowClass = keyboardWindowClass else {
|
guard let keyboardWindowClass = keyboardWindowClass else {
|
||||||
return nil
|
return nil
|
||||||
@ -149,10 +119,6 @@ private class ApplicationStatusBarHost: StatusBarHost {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var handleVolumeControl: Signal<Bool, NoError> {
|
|
||||||
return MediaManagerImpl.globalAudioSession.isPlaybackActive()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func legacyDocumentsPath() -> String {
|
private func legacyDocumentsPath() -> String {
|
||||||
@ -1460,16 +1426,84 @@ final class SharedApplicationContext {
|
|||||||
|
|
||||||
public func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
|
public func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
|
||||||
if #available(iOS 9.0, *) {
|
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)
|
|> 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)
|
sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 4.0)
|
||||||
|
|
||||||
if case PKPushType.voIP = type {
|
if case PKPushType.voIP = type {
|
||||||
Logger.shared.log("App \(self.episodeId)", "pushRegistry payload: \(payload.dictionaryPayload)")
|
Logger.shared.log("App \(self.episodeId)", "pushRegistry payload: \(payload.dictionaryPayload)")
|
||||||
sharedApplicationContext.notificationManager.addNotification(payload.dictionaryPayload)
|
sharedApplicationContext.notificationManager.addNotification(payload.dictionaryPayload)
|
||||||
}
|
}
|
||||||
})
|
})*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
@ -662,7 +662,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
let settings = self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings])
|
let settings = self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings])
|
||||||
|> map { sharedData -> (allAccounts: Bool, includeMuted: Bool) in
|
|> map { sharedData -> (allAccounts: Bool, includeMuted: Bool) in
|
||||||
let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings] as? InAppNotificationSettings ?? InAppNotificationSettings.defaultSettings
|
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
|
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||||
if lhs.allAccounts != rhs.allAccounts {
|
if lhs.allAccounts != rhs.allAccounts {
|
||||||
|
@ -4,14 +4,11 @@ import SwiftSignalKit
|
|||||||
|
|
||||||
public enum TotalUnreadCountDisplayStyle: Int32 {
|
public enum TotalUnreadCountDisplayStyle: Int32 {
|
||||||
case filtered = 0
|
case filtered = 0
|
||||||
case raw = 1
|
|
||||||
|
|
||||||
var category: ChatListTotalUnreadStateCategory {
|
var category: ChatListTotalUnreadStateCategory {
|
||||||
switch self {
|
switch self {
|
||||||
case .filtered:
|
case .filtered:
|
||||||
return .filtered
|
return .filtered
|
||||||
case .raw:
|
|
||||||
return .raw
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,7 +38,7 @@ public struct InAppNotificationSettings: PreferencesEntry, Equatable {
|
|||||||
public var displayNotificationsFromAllAccounts: Bool
|
public var displayNotificationsFromAllAccounts: Bool
|
||||||
|
|
||||||
public static var defaultSettings: InAppNotificationSettings {
|
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) {
|
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.playSounds = decoder.decodeInt32ForKey("s", orElse: 0) != 0
|
||||||
self.vibrate = decoder.decodeInt32ForKey("v", orElse: 0) != 0
|
self.vibrate = decoder.decodeInt32ForKey("v", orElse: 0) != 0
|
||||||
self.displayPreviews = decoder.decodeInt32ForKey("p", 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
|
self.totalUnreadCountDisplayCategory = TotalUnreadCountDisplayCategory(rawValue: decoder.decodeInt32ForKey("totalUnreadCountDisplayCategory", orElse: 1)) ?? .messages
|
||||||
if let value = decoder.decodeOptionalInt32ForKey("totalUnreadCountIncludeTags") {
|
if let value = decoder.decodeOptionalInt32ForKey("totalUnreadCountIncludeTags") {
|
||||||
self.totalUnreadCountIncludeTags = PeerSummaryCounterTags(rawValue: value)
|
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.playSounds ? 1 : 0, forKey: "s")
|
||||||
encoder.encodeInt32(self.vibrate ? 1 : 0, forKey: "v")
|
encoder.encodeInt32(self.vibrate ? 1 : 0, forKey: "v")
|
||||||
encoder.encodeInt32(self.displayPreviews ? 1 : 0, forKey: "p")
|
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.totalUnreadCountDisplayCategory.rawValue, forKey: "totalUnreadCountDisplayCategory")
|
||||||
encoder.encodeInt32(self.totalUnreadCountIncludeTags.rawValue, forKey: "totalUnreadCountIncludeTags")
|
encoder.encodeInt32(self.totalUnreadCountIncludeTags.rawValue, forKey: "totalUnreadCountIncludeTags")
|
||||||
encoder.encodeInt32(self.displayNameOnLockscreen ? 1 : 0, forKey: "displayNameOnLockscreen")
|
encoder.encodeInt32(self.displayNameOnLockscreen ? 1 : 0, forKey: "displayNameOnLockscreen")
|
||||||
|
@ -15,8 +15,6 @@ public func renderedTotalUnreadCount(inAppNotificationSettings: InAppNotificatio
|
|||||||
public func renderedTotalUnreadCount(inAppSettings: InAppNotificationSettings, totalUnreadState: ChatListTotalUnreadState) -> (Int32, RenderedTotalUnreadCountType) {
|
public func renderedTotalUnreadCount(inAppSettings: InAppNotificationSettings, totalUnreadState: ChatListTotalUnreadState) -> (Int32, RenderedTotalUnreadCountType) {
|
||||||
let type: RenderedTotalUnreadCountType
|
let type: RenderedTotalUnreadCountType
|
||||||
switch inAppSettings.totalUnreadCountDisplayStyle {
|
switch inAppSettings.totalUnreadCountDisplayStyle {
|
||||||
case .raw:
|
|
||||||
type = .raw
|
|
||||||
case .filtered:
|
case .filtered:
|
||||||
type = .filtered
|
type = .filtered
|
||||||
}
|
}
|
||||||
@ -42,8 +40,6 @@ public func renderedTotalUnreadCount(accountManager: AccountManager, postbox: Po
|
|||||||
}
|
}
|
||||||
let type: RenderedTotalUnreadCountType
|
let type: RenderedTotalUnreadCountType
|
||||||
switch inAppSettings.totalUnreadCountDisplayStyle {
|
switch inAppSettings.totalUnreadCountDisplayStyle {
|
||||||
case .raw:
|
|
||||||
type = .raw
|
|
||||||
case .filtered:
|
case .filtered:
|
||||||
type = .filtered
|
type = .filtered
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
@interface TON : NSObject
|
@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;
|
- (SSignal *)updateConfig:(NSString *)config blockchainName:(NSString *)blockchainName;
|
||||||
|
|
||||||
|
@ -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];
|
self = [super init];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_queue = [SQueue mainQueue];
|
_queue = [SQueue mainQueue];
|
||||||
@ -291,7 +291,23 @@ typedef enum {
|
|||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
return;
|
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);
|
NSNumber *requestId = @(response.id);
|
||||||
[requestHandlersLock lock];
|
[requestHandlersLock lock];
|
||||||
TONRequestHandler *handler = requestHandlers[requestId];
|
TONRequestHandler *handler = requestHandlers[requestId];
|
||||||
|
@ -55,6 +55,7 @@ private final class TonInstanceImpl {
|
|||||||
private let blockchainName: String
|
private let blockchainName: String
|
||||||
private let proxy: TonNetworkProxy?
|
private let proxy: TonNetworkProxy?
|
||||||
private var instance: TON?
|
private var instance: TON?
|
||||||
|
private let syncStateProgress = ValuePromise<Float>(0.0)
|
||||||
|
|
||||||
init(queue: Queue, basePath: String, config: String, blockchainName: String, proxy: TonNetworkProxy?) {
|
init(queue: Queue, basePath: String, config: String, blockchainName: String, proxy: TonNetworkProxy?) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
@ -70,6 +71,7 @@ private final class TonInstanceImpl {
|
|||||||
instance = current
|
instance = current
|
||||||
} else {
|
} else {
|
||||||
let proxy = self.proxy
|
let proxy = self.proxy
|
||||||
|
let syncStateProgress = self.syncStateProgress
|
||||||
instance = TON(keystoreDirectory: self.basePath + "/ton-keystore", config: self.config, blockchainName: self.blockchainName, performExternalRequest: { request in
|
instance = TON(keystoreDirectory: self.basePath + "/ton-keystore", config: self.config, blockchainName: self.blockchainName, performExternalRequest: { request in
|
||||||
if let proxy = proxy {
|
if let proxy = proxy {
|
||||||
let _ = proxy.request(data: request.data, timeout: 20.0, completion: { result in
|
let _ = proxy.request(data: request.data, timeout: 20.0, completion: { result in
|
||||||
@ -83,7 +85,9 @@ private final class TonInstanceImpl {
|
|||||||
} else {
|
} else {
|
||||||
request.onResult(nil, "NETWORK_DISABLED")
|
request.onResult(nil, "NETWORK_DISABLED")
|
||||||
}
|
}
|
||||||
}, enableExternalRequests: proxy != nil)
|
}, enableExternalRequests: proxy != nil, syncStateUpdated: { progress in
|
||||||
|
syncStateProgress.set(progress)
|
||||||
|
})
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
}
|
}
|
||||||
f(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 {
|
public protocol WalletStorageInterface {
|
||||||
func watchWalletRecords() -> Signal<[WalletStateRecord], NoError>
|
func watchWalletRecords() -> Signal<[WalletStateRecord], NoError>
|
||||||
func getWalletRecords() -> Signal<[WalletStateRecord], NoError>
|
func getWalletRecords() -> Signal<[WalletStateRecord], NoError>
|
||||||
func updateWalletRecords(_ f: @escaping ([WalletStateRecord]) -> [WalletStateRecord]) -> Signal<[WalletStateRecord], NoError>
|
func updateWalletRecords(_ f: @escaping ([WalletStateRecord]) -> [WalletStateRecord]) -> Signal<[WalletStateRecord], NoError>
|
||||||
|
func customWalletConfiguration() -> Signal<CustomWalletConfiguration?, NoError>
|
||||||
|
func updateCustomWalletConfiguration(_ value: CustomWalletConfiguration?)
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
187
submodules/WalletUI/Sources/WalletConfgurationScreen.swift
Normal file
187
submodules/WalletUI/Sources/WalletConfgurationScreen.swift
Normal 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
|
||||||
|
}
|
@ -13,6 +13,8 @@ public protocol WalletContext {
|
|||||||
var keychain: TonKeychain { get }
|
var keychain: TonKeychain { get }
|
||||||
var presentationData: WalletPresentationData { get }
|
var presentationData: WalletPresentationData { get }
|
||||||
|
|
||||||
|
var supportsCustomConfigurations: Bool { get }
|
||||||
|
|
||||||
var inForeground: Signal<Bool, NoError> { get }
|
var inForeground: Signal<Bool, NoError> { get }
|
||||||
|
|
||||||
func getServerSalt() -> Signal<Data, WalletContextGetServerSaltError>
|
func getServerSalt() -> Signal<Data, WalletContextGetServerSaltError>
|
||||||
|
@ -9,27 +9,33 @@ import OverlayStatusController
|
|||||||
import WalletCore
|
import WalletCore
|
||||||
|
|
||||||
private final class WalletSettingsControllerArguments {
|
private final class WalletSettingsControllerArguments {
|
||||||
|
let openConfiguration: () -> Void
|
||||||
let exportWallet: () -> Void
|
let exportWallet: () -> Void
|
||||||
let deleteWallet: () -> 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.exportWallet = exportWallet
|
||||||
self.deleteWallet = deleteWallet
|
self.deleteWallet = deleteWallet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum WalletSettingsSection: Int32 {
|
private enum WalletSettingsSection: Int32 {
|
||||||
|
case configuration
|
||||||
case exportWallet
|
case exportWallet
|
||||||
case deleteWallet
|
case deleteWallet
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum WalletSettingsEntry: ItemListNodeEntry {
|
private enum WalletSettingsEntry: ItemListNodeEntry {
|
||||||
|
case configuration(WalletTheme, String)
|
||||||
case exportWallet(WalletTheme, String)
|
case exportWallet(WalletTheme, String)
|
||||||
case deleteWallet(WalletTheme, String)
|
case deleteWallet(WalletTheme, String)
|
||||||
case deleteWalletInfo(WalletTheme, String)
|
case deleteWalletInfo(WalletTheme, String)
|
||||||
|
|
||||||
var section: ItemListSectionId {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
|
case .configuration:
|
||||||
|
return WalletSettingsSection.configuration.rawValue
|
||||||
case .exportWallet:
|
case .exportWallet:
|
||||||
return WalletSettingsSection.exportWallet.rawValue
|
return WalletSettingsSection.exportWallet.rawValue
|
||||||
case .deleteWallet, .deleteWalletInfo:
|
case .deleteWallet, .deleteWalletInfo:
|
||||||
@ -39,12 +45,14 @@ private enum WalletSettingsEntry: ItemListNodeEntry {
|
|||||||
|
|
||||||
var stableId: Int32 {
|
var stableId: Int32 {
|
||||||
switch self {
|
switch self {
|
||||||
case .exportWallet:
|
case .configuration:
|
||||||
return 0
|
return 0
|
||||||
case .deleteWallet:
|
case .exportWallet:
|
||||||
return 1
|
return 1
|
||||||
case .deleteWalletInfo:
|
case .deleteWallet:
|
||||||
return 2
|
return 2
|
||||||
|
case .deleteWalletInfo:
|
||||||
|
return 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +63,10 @@ private enum WalletSettingsEntry: ItemListNodeEntry {
|
|||||||
func item(_ arguments: Any) -> ListViewItem {
|
func item(_ arguments: Any) -> ListViewItem {
|
||||||
let arguments = arguments as! WalletSettingsControllerArguments
|
let arguments = arguments as! WalletSettingsControllerArguments
|
||||||
switch self {
|
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):
|
case let .exportWallet(theme, text):
|
||||||
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.exportWallet()
|
arguments.exportWallet()
|
||||||
@ -72,14 +84,16 @@ private enum WalletSettingsEntry: ItemListNodeEntry {
|
|||||||
private struct WalletSettingsControllerState: Equatable {
|
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] = []
|
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(.deleteWallet(presentationData.theme, presentationData.strings.Wallet_Settings_DeleteWallet))
|
||||||
entries.append(.deleteWalletInfo(presentationData.theme, presentationData.strings.Wallet_Settings_DeleteWalletInfo))
|
entries.append(.deleteWalletInfo(presentationData.theme, presentationData.strings.Wallet_Settings_DeleteWalletInfo))
|
||||||
|
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +110,13 @@ public func walletSettingsController(context: WalletContext, walletInfo: WalletI
|
|||||||
|
|
||||||
var replaceAllWalletControllersImpl: ((ViewController) -> Void)?
|
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 presentationData = context.presentationData
|
||||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
||||||
presentControllerImpl?(controller, 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())
|
let signal = combineLatest(queue: .mainQueue(), .single(context.presentationData), statePromise.get())
|
||||||
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> 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 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))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
@ -199,228 +199,235 @@ public final class WalletStrings: Equatable {
|
|||||||
public var Wallet_Receive_ShareUrlInfo: String { return self._s[7]! }
|
public var Wallet_Receive_ShareUrlInfo: String { return self._s[7]! }
|
||||||
public var Wallet_RestoreFailed_Title: String { return self._s[8]! }
|
public var Wallet_RestoreFailed_Title: String { return self._s[8]! }
|
||||||
public var Wallet_TransactionInfo_CopyAddress: String { return self._s[10]! }
|
public var Wallet_TransactionInfo_CopyAddress: String { return self._s[10]! }
|
||||||
public var Wallet_Send_NetworkErrorTitle: String { return self._s[11]! }
|
public var Wallet_Settings_BackupWallet: String { return self._s[11]! }
|
||||||
public var Wallet_Month_ShortJune: String { return self._s[12]! }
|
public var Wallet_Send_NetworkErrorTitle: String { return self._s[12]! }
|
||||||
public var Wallet_TransactionInfo_StorageFeeInfo: String { return self._s[13]! }
|
public var Wallet_Month_ShortJune: String { return self._s[13]! }
|
||||||
public var Wallet_Created_Title: String { return self._s[14]! }
|
public var Wallet_TransactionInfo_StorageFeeInfo: String { return self._s[14]! }
|
||||||
public var Wallet_Info_YourBalance: String { return self._s[15]! }
|
public var Wallet_Created_Title: String { return self._s[15]! }
|
||||||
public var Wallet_TransactionInfo_CommentHeader: String { return self._s[16]! }
|
public var Wallet_Info_YourBalance: String { return self._s[16]! }
|
||||||
public var Wallet_TransactionInfo_OtherFeeHeader: String { return self._s[17]! }
|
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)]) {
|
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_WordImport_IncorrectText: String { return self._s[20]! }
|
||||||
public var Wallet_Month_GenJanuary: String { return self._s[20]! }
|
public var Wallet_Month_GenJanuary: String { return self._s[21]! }
|
||||||
public var Wallet_Receive_ShareAddress: String { return self._s[21]! }
|
public var Wallet_Receive_ShareAddress: String { return self._s[22]! }
|
||||||
public var Wallet_WordImport_Title: String { return self._s[22]! }
|
public var Wallet_WordImport_Title: String { return self._s[23]! }
|
||||||
public var Wallet_TransactionInfo_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[25]! }
|
public var Wallet_Words_NotDoneText: String { return self._s[26]! }
|
||||||
public var Wallet_RestoreFailed_EnterWords: String { return self._s[26]! }
|
public var Wallet_RestoreFailed_EnterWords: String { return self._s[27]! }
|
||||||
public var Wallet_WordImport_Text: String { return self._s[27]! }
|
public var Wallet_WordImport_Text: String { return self._s[28]! }
|
||||||
public var Wallet_RestoreFailed_Text: String { return self._s[29]! }
|
public var Wallet_RestoreFailed_Text: String { return self._s[30]! }
|
||||||
public var Wallet_TransactionInfo_NoAddress: String { return self._s[30]! }
|
public var Wallet_TransactionInfo_NoAddress: String { return self._s[31]! }
|
||||||
public var Wallet_Navigation_Back: String { return self._s[31]! }
|
public var Wallet_Navigation_Back: String { return self._s[32]! }
|
||||||
public var Wallet_Intro_Terms: 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)]) {
|
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)]) {
|
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)]) {
|
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_Send_NetworkErrorText: String { return self._s[38]! }
|
||||||
public var Wallet_VoiceOver_Editing_ClearText: 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[39]! }
|
public var Wallet_Intro_ImportExisting: String { return self._s[40]! }
|
||||||
public var Wallet_Receive_CommentInfo: String { return self._s[40]! }
|
public var Wallet_Receive_CommentInfo: String { return self._s[41]! }
|
||||||
public var Wallet_WordCheck_Continue: String { return self._s[41]! }
|
public var Wallet_WordCheck_Continue: String { return self._s[42]! }
|
||||||
public var Wallet_Receive_InvoiceUrlCopied: String { return self._s[42]! }
|
public var Wallet_Receive_InvoiceUrlCopied: String { return self._s[43]! }
|
||||||
public var Wallet_Completed_Text: String { return self._s[43]! }
|
public var Wallet_Completed_Text: String { return self._s[44]! }
|
||||||
public var Wallet_WordCheck_IncorrectHeader: String { return self._s[45]! }
|
public var Wallet_WordCheck_IncorrectHeader: String { return self._s[46]! }
|
||||||
public var Wallet_Receive_Title: String { return self._s[46]! }
|
public var Wallet_Receive_Title: String { return self._s[47]! }
|
||||||
public var Wallet_Info_WalletCreated: String { return self._s[47]! }
|
public var Wallet_Info_WalletCreated: String { return self._s[48]! }
|
||||||
public var Wallet_Navigation_Cancel: String { return self._s[48]! }
|
public var Wallet_Navigation_Cancel: String { return self._s[49]! }
|
||||||
public var Wallet_CreateInvoice_Title: 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)]) {
|
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)]) {
|
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_Month_GenAugust: String { return self._s[54]! }
|
||||||
public var Wallet_Info_UnknownTransaction: String { return self._s[54]! }
|
public var Wallet_Info_UnknownTransaction: String { return self._s[55]! }
|
||||||
public var Wallet_Receive_CreateInvoice: String { return self._s[55]! }
|
public var Wallet_Receive_CreateInvoice: String { return self._s[56]! }
|
||||||
public var Wallet_Month_GenSeptember: String { return self._s[56]! }
|
public var Wallet_Month_GenSeptember: String { return self._s[57]! }
|
||||||
public var Wallet_Month_GenJuly: String { return self._s[57]! }
|
public var Wallet_Month_GenJuly: String { return self._s[58]! }
|
||||||
public var Wallet_Receive_AddressHeader: String { return self._s[58]! }
|
public var Wallet_Receive_AddressHeader: String { return self._s[59]! }
|
||||||
public var Wallet_Send_AmountText: String { return self._s[59]! }
|
public var Wallet_Send_AmountText: String { return self._s[60]! }
|
||||||
public var Wallet_SecureStorageNotAvailable_Text: 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)]) {
|
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)]) {
|
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_Configuration_Title: String { return self._s[65]! }
|
||||||
public var Wallet_Month_ShortMay: String { return self._s[65]! }
|
public var Wallet_Words_Title: String { return self._s[66]! }
|
||||||
public var Wallet_WordCheck_Title: String { return self._s[66]! }
|
public var Wallet_Month_ShortMay: String { return self._s[67]! }
|
||||||
public var Wallet_Words_NotDoneResponse: String { return self._s[67]! }
|
public var Wallet_WordCheck_Title: String { return self._s[68]! }
|
||||||
public var Wallet_Send_ErrorNotEnoughFundsText: String { return self._s[68]! }
|
public var Wallet_Words_NotDoneResponse: String { return self._s[69]! }
|
||||||
public var Wallet_Receive_CreateInvoiceInfo: 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)]) {
|
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_Info_Address: String { return self._s[73]! }
|
||||||
public var Wallet_Intro_CreateWallet: String { return self._s[72]! }
|
public var Wallet_Intro_CreateWallet: String { return self._s[74]! }
|
||||||
public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[73]! }
|
public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[75]! }
|
||||||
public func Wallet_SecureStorageReset_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) {
|
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_Send_SendAnyway: String { return self._s[77]! }
|
||||||
public var Wallet_UnknownError: String { return self._s[76]! }
|
public var Wallet_UnknownError: String { return self._s[78]! }
|
||||||
public var Wallet_SecureStorageChanged_ImportWallet: String { return self._s[77]! }
|
public var Wallet_SecureStorageChanged_ImportWallet: String { return self._s[79]! }
|
||||||
public var Wallet_SecureStorageChanged_CreateWallet: String { return self._s[79]! }
|
public var Wallet_SecureStorageChanged_CreateWallet: String { return self._s[81]! }
|
||||||
public var Wallet_Words_NotDoneOk: String { return self._s[80]! }
|
public var Wallet_Words_NotDoneOk: String { return self._s[82]! }
|
||||||
public var Wallet_Intro_Title: String { return self._s[81]! }
|
public var Wallet_Intro_Title: String { return self._s[83]! }
|
||||||
public var Wallet_Info_Receive: String { return self._s[82]! }
|
public var Wallet_Info_Receive: String { return self._s[84]! }
|
||||||
public var Wallet_Completed_ViewWallet: String { return self._s[83]! }
|
public var Wallet_Completed_ViewWallet: String { return self._s[85]! }
|
||||||
public var Wallet_Month_ShortJuly: String { return self._s[84]! }
|
public var Wallet_Month_ShortJuly: String { return self._s[86]! }
|
||||||
public var Wallet_Month_ShortApril: String { return self._s[85]! }
|
public var Wallet_Month_ShortApril: String { return self._s[87]! }
|
||||||
public func Wallet_Info_TransactionDateHeader(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
|
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)]) {
|
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)]) {
|
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)]) {
|
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_GenApril: String { return self._s[96]! }
|
||||||
public var Wallet_Month_ShortMarch: String { return self._s[95]! }
|
public var Wallet_Month_ShortMarch: String { return self._s[97]! }
|
||||||
public var Wallet_Month_GenFebruary: String { return self._s[96]! }
|
public var Wallet_Month_GenFebruary: String { return self._s[98]! }
|
||||||
public var Wallet_Qr_ScanCode: String { return self._s[97]! }
|
public var Wallet_Qr_ScanCode: String { return self._s[99]! }
|
||||||
public var Wallet_Receive_AddressCopied: String { return self._s[98]! }
|
public var Wallet_Receive_AddressCopied: String { return self._s[100]! }
|
||||||
public var Wallet_Send_UninitializedTitle: String { return self._s[99]! }
|
public var Wallet_Send_UninitializedTitle: String { return self._s[101]! }
|
||||||
public var Wallet_Send_Send: String { return self._s[100]! }
|
public var Wallet_Send_Send: String { return self._s[102]! }
|
||||||
public var Wallet_Info_RefreshErrorTitle: String { return self._s[101]! }
|
public var Wallet_Info_RefreshErrorTitle: String { return self._s[103]! }
|
||||||
public var Wallet_Month_GenJune: String { return self._s[102]! }
|
public var Wallet_Month_GenJune: String { return self._s[104]! }
|
||||||
public var Wallet_Send_AddressHeader: String { return self._s[103]! }
|
public var Wallet_Send_AddressHeader: String { return self._s[105]! }
|
||||||
public var Wallet_SecureStorageReset_BiometryTouchId: String { return self._s[104]! }
|
public var Wallet_SecureStorageReset_BiometryTouchId: String { return self._s[106]! }
|
||||||
public var Wallet_Send_Confirmation: String { return self._s[105]! }
|
public var Wallet_Send_Confirmation: String { return self._s[107]! }
|
||||||
public var Wallet_Completed_Title: String { return self._s[106]! }
|
public var Wallet_Completed_Title: String { return self._s[108]! }
|
||||||
public var Wallet_Alert_OK: String { return self._s[107]! }
|
public var Wallet_Alert_OK: String { return self._s[109]! }
|
||||||
public var Wallet_Settings_DeleteWallet: String { return self._s[108]! }
|
public var Wallet_Settings_DeleteWallet: String { return self._s[110]! }
|
||||||
public var Wallet_SecureStorageReset_PasscodeText: String { return self._s[109]! }
|
public var Wallet_SecureStorageReset_PasscodeText: String { return self._s[111]! }
|
||||||
public var Wallet_Month_ShortSeptember: String { return self._s[110]! }
|
public var Wallet_Month_ShortSeptember: String { return self._s[112]! }
|
||||||
public var Wallet_Info_TransactionTo: String { return self._s[111]! }
|
public var Wallet_Info_TransactionTo: String { return self._s[113]! }
|
||||||
public var Wallet_Send_ConfirmationConfirm: String { return self._s[112]! }
|
public var Wallet_Send_ConfirmationConfirm: String { return self._s[114]! }
|
||||||
public var Wallet_TransactionInfo_OtherFeeInfo: String { return self._s[113]! }
|
public var Wallet_TransactionInfo_OtherFeeInfo: String { return self._s[115]! }
|
||||||
public var Wallet_Receive_AmountText: String { return self._s[114]! }
|
public var Wallet_Receive_AmountText: String { return self._s[116]! }
|
||||||
public var Wallet_Receive_CopyAddress: String { return self._s[115]! }
|
public var Wallet_Receive_CopyAddress: String { return self._s[117]! }
|
||||||
public var Wallet_Intro_Text: 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)]) {
|
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)]) {
|
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_RestoreFailed_CreateWallet: String { return self._s[124]! }
|
||||||
public var Wallet_Weekday_Yesterday: String { return self._s[122]! }
|
public var Wallet_Weekday_Yesterday: String { return self._s[125]! }
|
||||||
public var Wallet_Receive_AmountHeader: String { return self._s[123]! }
|
public var Wallet_Receive_AmountHeader: String { return self._s[126]! }
|
||||||
public var Wallet_Month_ShortFebruary: String { return self._s[124]! }
|
public var Wallet_Month_ShortFebruary: String { return self._s[127]! }
|
||||||
public var Wallet_Alert_Cancel: String { return self._s[125]! }
|
public var Wallet_Alert_Cancel: String { return self._s[128]! }
|
||||||
public var Wallet_TransactionInfo_RecipientHeader: String { return self._s[126]! }
|
public var Wallet_TransactionInfo_RecipientHeader: String { return self._s[129]! }
|
||||||
public var Wallet_Info_TransactionFrom: String { return self._s[127]! }
|
public var Wallet_Info_TransactionFrom: String { return self._s[130]! }
|
||||||
public var Wallet_Send_ErrorDecryptionFailed: String { return self._s[128]! }
|
public var Wallet_Send_ErrorDecryptionFailed: String { return self._s[131]! }
|
||||||
public var Wallet_Words_NotDoneTitle: String { return self._s[129]! }
|
public var Wallet_Words_NotDoneTitle: String { return self._s[132]! }
|
||||||
public var Wallet_Month_ShortOctober: String { return self._s[130]! }
|
public var Wallet_Month_ShortOctober: String { return self._s[133]! }
|
||||||
public var Wallet_Month_GenMay: String { return self._s[131]! }
|
public var Wallet_Month_GenMay: String { return self._s[134]! }
|
||||||
public var Wallet_Intro_CreateErrorTitle: String { return self._s[132]! }
|
public var Wallet_Intro_CreateErrorTitle: String { return self._s[135]! }
|
||||||
public var Wallet_SecureStorageReset_BiometryFaceId: String { return self._s[133]! }
|
public var Wallet_SecureStorageReset_BiometryFaceId: String { return self._s[136]! }
|
||||||
public var Wallet_Month_ShortJanuary: String { return self._s[134]! }
|
public var Wallet_Month_ShortJanuary: String { return self._s[137]! }
|
||||||
public var Wallet_Month_GenMarch: String { return self._s[135]! }
|
public var Wallet_Month_GenMarch: String { return self._s[138]! }
|
||||||
public var Wallet_Sending_Text: String { return self._s[136]! }
|
public var Wallet_Sending_Text: String { return self._s[139]! }
|
||||||
public var Wallet_Month_GenOctober: String { return self._s[137]! }
|
public var Wallet_Month_GenOctober: String { return self._s[140]! }
|
||||||
public var Wallet_Receive_CopyInvoiceUrl: String { return self._s[138]! }
|
public var Wallet_Receive_CopyInvoiceUrl: String { return self._s[141]! }
|
||||||
public var Wallet_ContextMenuCopy: String { return self._s[139]! }
|
public var Wallet_ContextMenuCopy: String { return self._s[142]! }
|
||||||
public func Wallet_Time_PreciseDate_m6(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
|
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_Info_Updating: String { return self._s[145]! }
|
||||||
public var Wallet_Intro_TermsUrl: String { return self._s[143]! }
|
public var Wallet_Intro_TermsUrl: String { return self._s[146]! }
|
||||||
public var Wallet_Created_ExportErrorTitle: String { return self._s[144]! }
|
public var Wallet_Created_ExportErrorTitle: String { return self._s[147]! }
|
||||||
public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[145]! }
|
public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[148]! }
|
||||||
public var Wallet_Sending_Title: String { return self._s[146]! }
|
public var Wallet_Sending_Title: String { return self._s[149]! }
|
||||||
public var Wallet_Navigation_Done: String { return self._s[147]! }
|
public var Wallet_Navigation_Done: String { return self._s[150]! }
|
||||||
public var Wallet_Settings_Title: String { return self._s[148]! }
|
public var Wallet_Settings_Title: String { return self._s[151]! }
|
||||||
public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[149]! }
|
public func Wallet_Receive_ShareInvoiceUrlInfo(_ _0: String) -> (String, [(Int, NSRange)]) {
|
||||||
public var Wallet_Weekday_Today: String { return self._s[151]! }
|
return formatWithArgumentRanges(self._s[152]!, self._r[152]!, [_0])
|
||||||
public var Wallet_Month_ShortDecember: String { return self._s[152]! }
|
}
|
||||||
public var Wallet_Words_Text: String { return self._s[153]! }
|
public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[153]! }
|
||||||
public var Wallet_WordCheck_ViewWords: String { return self._s[154]! }
|
public var Wallet_Weekday_Today: String { return self._s[155]! }
|
||||||
public var Wallet_Send_AddressInfo: 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)]) {
|
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_Intro_NotNow: String { return self._s[161]! }
|
||||||
public var Wallet_Navigation_Close: String { return self._s[158]! }
|
public var Wallet_Navigation_Close: String { return self._s[162]! }
|
||||||
public var Wallet_Month_GenDecember: String { return self._s[160]! }
|
public var Wallet_Month_GenDecember: String { return self._s[164]! }
|
||||||
public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[161]! }
|
public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[165]! }
|
||||||
public var Wallet_WordImport_IncorrectTitle: String { return self._s[162]! }
|
public var Wallet_WordImport_IncorrectTitle: String { return self._s[166]! }
|
||||||
public var Wallet_Send_AddressText: String { return self._s[163]! }
|
public var Wallet_Send_AddressText: String { return self._s[167]! }
|
||||||
public var Wallet_Receive_AmountInfo: String { return self._s[164]! }
|
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)]) {
|
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_Month_ShortAugust: String { return self._s[170]! }
|
||||||
public var Wallet_Qr_Title: String { return self._s[167]! }
|
public var Wallet_Qr_Title: String { return self._s[171]! }
|
||||||
public var Wallet_WordCheck_TryAgain: String { return self._s[168]! }
|
public var Wallet_Settings_Configuration: String { return self._s[172]! }
|
||||||
public var Wallet_Info_TransactionPendingHeader: String { return self._s[169]! }
|
public var Wallet_WordCheck_TryAgain: String { return self._s[173]! }
|
||||||
public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[170]! }
|
public var Wallet_Info_TransactionPendingHeader: String { return self._s[174]! }
|
||||||
public var Wallet_Created_Text: String { return self._s[171]! }
|
public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[175]! }
|
||||||
public var Wallet_Created_Proceed: String { return self._s[172]! }
|
public var Wallet_Created_Text: String { return self._s[176]! }
|
||||||
public var Wallet_Words_Done: String { return self._s[173]! }
|
public var Wallet_Created_Proceed: String { return self._s[177]! }
|
||||||
public var Wallet_WordImport_Continue: String { return self._s[174]! }
|
public var Wallet_Words_Done: String { return self._s[178]! }
|
||||||
public var Wallet_TransactionInfo_StorageFeeHeader: String { return self._s[175]! }
|
public var Wallet_WordImport_Continue: String { return self._s[179]! }
|
||||||
public var Wallet_WordImport_CanNotRemember: String { return self._s[176]! }
|
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)]) {
|
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)]) {
|
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)]) {
|
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_Settings_DeleteWalletInfo: String { return self._s[187]! }
|
||||||
public var Wallet_Intro_CreateErrorText: String { return self._s[183]! }
|
public var Wallet_Intro_CreateErrorText: String { return self._s[188]! }
|
||||||
public var Wallet_Sent_ViewWallet: String { return self._s[184]! }
|
public var Wallet_Sent_ViewWallet: String { return self._s[189]! }
|
||||||
public var Wallet_Send_ErrorInvalidAddress: String { return self._s[185]! }
|
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)]) {
|
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_Send_Title: String { return self._s[192]! }
|
||||||
public var Wallet_Info_RefreshErrorText: String { return self._s[188]! }
|
public var Wallet_Info_RefreshErrorText: String { return self._s[193]! }
|
||||||
public var Wallet_SecureStorageReset_Title: String { return self._s[189]! }
|
public var Wallet_SecureStorageReset_Title: String { return self._s[194]! }
|
||||||
public var Wallet_Receive_CommentHeader: String { return self._s[190]! }
|
public var Wallet_Receive_CommentHeader: String { return self._s[195]! }
|
||||||
public var Wallet_Info_ReceiveGrams: String { return self._s[191]! }
|
public var Wallet_Info_ReceiveGrams: String { return self._s[196]! }
|
||||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||||
let form = getPluralizationForm(self.lc, value)
|
let form = getPluralizationForm(self.lc, value)
|
||||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||||
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
|
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 form = getPluralizationForm(self.lc, value)
|
||||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||||
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
|
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user