Merge branch 'master' into features/64-bit

This commit is contained in:
Ali 2021-07-07 02:08:56 +04:00
commit ee8ec6ed30
155 changed files with 5571 additions and 6557 deletions

View File

@ -138,7 +138,7 @@ private func rootPathForBasePath(_ appGroupPath: String) -> String {
self.audioConverter = CustomAudioConverter(asbd: asbd)
}
if let audioConverter = self.audioConverter {
if let data = audioConverter.convert(sampleBuffer: sampleBuffer) {
if let data = audioConverter.convert(sampleBuffer: sampleBuffer), !data.isEmpty {
self.screencastBufferClientContext?.writeAudioData(data: data)
}
}

View File

@ -6532,7 +6532,7 @@ Sorry for the inconvenience.";
"Settings.TryEnterPassword" = "Not sure, let me try";
"TwoFactorSetup.PasswordRecovery.Title" = "Create New Password";
"TwoFactorSetup.PasswordRecovery.Text" = "You have successfully reset your password.\nPlease enter a new password to continue";
"TwoFactorSetup.PasswordRecovery.Text" = "You can now set a new password that will be used to log into your account.";
"TwoFactorSetup.PasswordRecovery.PlaceholderPassword" = "New Password";
"TwoFactorSetup.PasswordRecovery.PlaceholderConfirmPassword" = "Re-enter New Password";
"TwoFactorSetup.PasswordRecovery.Action" = "Continue";
@ -6549,3 +6549,8 @@ Sorry for the inconvenience.";
"TwoStepAuth.CancelResetTitle" = "Cancel Reset";
"TwoStepAuth.ResetAction" = "Reset Password";
"TwoStepAuth.CancelResetText" = "Cancel the password resetting process? If you proceed, the expired part of the 7-day delay will be lost.";
"TwoStepAuth.RecoveryEmailResetNoAccess" = "Lost access to your Email?";
"TwoFactorSetup.ResetDone.Title" = "New Password Set!";
"TwoFactorSetup.ResetDone.Text" = "This password will be required when you log in on a new device in addition to the code you get via SMS.";
"TwoFactorSetup.ResetDone.Action" = "Continue";

View File

@ -686,7 +686,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
controller.dismiss()
}
switch update {
case .noPassword, .awaitingEmailConfirmation:
case .noPassword, .awaitingEmailConfirmation, .pendingPasswordReset:
break
case .passwordSet:
var updatedToken = webToken
@ -754,7 +754,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
controller.dismiss()
}
switch update {
case .noPassword, .awaitingEmailConfirmation:
case .noPassword, .awaitingEmailConfirmation, .pendingPasswordReset:
break
case .passwordSet:
var updatedToken = webToken
@ -810,7 +810,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
controller.dismiss()
}
switch update {
case .noPassword, .awaitingEmailConfirmation:
case .noPassword, .awaitingEmailConfirmation, .pendingPasswordReset:
break
case .passwordSet:
var updatedToken = token

View File

@ -322,7 +322,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
isMuted = true
}
items.append(.action(ContextMenuActionItem(text: isMuted ? strings.ChatList_Context_Unmute : strings.ChatList_Context_Mute, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = (togglePeerMuted(account: context.account, peerId: peerId)
let _ = (context.engine.peers.togglePeerMuted(peerId: peerId)
|> deliverOnMainQueue).start(completed: {
f(.default)
})
@ -332,7 +332,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
if case .search = source {
if let _ = peer as? TelegramChannel {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_JoinChannel, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) }, action: { _, f in
var createSignal = context.peerChannelMemberCategoriesContextsManager.join(account: context.account, peerId: peerId, hash: nil)
var createSignal = context.peerChannelMemberCategoriesContextsManager.join(engine: context.engine, peerId: peerId, hash: nil)
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }

View File

@ -688,7 +688,7 @@ public final class ChatListNode: ListView {
return
}
strongSelf.setCurrentRemovingPeerId(peerId)
let _ = (togglePeerMuted(account: context.account, peerId: peerId)
let _ = (context.engine.peers.togglePeerMuted(peerId: peerId)
|> deliverOnMainQueue).start(completed: {
self?.updateState { state in
var state = state

View File

@ -365,7 +365,7 @@ final class InviteContactsControllerNode: ASDisplayNode {
return DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phoneNumber.value))
})))
}
return deviceContactsImportedByCount(postbox: context.account.postbox, contacts: mappedContacts)
return context.engine.contacts.deviceContactsImportedByCount(contacts: mappedContacts)
|> map { counts -> [(DeviceContactStableId, DeviceContactBasicData, Int32)]? in
var result: [(DeviceContactStableId, DeviceContactBasicData, Int32)] = []
var contactValues: [DeviceContactStableId: DeviceContactBasicData] = [:]

View File

@ -310,7 +310,7 @@ final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode {
@objc func joinPressed() {
if let peer = self.peer, case .notJoined = self.joinState {
self.updateJoinState(.inProgress)
self.joinDisposable.set((joinChannel(account: self.context.account, peerId: peer.id, hash: nil) |> deliverOnMainQueue).start(error: { [weak self] _ in
self.joinDisposable.set((self.context.engine.peers.joinChannel(peerId: peer.id, hash: nil) |> deliverOnMainQueue).start(error: { [weak self] _ in
if let strongSelf = self {
if case .inProgress = strongSelf.joinState {
strongSelf.updateJoinState(.notJoined)

View File

@ -1,24 +0,0 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "MessageReactionListUI",
module_name = "MessageReactionListUI",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/TelegramCore:TelegramCore",
"//submodules/SyncCore:SyncCore",
"//submodules/Postbox:Postbox",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/AccountContext:AccountContext",
"//submodules/MergeLists:MergeLists",
"//submodules/ItemListPeerItem:ItemListPeerItem",
],
visibility = [
"//visibility:public",
],
)

View File

@ -1,104 +0,0 @@
import Foundation
import AsyncDisplayKit
import Display
import TelegramPresentationData
import TelegramCore
import SyncCore
final class MessageReactionCategoryNode: ASDisplayNode {
let category: MessageReactionListCategory
private let action: () -> Void
private let buttonNode: HighlightableButtonNode
private let highlightedBackgroundNode: ASImageNode
private let iconNode: ASImageNode
private let emojiNode: ImmediateTextNode
private let countNode: ImmediateTextNode
var isSelected = false {
didSet {
self.highlightedBackgroundNode.alpha = self.isSelected ? 1.0 : 0.0
}
}
init(theme: PresentationTheme, category: MessageReactionListCategory, count: Int, action: @escaping () -> Void) {
self.category = category
self.action = action
self.buttonNode = HighlightableButtonNode()
self.highlightedBackgroundNode = ASImageNode()
self.highlightedBackgroundNode.displaysAsynchronously = false
self.highlightedBackgroundNode.displayWithoutProcessing = true
self.highlightedBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: UIColor(rgb: 0xe6e6e8))
self.highlightedBackgroundNode.alpha = 1.0
self.iconNode = ASImageNode()
self.emojiNode = ImmediateTextNode()
self.emojiNode.displaysAsynchronously = false
let emojiText: String
switch category {
case .all:
emojiText = ""
self.iconNode.image = PresentationResourcesChat.chatInputTextFieldTimerImage(theme)
case let .reaction(value):
emojiText = value
}
self.emojiNode.attributedText = NSAttributedString(string: emojiText, font: Font.regular(18.0), textColor: .black)
self.countNode = ImmediateTextNode()
self.countNode.displaysAsynchronously = false
self.countNode.attributedText = NSAttributedString(string: "\(count)", font: Font.regular(16.0), textColor: .black)
super.init()
self.addSubnode(self.highlightedBackgroundNode)
self.addSubnode(self.iconNode)
self.addSubnode(self.emojiNode)
self.addSubnode(self.countNode)
self.addSubnode(self.buttonNode)
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
}
func updateLayout() -> CGSize {
let sideInset: CGFloat = 6.0
let spacing: CGFloat = 2.0
let emojiSize = self.emojiNode.updateLayout(CGSize(width: 100.0, height: 100.0))
let iconSize = self.iconNode.image?.size ?? CGSize()
let countSize = self.countNode.updateLayout(CGSize(width: 100.0, height: 100.0))
let height: CGFloat = 60.0
let backgroundHeight: CGFloat = 36.0
self.emojiNode.frame = CGRect(origin: CGPoint(x: sideInset, y: floor((height - emojiSize.height) / 2.0)), size: emojiSize)
self.iconNode.frame = CGRect(origin: CGPoint(x: sideInset, y: floor((height - iconSize.height) / 2.0)), size: iconSize)
let iconFrame: CGRect
if self.iconNode.image != nil {
iconFrame = self.iconNode.frame
} else {
iconFrame = self.emojiNode.frame
}
self.countNode.frame = CGRect(origin: CGPoint(x: iconFrame.maxX + spacing, y: floor((height - countSize.height) / 2.0)), size: countSize)
let contentWidth = sideInset * 2.0 + spacing + iconFrame.width + countSize.width
self.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((height - backgroundHeight) / 2.0)), size: CGSize(width: contentWidth, height: backgroundHeight))
let size = CGSize(width: contentWidth, height: height)
self.buttonNode.frame = CGRect(origin: CGPoint(), size: size)
return size
}
@objc private func buttonPressed() {
self.action()
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.buttonNode.frame.contains(point) {
return self.buttonNode.view
}
return nil
}
}

View File

@ -1,447 +0,0 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Display
import AccountContext
import TelegramPresentationData
import Postbox
import TelegramCore
import SyncCore
import SwiftSignalKit
import MergeLists
import ItemListPeerItem
import ItemListUI
public final class MessageReactionListController: ViewController {
private let context: AccountContext
private let messageId: MessageId
private let presentationData: PresentationData
private let initialReactions: [MessageReaction]
private var controllerNode: MessageReactionListControllerNode {
return self.displayNode as! MessageReactionListControllerNode
}
private var animatedIn: Bool = false
private let _ready = Promise<Bool>()
override public var ready: Promise<Bool> {
return self._ready
}
public init(context: AccountContext, messageId: MessageId, initialReactions: [MessageReaction]) {
self.context = context
self.messageId = messageId
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.initialReactions = initialReactions
super.init(navigationBarPresentationData: nil)
self.statusBar.statusBarStyle = .Ignore
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func loadDisplayNode() {
self.displayNode = MessageReactionListControllerNode(context: self.context, presentationData: self.presentationData, messageId: messageId, initialReactions: initialReactions, dismiss: { [weak self] in
self?.dismiss()
})
super.displayNodeDidLoad()
self._ready.set(self.controllerNode.isReady.get())
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.controllerNode.containerLayoutUpdated(layout: layout, transition: transition)
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.animatedIn {
self.animatedIn = true
self.controllerNode.animateIn()
}
}
override public func dismiss(completion: (() -> Void)? = nil) {
self.controllerNode.animateOut(completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
completion?()
})
}
}
private struct MessageReactionListTransaction {
let deletions: [ListViewDeleteItem]
let insertions: [ListViewInsertItem]
let updates: [ListViewUpdateItem]
}
private struct MessageReactionListEntry: Comparable, Identifiable {
let index: Int
let item: MessageReactionListCategoryItem
var stableId: PeerId {
return self.item.peer.id
}
static func <(lhs: MessageReactionListEntry, rhs: MessageReactionListEntry) -> Bool {
return lhs.index < rhs.index
}
func item(context: AccountContext, presentationData: PresentationData) -> ListViewItem {
return ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: self.item.peer, height: .peerList, nameStyle: .distinctBold, presence: nil, text: .none, label: .text(self.item.reaction, .custom(Font.regular(19.0))), editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: false, sectionId: 0, action: {
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, noInsets: true, tag: nil)
}
}
private func preparedTransition(from fromEntries: [MessageReactionListEntry], to toEntries: [MessageReactionListEntry], context: AccountContext, presentationData: PresentationData) -> MessageReactionListTransaction {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData), directionHint: nil) }
return MessageReactionListTransaction(deletions: deletions, insertions: insertions, updates: updates)
}
private let headerHeight: CGFloat = 60.0
private let itemHeight: CGFloat = 50.0
private func topInsetForLayout(layout: ContainerViewLayout, itemCount: Int) -> CGFloat {
let contentHeight = CGFloat(itemCount) * itemHeight
let minimumItemHeights: CGFloat = max(contentHeight, itemHeight * 5.0)
return max(layout.size.height - layout.intrinsicInsets.bottom - minimumItemHeights, headerHeight)
}
private final class MessageReactionListControllerNode: ViewControllerTracingNode {
private let context: AccountContext
private let presentationData: PresentationData
private let dismiss: () -> Void
private let listContext: MessageReactionListContext
private let dimNode: ASDisplayNode
private let backgroundNode: ASDisplayNode
private let contentHeaderContainerNode: ASDisplayNode
private let contentHeaderContainerBackgroundNode: ASImageNode
private let contentHeaderContainerSeparatorNode: ASDisplayNode
private var categoryItemNodes: [MessageReactionCategoryNode] = []
private let categoryScrollNode: ASScrollNode
private let listNode: ListView
private var placeholderNode: MessageReactionListLoadingPlaceholder?
private var placeholderNodeIsAnimatingOut = false
private var validLayout: ContainerViewLayout?
private var currentCategory: MessageReactionListCategory = .all
private var currentState: MessageReactionListState?
private var enqueuedTransactions: [MessageReactionListTransaction] = []
private let disposable = MetaDisposable()
let isReady = Promise<Bool>()
private var forceHeaderTransition: ContainedViewLayoutTransition?
init(context: AccountContext, presentationData: PresentationData, messageId: MessageId, initialReactions: [MessageReaction], dismiss: @escaping () -> Void) {
self.context = context
self.presentationData = presentationData
self.dismiss = dismiss
self.dimNode = ASDisplayNode()
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
self.backgroundNode = ASDisplayNode()
self.backgroundNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor
self.contentHeaderContainerNode = ASDisplayNode()
self.contentHeaderContainerBackgroundNode = ASImageNode()
self.contentHeaderContainerBackgroundNode.displaysAsynchronously = false
self.contentHeaderContainerSeparatorNode = ASDisplayNode()
self.contentHeaderContainerSeparatorNode.backgroundColor = self.presentationData.theme.list.itemPlainSeparatorColor
self.categoryScrollNode = ASScrollNode()
self.contentHeaderContainerBackgroundNode.displayWithoutProcessing = true
self.contentHeaderContainerBackgroundNode.image = generateImage(CGSize(width: 10.0, height: 10.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(presentationData.theme.rootController.navigationBar.opaqueBackgroundColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.height / 2.0), size: CGSize(width: size.width, height: size.height / 2.0)))
})?.stretchableImage(withLeftCapWidth: 5, topCapHeight: 5)
self.listNode = ListView()
self.listNode.limitHitTestToNodes = true
self.listNode.accessibilityPageScrolledString = { row, count in
return presentationData.strings.VoiceOver_ScrollStatus(row, count).0
}
self.placeholderNode = MessageReactionListLoadingPlaceholder(theme: presentationData.theme, itemHeight: itemHeight)
self.placeholderNode?.isUserInteractionEnabled = false
self.listContext = MessageReactionListContext(postbox: self.context.account.postbox, network: self.context.account.network, messageId: messageId, initialReactions: initialReactions)
super.init()
self.addSubnode(self.dimNode)
self.addSubnode(self.backgroundNode)
self.listNode.stackFromBottom = false
self.addSubnode(self.listNode)
self.placeholderNode.flatMap(self.addSubnode)
self.addSubnode(self.contentHeaderContainerNode)
self.contentHeaderContainerNode.addSubnode(self.contentHeaderContainerBackgroundNode)
self.contentHeaderContainerNode.addSubnode(self.contentHeaderContainerSeparatorNode)
self.contentHeaderContainerNode.addSubnode(self.categoryScrollNode)
self.listNode.updateFloatingHeaderOffset = { [weak self] offset, listTransition in
guard let strongSelf = self, let layout = strongSelf.validLayout else {
return
}
let transition = strongSelf.forceHeaderTransition ?? listTransition
strongSelf.forceHeaderTransition = nil
let topOffset = offset
transition.updateFrame(node: strongSelf.contentHeaderContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topOffset - headerHeight), size: CGSize(width: layout.size.width, height: headerHeight)))
transition.updateFrame(node: strongSelf.contentHeaderContainerBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: headerHeight)))
transition.updateFrame(node: strongSelf.contentHeaderContainerSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: headerHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
if let placeholderNode = strongSelf.placeholderNode {
transition.updateFrame(node: placeholderNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topOffset), size: placeholderNode.bounds.size))
}
transition.updateFrame(node: strongSelf.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topOffset - headerHeight / 2.0), size: CGSize(width: layout.size.width, height: layout.size.height + 300.0)))
}
self.disposable.set((self.listContext.state
|> deliverOnMainQueue).start(next: { [weak self] state in
self?.updateState(state)
}))
}
deinit {
self.disposable.dispose()
}
override func didLoad() {
super.didLoad()
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimNodeTapGesture)))
}
func containerLayoutUpdated(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
let isFirstLayout = self.validLayout == nil
self.validLayout = layout
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateBounds(node: self.listNode, bounds: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height))
transition.updatePosition(node: self.listNode, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0))
var currentCategoryItemCount = 0
if let currentState = self.currentState {
for (category, categoryState) in currentState.states {
if category == self.currentCategory {
currentCategoryItemCount = categoryState.count
break
}
}
}
var insets = UIEdgeInsets()
insets.top = topInsetForLayout(layout: layout, itemCount: currentCategoryItemCount)
insets.bottom = layout.intrinsicInsets.bottom
if let placeholderNode = self.placeholderNode, !self.placeholderNodeIsAnimatingOut {
let placeholderHeight = min(CGFloat(currentCategoryItemCount) * itemHeight, layout.size.height) + UIScreenPixel
placeholderNode.frame = CGRect(origin: placeholderNode.frame.origin, size: CGSize(width: layout.size.width, height: placeholderHeight))
placeholderNode.updateLayout(size: CGSize(width: layout.size.width, height: placeholderHeight))
}
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: curve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
let sideInset: CGFloat = 12.0
let spacing: CGFloat = 6.0
var leftX = sideInset
for itemNode in self.categoryItemNodes {
let itemSize = itemNode.updateLayout()
itemNode.frame = CGRect(origin: CGPoint(x: leftX, y: 0.0), size: itemSize)
leftX += spacing + itemSize.width
}
leftX += sideInset
self.categoryScrollNode.view.contentSize = CGSize(width: leftX, height: 60.0)
self.categoryScrollNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: 60.0))
if isFirstLayout {
while !self.enqueuedTransactions.isEmpty {
self.dequeueTransaction()
}
}
}
func animateIn() {
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.dimNode.layer.animatePosition(from: CGPoint(x: self.dimNode.position.x, y: self.dimNode.position.y - self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
})
self.layer.animatePosition(from: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
})
}
func animateOut(completion: @escaping () -> Void) {
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.dimNode.layer.animatePosition(from: self.dimNode.position, to: CGPoint(x: self.dimNode.position.x, y: self.dimNode.position.y - self.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false)
self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in
completion()
})
}
func updateState(_ state: MessageReactionListState) {
if self.currentState != state {
self.currentState = state
self.updateItems()
if let validLayout = self.validLayout {
self.containerLayoutUpdated(layout: validLayout, transition: .immediate)
}
}
}
private var currentEntries: [MessageReactionListEntry]?
private func updateItems() {
var entries: [MessageReactionListEntry] = []
var index = 0
let states = self.currentState?.states ?? []
for (category, categoryState) in states {
if self.categoryItemNodes.count <= index {
let itemNode = MessageReactionCategoryNode(theme: self.presentationData.theme, category: category, count: categoryState.count, action: { [weak self] in
self?.setCategory(category)
})
self.categoryItemNodes.append(itemNode)
self.categoryScrollNode.addSubnode(itemNode)
if category == self.currentCategory {
itemNode.isSelected = true
} else {
itemNode.isSelected = false
}
}
if category == self.currentCategory {
for item in categoryState.items {
entries.append(MessageReactionListEntry(index: entries.count, item: item))
}
}
index += 1
}
let transaction = preparedTransition(from: self.currentEntries ?? [], to: entries, context: self.context, presentationData: self.presentationData)
let previousWasEmpty = self.currentEntries == nil || self.currentEntries?.count == 0
let isEmpty = entries.isEmpty
self.currentEntries = entries
self.enqueuedTransactions.append(transaction)
self.dequeueTransaction()
if previousWasEmpty && !isEmpty {
if let placeholderNode = self.placeholderNode {
self.placeholderNodeIsAnimatingOut = true
placeholderNode.allowsGroupOpacity = true
placeholderNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.placeholderNode?.removeFromSupernode()
strongSelf.placeholderNode = nil
})
}
self.listNode.forEachItemNode({ itemNode in
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
})
}
}
func setCategory(_ category: MessageReactionListCategory) {
if self.currentCategory != category {
self.currentCategory = category
for itemNode in self.categoryItemNodes {
itemNode.isSelected = category == itemNode.category
}
//self.forceHeaderTransition = .animated(duration: 0.3, curve: .spring)
if let validLayout = self.validLayout {
self.containerLayoutUpdated(layout: validLayout, transition: .animated(duration: 0.3, curve: .spring))
}
self.updateItems()
}
}
private func dequeueTransaction() {
guard let layout = self.validLayout, let transaction = self.enqueuedTransactions.first else {
return
}
self.enqueuedTransactions.remove(at: 0)
var options = ListViewDeleteAndInsertOptions()
options.insert(.Synchronous)
options.insert(.PreferSynchronousResourceLoading)
options.insert(.PreferSynchronousDrawing)
var currentCategoryItemCount = 0
if let currentState = self.currentState {
for (category, categoryState) in currentState.states {
if category == self.currentCategory {
currentCategoryItemCount = categoryState.count
break
}
}
}
var insets = UIEdgeInsets()
insets.top = topInsetForLayout(layout: layout, itemCount: currentCategoryItemCount)
insets.bottom = layout.intrinsicInsets.bottom
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: self.listNode.bounds.size, insets: insets, duration: 0.3, curve: .Default(duration: 0.3))
self.listNode.transaction(deleteIndices: transaction.deletions, insertIndicesAndItems: transaction.insertions, updateIndicesAndItems: transaction.updates, options: options, updateSizeAndInsets: updateSizeAndInsets, updateOpaqueState: nil, completion: { [weak self] _ in
self?.isReady.set(.single(true))
})
}
@objc private func dimNodeTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.dismiss()
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for itemNode in self.categoryItemNodes {
if let result = itemNode.hitTest(self.view.convert(point, to: itemNode.view), with: event) {
return result
}
}
if let result = self.listNode.hitTest(self.view.convert(point, to: self.listNode.view), with: event) {
return result
}
if point.y >= self.contentHeaderContainerNode.frame.minY && point.y < self.bounds.height {
return self.listNode.view
}
if point.y >= 0 && point.y < self.contentHeaderContainerNode.frame.minY {
return self.dimNode.view
}
return nil
}
}

View File

@ -1,81 +0,0 @@
import Foundation
import AsyncDisplayKit
import Display
import TelegramPresentationData
import TelegramCore
import SyncCore
final class MessageReactionListLoadingPlaceholder: ASDisplayNode {
private let theme: PresentationTheme
private let itemHeight: CGFloat
private let itemImage: UIImage?
private let backgroundNode: ASDisplayNode
private let separatorNode: ASDisplayNode
private let highlightNode: ASImageNode
private var itemNodes: [ASImageNode] = []
init(theme: PresentationTheme, itemHeight: CGFloat) {
self.theme = theme
self.itemHeight = itemHeight
self.backgroundNode = ASDisplayNode()
self.backgroundNode.backgroundColor = UIColor(white: 0.92, alpha: 1.0)
self.separatorNode = ASDisplayNode()
self.separatorNode.backgroundColor = theme.list.itemPlainSeparatorColor
self.highlightNode = ASImageNode()
self.highlightNode.displaysAsynchronously = false
self.highlightNode.displayWithoutProcessing = true
let leftInset: CGFloat = 15.0
let avatarSize: CGFloat = 40.0
let avatarSpacing: CGFloat = 11.0
let contentWidth: CGFloat = 4.0
let contentHeight: CGFloat = 14.0
let rightInset: CGFloat = 54.0
self.itemImage = generateImage(CGSize(width: leftInset + avatarSize + avatarSpacing + contentWidth + rightInset, height: itemHeight), rotatedContext: { size, context in
context.setFillColor(theme.actionSheet.opaqueItemBackgroundColor.cgColor)
context.fill(CGRect(origin: CGPoint(), size: size))
context.setFillColor(theme.list.itemPlainSeparatorColor.cgColor)
context.fill(CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: UIScreenPixel)))
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: leftInset, y: floor((itemHeight - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize)))
let contentOrigin = leftInset + avatarSize + avatarSpacing
context.fill(CGRect(origin: CGPoint(x: contentOrigin, y: floor((size.height - contentHeight) / 2.0)), size: CGSize(width: size.width - contentOrigin - rightInset, height: contentHeight)))
})?.stretchableImage(withLeftCapWidth: Int(leftInset + avatarSize + avatarSpacing + 1), topCapHeight: 0)
super.init()
self.addSubnode(self.backgroundNode)
self.addSubnode(self.highlightNode)
self.addSubnode(self.separatorNode)
}
func updateLayout(size: CGSize) {
self.backgroundNode.frame = CGRect(origin: CGPoint(), size: size)
var verticalOffset: CGFloat = 0.0
var index = 0
while verticalOffset < size.height - 1.0 {
if self.itemNodes.count >= index {
let itemNode = ASImageNode()
itemNode.image = self.itemImage
self.itemNodes.append(itemNode)
self.addSubnode(itemNode)
}
self.itemNodes[index].frame = CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: CGSize(width: size.width, height: self.itemHeight))
verticalOffset += self.itemHeight
index += 1
}
self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: CGSize(width: size.width, height: UIScreenPixel))
if index < self.itemNodes.count {
for i in index ..< self.itemNodes.count {
self.itemNodes[i].removeFromSupernode()
}
self.itemNodes.removeLast(self.itemNodes.count - index)
}
}
}

View File

@ -423,28 +423,31 @@ static NSData *encryptRSALegacy(id<EncryptionProvider> encryptionProvider, NSDat
return encryptedData;
}
static NSData *reversedBytes(NSData *data) {
NSMutableData *result = [[NSMutableData alloc] initWithLength:data.length];
for (NSUInteger i = 0; i < result.length; i++) {
((uint8_t *)result.mutableBytes)[i] = ((uint8_t *)data.bytes)[result.length - i - 1];
}
return result;
}
static NSData *encryptRSAModernPadding(id<EncryptionProvider> encryptionProvider, NSData *pqInnerData, NSString *publicKey) {
NSMutableData *dataWithPadding = [[NSMutableData alloc] init];
[dataWithPadding appendData:pqInnerData];
if (dataWithPadding.length > 176) {
if (dataWithPadding.length > 192) {
return nil;
}
if (dataWithPadding.length != 176) {
if (dataWithPadding.length != 192) {
int originalLength = (int)dataWithPadding.length;
int numPaddingBytes = 176 - originalLength;
[dataWithPadding setLength:176];
int numPaddingBytes = 192 - originalLength;
[dataWithPadding setLength:192];
int randomResult = SecRandomCopyBytes(kSecRandomDefault, numPaddingBytes, ((uint8_t *)dataWithPadding.mutableBytes) + originalLength);
if (randomResult != errSecSuccess) {
return nil;
}
}
NSMutableData *dataWithHash = [[NSMutableData alloc] init];
[dataWithHash appendData:dataWithPadding];
[dataWithHash appendData:MTSha256(dataWithPadding)];
if (dataWithHash.length != 208) {
return nil;
}
NSData *dataWithPaddingReversed = reversedBytes(dataWithPadding);
while (true) {
int randomResult = 0;
@ -454,33 +457,36 @@ static NSData *encryptRSAModernPadding(id<EncryptionProvider> encryptionProvider
return nil;
}
NSMutableData *tempIv = [[NSMutableData alloc] initWithLength:16];
randomResult = SecRandomCopyBytes(kSecRandomDefault, tempIv.length, tempIv.mutableBytes);
if (randomResult != errSecSuccess) {
NSMutableData *tempKeyAndDataWithPadding = [[NSMutableData alloc] init];
[tempKeyAndDataWithPadding appendData:tempKey];
[tempKeyAndDataWithPadding appendData:dataWithPadding];
NSMutableData *dataWithHash = [[NSMutableData alloc] init];
[dataWithHash appendData:dataWithPaddingReversed];
[dataWithHash appendData:MTSha256(tempKeyAndDataWithPadding)];
if (dataWithHash.length != 224) {
return nil;
}
NSData *aesEncrypted = aesCbcEncrypt(dataWithHash, tempKey, tempIv);
NSMutableData *zeroIv = [[NSMutableData alloc] initWithLength:32];
memset(zeroIv.mutableBytes, 0, zeroIv.length);
NSData *aesEncrypted = MTAesEncrypt(dataWithHash, tempKey, zeroIv);
if (aesEncrypted == nil) {
return nil;
}
NSMutableData *tempIvPlusAesEncrypted = [[NSMutableData alloc] init];
[tempIvPlusAesEncrypted appendData:tempIv];
[tempIvPlusAesEncrypted appendData:aesEncrypted];
NSData *shaTempIvPlusAesEncrypted = MTSha256(tempIvPlusAesEncrypted);
NSData *shaAesEncrypted = MTSha256(aesEncrypted);
NSMutableData *tempKeyXor = [[NSMutableData alloc] initWithLength:tempKey.length];
if (tempKeyXor.length != shaTempIvPlusAesEncrypted.length) {
if (tempKeyXor.length != shaAesEncrypted.length) {
return nil;
}
for (NSUInteger i = 0; i < tempKey.length; i++) {
((uint8_t *)tempKeyXor.mutableBytes)[i] = ((uint8_t *)tempKey.bytes)[i] ^ ((uint8_t *)shaTempIvPlusAesEncrypted.bytes)[i];
((uint8_t *)tempKeyXor.mutableBytes)[i] = ((uint8_t *)tempKey.bytes)[i] ^ ((uint8_t *)shaAesEncrypted.bytes)[i];
}
NSMutableData *keyAesEncrypted = [[NSMutableData alloc] init];
[keyAesEncrypted appendData:tempKeyXor];
[keyAesEncrypted appendData:tempIv];
[keyAesEncrypted appendData:aesEncrypted];
if (keyAesEncrypted.length != 256) {
return nil;

View File

@ -717,8 +717,16 @@
request.errorContext.minimalExecuteTime = MAX(request.errorContext.minimalExecuteTime, MTAbsoluteSystemTime() + 2.0);
}
}
else if (rpcError.errorCode == 400 && [rpcError.errorDescription isEqualToString:@"MSG_WAIT_TIMEOUT"])
{
else if (
(
rpcError.errorCode == 400 &&
[rpcError.errorDescription isEqualToString:@"MSG_WAIT_TIMEOUT"]
) ||
(
rpcError.errorCode == 500 &&
[rpcError.errorDescription isEqualToString:@"MSG_WAIT_FAILED"]
)
) {
if (request.errorContext == nil) {
request.errorContext = [[MTRequestErrorContext alloc] init];
}

View File

@ -558,7 +558,7 @@ public final class SecureIdAuthController: ViewController, StandalonePresentable
return
}
switch update {
case .noPassword:
case .noPassword, .pendingPasswordReset:
strongSelf.updateState(animated: false, { state in
var state = state
if let verificationState = state.verificationState, case .noChallenge = verificationState {

View File

@ -179,7 +179,7 @@ public func resetPasswordController(context: AccountContext, emailPattern: Strin
state.checking = true
return state
}
saveDisposable.set((context.engine.auth.recoverTwoStepVerificationPassword(code: state.code)
saveDisposable.set((context.engine.auth.performPasswordRecovery(code: state.code, updatedPassword: .none)
|> deliverOnMainQueue).start(error: { error in
updateState { state in
var state = state
@ -190,7 +190,7 @@ public func resetPasswordController(context: AccountContext, emailPattern: Strin
switch error {
case .invalidCode:
text = presentationData.strings.TwoStepAuth_RecoveryCodeInvalid
case .codeExpired:
case .expired:
text = presentationData.strings.TwoStepAuth_RecoveryCodeExpired
case .limitExceeded:
text = presentationData.strings.TwoStepAuth_FloodError

View File

@ -138,6 +138,7 @@ public enum SetupTwoStepVerificationStateUpdate {
case noPassword
case awaitingEmailConfirmation(password: String, pattern: String, codeLength: Int32?)
case passwordSet(password: String?, hasRecoveryEmail: Bool, hasSecureValues: Bool)
case pendingPasswordReset
}
final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode {

View File

@ -14,18 +14,27 @@ import AnimatedStickerNode
public enum TwoFactorDataInputMode {
public struct Recovery {
public var code: String
public var syncContacts: Bool
public var account: UnauthorizedAccount
public enum Mode {
case notAuthorized(syncContacts: Bool)
case authorized
}
public init(code: String, syncContacts: Bool, account: UnauthorizedAccount) {
public var code: String
public var mode: Mode
public init(code: String, mode: Mode) {
self.code = code
self.syncContacts = syncContacts
self.account = account
self.mode = mode
}
}
public enum PasswordRecoveryEmailMode {
case notAuthorized(syncContacts: Bool)
case authorized
}
case password
case passwordRecoveryEmail(emailPattern: String, mode: PasswordRecoveryEmailMode)
case passwordRecovery(Recovery)
case emailAddress(password: String, hint: String)
case updateEmailAddress(password: String)
@ -39,8 +48,11 @@ public final class TwoFactorDataInputScreen: ViewController {
private var presentationData: PresentationData
private let mode: TwoFactorDataInputMode
private let stateUpdated: (SetupTwoStepVerificationStateUpdate) -> Void
private let actionDisposable = MetaDisposable()
public var passwordRecoveryFailed: (() -> Void)?
public init(sharedContext: SharedAccountContext, engine: SomeTelegramEngine, mode: TwoFactorDataInputMode, stateUpdated: @escaping (SetupTwoStepVerificationStateUpdate) -> Void) {
public init(sharedContext: SharedAccountContext, engine: SomeTelegramEngine, mode: TwoFactorDataInputMode, stateUpdated: @escaping (SetupTwoStepVerificationStateUpdate) -> Void, presentation: ViewControllerNavigationPresentation = .modalInLargeLayout) {
self.sharedContext = sharedContext
self.engine = engine
self.mode = mode
@ -54,7 +66,7 @@ public final class TwoFactorDataInputScreen: ViewController {
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.navigationPresentation = .modalInLargeLayout
self.navigationPresentation = presentation
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.navigationBar?.intrinsicCanTransitionInline = false
@ -64,6 +76,10 @@ public final class TwoFactorDataInputScreen: ViewController {
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.actionDisposable.dispose()
}
@objc private func backPressed() {
self.dismiss()
@ -101,8 +117,57 @@ public final class TwoFactorDataInputScreen: ViewController {
}
return true
}
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .passwordHint(recovery: nil, password: values[0]), stateUpdated: strongSelf.stateUpdated))
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .passwordHint(recovery: nil, password: values[0]), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation))
navigationController.setViewControllers(controllers, animated: true)
case let .passwordRecoveryEmail(_, mode):
guard let text = (strongSelf.displayNode as! TwoFactorDataInputScreenNode).inputText.first, !text.isEmpty else {
return
}
let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: nil))
strongSelf.present(statusController, in: .window(.root))
strongSelf.actionDisposable.set((strongSelf.engine.auth.checkPasswordRecoveryCode(code: text)
|> deliverOnMainQueue).start(error: { [weak statusController] error in
statusController?.dismiss()
guard let strongSelf = self else {
return
}
let text: String
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.LoginPassword_FloodError
case .invalidCode:
text = strongSelf.presentationData.strings.Login_InvalidCodeError
case .expired:
text = strongSelf.presentationData.strings.Login_CodeExpiredError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}, completed: { [weak statusController] in
statusController?.dismiss()
guard let strongSelf = self else {
return
}
let mappedMode: TwoFactorDataInputMode.Recovery.Mode
switch mode {
case .authorized:
mappedMode = .authorized
case let .notAuthorized(syncContacts):
mappedMode = .notAuthorized(syncContacts: syncContacts)
}
let setupController = TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .passwordRecovery(TwoFactorDataInputMode.Recovery(code: text, mode: mappedMode)), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation)
guard let navigationController = strongSelf.navigationController as? NavigationController else {
return
}
navigationController.replaceController(strongSelf, with: setupController, animated: true)
}))
case let .passwordRecovery(recovery):
let values = (strongSelf.displayNode as! TwoFactorDataInputScreenNode).inputText
if values.count != 2 {
@ -120,17 +185,7 @@ public final class TwoFactorDataInputScreen: ViewController {
guard let navigationController = strongSelf.navigationController as? NavigationController else {
return
}
var controllers = navigationController.viewControllers.filter { controller in
if controller is TwoFactorAuthSplashScreen {
return false
}
if controller is TwoFactorDataInputScreen && controller !== strongSelf {
return false
}
return true
}
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .passwordHint(recovery: recovery, password: values[0]), stateUpdated: strongSelf.stateUpdated))
navigationController.setViewControllers(controllers, animated: true)
navigationController.replaceController(strongSelf, with: TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .passwordHint(recovery: recovery, password: values[0]), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation), animated: true)
case let .emailAddress(password, hint):
guard let text = (strongSelf.displayNode as! TwoFactorDataInputScreenNode).inputText.first, !text.isEmpty else {
return
@ -163,7 +218,7 @@ public final class TwoFactorDataInputScreen: ViewController {
}
return true
}
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailConfirmation(passwordAndHint: (password, hint), emailPattern: text, codeLength: pendingEmail.codeLength.flatMap(Int.init)), stateUpdated: strongSelf.stateUpdated))
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailConfirmation(passwordAndHint: (password, hint), emailPattern: text, codeLength: pendingEmail.codeLength.flatMap(Int.init)), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation))
navigationController.setViewControllers(controllers, animated: true)
} else {
guard let navigationController = strongSelf.navigationController as? NavigationController else {
@ -337,7 +392,7 @@ public final class TwoFactorDataInputScreen: ViewController {
if let recovery = recovery {
strongSelf.performRecovery(recovery: recovery, password: password, hint: value)
} else {
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: value), stateUpdated: strongSelf.stateUpdated))
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: value), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation))
}
}
}, skipAction: { [weak self] in
@ -405,7 +460,7 @@ public final class TwoFactorDataInputScreen: ViewController {
if let recovery = recovery {
strongSelf.performRecovery(recovery: recovery, password: password, hint: "")
} else {
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: ""), stateUpdated: strongSelf.stateUpdated))
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: ""), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation))
}
case let .passwordRecovery(recovery):
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.TwoFactorSetup_PasswordRecovery_SkipAlertTitle, text: strongSelf.presentationData.strings.TwoFactorSetup_PasswordRecovery_SkipAlertText, actions: [
@ -439,10 +494,37 @@ public final class TwoFactorDataInputScreen: ViewController {
}
return true
}
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: hint), stateUpdated: strongSelf.stateUpdated))
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: hint), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation))
navigationController.setViewControllers(controllers, animated: true)
} else {
}
case .passwordRecoveryEmail:
switch strongSelf.engine {
case let .authorized(engine):
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailableResetTitle, text: strongSelf.presentationData.strings.TwoStepAuth_RecoveryEmailResetText, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailableResetAction, action: {
let _ = (engine.auth.requestTwoStepPasswordReset()
|> deliverOnMainQueue).start(next: { result in
guard let strongSelf = self else {
return
}
switch result {
case .done, .waitingForReset:
strongSelf.stateUpdated(.pendingPasswordReset)
case .declined:
break
case let .error(reason):
break
}
})
})]), in: .window(.root))
case .unauthorized:
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_RecoveryFailed, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {
guard let strongSelf = self else {
return
}
strongSelf.passwordRecoveryFailed?()
})]), in: .window(.root))
}
default:
break
}
@ -450,29 +532,57 @@ public final class TwoFactorDataInputScreen: ViewController {
guard let strongSelf = self else {
return
}
let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: nil))
strongSelf.present(statusController, in: .window(.root))
switch strongSelf.mode {
case .passwordRecoveryEmail:
let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: nil))
strongSelf.present(statusController, in: .window(.root))
let _ = (strongSelf.engine.auth.resendTwoStepRecoveryEmail()
|> deliverOnMainQueue).start(error: { [weak statusController] error in
statusController?.dismiss()
guard let strongSelf = self else {
let _ = (strongSelf.engine.auth.requestTwoStepVerificationPasswordRecoveryCode()
|> deliverOnMainQueue).start(error: { [weak statusController] error in
statusController?.dismiss()
guard let strongSelf = self else {
return
}
let text: String
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.TwoStepAuth_FloodError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
}
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}, completed: { [weak statusController] in
statusController?.dismiss()
})
default:
guard case let .authorized(engine) = strongSelf.engine else {
return
}
let text: String
switch error {
case .flood:
text = strongSelf.presentationData.strings.TwoStepAuth_FloodError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
}
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}, completed: { [weak statusController] in
statusController?.dismiss()
})
let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: nil))
strongSelf.present(statusController, in: .window(.root))
let _ = (engine.auth.resendTwoStepRecoveryEmail()
|> deliverOnMainQueue).start(error: { [weak statusController] error in
statusController?.dismiss()
guard let strongSelf = self else {
return
}
let text: String
switch error {
case .flood:
text = strongSelf.presentationData.strings.TwoStepAuth_FloodError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
}
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}, completed: { [weak statusController] in
statusController?.dismiss()
})
}
})
self.displayNodeDidLoad()
@ -485,42 +595,93 @@ public final class TwoFactorDataInputScreen: ViewController {
}
private func performRecovery(recovery: TwoFactorDataInputMode.Recovery, password: String, hint: String) {
guard case let .unauthorized(engine) = self.engine else {
return
}
let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: nil))
self.present(statusController, in: .window(.root))
let _ = (engine.auth.performPasswordRecovery(accountManager: self.sharedContext.accountManager, code: recovery.code, syncContacts: recovery.syncContacts, updatedPassword: password.isEmpty ? .none : .password(password: password, hint: hint, email: nil))
|> deliverOnMainQueue).start(error: { [weak self, weak statusController] error in
statusController?.dismiss()
guard let strongSelf = self else {
return
switch self.engine {
case let .unauthorized(engine):
var syncContacts = false
switch recovery.mode {
case let .notAuthorized(syncContactsValue):
syncContacts = syncContactsValue
case .authorized:
break
}
let _ = (engine.auth.performPasswordRecovery(code: recovery.code, updatedPassword: password.isEmpty ? .none : .password(password: password, hint: hint, email: nil))
|> deliverOnMainQueue).start(next: { [weak self, weak statusController] recoveredAccountData in
statusController?.dismiss()
let text: String
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.LoginPassword_FloodError
case .invalidCode:
text = strongSelf.presentationData.strings.Login_InvalidCodeError
case .expired:
text = strongSelf.presentationData.strings.Login_CodeExpiredError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
}
guard let strongSelf = self else {
return
}
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}, completed: { [weak self, weak statusController] in
statusController?.dismiss()
if password.isEmpty {
strongSelf.stateUpdated(.noPassword)
} else {
strongSelf.stateUpdated(.passwordSet(password: password, hasRecoveryEmail: true, hasSecureValues: false))
}
guard let strongSelf = self else {
return
}
(strongSelf.navigationController as? NavigationController)?.replaceController(strongSelf, with: TwoFactorAuthSplashScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .recoveryDone(recoveredAccountData: recoveredAccountData, syncContacts: syncContacts)), animated: true)
}, error: { [weak self, weak statusController] error in
statusController?.dismiss()
strongSelf.dismiss()
})
guard let strongSelf = self else {
return
}
let text: String
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.LoginPassword_FloodError
case .invalidCode:
text = strongSelf.presentationData.strings.Login_InvalidCodeError
case .expired:
text = strongSelf.presentationData.strings.Login_CodeExpiredError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
}
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}, completed: { [weak self, weak statusController] in
statusController?.dismiss()
})
case let .authorized(engine):
let _ = (engine.auth.performPasswordRecovery(code: recovery.code, updatedPassword: password.isEmpty ? .none : .password(password: password, hint: hint, email: nil))
|> deliverOnMainQueue).start(error: { [weak self, weak statusController] error in
statusController?.dismiss()
guard let strongSelf = self else {
return
}
let text: String
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.LoginPassword_FloodError
case .invalidCode:
text = strongSelf.presentationData.strings.Login_InvalidCodeError
case .expired:
text = strongSelf.presentationData.strings.Login_CodeExpiredError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
}
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}, completed: { [weak self, weak statusController] in
statusController?.dismiss()
guard let strongSelf = self else {
return
}
if password.isEmpty {
strongSelf.stateUpdated(.noPassword)
} else {
strongSelf.stateUpdated(.passwordSet(password: password, hasRecoveryEmail: true, hasSecureValues: false))
}
strongSelf.dismiss()
})
}
}
}
@ -807,7 +968,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
switch mode {
case .password, .passwordRecovery, .emailAddress, .updateEmailAddress:
self.monkeyNode = ManagedMonkeyAnimationNode()
case .emailConfirmation:
case .emailConfirmation, .passwordRecoveryEmail:
if let path = getAppBundle().path(forResource: "TwoFactorSetupMail", ofType: "tgs") {
let animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 272, height: 272, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
@ -909,6 +1070,33 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
toggleTextHidden?(node)
}),
]
case let .passwordRecoveryEmail(emailPattern, _):
title = presentationData.strings.TwoFactorSetup_EmailVerification_Title
let (rawText, ranges) = presentationData.strings.TwoFactorSetup_EmailVerification_Text(emailPattern)
let string = NSMutableAttributedString()
string.append(NSAttributedString(string: rawText, font: Font.regular(16.0), textColor: presentationData.theme.list.itemPrimaryTextColor))
for (_, range) in ranges {
string.addAttribute(.font, value: Font.semibold(16.0), range: range)
}
text = string
buttonText = presentationData.strings.TwoFactorSetup_EmailVerification_Action
skipActionText = ""
changeEmailActionText = presentationData.strings.TwoStepAuth_RecoveryEmailResetNoAccess
resendCodeActionText = presentationData.strings.TwoFactorSetup_EmailVerification_ResendAction
inputNodes = [
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .code, placeholder: presentationData.strings.TwoFactorSetup_EmailVerification_Placeholder, focusUpdated: { node, focused in
focusUpdated?(node, focused)
}, next: { node in
next?(node)
}, updated: { node in
updated?(node)
}, toggleTextHidden: { node in
toggleTextHidden?(node)
}),
]
case let .emailConfirmation(_, emailPattern, _):
title = presentationData.strings.TwoFactorSetup_EmailVerification_Title
let (rawText, ranges) = presentationData.strings.TwoFactorSetup_EmailVerification_Text(emailPattern)
@ -985,7 +1173,6 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
self.changeEmailActionTitleNode.attributedText = NSAttributedString(string: changeEmailActionText, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemAccentColor)
self.changeEmailActionButtonNode = HighlightTrackingButtonNode()
self.changeEmailActionButtonNode.isHidden = changeEmailActionText.isEmpty
self.changeEmailActionButtonNode.isHidden = changeEmailActionText.isEmpty
self.resendCodeActionTitleNode = ImmediateTextNode()
self.resendCodeActionTitleNode.isUserInteractionEnabled = false
@ -1088,7 +1275,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
return
}
switch strongSelf.mode {
case .password:
case .password, .passwordRecovery:
if strongSelf.inputNodes[1].isFocused {
let maxWidth = strongSelf.inputNodes[1].bounds.width
@ -1129,7 +1316,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
break
}
}
focusUpdated = { [weak self] node, _ in
focusUpdated = { node, _ in
DispatchQueue.main.async {
updateAnimations()
}
@ -1156,6 +1343,18 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
if let codeLength = codeLength, text.count == codeLength {
action()
}
case .passwordRecoveryEmail:
let text = strongSelf.inputNodes[0].text
let hasText = !text.isEmpty
strongSelf.buttonNode.isHidden = !hasText
strongSelf.changeEmailActionTitleNode.isHidden = hasText
strongSelf.changeEmailActionButtonNode.isHidden = hasText
strongSelf.resendCodeActionTitleNode.isHidden = hasText
strongSelf.resendCodeActionButtonNode.isHidden = hasText
if text.count == 6 {
action()
}
case .passwordHint:
let hasText = strongSelf.inputNodes.contains(where: { !$0.text.isEmpty })
strongSelf.buttonNode.isHidden = !hasText
@ -1171,7 +1370,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
return
}
switch strongSelf.mode {
case .password:
case .password, .passwordRecovery:
textHidden = !textHidden
for node in strongSelf.inputNodes {
node.updateTextHidden(textHidden)
@ -1320,7 +1519,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
let buttonWidth = contentAreaSize.width - buttonSideInset * 2.0
let maxButtonY = min(areaHeight - buttonSpacing, layout.size.height - buttonBottomInset) - buttonHeight
let maxButtonY = min(areaHeight - buttonSpacing, layout.size.height - buttonBottomInset) - buttonHeight * 2.0
let buttonFrame = CGRect(origin: CGPoint(x: floor((contentAreaSize.width - buttonWidth) / 2.0), y: max(contentHeight + buttonSpacing, maxButtonY)), size: CGSize(width: buttonWidth, height: buttonHeight))
transition.updateFrame(node: self.buttonNode, frame: buttonFrame)
@ -1338,7 +1537,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
let changeEmailActionButtonFrame: CGRect
let resendCodeActionFrame: CGRect
let resendCodeActionButtonFrame: CGRect
if changeEmailActionSize.width + resendCodeActionSize.width > layout.size.width - 24.0 {
if changeEmailActionSize.width + resendCodeActionSize.width > layout.size.width - buttonFrame.minX * 2.0 {
changeEmailActionButtonFrame = CGRect(origin: CGPoint(x: buttonFrame.minX, y: buttonFrame.minY), size: CGSize(width: buttonFrame.width, height: buttonFrame.height))
changeEmailActionFrame = CGRect(origin: CGPoint(x: changeEmailActionButtonFrame.minX + floor((changeEmailActionButtonFrame.width - changeEmailActionSize.width) / 2.0), y: changeEmailActionButtonFrame.minY + floor((changeEmailActionButtonFrame.height - changeEmailActionSize.height) / 2.0)), size: changeEmailActionSize)
resendCodeActionButtonFrame = CGRect(origin: CGPoint(x: buttonFrame.minX, y: buttonFrame.maxY), size: CGSize(width: buttonFrame.width, height: buttonFrame.height))

View File

@ -15,6 +15,7 @@ import TelegramCore
public enum TwoFactorAuthSplashMode {
case intro
case done
case recoveryDone(recoveredAccountData: RecoveredAccountData, syncContacts: Bool)
}
public final class TwoFactorAuthSplashScreen: ViewController {
@ -23,7 +24,7 @@ public final class TwoFactorAuthSplashScreen: ViewController {
private var presentationData: PresentationData
private var mode: TwoFactorAuthSplashMode
public init(sharedContext: SharedAccountContext, engine: SomeTelegramEngine, mode: TwoFactorAuthSplashMode) {
public init(sharedContext: SharedAccountContext, engine: SomeTelegramEngine, mode: TwoFactorAuthSplashMode, presentation: ViewControllerNavigationPresentation = .modalInLargeLayout) {
self.sharedContext = sharedContext
self.engine = engine
self.mode = mode
@ -34,9 +35,10 @@ public final class TwoFactorAuthSplashScreen: ViewController {
let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor)
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))
self.navigationPresentation = presentation
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.navigationPresentation = .modalInLargeLayout
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.navigationBar?.intrinsicCanTransitionInline = false
@ -58,12 +60,22 @@ public final class TwoFactorAuthSplashScreen: ViewController {
switch strongSelf.mode {
case .intro:
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .password, stateUpdated: { _ in
}))
}, presentation: strongSelf.navigationPresentation))
case .done:
guard let navigationController = strongSelf.navigationController as? NavigationController else {
return
}
navigationController.filterController(strongSelf, animated: true)
case let .recoveryDone(recoveredAccountData, syncContacts):
guard let navigationController = strongSelf.navigationController as? NavigationController else {
return
}
switch strongSelf.engine {
case let .unauthorized(engine):
let _ = loginWithRecoveredAccountData(accountManager: strongSelf.sharedContext.accountManager, account: engine.account, recoveredAccountData: recoveredAccountData, syncContacts: syncContacts).start()
case .authorized:
navigationController.filterController(strongSelf, animated: true)
}
}
})
@ -124,6 +136,16 @@ private final class TwoFactorAuthSplashScreenNode: ViewControllerTracingNode {
text = NSAttributedString(string: self.presentationData.strings.TwoFactorSetup_Done_Text, font: textFont, textColor: textColor)
buttonText = self.presentationData.strings.TwoFactorSetup_Done_Action
if let path = getAppBundle().path(forResource: "TwoFactorSetupDone", ofType: "tgs") {
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 248, height: 248, mode: .direct(cachePathPrefix: nil))
self.animationSize = CGSize(width: 124.0, height: 124.0)
self.animationNode.visibility = true
}
case .recoveryDone:
title = self.presentationData.strings.TwoFactorSetup_ResetDone_Title
text = NSAttributedString(string: self.presentationData.strings.TwoFactorSetup_ResetDone_Text, font: textFont, textColor: textColor)
buttonText = self.presentationData.strings.TwoFactorSetup_ResetDone_Action
if let path = getAppBundle().path(forResource: "TwoFactorSetupDone", ofType: "tgs") {
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 248, height: 248, mode: .direct(cachePathPrefix: nil))
self.animationSize = CGSize(width: 124.0, height: 124.0)

View File

@ -36,7 +36,7 @@ public func peerInfoProfilePhotos(context: AccountContext, peerId: PeerId) -> Si
if let firstEntry = entries.first {
return context.account.postbox.loadedPeerWithId(peerId)
|> mapToSignal { peer -> Signal<(Bool, [AvatarGalleryEntry])?, NoError>in
return fetchedAvatarGalleryEntries(account: context.account, peer: peer, firstEntry: firstEntry)
return fetchedAvatarGalleryEntries(engine: context.engine, account: context.account, peer: peer, firstEntry: firstEntry)
|> map(Optional.init)
}
} else {
@ -210,7 +210,7 @@ public func initialAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<
}
}
public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<[AvatarGalleryEntry], NoError> {
public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account, peer: Peer) -> Signal<[AvatarGalleryEntry], NoError> {
return initialAvatarGalleryEntries(account: account, peer: peer)
|> map { entries -> [AvatarGalleryEntry] in
return entries ?? []
@ -218,7 +218,7 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<
|> mapToSignal { initialEntries in
return .single(initialEntries)
|> then(
requestPeerPhotos(postbox: account.postbox, network: account.network, peerId: peer.id)
engine.peers.requestPeerPhotos(peerId: peer.id)
|> map { photos -> [AvatarGalleryEntry] in
var result: [AvatarGalleryEntry] = []
if photos.isEmpty {
@ -269,11 +269,11 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<
}
}
public func fetchedAvatarGalleryEntries(account: Account, peer: Peer, firstEntry: AvatarGalleryEntry) -> Signal<(Bool, [AvatarGalleryEntry]), NoError> {
public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account, peer: Peer, firstEntry: AvatarGalleryEntry) -> Signal<(Bool, [AvatarGalleryEntry]), NoError> {
let initialEntries = [firstEntry]
return Signal<(Bool, [AvatarGalleryEntry]), NoError>.single((false, initialEntries))
|> then(
requestPeerPhotos(postbox: account.postbox, network: account.network, peerId: peer.id)
engine.peers.requestPeerPhotos(peerId: peer.id)
|> map { photos -> (Bool, [AvatarGalleryEntry]) in
var result: [AvatarGalleryEntry] = []
let initialEntries = [firstEntry]
@ -403,7 +403,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
if let remoteEntries = remoteEntries {
remoteEntriesSignal = remoteEntries.get()
} else {
remoteEntriesSignal = fetchedAvatarGalleryEntries(account: context.account, peer: peer)
remoteEntriesSignal = fetchedAvatarGalleryEntries(engine: context.engine, account: context.account, peer: peer)
}
let initialSignal = initialAvatarGalleryEntries(account: context.account, peer: peer)

View File

@ -889,7 +889,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
return
}
transferOwnershipDisposable.set((checkOwnershipTranfserAvailability(postbox: context.account.postbox, network: context.account.network, accountStateManager: context.account.stateManager, memberId: adminId) |> deliverOnMainQueue).start(error: { error in
transferOwnershipDisposable.set((context.engine.peers.checkOwnershipTranfserAvailability(memberId: adminId) |> deliverOnMainQueue).start(error: { error in
let controller = channelOwnershipTransferController(context: context, peer: peer, member: member, initialError: error, present: { c, a in
presentControllerImpl?(c, a)
}, completion: { upgradedPeerId in

View File

@ -665,7 +665,7 @@ public func channelAdminsController(context: AccountContext, peerId initialPeerI
|> deliverOnMainQueue).start(next: { peerId in
if peerId.namespace == Namespaces.Peer.CloudChannel {
var didReportLoadCompleted = false
let membersAndLoadMoreControl: (Disposable, PeerChannelMemberCategoryControl?) = context.peerChannelMemberCategoriesContextsManager.admins(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId) { membersState in
let membersAndLoadMoreControl: (Disposable, PeerChannelMemberCategoryControl?) = context.peerChannelMemberCategoriesContextsManager.admins(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId) { membersState in
if case .loading = membersState.loadingState, membersState.list.isEmpty {
adminsPromise.set(.single(nil))
} else {

View File

@ -506,7 +506,7 @@ public func channelBannedMemberController(context: AccountContext, peerId: PeerI
state.updating = true
return state
}
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: context.account, peerId: peerId, memberId: memberId, bannedRights: nil)
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: nil)
|> deliverOnMainQueue).start(error: { _ in
}, completed: {
@ -667,7 +667,7 @@ public func channelBannedMemberController(context: AccountContext, peerId: PeerI
guard let upgradedPeerId = upgradedPeerId else {
return .single(nil)
}
return context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: context.account, peerId: upgradedPeerId, memberId: memberId, bannedRights: cleanResolvedRights)
return context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: upgradedPeerId, memberId: memberId, bannedRights: cleanResolvedRights)
|> mapToSignal { _ -> Signal<PeerId?, NoError> in
return .complete()
}
@ -700,7 +700,7 @@ public func channelBannedMemberController(context: AccountContext, peerId: PeerI
}
}))
} else {
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: context.account, peerId: peerId, memberId: memberId, bannedRights: cleanResolvedRights)
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: cleanResolvedRights)
|> deliverOnMainQueue).start(error: { _ in
}, completed: {

View File

@ -324,7 +324,7 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let progress = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
presentControllerImpl?(progress, nil)
removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: context.account, peerId: peerId, memberId: peer.id, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max))
removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: peer.id, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max))
|> deliverOnMainQueue).start(error: { [weak progress] _ in
progress?.dismiss()
dismissController?()
@ -343,7 +343,7 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId)
return $0.withUpdatedRemovingPeerId(memberId)
}
removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: context.account, peerId: peerId, memberId: memberId, bannedRights: nil) |> deliverOnMainQueue).start(error: { _ in
removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: nil) |> deliverOnMainQueue).start(error: { _ in
updateState {
return $0.withUpdatedRemovingPeerId(nil)
}
@ -388,7 +388,7 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId)
updateState {
return $0.withUpdatedRemovingPeerId(memberId)
}
let signal = context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: context.account, peerId: peerId, memberId: memberId, bannedRights: nil)
let signal = context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: nil)
|> ignoreValues
|> then(
context.peerChannelMemberCategoriesContextsManager.addMember(account: context.account, peerId: peerId, memberId: memberId)
@ -418,7 +418,7 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId)
return $0.withUpdatedRemovingPeerId(memberId)
}
removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: context.account, peerId: peerId, memberId: memberId, bannedRights: nil) |> deliverOnMainQueue).start(error: { _ in
removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: nil) |> deliverOnMainQueue).start(error: { _ in
updateState {
return $0.withUpdatedRemovingPeerId(nil)
}
@ -437,7 +437,7 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId)
})
})
let (listDisposable, loadMoreControl) = context.peerChannelMemberCategoriesContextsManager.banned(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { listState in
let (listDisposable, loadMoreControl) = context.peerChannelMemberCategoriesContextsManager.banned(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { listState in
if case .loading(true) = listState.loadingState, listState.list.isEmpty {
blacklistPromise.set(.single(nil))
} else {

View File

@ -230,7 +230,7 @@ public func channelDiscussionGroupSetupController(context: AccountContext, peerI
let groupPeers = Promise<[Peer]?>()
groupPeers.set(.single(nil)
|> then(
availableGroupsForChannelDiscussion(postbox: context.account.postbox, network: context.account.network)
context.engine.peers.availableGroupsForChannelDiscussion()
|> map(Optional.init)
|> `catch` { _ -> Signal<[Peer]?, NoError> in
return .single(nil)
@ -260,7 +260,7 @@ public func channelDiscussionGroupSetupController(context: AccountContext, peerI
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
pushControllerImpl?(context.sharedContext.makeCreateGroupController(context: context, peerIds: [], initialTitle: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + " Chat", mode: .supergroup, completion: { groupId, dismiss in
var applySignal = updateGroupDiscussionForChannel(network: context.account.network, postbox: context.account.postbox, channelId: peerId, groupId: groupId)
var applySignal = context.engine.peers.updateGroupDiscussionForChannel(channelId: peerId, groupId: groupId)
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
@ -358,13 +358,13 @@ public func channelDiscussionGroupSetupController(context: AccountContext, peerI
})
}
return updateGroupDiscussionForChannel(network: context.account.network, postbox: context.account.postbox, channelId: peerId, groupId: resultPeerId)
return context.engine.peers.updateGroupDiscussionForChannel(channelId: peerId, groupId: resultPeerId)
}
|> castError(ChannelDiscussionGroupError.self)
|> switchToLatest
}
} else {
applySignal = updateGroupDiscussionForChannel(network: context.account.network, postbox: context.account.postbox, channelId: peerId, groupId: groupId)
applySignal = context.engine.peers.updateGroupDiscussionForChannel(channelId: peerId, groupId: groupId)
}
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
@ -413,7 +413,7 @@ public func channelDiscussionGroupSetupController(context: AccountContext, peerI
case .groupHistoryIsCurrentlyPrivate:
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_DiscussionGroup_MakeHistoryPublic, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Channel_DiscussionGroup_MakeHistoryPublicProceed, action: {
var applySignal: Signal<Bool, ChannelDiscussionGroupError> = updateChannelHistoryAvailabilitySettingsInteractively(postbox: context.account.postbox, network: context.account.network, accountStateManager: context.account.stateManager, peerId: updatedPeerId ?? groupId, historyAvailableForNewMembers: true)
var applySignal: Signal<Bool, ChannelDiscussionGroupError> = context.engine.peers.updateChannelHistoryAvailabilitySettingsInteractively(peerId: updatedPeerId ?? groupId, historyAvailableForNewMembers: true)
|> mapError { _ -> ChannelDiscussionGroupError in
return .generic
}
@ -421,7 +421,7 @@ public func channelDiscussionGroupSetupController(context: AccountContext, peerI
return .complete()
}
|> then(
updateGroupDiscussionForChannel(network: context.account.network, postbox: context.account.postbox, channelId: peerId, groupId: updatedPeerId ?? groupId)
context.engine.peers.updateGroupDiscussionForChannel(channelId: peerId, groupId: updatedPeerId ?? groupId)
)
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
@ -502,7 +502,7 @@ public func channelDiscussionGroupSetupController(context: AccountContext, peerI
return
}
var applySignal = updateGroupDiscussionForChannel(network: context.account.network, postbox: context.account.postbox, channelId: applyPeerId, groupId: nil)
var applySignal = context.engine.peers.updateGroupDiscussionForChannel(channelId: applyPeerId, groupId: nil)
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }

View File

@ -836,11 +836,11 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi
}
let controller = notificationMuteSettingsController(presentationData: presentationData, notificationSettings: globalSettings.effective.groupChats, soundSettings: soundSettings, openSoundSettings: {
let controller = notificationSoundSelectionController(context: context, isModal: true, currentSound: peerSettings.messageSound, defaultSound: globalSettings.effective.groupChats.sound, completion: { sound in
let _ = updatePeerNotificationSoundInteractive(account: context.account, peerId: peerId, sound: sound).start()
let _ = context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, sound: sound).start()
})
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}, updateSettings: { value in
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: context.account, peerId: peerId, muteInterval: value).start())
changeMuteSettingsDisposable.set(context.engine.peers.updatePeerMuteSetting(peerId: peerId, muteInterval: value).start())
})
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
})
@ -918,7 +918,7 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi
}, aboutLinkAction: { action, itemLink in
aboutLinkActionImpl?(action, itemLink)
}, toggleSignatures: { enabled in
actionsDisposable.add(toggleShouldChannelMessagesSignatures(account: context.account, peerId: peerId, enabled: enabled).start())
actionsDisposable.add(context.engine.peers.toggleShouldChannelMessagesSignatures(peerId: peerId, enabled: enabled).start())
})
var wasEditing: Bool?

View File

@ -443,7 +443,7 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
return $0.withUpdatedRemovingPeerId(memberId)
}
removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: context.account, peerId: peerId, memberId: memberId, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max))
removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max))
|> deliverOnMainQueue).start(completed: {
updateState {
return $0.withUpdatedRemovingPeerId(nil)
@ -462,7 +462,7 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
let peerView = context.account.viewTracker.peerView(peerId)
let (disposable, loadMoreControl) = context.peerChannelMemberCategoriesContextsManager.recent(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { state in
let (disposable, loadMoreControl) = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { state in
peersPromise.set(.single(state.list))
})
actionsDisposable.add(disposable)

View File

@ -231,13 +231,13 @@ private func categorySignal(context: AccountContext, peerId: PeerId, category: G
}
switch category {
case .admins:
disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.admins(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: processListState)
disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.admins(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: processListState)
case .contacts:
disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.contacts(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: processListState)
disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.contacts(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: processListState)
case .bots:
disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.bots(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: processListState)
disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.bots(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: processListState)
case .members:
disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.recent(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: processListState)
disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: processListState)
}
let (disposable, _) = disposableAndLoadMoreControl
@ -452,7 +452,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
}
}
return context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: context.account, peerId: peerId, memberId: memberId, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max))
return context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max))
|> afterDisposed {
Queue.mainQueue().async {
updateState { state in
@ -479,7 +479,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
}
}
return removePeerMember(account: context.account, peerId: peerId, memberId: memberId)
return context.engine.peers.removePeerMember(peerId: peerId, memberId: memberId)
|> deliverOnMainQueue
|> afterDisposed {
updateState { state in
@ -608,7 +608,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
switch mode {
case .searchMembers, .banAndPromoteActions:
foundGroupMembers = Signal { subscriber in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query, updated: { state in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query, updated: { state in
if case .ready = state.loadingState {
subscriber.putNext(state.list)
}
@ -619,11 +619,11 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
foundMembers = .single([])
case .inviteActions:
foundGroupMembers = .single([])
foundMembers = channelMembers(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, category: .recent(.search(query)))
foundMembers = context.engine.peers.channelMembers(peerId: peerId, category: .recent(.search(query)))
|> map { $0 ?? [] }
case .searchAdmins:
foundGroupMembers = Signal { subscriber in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.admins(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query, updated: { state in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.admins(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query, updated: { state in
if case .ready = state.loadingState {
subscriber.putNext(state.list)
}
@ -633,7 +633,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
foundMembers = .single([])
case .searchBanned:
foundGroupMembers = Signal { subscriber in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.restricted(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query, updated: { state in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.restricted(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query, updated: { state in
if case .ready = state.loadingState {
subscriber.putNext(state.list)
subscriber.putCompletion()
@ -643,7 +643,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
}
|> runOn(Queue.mainQueue())
foundMembers = Signal { subscriber in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query, updated: { state in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query, updated: { state in
if case .ready = state.loadingState {
subscriber.putNext(state.list.filter({ participant in
return participant.peer.id != context.account.peerId
@ -655,7 +655,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|> runOn(Queue.mainQueue())
case .searchKicked:
foundGroupMembers = Signal { subscriber in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.banned(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query, updated: { state in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.banned(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query, updated: { state in
if case .ready = state.loadingState {
subscriber.putNext(state.list)
subscriber.putCompletion()

View File

@ -419,7 +419,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
} else {
let membersState = Promise<ChannelMemberListState>()
disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.recent(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { state in
disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { state in
membersState.set(.single(state))
})

View File

@ -455,7 +455,7 @@ private func commitChannelOwnershipTransferController(context: AccountContext, p
let signal: Signal<PeerId?, ChannelOwnershipTransferError>
if let peer = peer as? TelegramChannel {
signal = context.peerChannelMemberCategoriesContextsManager.transferOwnership(account: context.account, peerId: peer.id, memberId: member.id, password: contentNode.password) |> mapToSignal { _ in
signal = context.peerChannelMemberCategoriesContextsManager.transferOwnership(engine: context.engine, peerId: peer.id, memberId: member.id, password: contentNode.password) |> mapToSignal { _ in
return .complete()
}
|> then(.single(nil))
@ -475,7 +475,7 @@ private func commitChannelOwnershipTransferController(context: AccountContext, p
guard let upgradedPeerId = upgradedPeerId else {
return .fail(.generic)
}
return context.peerChannelMemberCategoriesContextsManager.transferOwnership(account: context.account, peerId: upgradedPeerId, memberId: member.id, password: contentNode.password) |> mapToSignal { _ in
return context.peerChannelMemberCategoriesContextsManager.transferOwnership(engine: context.engine, peerId: upgradedPeerId, memberId: member.id, password: contentNode.password) |> mapToSignal { _ in
return .complete()
}
|> then(.single(upgradedPeerId))

View File

@ -536,7 +536,7 @@ public func channelPermissionsController(context: AccountContext, peerId origina
peersPromise.set(.single((peerId, nil)))
} else {
var loadCompletedCalled = false
let disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.restricted(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { state in
let disposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.restricted(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { state in
if case .loading(true) = state.loadingState, !updated {
peersPromise.set(.single((peerId, nil)))
} else {
@ -594,7 +594,7 @@ public func channelPermissionsController(context: AccountContext, peerId origina
}
let state = stateValue.with { $0 }
if let modifiedRightsFlags = state.modifiedRightsFlags {
updateDefaultRightsDisposable.set((updateDefaultChannelMemberBannedRights(account: context.account, peerId: view.peerId, rights: TelegramChatBannedRights(flags: completeRights(modifiedRightsFlags), untilDate: Int32.max))
updateDefaultRightsDisposable.set((context.engine.peers.updateDefaultChannelMemberBannedRights(peerId: view.peerId, rights: TelegramChatBannedRights(flags: completeRights(modifiedRightsFlags), untilDate: Int32.max))
|> deliverOnMainQueue).start())
}
} else if let group = view.peers[view.peerId] as? TelegramGroup, let _ = view.cachedData as? CachedGroupData {
@ -624,7 +624,7 @@ public func channelPermissionsController(context: AccountContext, peerId origina
}
let state = stateValue.with { $0 }
if let modifiedRightsFlags = state.modifiedRightsFlags {
updateDefaultRightsDisposable.set((updateDefaultChannelMemberBannedRights(account: context.account, peerId: view.peerId, rights: TelegramChatBannedRights(flags: completeRights(modifiedRightsFlags), untilDate: Int32.max))
updateDefaultRightsDisposable.set((context.engine.peers.updateDefaultChannelMemberBannedRights(peerId: view.peerId, rights: TelegramChatBannedRights(flags: completeRights(modifiedRightsFlags), untilDate: Int32.max))
|> deliverOnMainQueue).start())
}
}
@ -679,7 +679,7 @@ public func channelPermissionsController(context: AccountContext, peerId origina
return state
}
removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: context.account, peerId: peerId, memberId: memberId, bannedRights: nil)
removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: nil)
|> deliverOnMainQueue).start(error: { _ in
updateState { state in
var state = state

View File

@ -1104,7 +1104,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
switch subject {
case let .create(peer, _, share, shareViaException, _):
if share, filteredPhoneNumbers.count <= 1, let peer = peer {
addContactDisposable.set((addContactInteractively(account: context.account, peerId: peer.id, firstName: composedContactData.basicData.firstName, lastName: composedContactData.basicData.lastName, phoneNumber: filteredPhoneNumbers.first?.value ?? "", addToPrivacyExceptions: shareViaException && addToPrivacyExceptions)
addContactDisposable.set((context.engine.contacts.addContactInteractively(peerId: peer.id, firstName: composedContactData.basicData.firstName, lastName: composedContactData.basicData.lastName, phoneNumber: filteredPhoneNumbers.first?.value ?? "", addToPrivacyExceptions: shareViaException && addToPrivacyExceptions)
|> deliverOnMainQueue).start(error: { _ in
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}, completed: {
@ -1138,7 +1138,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
switch subject {
case let .create(peer, _, share, shareViaException, _):
if share, let peer = peer {
return addContactInteractively(account: context.account, peerId: peer.id, firstName: composedContactData.basicData.firstName, lastName: composedContactData.basicData.lastName, phoneNumber: filteredPhoneNumbers.first?.value ?? "", addToPrivacyExceptions: shareViaException && addToPrivacyExceptions)
return context.engine.contacts.addContactInteractively(peerId: peer.id, firstName: composedContactData.basicData.firstName, lastName: composedContactData.basicData.lastName, phoneNumber: filteredPhoneNumbers.first?.value ?? "", addToPrivacyExceptions: shareViaException && addToPrivacyExceptions)
|> mapToSignal { _ -> Signal<(DeviceContactStableId, DeviceContactExtendedData, Peer?)?, AddContactError> in
return .complete()
}
@ -1153,7 +1153,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
break
}
return importContact(account: context.account, firstName: composedContactData.basicData.firstName, lastName: composedContactData.basicData.lastName, phoneNumber: filteredPhoneNumbers[0].value)
return context.engine.contacts.importContact(firstName: composedContactData.basicData.firstName, lastName: composedContactData.basicData.lastName, phoneNumber: filteredPhoneNumbers[0].value)
|> castError(AddContactError.self)
|> mapToSignal { peerId -> Signal<(DeviceContactStableId, DeviceContactExtendedData, Peer?)?, AddContactError> in
if let peerId = peerId {

View File

@ -163,7 +163,7 @@ public func groupPreHistorySetupController(context: AccountContext, peerId: Peer
if peerId.namespace == Namespaces.Peer.CloudGroup {
let signal = context.engine.peers.convertGroupToSupergroup(peerId: peerId)
|> mapToSignal { upgradedPeerId -> Signal<PeerId?, ConvertGroupToSupergroupError> in
return updateChannelHistoryAvailabilitySettingsInteractively(postbox: context.account.postbox, network: context.account.network, accountStateManager: context.account.stateManager, peerId: upgradedPeerId, historyAvailableForNewMembers: value)
return context.engine.peers.updateChannelHistoryAvailabilitySettingsInteractively(peerId: upgradedPeerId, historyAvailableForNewMembers: value)
|> `catch` { _ -> Signal<Void, NoError> in
return .complete()
}
@ -190,7 +190,7 @@ public func groupPreHistorySetupController(context: AccountContext, peerId: Peer
}
}))
} else {
applyDisposable.set((updateChannelHistoryAvailabilitySettingsInteractively(postbox: context.account.postbox, network: context.account.network, accountStateManager: context.account.stateManager, peerId: peerId, historyAvailableForNewMembers: value)
applyDisposable.set((context.engine.peers.updateChannelHistoryAvailabilitySettingsInteractively(peerId: peerId, historyAvailableForNewMembers: value)
|> deliverOnMainQueue).start(completed: {
dismissImpl?()
}))

View File

@ -448,7 +448,7 @@ public func groupStickerPackSetupController(context: AccountContext, peerId: Pee
state.isSaving = true
return state
}
saveDisposable.set((updateGroupSpecificStickerset(postbox: context.account.postbox, network: context.account.network, peerId: peerId, info: info)
saveDisposable.set((context.engine.peers.updateGroupSpecificStickerset(peerId: peerId, info: info)
|> deliverOnMainQueue).start(error: { _ in
updateState { state in
var state = state

View File

@ -18,7 +18,7 @@ public func searchPeerMembers(context: AccountContext, peerId: PeerId, chatLocat
|> mapToSignal { cachedData -> Signal<([Peer], Bool), NoError> in
if case .peer = chatLocation, let cachedData = cachedData, let memberCount = cachedData.participantsSummary.memberCount, memberCount <= 64 {
return Signal { subscriber in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, requestUpdate: false, updated: { state in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, requestUpdate: false, updated: { state in
if case .ready = state.loadingState {
let normalizedQuery = query.lowercased()
subscriber.putNext((state.list.compactMap { participant -> Peer? in
@ -54,7 +54,7 @@ public func searchPeerMembers(context: AccountContext, peerId: PeerId, chatLocat
return Signal { subscriber in
switch chatLocation {
case let .peer(peerId):
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query.isEmpty ? nil : query, updated: { state in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: query.isEmpty ? nil : query, updated: { state in
if case .ready = state.loadingState {
subscriber.putNext((state.list.compactMap { participant in
if participant.peer.isDeleted {
@ -69,7 +69,7 @@ public func searchPeerMembers(context: AccountContext, peerId: PeerId, chatLocat
disposable.dispose()
}
case let .replyThread(replyThreadMessage):
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.mentions(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, threadMessageId: replyThreadMessage.messageId, searchQuery: query.isEmpty ? nil : query, updated: { state in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.mentions(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, threadMessageId: replyThreadMessage.messageId, searchQuery: query.isEmpty ? nil : query, updated: { state in
if case .ready = state.loadingState {
subscriber.putNext((state.list.compactMap { participant in
if participant.peer.isDeleted {
@ -117,6 +117,6 @@ public func searchPeerMembers(context: AccountContext, peerId: PeerId, chatLocat
}
}
} else {
return searchGroupMembers(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, query: query)
return context.engine.peers.searchGroupMembers(peerId: peerId, query: query)
}
}

View File

@ -230,7 +230,7 @@ func changePhoneNumberCodeController(context: AccountContext, phoneNumber: Strin
|> take(1)
|> mapToSignal { _ -> Signal<Void, NoError> in
return Signal { subscriber in
return requestNextChangeAccountPhoneNumberVerification(account: context.account, phoneNumber: phoneNumber, phoneCodeHash: data.hash).start(next: { next in
return context.engine.accountData.requestNextChangeAccountPhoneNumberVerification(phoneNumber: phoneNumber, phoneCodeHash: data.hash).start(next: { next in
currentDataPromise?.set(.single(next))
}, error: { error in
@ -254,7 +254,7 @@ func changePhoneNumberCodeController(context: AccountContext, phoneNumber: Strin
}
}
if let code = code {
changePhoneDisposable.set((requestChangeAccountPhoneNumber(account: context.account, phoneNumber: phoneNumber, phoneCodeHash: codeData.hash, phoneCode: code) |> deliverOnMainQueue).start(error: { error in
changePhoneDisposable.set((context.engine.accountData.requestChangeAccountPhoneNumber(phoneNumber: phoneNumber, phoneCodeHash: codeData.hash, phoneCode: code) |> deliverOnMainQueue).start(error: { error in
updateState {
return $0.withUpdatedChecking(false)
}

View File

@ -123,7 +123,7 @@ final class ChangePhoneNumberController: ViewController, MFMailComposeViewContro
}
if !number.isEmpty {
self.inProgress = true
self.requestDisposable.set((requestChangeAccountPhoneNumberVerification(account: self.context.account, phoneNumber: self.controllerNode.currentNumber) |> deliverOnMainQueue).start(next: { [weak self] next in
self.requestDisposable.set((self.context.engine.accountData.requestChangeAccountPhoneNumberVerification(phoneNumber: self.controllerNode.currentNumber) |> deliverOnMainQueue).start(next: { [weak self] next in
if let strongSelf = self {
strongSelf.inProgress = false
(strongSelf.navigationController as? NavigationController)?.pushViewController(changePhoneNumberCodeController(context: strongSelf.context, phoneNumber: strongSelf.controllerNode.currentNumber, codeData: next))

View File

@ -780,16 +780,16 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
let presentationData = context.sharedContext.currentPresentationData.modify {$0}
let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
return updatePeerNotificationSoundInteractive(account: context.account, peerId: peerId, sound: sound) |> deliverOnMainQueue
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue
}
let updatePeerNotificationInterval:(PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
return updatePeerMuteSetting(account: context.account, peerId: peerId, muteInterval: muteInterval) |> deliverOnMainQueue
let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, muteInterval: muteInterval) |> deliverOnMainQueue
}
let updatePeerDisplayPreviews:(PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
peerId, displayPreviews in
return updatePeerDisplayPreviewsSetting(account: context.account, peerId: peerId, displayPreviews: displayPreviews) |> deliverOnMainQueue
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, displayPreviews: displayPreviews) |> deliverOnMainQueue
}
self.backgroundColor = presentationData.theme.list.blocksBackgroundColor
@ -842,13 +842,11 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
updateNotificationsView({})
})
}, removePeerFromExceptions: {
let _ = (context.account.postbox.transaction { transaction -> Peer? in
updatePeerMuteSetting(transaction: transaction, peerId: peerId, muteInterval: nil)
updatePeerDisplayPreviewsSetting(transaction: transaction, peerId: peerId, displayPreviews: .default)
updatePeerNotificationSoundInteractive(transaction: transaction, peerId: peerId, sound: .default)
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: [peerId])
|> map { _ -> Peer? in }
|> then(context.account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId)
}
|> deliverOnMainQueue).start(next: { peer in
})).start(next: { peer in
guard let peer = peer else {
return
}
@ -917,11 +915,7 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
updateState { value in
return value.withUpdatedPeerMuteInterval(peer, nil).withUpdatedPeerSound(peer, .default).withUpdatedPeerDisplayPreviews(peer, .default)
}
_ = (context.account.postbox.transaction { transaction in
updatePeerNotificationSoundInteractive(transaction: transaction, peerId: peer.id, sound: .default)
updatePeerMuteSetting(transaction: transaction, peerId: peer.id, muteInterval: nil)
updatePeerDisplayPreviewsSetting(transaction: transaction, peerId: peer.id, displayPreviews: .default)
}
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: [peer.id])
|> deliverOnMainQueue).start(completed: {
updateNotificationsView({})
})
@ -953,13 +947,7 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
}
return state
}
let _ = (context.account.postbox.transaction { transaction -> Void in
for value in values {
updatePeerNotificationSoundInteractive(transaction: transaction, peerId: value.peer.id, sound: .default)
updatePeerMuteSetting(transaction: transaction, peerId: value.peer.id, muteInterval: nil)
updatePeerDisplayPreviewsSetting(transaction: transaction, peerId: value.peer.id, displayPreviews: .default)
}
}
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: values.map(\.peer.id))
|> deliverOnMainQueue).start(completed: {
updateNotificationsView({})
})

View File

@ -216,7 +216,7 @@ public func confirmPhoneNumberCodeController(context: AccountContext, phoneNumbe
|> take(1)
|> mapToSignal { _ -> Signal<Void, NoError> in
return Signal { subscriber in
return requestNextCancelAccountResetOption(network: context.account.network, phoneNumber: phoneNumber, phoneCodeHash: data.hash).start(next: { next in
return context.engine.auth.requestNextCancelAccountResetOption(phoneNumber: phoneNumber, phoneCodeHash: data.hash).start(next: { next in
currentDataPromise?.set(.single(next))
}, error: { error in
@ -242,7 +242,7 @@ public func confirmPhoneNumberCodeController(context: AccountContext, phoneNumbe
}
}
if let code = code {
confirmPhoneDisposable.set((requestCancelAccountReset(network: context.account.network, phoneCodeHash: codeData.hash, phoneCode: code)
confirmPhoneDisposable.set((context.engine.auth.requestCancelAccountReset(phoneCodeHash: codeData.hash, phoneCode: code)
|> deliverOnMainQueue).start(error: { error in
updateState { state in
var state = state

View File

@ -426,7 +426,7 @@ public func dataPrivacyController(context: AccountContext) -> ViewController {
})
}).start()
actionsDisposable.add((deleteAllContacts(account: context.account)
actionsDisposable.add((context.engine.contacts.deleteAllContacts()
|> deliverOnMainQueue).start(completed: {
updateState { state in
var state = state

View File

@ -506,11 +506,11 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
actionsDisposable.add(updateAutoArchiveDisposable)
let privacySettingsPromise = Promise<AccountPrivacySettings?>()
privacySettingsPromise.set(.single(initialSettings) |> then(requestAccountPrivacySettings(account: context.account) |> map(Optional.init)))
privacySettingsPromise.set(.single(initialSettings) |> then(context.engine.privacy.requestAccountPrivacySettings() |> map(Optional.init)))
let blockedPeersContext = blockedPeersContext ?? BlockedPeersContext(account: context.account)
let activeSessionsContext = activeSessionsContext ?? ActiveSessionsContext(account: context.account)
let webSessionsContext = webSessionsContext ?? WebSessionsContext(account: context.account)
let activeSessionsContext = activeSessionsContext ?? context.engine.privacy.activeSessions()
let webSessionsContext = webSessionsContext ?? context.engine.privacy.webSessions()
let blockedPeersState = Promise<BlockedPeersContextState>()
blockedPeersState.set(blockedPeersContext.state)
@ -779,7 +779,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
return .complete()
}
updateAutoArchiveDisposable.set((updateAccountAutoArchiveChats(account: context.account, value: archiveValue)
updateAutoArchiveDisposable.set((context.engine.privacy.updateAccountAutoArchiveChats(value: archiveValue)
|> mapToSignal { _ -> Signal<Void, NoError> in }
|> then(applyTimeout)
|> deliverOnMainQueue).start(completed: {
@ -817,7 +817,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
}
return .complete()
}
updateAccountTimeoutDisposable.set((updateAccountRemovalTimeout(account: context.account, timeout: timeout)
updateAccountTimeoutDisposable.set((context.engine.privacy.updateAccountRemovalTimeout(timeout: timeout)
|> then(applyTimeout)
|> deliverOnMainQueue).start(completed: {
updateState { state in

View File

@ -1038,14 +1038,14 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
type = .phoneNumber
}
let updateSettingsSignal = updateSelectiveAccountPrivacySettings(account: context.account, type: type, settings: settings)
let updateSettingsSignal = context.engine.privacy.updateSelectiveAccountPrivacySettings(type: type, settings: settings)
var updateCallP2PSettingsSignal: Signal<Void, NoError> = Signal.complete()
if let callP2PSettings = callP2PSettings {
updateCallP2PSettingsSignal = updateSelectiveAccountPrivacySettings(account: context.account, type: .voiceCallsP2P, settings: callP2PSettings)
updateCallP2PSettingsSignal = context.engine.privacy.updateSelectiveAccountPrivacySettings(type: .voiceCallsP2P, settings: callP2PSettings)
}
var updatePhoneDiscoverySignal: Signal<Void, NoError> = Signal.complete()
if let phoneDiscoveryEnabled = phoneDiscoveryEnabled {
updatePhoneDiscoverySignal = updatePhoneNumberDiscovery(account: context.account, value: phoneDiscoveryEnabled)
updatePhoneDiscoverySignal = context.engine.privacy.updatePhoneNumberDiscovery(value: phoneDiscoveryEnabled)
}
let _ = (combineLatest(updateSettingsSignal, updateCallP2PSettingsSignal, updatePhoneDiscoverySignal)

View File

@ -1,260 +0,0 @@
import Foundation
import UIKit
import Display
import SwiftSignalKit
import Postbox
import TelegramCore
import SyncCore
import TelegramPresentationData
import ItemListUI
import PresentationDataUtils
import TextFormat
import AccountContext
import AlertUI
import PresentationDataUtils
import Markdown
private final class TwoStepVerificationResetControllerArguments {
let updateEntryText: (String) -> Void
let next: () -> Void
let openEmailInaccessible: () -> Void
init(updateEntryText: @escaping (String) -> Void, next: @escaping () -> Void, openEmailInaccessible: @escaping () -> Void) {
self.updateEntryText = updateEntryText
self.next = next
self.openEmailInaccessible = openEmailInaccessible
}
}
private enum TwoStepVerificationResetSection: Int32 {
case password
}
private enum TwoStepVerificationResetTag: ItemListItemTag {
case input
func isEqual(to other: ItemListItemTag) -> Bool {
if let other = other as? TwoStepVerificationResetTag {
switch self {
case .input:
if case .input = other {
return true
} else {
return false
}
}
} else {
return false
}
}
}
private enum TwoStepVerificationResetEntry: ItemListNodeEntry {
case codeEntry(PresentationTheme, PresentationStrings, String, String)
case codeInfo(PresentationTheme, String)
var section: ItemListSectionId {
return TwoStepVerificationResetSection.password.rawValue
}
var stableId: Int32 {
switch self {
case .codeEntry:
return 0
case .codeInfo:
return 1
}
}
static func ==(lhs: TwoStepVerificationResetEntry, rhs: TwoStepVerificationResetEntry) -> Bool {
switch lhs {
case let .codeEntry(lhsTheme, lhsStrings, lhsPlaceholder, lhsText):
if case let .codeEntry(rhsTheme, rhsStrings, rhsPlaceholder, rhsText) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsPlaceholder == rhsPlaceholder, lhsText == rhsText {
return true
} else {
return false
}
case let .codeInfo(lhsTheme, lhsText):
if case let .codeInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
}
}
static func <(lhs: TwoStepVerificationResetEntry, rhs: TwoStepVerificationResetEntry) -> Bool {
return lhs.stableId < rhs.stableId
}
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
let arguments = arguments as! TwoStepVerificationResetControllerArguments
switch self {
case let .codeEntry(theme, strings, placeholder, text):
return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(string: placeholder, textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .password, spacing: 10.0, tag: TwoStepVerificationResetTag.input, sectionId: self.section, textUpdated: { updatedText in
arguments.updateEntryText(updatedText)
}, action: {
arguments.next()
})
case let .codeInfo(theme, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
}
}
}
private struct TwoStepVerificationResetControllerState: Equatable {
let codeText: String
let checking: Bool
init(codeText: String, checking: Bool) {
self.codeText = codeText
self.checking = checking
}
static func ==(lhs: TwoStepVerificationResetControllerState, rhs: TwoStepVerificationResetControllerState) -> Bool {
if lhs.codeText != rhs.codeText {
return false
}
if lhs.checking != rhs.checking {
return false
}
return true
}
func withUpdatedCodeText(_ codeText: String) -> TwoStepVerificationResetControllerState {
return TwoStepVerificationResetControllerState(codeText: codeText, checking: self.checking)
}
func withUpdatedChecking(_ checking: Bool) -> TwoStepVerificationResetControllerState {
return TwoStepVerificationResetControllerState(codeText: self.codeText, checking: checking)
}
}
private func twoStepVerificationResetControllerEntries(presentationData: PresentationData, state: TwoStepVerificationResetControllerState, emailPattern: String) -> [TwoStepVerificationResetEntry] {
var entries: [TwoStepVerificationResetEntry] = []
entries.append(.codeEntry(presentationData.theme, presentationData.strings, presentationData.strings.TwoStepAuth_RecoveryCode, state.codeText))
entries.append(.codeInfo(presentationData.theme, "\(presentationData.strings.TwoStepAuth_RecoveryCodeHelp)\n\n[\(presentationData.strings.TwoStepAuth_RecoveryEmailUnavailable(escapedPlaintextForMarkdown(emailPattern)).0)]()"))
return entries
}
func twoStepVerificationResetController(context: AccountContext, emailPattern: String, result: Promise<Bool>, requestedRecoveryReset: @escaping () -> Void) -> ViewController {
let initialState = TwoStepVerificationResetControllerState(codeText: "", checking: false)
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let stateValue = Atomic(value: initialState)
let updateState: ((TwoStepVerificationResetControllerState) -> TwoStepVerificationResetControllerState) -> Void = { f in
statePromise.set(stateValue.modify { f($0) })
}
var dismissImpl: (() -> Void)?
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments) -> Void)?
let actionsDisposable = DisposableSet()
let resetPasswordDisposable = MetaDisposable()
actionsDisposable.add(resetPasswordDisposable)
let checkCode: () -> Void = {
var code: String?
updateState { state in
if state.checking || state.codeText.isEmpty {
return state
} else {
code = state.codeText
return state.withUpdatedChecking(true)
}
}
if let code = code {
resetPasswordDisposable.set((context.engine.auth.recoverTwoStepVerificationPassword(code: code) |> deliverOnMainQueue).start(error: { error in
updateState {
return $0.withUpdatedChecking(false)
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let alertText: String
switch error {
case .generic:
alertText = presentationData.strings.Login_UnknownError
case .invalidCode:
alertText = presentationData.strings.Login_InvalidCodeError
case .codeExpired:
alertText = presentationData.strings.Login_CodeExpiredError
case .limitExceeded:
alertText = presentationData.strings.Login_CodeFloodError
}
presentControllerImpl?(textAlertController(context: context, title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}, completed: {
updateState {
return $0.withUpdatedChecking(false)
}
result.set(.single(true))
}))
}
}
let arguments = TwoStepVerificationResetControllerArguments(updateEntryText: { updatedText in
updateState {
$0.withUpdatedCodeText(updatedText)
}
}, next: {
checkCode()
}, openEmailInaccessible: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.TwoStepAuth_RecoveryUnavailableResetTitle, text: presentationData.strings.TwoStepAuth_RecoveryEmailResetText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.TwoStepAuth_RecoveryUnavailableResetAction, action: {
let _ = (context.engine.auth.requestTwoStepPasswordReset()
|> deliverOnMainQueue).start(next: { result in
switch result {
case .done, .waitingForReset:
requestedRecoveryReset()
case .declined:
break
case let .error(reason):
break
}
})
})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
})
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get()) |> deliverOnMainQueue
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
dismissImpl?()
})
var rightNavigationButton: ItemListNavigationButton?
if state.checking {
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
} else {
var nextEnabled = true
if state.codeText.isEmpty {
nextEnabled = false
}
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Next), style: .bold, enabled: nextEnabled, action: {
checkCode()
})
}
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.TwoStepAuth_RecoveryTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: twoStepVerificationResetControllerEntries(presentationData: presentationData, state: state, emailPattern: emailPattern), style: .blocks, focusItemTag: TwoStepVerificationResetTag.input, emptyStateItem: nil, animateChanges: false)
return (controllerState, (listState, arguments))
} |> afterDisposed {
actionsDisposable.dispose()
}
let controller = ItemListController(context: context, state: signal)
presentControllerImpl = { [weak controller] c, p in
if let controller = controller {
controller.present(c, in: .window(.root), with: p)
}
}
dismissImpl = { [weak controller] in
controller?.dismiss()
}
return controller
}

View File

@ -293,6 +293,7 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
var replaceControllerImpl: ((ViewController, Bool) -> Void)?
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
var pushControllerImpl: ((ViewController) -> Void)?
var dismissImpl: (() -> Void)?
let actionsDisposable = DisposableSet()
@ -507,28 +508,28 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
state.checking = false
return state
}
var completionImpl: ((Bool) -> Void)?
let controller = resetPasswordController(context: context, emailPattern: emailPattern, completion: { result in
completionImpl?(result)
var stateUpdated: ((SetupTwoStepVerificationStateUpdate) -> Void)?
let controller = TwoFactorDataInputScreen(sharedContext: context.sharedContext, engine: .authorized(context.engine), mode: .passwordRecoveryEmail(emailPattern: emailPattern, mode: .authorized), stateUpdated: { state in
stateUpdated?(state)
})
completionImpl = { [weak controller] result in
if !result {
stateUpdated = { [weak controller] state in
controller?.view.endEditing(true)
controller?.dismiss()
switch state {
case .noPassword, .awaitingEmailConfirmation, .passwordSet:
controller?.dismiss()
dismissImpl?()
case .pendingPasswordReset:
dataPromise.set(context.engine.auth.twoStepVerificationConfiguration()
|> map { TwoStepVerificationUnlockSettingsControllerData.access(configuration: TwoStepVerificationAccessConfiguration(configuration: $0, password: nil))
})
controller?.view.endEditing(true)
controller?.dismiss()
} else {
dataPromise.set(.single(TwoStepVerificationUnlockSettingsControllerData.access(configuration: .notSet(pendingEmail: nil))))
controller?.view.endEditing(true)
controller?.dismiss()
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, type: .genericSuccess(presentationData.strings.TwoStepAuth_DisableSuccess, false)), nil)
}
}
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
pushControllerImpl?(controller)
}, error: { _ in
updateState { state in
var state = state
@ -592,6 +593,8 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
case .notSet:
let controller = SetupTwoStepVerificationController(context: context, initialState: .createPassword, stateUpdated: { update, shouldDismiss, controller in
switch update {
case .pendingPasswordReset:
break
case .noPassword:
dataPromise.set(.single(.access(configuration: .notSet(pendingEmail: nil))))
case let .awaitingEmailConfirmation(password, pattern, codeLength):
@ -623,6 +626,8 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
case let .manage(password, hasRecovery, pendingEmail, hasSecureValues):
let controller = SetupTwoStepVerificationController(context: context, initialState: .updatePassword(current: password, hasRecoveryEmail: hasRecovery, hasSecureValues: hasSecureValues), stateUpdated: { update, shouldDismiss, controller in
switch update {
case .pendingPasswordReset:
break
case .noPassword:
dataPromise.set(.single(.access(configuration: .notSet(pendingEmail: nil))))
case .awaitingEmailConfirmation:
@ -712,10 +717,10 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
case .access:
break
case let .manage(password, emailSet, _, hasSecureValues):
//let controller = TwoFactorDataInputScreen(context: context, mode: .updateEmailAddress(password: password))
let controller = SetupTwoStepVerificationController(context: context, initialState: .addEmail(hadRecoveryEmail: emailSet, hasSecureValues: hasSecureValues, password: password), stateUpdated: { update, shouldDismiss, controller in
switch update {
case .pendingPasswordReset:
break
case .noPassword:
assertionFailure()
break
@ -803,6 +808,8 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
}
let controller = SetupTwoStepVerificationController(context: context, initialState: .confirmEmail(password: password, hasSecureValues: hasSecureValues, pattern: pendingEmail.pattern, codeLength: pendingEmail.codeLength), stateUpdated: { update, shouldDismiss, controller in
switch update {
case .pendingPasswordReset:
break
case .noPassword:
assertionFailure()
break
@ -941,6 +948,9 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
controller.present(c, in: .window(.root), with: p)
}
}
pushControllerImpl = { [weak controller] c in
controller?.push(c)
}
dismissImpl = { [weak controller] in
controller?.dismiss()
}

View File

@ -430,7 +430,7 @@ private func privacySearchableItems(context: AccountContext, privacySettings: Ac
if let privacySettings = privacySettings {
privacySignal = .single(privacySettings)
} else {
privacySignal = requestAccountPrivacySettings(account: context.account)
privacySignal = context.engine.privacy.requestAccountPrivacySettings()
}
let callsSignal: Signal<(VoiceCallSettings, VoipConfiguration)?, NoError>
if case .voiceCalls = kind {
@ -535,10 +535,10 @@ private func privacySearchableItems(context: AccountContext, privacySettings: Ac
present(.push, twoStepVerificationUnlockSettingsController(context: context, mode: .access(intro: true, data: nil)))
}),
activeSessionsContext == nil ? nil : SettingsSearchableItem(id: .privacy(9), title: strings.Settings_Devices, alternate: synonyms(strings.SettingsSearch_Synonyms_Privacy_AuthSessions) + [strings.PrivacySettings_AuthSessions], icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
present(.push, recentSessionsController(context: context, activeSessionsContext: activeSessionsContext!, webSessionsContext: webSessionsContext ?? WebSessionsContext(account: context.account), websitesOnly: false))
present(.push, recentSessionsController(context: context, activeSessionsContext: activeSessionsContext!, webSessionsContext: webSessionsContext ?? context.engine.privacy.webSessions(), websitesOnly: false))
}),
webSessionsContext == nil ? nil : SettingsSearchableItem(id: .privacy(10), title: strings.PrivacySettings_WebSessions, alternate: synonyms(strings.SettingsSearch_Synonyms_Privacy_AuthSessions), icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
present(.push, recentSessionsController(context: context, activeSessionsContext: activeSessionsContext ?? ActiveSessionsContext(account: context.account), webSessionsContext: webSessionsContext ?? WebSessionsContext(account: context.account), websitesOnly: true))
present(.push, recentSessionsController(context: context, activeSessionsContext: activeSessionsContext ?? context.engine.privacy.activeSessions(), webSessionsContext: webSessionsContext ?? context.engine.privacy.webSessions(), websitesOnly: true))
}),
SettingsSearchableItem(id: .privacy(11), title: strings.PrivacySettings_DeleteAccountTitle, alternate: synonyms(strings.SettingsSearch_Synonyms_Privacy_DeleteAccountIfAwayFor), icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in
presentPrivacySettings(context, present, .accountTimeout)

View File

@ -379,7 +379,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
let animation = contentNode.layer.makeAnimation(from: 0.0 as NSNumber, to: 1.0 as NSNumber, keyPath: "opacity", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.35)
animation.fillMode = .both
if !fastOut {
animation.beginTime = CACurrentMediaTime() + 0.1
animation.beginTime = contentNode.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.1
}
contentNode.layer.add(animation, forKey: "opacity")
}

View File

@ -584,7 +584,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1160215659] = { return Api.InputMessage.parse_inputMessageReplyTo($0) }
dict[-2037963464] = { return Api.InputMessage.parse_inputMessagePinned($0) }
dict[-1392895362] = { return Api.InputMessage.parse_inputMessageCallbackQuery($0) }
dict[2028213859] = { return Api.GroupCallParticipantVideo.parse_groupCallParticipantVideo($0) }
dict[1735736008] = { return Api.GroupCallParticipantVideo.parse_groupCallParticipantVideo($0) }
dict[-58224696] = { return Api.PhoneCallProtocol.parse_phoneCallProtocol($0) }
dict[-1237848657] = { return Api.StatsDateRangeDays.parse_statsDateRangeDays($0) }
dict[-275956116] = { return Api.messages.AffectedFoundMessages.parse_affectedFoundMessages($0) }

View File

@ -14974,13 +14974,13 @@ public extension Api {
}
public enum GroupCallParticipantVideo: TypeConstructorDescription {
case groupCallParticipantVideo(flags: Int32, endpoint: String, sourceGroups: [Api.GroupCallParticipantVideoSourceGroup])
case groupCallParticipantVideo(flags: Int32, endpoint: String, sourceGroups: [Api.GroupCallParticipantVideoSourceGroup], audioSource: Int32?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .groupCallParticipantVideo(let flags, let endpoint, let sourceGroups):
case .groupCallParticipantVideo(let flags, let endpoint, let sourceGroups, let audioSource):
if boxed {
buffer.appendInt32(2028213859)
buffer.appendInt32(1735736008)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(endpoint, buffer: buffer, boxed: false)
@ -14989,14 +14989,15 @@ public extension Api {
for item in sourceGroups {
item.serialize(buffer, true)
}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(audioSource!, buffer: buffer, boxed: false)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .groupCallParticipantVideo(let flags, let endpoint, let sourceGroups):
return ("groupCallParticipantVideo", [("flags", flags), ("endpoint", endpoint), ("sourceGroups", sourceGroups)])
case .groupCallParticipantVideo(let flags, let endpoint, let sourceGroups, let audioSource):
return ("groupCallParticipantVideo", [("flags", flags), ("endpoint", endpoint), ("sourceGroups", sourceGroups), ("audioSource", audioSource)])
}
}
@ -15009,11 +15010,14 @@ public extension Api {
if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallParticipantVideoSourceGroup.self)
}
var _4: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.GroupCallParticipantVideo.groupCallParticipantVideo(flags: _1!, endpoint: _2!, sourceGroups: _3!)
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.GroupCallParticipantVideo.groupCallParticipantVideo(flags: _1!, endpoint: _2!, sourceGroups: _3!, audioSource: _4)
}
else {
return nil

View File

@ -415,12 +415,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private var screencastBufferServerContext: IpcGroupCallBufferAppContext?
private var screencastCapturer: OngoingCallVideoCapturer?
//private var screencastIpcContext: IpcGroupCallAppContext?
private var ssrcMapping: [UInt32: PeerId] = [:]
private var requestedSsrcs = Set<UInt32>()
private var summaryInfoState = Promise<SummaryInfoState?>(nil)
private var summaryParticipantsState = Promise<SummaryParticipantsState?>(nil)
@ -887,7 +883,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
guard let strongSelf = self else {
return
}
strongSelf.genericCallContext?.addExternalAudioData(data: data)
strongSelf.screencastCallContext?.addExternalAudioData(data: data)
})
self.screencastStateDisposable = (screencastBufferServerContext.isActive
|> distinctUntilChanged
@ -1140,7 +1136,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
let rawAdminIds: Signal<Set<PeerId>, NoError>
if peerId.namespace == Namespaces.Peer.CloudChannel {
rawAdminIds = Signal { subscriber in
let (disposable, _) = accountContext.peerChannelMemberCategoriesContextsManager.admins(postbox: accountContext.account.postbox, network: accountContext.account.network, accountPeerId: accountContext.account.peerId, peerId: peerId, updated: { list in
let (disposable, _) = accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: accountContext.engine, postbox: accountContext.account.postbox, network: accountContext.account.network, accountPeerId: accountContext.account.peerId, peerId: peerId, updated: { list in
var peerIds = Set<PeerId>()
for item in list.list {
if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
@ -1429,7 +1425,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
let peerId = strongSelf.peerId
if strongSelf.peerId.namespace == Namespaces.Peer.CloudChannel {
peerAdminIds = Signal { subscriber in
let (disposable, _) = strongSelf.accountContext.peerChannelMemberCategoriesContextsManager.admins(postbox: strongSelf.accountContext.account.postbox, network: strongSelf.accountContext.account.network, accountPeerId: strongSelf.accountContext.account.peerId, peerId: peerId, updated: { list in
let (disposable, _) = strongSelf.accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: strongSelf.accountContext.engine, postbox: strongSelf.accountContext.account.postbox, network: strongSelf.accountContext.account.network, accountPeerId: strongSelf.accountContext.account.peerId, peerId: peerId, updated: { list in
var peerIds = Set<PeerId>()
for item in list.list {
if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
@ -1674,7 +1670,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
let rawAdminIds: Signal<Set<PeerId>, NoError>
if peerId.namespace == Namespaces.Peer.CloudChannel {
rawAdminIds = Signal { subscriber in
let (disposable, _) = accountContext.peerChannelMemberCategoriesContextsManager.admins(postbox: accountContext.account.postbox, network: accountContext.account.network, accountPeerId: accountContext.account.peerId, peerId: peerId, updated: { list in
let (disposable, _) = accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: accountContext.engine, postbox: accountContext.account.postbox, network: accountContext.account.network, accountPeerId: accountContext.account.peerId, peerId: peerId, updated: { list in
var peerIds = Set<PeerId>()
for item in list.list {
if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
@ -2050,6 +2046,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
videoDescription: nil
))
}
if let screencastSsrc = participant.presentationDescription?.audioSsrc {
if remainingSsrcs.contains(screencastSsrc) {
remainingSsrcs.remove(screencastSsrc)
result.append(OngoingGroupCallContext.MediaChannelDescription(
kind: .audio,
audioSsrc: screencastSsrc,
videoDescription: nil
))
}
}
}
}

View File

@ -1545,7 +1545,7 @@ public final class VoiceChatController: ViewController {
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_EditBioTitle, text: presentationData.strings.VoiceChat_EditBioText, placeholder: presentationData.strings.VoiceChat_EditBioPlaceholder, doneButtonTitle: presentationData.strings.VoiceChat_EditBioSave, value: entry.about, maxLength: maxBioLength, apply: { bio in
if let strongSelf = self, let bio = bio {
if peer.id.namespace == Namespaces.Peer.CloudUser {
let _ = (updateAbout(account: strongSelf.context.account, about: bio)
let _ = (strongSelf.context.engine.accountData.updateAbout(about: bio)
|> `catch` { _ -> Signal<Void, NoError> in
return .complete()
}).start()
@ -1575,7 +1575,7 @@ public final class VoiceChatController: ViewController {
Queue.mainQueue().after(0.1) {
let controller = voiceChatUserNameController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_ChangeNameTitle, firstNamePlaceholder: presentationData.strings.UserInfo_FirstNamePlaceholder, lastNamePlaceholder: presentationData.strings.UserInfo_LastNamePlaceholder, doneButtonTitle: presentationData.strings.VoiceChat_EditBioSave, firstName: peer.firstName, lastName: peer.lastName, maxLength: 128, apply: { firstAndLastName in
if let strongSelf = self, let (firstName, lastName) = firstAndLastName {
let _ = updateAccountPeerName(account: context.account, firstName: firstName, lastName: lastName).start()
let _ = context.engine.accountData.updateAccountPeerName(firstName: firstName, lastName: lastName).start()
strongSelf.presentUndoOverlay(content: .info(text: strongSelf.presentationData.strings.VoiceChat_EditNameSuccess), action: { _ in return false })
}
@ -1711,7 +1711,7 @@ public final class VoiceChatController: ViewController {
return
}
let _ = strongSelf.context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: strongSelf.context.account, peerId: strongSelf.call.peerId, memberId: peer.id, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max)).start()
let _ = strongSelf.context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: strongSelf.context.engine, peerId: strongSelf.call.peerId, memberId: peer.id, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max)).start()
strongSelf.call.removedPeer(peer.id)
strongSelf.presentUndoOverlay(content: .banned(text: strongSelf.presentationData.strings.VoiceChat_RemovedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).0), action: { _ in return false })

View File

@ -4,7 +4,7 @@ import SwiftSignalKit
import SyncCore
public func currentWebDocumentsHostDatacenterId(postbox: Postbox, isTestingEnvironment: Bool) -> Signal<Int32, NoError> {
func currentWebDocumentsHostDatacenterId(postbox: Postbox, isTestingEnvironment: Bool) -> Signal<Int32, NoError> {
return postbox.transaction { transaction -> Int32 in
if let entry = transaction.getPreferencesEntry(key: PreferencesKeys.remoteStorageConfiguration) as? RemoteStorageConfiguration {
return entry.webDocumentsHostDatacenterId

View File

@ -342,30 +342,6 @@ public enum PasswordRecoveryOption {
case email(pattern: String)
}
public func requestPasswordRecovery(account: UnauthorizedAccount) -> Signal<PasswordRecoveryOption, PasswordRecoveryRequestError> {
return account.network.request(Api.functions.auth.requestPasswordRecovery())
|> map(Optional.init)
|> `catch` { error -> Signal<Api.auth.PasswordRecovery?, PasswordRecoveryRequestError> in
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
return .fail(.limitExceeded)
} else if error.errorDescription.hasPrefix("PASSWORD_RECOVERY_NA") {
return .single(nil)
} else {
return .fail(.generic)
}
}
|> map { result -> PasswordRecoveryOption in
if let result = result {
switch result {
case let .passwordRecovery(emailPattern):
return .email(pattern: emailPattern)
}
} else {
return .none
}
}
}
public enum PasswordRecoveryError {
case invalidCode
case limitExceeded
@ -373,7 +349,7 @@ public enum PasswordRecoveryError {
case generic
}
public func checkPasswordRecoveryCode(network: Network, code: String) -> Signal<Never, PasswordRecoveryError> {
func _internal_checkPasswordRecoveryCode(network: Network, code: String) -> Signal<Never, PasswordRecoveryError> {
return network.request(Api.functions.auth.checkRecoveryPassword(code: code), automaticFloodWait: false)
|> mapError { error -> PasswordRecoveryError in
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
@ -389,12 +365,40 @@ public func checkPasswordRecoveryCode(network: Network, code: String) -> Signal<
}
}
func _internal_performPasswordRecovery(accountManager: AccountManager, account: UnauthorizedAccount, code: String, syncContacts: Bool, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<Void, PasswordRecoveryError> {
return _internal_twoStepAuthData(account.network)
public final class RecoveredAccountData {
let authorization: Api.auth.Authorization
init(authorization: Api.auth.Authorization) {
self.authorization = authorization
}
}
public func loginWithRecoveredAccountData(accountManager: AccountManager, account: UnauthorizedAccount, recoveredAccountData: RecoveredAccountData, syncContacts: Bool) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
switch recoveredAccountData.authorization {
case let .authorization(_, _, user):
let user = TelegramUser(user: user)
let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil)
initializedAppSettingsAfterLogin(transaction: transaction, appVersion: account.networkArguments.appVersion, syncContacts: syncContacts)
transaction.setState(state)
return accountManager.transaction { transaction -> Void in
switchToAuthorizedAccount(transaction: transaction, account: account)
}
case .authorizationSignUpRequired:
return .complete()
}
}
|> switchToLatest
|> ignoreValues
}
func _internal_performPasswordRecovery(network: Network, code: String, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<RecoveredAccountData, PasswordRecoveryError> {
return _internal_twoStepAuthData(network)
|> mapError { _ -> PasswordRecoveryError in
return .generic
}
|> mapToSignal { authData -> Signal<Void, PasswordRecoveryError> in
|> mapToSignal { authData -> Signal<RecoveredAccountData, PasswordRecoveryError> in
let newSettings: Api.account.PasswordInputSettings?
switch updatedPassword {
case .none:
@ -405,7 +409,7 @@ func _internal_performPasswordRecovery(accountManager: AccountManager, account:
flags |= (1 << 1)
}
guard let (updatedPasswordHash, updatedPasswordDerivation) = passwordUpdateKDF(encryptionProvider: account.network.encryptionProvider, password: password, derivation: authData.nextPasswordDerivation) else {
guard let (updatedPasswordHash, updatedPasswordDerivation) = passwordUpdateKDF(encryptionProvider: network.encryptionProvider, password: password, derivation: authData.nextPasswordDerivation) else {
return .fail(.invalidCode)
}
@ -416,7 +420,7 @@ func _internal_performPasswordRecovery(accountManager: AccountManager, account:
if newSettings != nil {
flags |= 1 << 0
}
return account.network.request(Api.functions.auth.recoverPassword(flags: flags, code: code, newSettings: newSettings), automaticFloodWait: false)
return network.request(Api.functions.auth.recoverPassword(flags: flags, code: code, newSettings: newSettings), automaticFloodWait: false)
|> mapError { error -> PasswordRecoveryError in
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
return .limitExceeded
@ -426,24 +430,8 @@ func _internal_performPasswordRecovery(accountManager: AccountManager, account:
return .invalidCode
}
}
|> mapToSignal { result -> Signal<Void, PasswordRecoveryError> in
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
switch result {
case let .authorization(_, _, user):
let user = TelegramUser(user: user)
let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil)
initializedAppSettingsAfterLogin(transaction: transaction, appVersion: account.networkArguments.appVersion, syncContacts: syncContacts)
transaction.setState(state)
return accountManager.transaction { transaction -> Void in
switchToAuthorizedAccount(transaction: transaction, account: account)
}
case .authorizationSignUpRequired:
return .complete()
}
}
|> switchToLatest
|> mapError { _ -> PasswordRecoveryError in }
|> mapToSignal { result -> Signal<RecoveredAccountData, PasswordRecoveryError> in
return .single(RecoveredAccountData(authorization: result))
}
}
}

View File

@ -1,82 +0,0 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
import MtProtoKit
import SyncCore
public func channelAdmins(account: Account, peerId: PeerId) -> Signal<[RenderedChannelParticipant], NoError> {
return account.postbox.transaction { transaction -> Signal<[RenderedChannelParticipant], NoError> in
if let peer = transaction.getPeer(peerId), let inputChannel = apiInputChannel(peer) {
return account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsAdmins, offset: 0, limit: 100, hash: 0))
|> retryRequest
|> mapToSignal { result -> Signal<[RenderedChannelParticipant], NoError> in
switch result {
case let .channelParticipants(count, participants, chats, users):
var items: [RenderedChannelParticipant] = []
var peers: [PeerId: Peer] = [:]
var presences:[PeerId: PeerPresence] = [:]
for user in users {
let peer = TelegramUser(user: user)
peers[peer.id] = peer
if let presence = TelegramUserPresence(apiUser: user) {
presences[peer.id] = presence
}
}
for participant in CachedChannelParticipants(apiParticipants: participants).participants {
if let peer = peers[participant.peerId] {
items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presences: presences))
}
}
return account.postbox.transaction { transaction -> [RenderedChannelParticipant] in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedParticipantsSummary(cachedData.participantsSummary.withUpdatedAdminCount(count))
} else {
return cachedData
}
})
return items
}
case .channelParticipantsNotModified:
return .single([])
}
}
} else {
return .single([])
}
} |> switchToLatest
}
public func channelAdminIds(postbox: Postbox, network: Network, peerId: PeerId, hash: Int32) -> Signal<[PeerId], NoError> {
return postbox.transaction { transaction in
if let peer = transaction.getPeer(peerId) as? TelegramChannel, case .group = peer.info, let apiChannel = apiInputChannel(peer) {
let api = Api.functions.channels.getParticipants(channel: apiChannel, filter: .channelParticipantsAdmins, offset: 0, limit: 100, hash: hash)
return network.request(api) |> retryRequest |> mapToSignal { result in
switch result {
case let .channelParticipants(_, participants, _, users):
let users = users.filter({ user in
return participants.contains(where: { participant in
switch participant {
case let .channelParticipantAdmin(_, userId, _, _, _, _, _):
return user.peerId.id._internalGetInt64Value() == userId
case let .channelParticipantCreator(_, userId, _, _):
return user.peerId.id._internalGetInt64Value() == userId
default:
return false
}
})
})
return .single(users.map({TelegramUser(user: $0).id}))
default:
return .complete()
}
}
}
return .complete()
} |> switchToLatest
}

View File

@ -1,84 +0,0 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
import MtProtoKit
import SyncCore
public struct RenderedChannelParticipant: Equatable {
public let participant: ChannelParticipant
public let peer: Peer
public let peers: [PeerId: Peer]
public let presences: [PeerId: PeerPresence]
public init(participant: ChannelParticipant, peer: Peer, peers: [PeerId: Peer] = [:], presences: [PeerId: PeerPresence] = [:]) {
self.participant = participant
self.peer = peer
self.peers = peers
self.presences = presences
}
public static func ==(lhs: RenderedChannelParticipant, rhs: RenderedChannelParticipant) -> Bool {
return lhs.participant == rhs.participant && lhs.peer.isEqual(rhs.peer)
}
}
func updateChannelParticipantsSummary(account: Account, peerId: PeerId) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
if let peer = transaction.getPeer(peerId), let inputChannel = apiInputChannel(peer) {
let admins = account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsAdmins, offset: 0, limit: 0, hash: 0))
let members = account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsRecent, offset: 0, limit: 0, hash: 0))
let banned = account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsBanned(q: ""), offset: 0, limit: 0, hash: 0))
let kicked = account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsKicked(q: ""), offset: 0, limit: 0, hash: 0))
return combineLatest(admins, members, banned, kicked)
|> mapToSignal { admins, members, banned, kicked -> Signal<Void, MTRpcError> in
return account.postbox.transaction { transaction -> Void in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let current = current as? CachedChannelData {
let adminCount: Int32
switch admins {
case let .channelParticipants(count, _, _, _):
adminCount = count
case .channelParticipantsNotModified:
assertionFailure()
adminCount = 0
}
let memberCount: Int32
switch members {
case let .channelParticipants(count, _, _, _):
memberCount = count
case .channelParticipantsNotModified:
assertionFailure()
memberCount = 0
}
let bannedCount: Int32
switch banned {
case let .channelParticipants(count, _, _, _):
bannedCount = count
case .channelParticipantsNotModified:
assertionFailure()
bannedCount = 0
}
let kickedCount: Int32
switch kicked {
case let .channelParticipants(count, _, _, _):
kickedCount = count
case .channelParticipantsNotModified:
assertionFailure()
kickedCount = 0
}
return current.withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: memberCount, adminCount: adminCount, bannedCount: bannedCount, kickedCount: kickedCount))
}
return current
})
} |> mapError { _ -> MTRpcError in return MTRpcError(errorCode: 0, errorDescription: "") }
}
|> `catch` { _ -> Signal<Void, NoError> in
return .complete()
}
} else {
return .complete()
}
} |> switchToLatest
}

View File

@ -461,7 +461,7 @@ final class MediaReferenceRevalidationContext {
func peerAvatars(postbox: Postbox, network: Network, background: Bool, peer: PeerReference) -> Signal<[TelegramPeerPhoto], RevalidateMediaReferenceError> {
return self.genericItem(key: .peerAvatars(peer: peer), background: background, request: { next, error in
return (requestPeerPhotos(postbox: postbox, network: network, peerId: peer.id)
return (_internal_requestPeerPhotos(postbox: postbox, network: network, peerId: peer.id)
|> mapError { _ -> RevalidateMediaReferenceError in
return .generic
}).start(next: { value in

View File

@ -855,6 +855,7 @@ public final class GroupCallParticipantsContext {
public var endpointId: String
public var ssrcGroups: [SsrcGroup]
public var audioSsrc: UInt32?
public var isPaused: Bool
}
@ -1527,6 +1528,9 @@ public final class GroupCallParticipantsContext {
if let ssrc = participant.ssrc {
existingSsrcs.insert(ssrc)
}
if let presentationDescription = participant.presentationDescription, let presentationAudioSsrc = presentationDescription.audioSsrc {
existingSsrcs.insert(presentationAudioSsrc)
}
}
for ssrc in ssrcs {
@ -2488,7 +2492,7 @@ extension GroupCallParticipantsContext.Participant {
private extension GroupCallParticipantsContext.Participant.VideoDescription {
init(_ apiVideo: Api.GroupCallParticipantVideo) {
switch apiVideo {
case let .groupCallParticipantVideo(flags, endpoint, sourceGroups):
case let .groupCallParticipantVideo(flags, endpoint, sourceGroups, audioSource):
var parsedSsrcGroups: [SsrcGroup] = []
for group in sourceGroups {
switch group {
@ -2497,7 +2501,7 @@ private extension GroupCallParticipantsContext.Participant.VideoDescription {
}
}
let isPaused = (flags & (1 << 0)) != 0
self.init(endpointId: endpoint, ssrcGroups: parsedSsrcGroups, isPaused: isPaused)
self.init(endpointId: endpoint, ssrcGroups: parsedSsrcGroups, audioSsrc: audioSource.flatMap(UInt32.init(bitPattern:)), isPaused: isPaused)
}
}
}

View File

@ -1,210 +0,0 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
import MtProtoKit
import SyncCore
public enum MessageReactionListCategory: Hashable {
case all
case reaction(String)
}
public final class MessageReactionListCategoryItem: Equatable {
public let peer: Peer
public let reaction: String
init(peer: Peer, reaction: String) {
self.peer = peer
self.reaction = reaction
}
public static func ==(lhs: MessageReactionListCategoryItem, rhs: MessageReactionListCategoryItem) -> Bool {
if lhs.peer.id != rhs.peer.id {
return false
}
if lhs.reaction != rhs.reaction {
return false
}
return true
}
}
public struct MessageReactionListCategoryState: Equatable {
public var count: Int
public var completed: Bool
public var items: [MessageReactionListCategoryItem]
public var loadingMore: Bool
fileprivate var nextOffset: String?
}
private enum LoadReactionsError {
case generic
}
private final class MessageReactionCategoryContext {
private let postbox: Postbox
private let network: Network
private let messageId: MessageId
private let category: MessageReactionListCategory
private var state: MessageReactionListCategoryState
var statePromise: ValuePromise<MessageReactionListCategoryState>
private let loadingDisposable = MetaDisposable()
init(postbox: Postbox, network: Network, messageId: MessageId, category: MessageReactionListCategory, initialState: MessageReactionListCategoryState) {
self.postbox = postbox
self.network = network
self.messageId = messageId
self.category = category
self.state = initialState
self.statePromise = ValuePromise(initialState)
}
deinit {
self.loadingDisposable.dispose()
}
func loadMore() {
if self.state.completed || self.state.loadingMore {
return
}
/*self.state.loadingMore = true
self.statePromise.set(self.state)
var flags: Int32 = 0
var reaction: String?
switch self.category {
case .all:
break
case let .reaction(value):
flags |= 1 << 0
reaction = value
}
let messageId = self.messageId
let offset = self.state.nextOffset
var request = self.postbox.transaction { transaction -> Api.InputPeer? in
let inputPeer = transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
return inputPeer
}
|> castError(LoadReactionsError.self)
|> mapToSignal { inputPeer -> Signal<Api.messages.MessageReactionsList, LoadReactionsError> in
guard let inputPeer = inputPeer else {
return .fail(.generic)
}
return self.network.request(Api.functions.messages.getMessageReactionsList(flags: flags, peer: inputPeer, id: messageId.id, reaction: reaction, offset: offset, limit: 64))
|> mapError { _ -> LoadReactionsError in
return .generic
}
}
//#if DEBUG
//request = request |> delay(1.0, queue: .mainQueue())
//#endif
self.loadingDisposable.set((request
|> deliverOnMainQueue).start(next: { [weak self] result in
guard let strongSelf = self else {
return
}
let currentState = strongSelf.state
let _ = (strongSelf.postbox.transaction { transaction -> MessageReactionListCategoryState in
var mergedItems = currentState.items
var currentIds = Set(mergedItems.lazy.map { $0.peer.id })
switch result {
case let .messageReactionsList(_, count, reactions, users, nextOffset):
var peers: [Peer] = []
for user in users {
let parsedUser = TelegramUser(user: user)
peers.append(parsedUser)
}
updatePeers(transaction: transaction, peers: peers, update: { _, updated in updated })
for reaction in reactions {
switch reaction {
case let .messageUserReaction(userId, reaction):
if let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)) {
if !currentIds.contains(peer.id) {
currentIds.insert(peer.id)
mergedItems.append(MessageReactionListCategoryItem(peer: peer, reaction: reaction))
}
}
}
}
return MessageReactionListCategoryState(count: max(mergedItems.count, Int(count)), completed: nextOffset == nil, items: mergedItems, loadingMore: false, nextOffset: nextOffset)
}
}
|> deliverOnMainQueue).start(next: { state in
guard let strongSelf = self else {
return
}
strongSelf.state = state
strongSelf.statePromise.set(state)
})
}, error: { _ in
}))*/
}
}
public struct MessageReactionListState: Equatable {
public var states: [(MessageReactionListCategory, MessageReactionListCategoryState)]
public static func ==(lhs: MessageReactionListState, rhs: MessageReactionListState) -> Bool {
if lhs.states.count != rhs.states.count {
return false
}
for i in 0 ..< lhs.states.count {
if lhs.states[i].0 != rhs.states[i].0 {
return false
}
if lhs.states[i].1 != rhs.states[i].1 {
return false
}
}
return true
}
}
public final class MessageReactionListContext {
private let postbox: Postbox
private let network: Network
private var categoryContexts: [MessageReactionListCategory: MessageReactionCategoryContext] = [:]
private let _state = Promise<MessageReactionListState>()
public var state: Signal<MessageReactionListState, NoError> {
return self._state.get()
}
public init(postbox: Postbox, network: Network, messageId: MessageId, initialReactions: [MessageReaction]) {
self.postbox = postbox
self.network = network
var allState = MessageReactionListCategoryState(count: 0, completed: false, items: [], loadingMore: false, nextOffset: nil)
var signals: [Signal<(MessageReactionListCategory, MessageReactionListCategoryState), NoError>] = []
for reaction in initialReactions {
allState.count += Int(reaction.count)
let context = MessageReactionCategoryContext(postbox: postbox, network: network, messageId: messageId, category: .reaction(reaction.value), initialState: MessageReactionListCategoryState(count: Int(reaction.count), completed: false, items: [], loadingMore: false, nextOffset: nil))
signals.append(context.statePromise.get() |> map { value -> (MessageReactionListCategory, MessageReactionListCategoryState) in
return (.reaction(reaction.value), value)
})
self.categoryContexts[.reaction(reaction.value)] = context
context.loadMore()
}
let allContext = MessageReactionCategoryContext(postbox: postbox, network: network, messageId: messageId, category: .all, initialState: allState)
signals.insert(allContext.statePromise.get() |> map { value -> (MessageReactionListCategory, MessageReactionListCategoryState) in
return (.all, value)
}, at: 0)
self.categoryContexts[.all] = allContext
self._state.set(combineLatest(queue: .mainQueue(), signals)
|> map { states in
return MessageReactionListState(states: states)
})
allContext.loadMore()
}
public func loadMore(category: MessageReactionListCategory) {
self.categoryContexts[category]?.loadMore()
}
}

View File

@ -1,40 +0,0 @@
import Foundation
import Postbox
import SwiftSignalKit
import SyncCore
private struct PeerParticipants: Equatable {
let peers: [Peer]
static func ==(lhs: PeerParticipants, rhs: PeerParticipants) -> Bool {
if lhs.peers.count != rhs.peers.count {
return false
}
for i in 0 ..< lhs.peers.count {
if !lhs.peers[i].isEqual(rhs.peers[i]) {
return false
}
}
return true
}
}
public func peerParticipants(postbox: Postbox, id: PeerId) -> Signal<[Peer], NoError> {
return postbox.peerView(id: id) |> map { view -> PeerParticipants in
if let cachedGroupData = view.cachedData as? CachedGroupData, let participants = cachedGroupData.participants {
var peers: [Peer] = []
for participant in participants.participants {
if let peer = view.peers[participant.peerId] {
peers.append(peer)
}
}
return PeerParticipants(peers: peers)
} else {
return PeerParticipants(peers: [])
}
}
|> distinctUntilChanged |> map { participants in
return participants.peers
}
}

View File

@ -65,5 +65,5 @@ public func unarchiveAutomaticallyArchivedPeer(account: Account, peerId: PeerId)
}
|> deliverOnMainQueue).start()
let _ = updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: nil).start()
let _ = _internal_updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: nil).start()
}

View File

@ -37,7 +37,7 @@ public enum RequestChangeAccountPhoneNumberVerificationError {
case generic
}
public func requestChangeAccountPhoneNumberVerification(account: Account, phoneNumber: String) -> Signal<ChangeAccountPhoneNumberData, RequestChangeAccountPhoneNumberVerificationError> {
func _internal_requestChangeAccountPhoneNumberVerification(account: Account, phoneNumber: String) -> Signal<ChangeAccountPhoneNumberData, RequestChangeAccountPhoneNumberVerificationError> {
return account.network.request(Api.functions.account.sendChangePhoneCode(phoneNumber: phoneNumber, settings: .codeSettings(flags: 0)), automaticFloodWait: false)
|> mapError { error -> RequestChangeAccountPhoneNumberVerificationError in
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
@ -64,7 +64,7 @@ public func requestChangeAccountPhoneNumberVerification(account: Account, phoneN
}
}
public func requestNextChangeAccountPhoneNumberVerification(account: Account, phoneNumber: String, phoneCodeHash: String) -> Signal<ChangeAccountPhoneNumberData, RequestChangeAccountPhoneNumberVerificationError> {
func _internal_requestNextChangeAccountPhoneNumberVerification(account: Account, phoneNumber: String, phoneCodeHash: String) -> Signal<ChangeAccountPhoneNumberData, RequestChangeAccountPhoneNumberVerificationError> {
return account.network.request(Api.functions.auth.resendCode(phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash), automaticFloodWait: false)
|> mapError { error -> RequestChangeAccountPhoneNumberVerificationError in
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
@ -96,7 +96,7 @@ public enum ChangeAccountPhoneNumberError {
case limitExceeded
}
public func requestChangeAccountPhoneNumber(account: Account, phoneNumber: String, phoneCodeHash: String, phoneCode: String) -> Signal<Void, ChangeAccountPhoneNumberError> {
func _internal_requestChangeAccountPhoneNumber(account: Account, phoneNumber: String, phoneCodeHash: String, phoneCode: String) -> Signal<Void, ChangeAccountPhoneNumberError> {
return account.network.request(Api.functions.account.changePhone(phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash, phoneCode: phoneCode), automaticFloodWait: false)
|> mapError { error -> ChangeAccountPhoneNumberError in
if error.errorDescription.hasPrefix("FLOOD_WAIT") {

View File

@ -15,5 +15,25 @@ public extension TelegramEngine {
public func resetAccountDueTermsOfService() -> Signal<Void, NoError> {
return _internal_resetAccountDueTermsOfService(network: self.account.network)
}
public func requestChangeAccountPhoneNumberVerification(phoneNumber: String) -> Signal<ChangeAccountPhoneNumberData, RequestChangeAccountPhoneNumberVerificationError> {
return _internal_requestChangeAccountPhoneNumberVerification(account: self.account, phoneNumber: phoneNumber)
}
public func requestNextChangeAccountPhoneNumberVerification(phoneNumber: String, phoneCodeHash: String) -> Signal<ChangeAccountPhoneNumberData, RequestChangeAccountPhoneNumberVerificationError> {
return _internal_requestNextChangeAccountPhoneNumberVerification(account: self.account, phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash)
}
public func requestChangeAccountPhoneNumber(phoneNumber: String, phoneCodeHash: String, phoneCode: String) -> Signal<Void, ChangeAccountPhoneNumberError> {
return _internal_requestChangeAccountPhoneNumber(account: self.account, phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash, phoneCode: phoneCode)
}
public func updateAccountPeerName(firstName: String, lastName: String) -> Signal<Void, NoError> {
return _internal_updateAccountPeerName(account: self.account, firstName: firstName, lastName: lastName)
}
public func updateAbout(about: String?) -> Signal<Void, UpdateAboutError> {
return _internal_updateAbout(account: self.account, about: about)
}
}
}

View File

@ -6,7 +6,7 @@ import MtProtoKit
import SyncCore
public func updateAccountPeerName(account: Account, firstName: String, lastName: String) -> Signal<Void, NoError> {
func _internal_updateAccountPeerName(account: Account, firstName: String, lastName: String) -> Signal<Void, NoError> {
return account.network.request(Api.functions.account.updateProfile(flags: (1 << 0) | (1 << 1), firstName: firstName, lastName: lastName, about: nil))
|> map { result -> Api.User? in
return result
@ -30,7 +30,7 @@ public enum UpdateAboutError {
}
public func updateAbout(account: Account, about: String?) -> Signal<Void, UpdateAboutError> {
func _internal_updateAbout(account: Account, about: String?) -> Signal<Void, UpdateAboutError> {
return account.network.request(Api.functions.account.updateProfile(flags: about == nil ? 0 : (1 << 2), firstName: nil, lastName: nil, about: about))
|> mapError { _ -> UpdateAboutError in
return .generic

View File

@ -18,7 +18,7 @@ public enum RequestCancelAccountResetDataError {
case generic
}
public func requestCancelAccountResetData(network: Network, hash: String) -> Signal<CancelAccountResetData, RequestCancelAccountResetDataError> {
func _internal_requestCancelAccountResetData(network: Network, hash: String) -> Signal<CancelAccountResetData, RequestCancelAccountResetDataError> {
return network.request(Api.functions.account.sendConfirmPhoneCode(hash: hash, settings: .codeSettings(flags: 0)), automaticFloodWait: false)
|> mapError { error -> RequestCancelAccountResetDataError in
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
@ -39,7 +39,7 @@ public func requestCancelAccountResetData(network: Network, hash: String) -> Sig
}
}
public func requestNextCancelAccountResetOption(network: Network, phoneNumber: String, phoneCodeHash: String) -> Signal<CancelAccountResetData, RequestCancelAccountResetDataError> {
func _internal_requestNextCancelAccountResetOption(network: Network, phoneNumber: String, phoneCodeHash: String) -> Signal<CancelAccountResetData, RequestCancelAccountResetDataError> {
return network.request(Api.functions.auth.resendCode(phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash), automaticFloodWait: false)
|> mapError { error -> RequestCancelAccountResetDataError in
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
@ -67,7 +67,7 @@ public enum CancelAccountResetError {
case limitExceeded
}
public func requestCancelAccountReset(network: Network, phoneCodeHash: String, phoneCode: String) -> Signal<Never, CancelAccountResetError> {
func _internal_requestCancelAccountReset(network: Network, phoneCodeHash: String, phoneCode: String) -> Signal<Never, CancelAccountResetError> {
return network.request(Api.functions.account.confirmPhone(phoneCodeHash: phoneCodeHash, phoneCode: phoneCode))
|> mapError { error -> CancelAccountResetError in
if error.errorDescription.hasPrefix("FLOOD_WAIT") {

View File

@ -24,8 +24,16 @@ public extension TelegramEngineUnauthorized {
return _internal_updateTwoStepVerificationPassword(network: self.account.network, currentPassword: currentPassword, updatedPassword: updatedPassword)
}
public func performPasswordRecovery(accountManager: AccountManager, code: String, syncContacts: Bool, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<Void, PasswordRecoveryError> {
return _internal_performPasswordRecovery(accountManager: accountManager, account: self.account, code: code, syncContacts: syncContacts, updatedPassword: updatedPassword)
public func requestTwoStepVerificationPasswordRecoveryCode() -> Signal<String, RequestTwoStepVerificationPasswordRecoveryCodeError> {
return _internal_requestTwoStepVerificationPasswordRecoveryCode(network: self.account.network)
}
public func checkPasswordRecoveryCode(code: String) -> Signal<Never, PasswordRecoveryError> {
return _internal_checkPasswordRecoveryCode(network: self.account.network, code: code)
}
public func performPasswordRecovery(code: String, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<RecoveredAccountData, PasswordRecoveryError> {
return _internal_performPasswordRecovery(network: self.account.network, code: code, updatedPassword: updatedPassword)
}
public func resendTwoStepRecoveryEmail() -> Signal<Never, ResendTwoStepRecoveryEmailError> {
@ -90,8 +98,8 @@ public extension TelegramEngine {
return _internal_requestTwoStepVerificationPasswordRecoveryCode(network: self.account.network)
}
public func recoverTwoStepVerificationPassword(code: String) -> Signal<Void, RecoverTwoStepVerificationPasswordError> {
return _internal_recoverTwoStepVerificationPassword(network: self.account.network, code: code)
public func performPasswordRecovery(code: String, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<RecoveredAccountData, PasswordRecoveryError> {
return _internal_performPasswordRecovery(network: self.account.network, code: code, updatedPassword: updatedPassword)
}
public func cachedTwoStepPasswordToken() -> Signal<TemporaryTwoStepPasswordToken?, NoError> {
@ -106,6 +114,10 @@ public extension TelegramEngine {
return _internal_requestTemporaryTwoStepPasswordToken(account: self.account, password: password, period: period, requiresBiometrics: requiresBiometrics)
}
public func checkPasswordRecoveryCode(code: String) -> Signal<Never, PasswordRecoveryError> {
return _internal_checkPasswordRecoveryCode(network: self.account.network, code: code)
}
public func requestTwoStepPasswordReset() -> Signal<RequestTwoStepPasswordResetResult, NoError> {
return _internal_requestTwoStepPasswordReset(network: self.account.network)
}
@ -113,6 +125,18 @@ public extension TelegramEngine {
public func declineTwoStepPasswordReset() -> Signal<Never, NoError> {
return _internal_declineTwoStepPasswordReset(network: self.account.network)
}
public func requestCancelAccountResetData(hash: String) -> Signal<CancelAccountResetData, RequestCancelAccountResetDataError> {
return _internal_requestCancelAccountResetData(network: self.account.network, hash: hash)
}
public func requestNextCancelAccountResetOption(phoneNumber: String, phoneCodeHash: String) -> Signal<CancelAccountResetData, RequestCancelAccountResetDataError> {
return _internal_requestNextCancelAccountResetOption(network: self.account.network, phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash)
}
public func requestCancelAccountReset(phoneCodeHash: String, phoneCode: String) -> Signal<Never, CancelAccountResetError> {
return _internal_requestCancelAccountReset(network: self.account.network, phoneCodeHash: phoneCodeHash, phoneCode: phoneCode)
}
}
}
@ -142,12 +166,21 @@ public extension SomeTelegramEngine {
}
}
public func resendTwoStepRecoveryEmail() -> Signal<Never, ResendTwoStepRecoveryEmailError> {
public func requestTwoStepVerificationPasswordRecoveryCode() -> Signal<String, RequestTwoStepVerificationPasswordRecoveryCodeError> {
switch self.engine {
case let .authorized(engine):
return engine.auth.resendTwoStepRecoveryEmail()
return engine.auth.requestTwoStepVerificationPasswordRecoveryCode()
case let .unauthorized(engine):
return engine.auth.resendTwoStepRecoveryEmail()
return engine.auth.requestTwoStepVerificationPasswordRecoveryCode()
}
}
public func checkPasswordRecoveryCode(code: String) -> Signal<Never, PasswordRecoveryError> {
switch self.engine {
case let .authorized(engine):
return engine.auth.checkPasswordRecoveryCode(code: code)
case let .unauthorized(engine):
return engine.auth.checkPasswordRecoveryCode(code: code)
}
}
}

View File

@ -312,19 +312,26 @@ func _internal_updateTwoStepVerificationEmail(network: Network, currentPassword:
public enum RequestTwoStepVerificationPasswordRecoveryCodeError {
case generic
case limitExceeded
}
func _internal_requestTwoStepVerificationPasswordRecoveryCode(network: Network) -> Signal<String, RequestTwoStepVerificationPasswordRecoveryCodeError> {
return network.request(Api.functions.auth.requestPasswordRecovery(), automaticFloodWait: false)
|> mapError { _ -> RequestTwoStepVerificationPasswordRecoveryCodeError in
|> mapError { error -> RequestTwoStepVerificationPasswordRecoveryCodeError in
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
return .limitExceeded
} else if error.errorDescription.hasPrefix("PASSWORD_RECOVERY_NA") {
return .generic
} else {
return .generic
}
|> map { result -> String in
switch result {
case let .passwordRecovery(emailPattern):
return emailPattern
}
}
|> map { result -> String in
switch result {
case let .passwordRecovery(emailPattern):
return emailPattern
}
}
}
public enum RecoverTwoStepVerificationPasswordError {
@ -334,35 +341,6 @@ public enum RecoverTwoStepVerificationPasswordError {
case invalidCode
}
func _internal_recoverTwoStepVerificationPassword(network: Network, code: String) -> Signal<Void, RecoverTwoStepVerificationPasswordError> {
return _internal_twoStepAuthData(network)
|> mapError { _ -> RecoverTwoStepVerificationPasswordError in
return .generic
}
|> mapToSignal { authData -> Signal<Void, RecoverTwoStepVerificationPasswordError> in
var flags: Int32 = (1 << 1)
if authData.currentPasswordDerivation != nil {
flags |= (1 << 0)
}
return network.request(Api.functions.auth.recoverPassword(flags: 0, code: code, newSettings: nil), automaticFloodWait: false)
|> mapError { error -> RecoverTwoStepVerificationPasswordError in
if error.errorDescription.hasPrefix("FLOOD_WAIT_") {
return .limitExceeded
} else if error.errorDescription == "PASSWORD_RECOVERY_EXPIRED" {
return .codeExpired
} else if error.errorDescription == "CODE_INVALID" {
return .invalidCode
} else {
return .generic
}
}
|> mapToSignal { _ -> Signal<Void, RecoverTwoStepVerificationPasswordError> in
return .complete()
}
}
}
func _internal_cachedTwoStepPasswordToken(postbox: Postbox) -> Signal<TemporaryTwoStepPasswordToken?, NoError> {
return postbox.transaction { transaction -> TemporaryTwoStepPasswordToken? in
let key = ValueBoxKey(length: 1)

View File

@ -105,7 +105,7 @@ func syncContactsOnce(network: Network, postbox: Postbox, accountPeerId: PeerId)
return appliedUpdatedPeers
}
public func deleteContactPeerInteractively(account: Account, peerId: PeerId) -> Signal<Never, NoError> {
func _internal_deleteContactPeerInteractively(account: Account, peerId: PeerId) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> Signal<Never, NoError> in
if let peer = transaction.getPeer(peerId), let inputUser = apiInputUser(peer) {
return account.network.request(Api.functions.contacts.deleteContacts(id: [inputUser]))
@ -133,7 +133,7 @@ public func deleteContactPeerInteractively(account: Account, peerId: PeerId) ->
|> switchToLatest
}
public func deleteAllContacts(account: Account) -> Signal<Never, NoError> {
func _internal_deleteAllContacts(account: Account) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> [Api.InputUser] in
return transaction.getContactPeerIds().compactMap(transaction.getPeer).compactMap({ apiInputUser($0) }).compactMap({ $0 })
}
@ -166,7 +166,7 @@ public func deleteAllContacts(account: Account) -> Signal<Never, NoError> {
}
}
public func resetSavedContacts(network: Network) -> Signal<Void, NoError> {
func _internal_resetSavedContacts(network: Network) -> Signal<Void, NoError> {
return network.request(Api.functions.contacts.resetSaved())
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .single(.boolFalse)

View File

@ -4,8 +4,7 @@ import SwiftSignalKit
import SyncCore
public func importContact(account: Account, firstName: String, lastName: String, phoneNumber: String) -> Signal<PeerId?, NoError> {
func _internal_importContact(account: Account, firstName: String, lastName: String, phoneNumber: String) -> Signal<PeerId?, NoError> {
let input = Api.InputContact.inputPhoneContact(clientId: 1, phone: phoneNumber, firstName: firstName, lastName: lastName)
return account.network.request(Api.functions.contacts.importContacts(contacts: [input]))
@ -42,7 +41,7 @@ public enum AddContactError {
case generic
}
public func addContactInteractively(account: Account, peerId: PeerId, firstName: String, lastName: String, phoneNumber: String, addToPrivacyExceptions: Bool) -> Signal<Never, AddContactError> {
func _internal_addContactInteractively(account: Account, peerId: PeerId, firstName: String, lastName: String, phoneNumber: String, addToPrivacyExceptions: Bool) -> Signal<Never, AddContactError> {
return account.postbox.transaction { transaction -> (Api.InputUser, String)? in
if let user = transaction.getPeer(peerId) as? TelegramUser, let inputUser = apiInputUser(user) {
return (inputUser, user.phone == nil ? phoneNumber : "")
@ -99,7 +98,7 @@ public enum AcceptAndShareContactError {
case generic
}
public func acceptAndShareContact(account: Account, peerId: PeerId) -> Signal<Never, AcceptAndShareContactError> {
func _internal_acceptAndShareContact(account: Account, peerId: PeerId) -> Signal<Never, AcceptAndShareContactError> {
return account.postbox.transaction { transaction -> Api.InputUser? in
return transaction.getPeer(peerId).flatMap(apiInputUser)
}

View File

@ -48,7 +48,7 @@ enum TelegramDeviceContactImportIdentifier: Hashable, Comparable, Equatable {
}
}
public func deviceContactsImportedByCount(postbox: Postbox, contacts: [(String, [DeviceContactNormalizedPhoneNumber])]) -> Signal<[String: Int32], NoError> {
func _internal_deviceContactsImportedByCount(postbox: Postbox, contacts: [(String, [DeviceContactNormalizedPhoneNumber])]) -> Signal<[String: Int32], NoError> {
return postbox.transaction { transaction -> [String: Int32] in
var result: [String: Int32] = [:]
for (id, numbers) in contacts {

View File

@ -0,0 +1,44 @@
import SwiftSignalKit
import Postbox
public extension TelegramEngine {
final class Contacts {
private let account: Account
init(account: Account) {
self.account = account
}
public func deleteContactPeerInteractively(peerId: PeerId) -> Signal<Never, NoError> {
return _internal_deleteContactPeerInteractively(account: self.account, peerId: peerId)
}
public func deleteAllContacts() -> Signal<Never, NoError> {
return _internal_deleteAllContacts(account: self.account)
}
public func resetSavedContacts() -> Signal<Void, NoError> {
return _internal_resetSavedContacts(network: self.account.network)
}
public func updateContactName(peerId: PeerId, firstName: String, lastName: String) -> Signal<Void, UpdateContactNameError> {
return _internal_updateContactName(account: self.account, peerId: peerId, firstName: firstName, lastName: lastName)
}
public func deviceContactsImportedByCount(contacts: [(String, [DeviceContactNormalizedPhoneNumber])]) -> Signal<[String: Int32], NoError> {
return _internal_deviceContactsImportedByCount(postbox: self.account.postbox, contacts: contacts)
}
public func importContact(firstName: String, lastName: String, phoneNumber: String) -> Signal<PeerId?, NoError> {
return _internal_importContact(account: self.account, firstName: firstName, lastName: lastName, phoneNumber: phoneNumber)
}
public func addContactInteractively(peerId: PeerId, firstName: String, lastName: String, phoneNumber: String, addToPrivacyExceptions: Bool) -> Signal<Never, AddContactError> {
return _internal_addContactInteractively(account: self.account, peerId: peerId, firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, addToPrivacyExceptions: addToPrivacyExceptions)
}
public func acceptAndShareContact(peerId: PeerId) -> Signal<Never, AcceptAndShareContactError> {
return _internal_acceptAndShareContact(account: self.account, peerId: peerId)
}
}
}

View File

@ -10,7 +10,7 @@ public enum UpdateContactNameError {
case generic
}
public func updateContactName(account: Account, peerId: PeerId, firstName: String, lastName: String) -> Signal<Void, UpdateContactNameError> {
func _internal_updateContactName(account: Account, peerId: PeerId, firstName: String, lastName: String) -> Signal<Void, UpdateContactNameError> {
return account.postbox.transaction { transaction -> Signal<Void, UpdateContactNameError> in
if let peer = transaction.getPeer(peerId) as? TelegramUser, let inputUser = apiInputUser(peer) {
return account.network.request(Api.functions.contacts.addContact(flags: 0, id: inputUser, firstName: firstName, lastName: lastName, phone: ""))

View File

@ -11,7 +11,7 @@ public enum GetMessagesStrategy {
case cloud
}
public func getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Postbox, network: Network, accountPeerId: PeerId, strategy: GetMessagesStrategy = .cloud) -> Signal <[Message], NoError> {
func _internal_getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Postbox, network: Network, accountPeerId: PeerId, strategy: GetMessagesStrategy = .cloud) -> Signal <[Message], NoError> {
let postboxSignal = postbox.transaction { transaction -> ([Message], Set<MessageId>, SimpleDictionary<PeerId, Peer>) in
var ids = messageIds

View File

@ -6,7 +6,7 @@ import MtProtoKit
import SyncCore
public func markAllChatsAsRead(postbox: Postbox, network: Network, stateManager: AccountStateManager) -> Signal<Void, NoError> {
func _internal_markAllChatsAsRead(postbox: Postbox, network: Network, stateManager: AccountStateManager) -> Signal<Void, NoError> {
return network.request(Api.functions.messages.getDialogUnreadMarks())
|> map(Optional.init)
|> `catch` { _ -> Signal<[Api.DialogPeer]?, NoError> in

View File

@ -453,7 +453,7 @@ public enum FetchChannelReplyThreadMessageError {
case generic
}
public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageId, atMessageId: MessageId?) -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> {
func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: MessageId, atMessageId: MessageId?) -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> {
return account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
}

View File

@ -6,7 +6,7 @@ import MtProtoKit
import SyncCore
public func requestStartBot(account: Account, botPeerId: PeerId, payload: String?) -> Signal<Void, NoError> {
func _internal_requestStartBot(account: Account, botPeerId: PeerId, payload: String?) -> Signal<Void, NoError> {
if let payload = payload, !payload.isEmpty {
return account.postbox.loadedPeerWithId(botPeerId)
|> mapToSignal { botPeer -> Signal<Void, NoError> in
@ -41,7 +41,7 @@ public enum StartBotInGroupResult {
case channelParticipant(RenderedChannelParticipant)
}
public func requestStartBotInGroup(account: Account, botPeerId: PeerId, groupPeerId: PeerId, payload: String?) -> Signal<StartBotInGroupResult, RequestStartBotInGroupError> {
func _internal_requestStartBotInGroup(account: Account, botPeerId: PeerId, groupPeerId: PeerId, payload: String?) -> Signal<StartBotInGroupResult, RequestStartBotInGroupError> {
return account.postbox.transaction { transaction -> (Peer?, Peer?) in
return (transaction.getPeer(botPeerId), transaction.getPeer(groupPeerId))
}

View File

@ -94,5 +94,33 @@ public extension TelegramEngine {
public func forwardGameWithScore(messageId: MessageId, to peerId: PeerId) -> Signal<Void, NoError> {
return _internal_forwardGameWithScore(account: self.account, messageId: messageId, to: peerId)
}
public func requestUpdatePinnedMessage(peerId: PeerId, update: PinnedMessageUpdate) -> Signal<Void, UpdatePinnedMessageError> {
return _internal_requestUpdatePinnedMessage(account: self.account, peerId: peerId, update: update)
}
public func requestUnpinAllMessages(peerId: PeerId) -> Signal<Never, UpdatePinnedMessageError> {
return _internal_requestUnpinAllMessages(account: self.account, peerId: peerId)
}
public func fetchChannelReplyThreadMessage(messageId: MessageId, atMessageId: MessageId?) -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> {
return _internal_fetchChannelReplyThreadMessage(account: self.account, messageId: messageId, atMessageId: atMessageId)
}
public func requestStartBot(botPeerId: PeerId, payload: String?) -> Signal<Void, NoError> {
return _internal_requestStartBot(account: self.account, botPeerId: botPeerId, payload: payload)
}
public func requestStartBotInGroup(botPeerId: PeerId, groupPeerId: PeerId, payload: String?) -> Signal<StartBotInGroupResult, RequestStartBotInGroupError> {
return _internal_requestStartBotInGroup(account: self.account, botPeerId: botPeerId, groupPeerId: groupPeerId, payload: payload)
}
public func markAllChatsAsRead() -> Signal<Void, NoError> {
return _internal_markAllChatsAsRead(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager)
}
public func getMessagesLoadIfNecessary(_ messageIds: [MessageId], strategy: GetMessagesStrategy = .cloud) -> Signal <[Message], NoError> {
return _internal_getMessagesLoadIfNecessary(messageIds, postbox: self.account.postbox, network: self.account.network, accountPeerId: self.account.peerId, strategy: strategy)
}
}
}

View File

@ -15,7 +15,7 @@ public enum PinnedMessageUpdate {
case clear(id: MessageId)
}
public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update: PinnedMessageUpdate) -> Signal<Void, UpdatePinnedMessageError> {
func _internal_requestUpdatePinnedMessage(account: Account, peerId: PeerId, update: PinnedMessageUpdate) -> Signal<Void, UpdatePinnedMessageError> {
return account.postbox.transaction { transaction -> (Peer?, CachedPeerData?) in
return (transaction.getPeer(peerId), transaction.getPeerCachedData(peerId: peerId))
}
@ -112,7 +112,7 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
}
}
public func requestUnpinAllMessages(account: Account, peerId: PeerId) -> Signal<Never, UpdatePinnedMessageError> {
func _internal_requestUnpinAllMessages(account: Account, peerId: PeerId) -> Signal<Never, UpdatePinnedMessageError> {
return account.postbox.transaction { transaction -> (Peer?, CachedPeerData?) in
return (transaction.getPeer(peerId), transaction.getPeerCachedData(peerId: peerId))
}

View File

@ -4,7 +4,7 @@ import SwiftSignalKit
import SyncCore
public func togglePeerMuted(account: Account, peerId: PeerId) -> Signal<Void, NoError> {
func _internal_togglePeerMuted(account: Account, peerId: PeerId) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Void in
if let peer = transaction.getPeer(peerId) {
var notificationPeerId = peerId
@ -39,13 +39,13 @@ public func togglePeerMuted(account: Account, peerId: PeerId) -> Signal<Void, No
}
}
public func updatePeerMuteSetting(account: Account, peerId: PeerId, muteInterval: Int32?) -> Signal<Void, NoError> {
func _internal_updatePeerMuteSetting(account: Account, peerId: PeerId, muteInterval: Int32?) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Void in
updatePeerMuteSetting(transaction: transaction, peerId: peerId, muteInterval: muteInterval)
}
}
public func updatePeerMuteSetting(transaction: Transaction, peerId: PeerId, muteInterval: Int32?) {
func updatePeerMuteSetting(transaction: Transaction, peerId: PeerId, muteInterval: Int32?) {
if let peer = transaction.getPeer(peerId) {
var notificationPeerId = peerId
if let associatedPeerId = peer.associatedPeerId {
@ -82,13 +82,13 @@ public func updatePeerMuteSetting(transaction: Transaction, peerId: PeerId, mute
}
}
public func updatePeerDisplayPreviewsSetting(account: Account, peerId: PeerId, displayPreviews: PeerNotificationDisplayPreviews) -> Signal<Void, NoError> {
func _internal_updatePeerDisplayPreviewsSetting(account: Account, peerId: PeerId, displayPreviews: PeerNotificationDisplayPreviews) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Void in
updatePeerDisplayPreviewsSetting(transaction: transaction, peerId: peerId, displayPreviews: displayPreviews)
}
}
public func updatePeerDisplayPreviewsSetting(transaction: Transaction, peerId: PeerId, displayPreviews: PeerNotificationDisplayPreviews) {
func updatePeerDisplayPreviewsSetting(transaction: Transaction, peerId: PeerId, displayPreviews: PeerNotificationDisplayPreviews) {
if let peer = transaction.getPeer(peerId) {
var notificationPeerId = peerId
if let associatedPeerId = peer.associatedPeerId {
@ -108,13 +108,13 @@ public func updatePeerDisplayPreviewsSetting(transaction: Transaction, peerId: P
}
}
public func updatePeerNotificationSoundInteractive(account: Account, peerId: PeerId, sound: PeerMessageSound) -> Signal<Void, NoError> {
func _internal_updatePeerNotificationSoundInteractive(account: Account, peerId: PeerId, sound: PeerMessageSound) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Void in
updatePeerNotificationSoundInteractive(transaction: transaction, peerId: peerId, sound: sound)
}
}
public func updatePeerNotificationSoundInteractive(transaction: Transaction, peerId: PeerId, sound: PeerMessageSound) {
func updatePeerNotificationSoundInteractive(transaction: Transaction, peerId: PeerId, sound: PeerMessageSound) {
if let peer = transaction.getPeer(peerId) {
var notificationPeerId = peerId
if let associatedPeerId = peer.associatedPeerId {

Some files were not shown because too many files have changed in this diff Show More