mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
My Profile improvements
This commit is contained in:
parent
53f6799f98
commit
8dc59be281
@ -316,7 +316,7 @@ public class ItemListAddressItemNode: ListViewItemNode {
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
strongSelf.topStripeNode.isHidden = !item.displayDecorations
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners || !item.displayDecorations
|
||||
@ -327,12 +327,12 @@ public class ItemListAddressItemNode: ListViewItemNode {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = 16.0 + params.leftInset
|
||||
bottomStripeOffset = -separatorHeight
|
||||
strongSelf.bottomStripeNode.isHidden = false
|
||||
strongSelf.bottomStripeNode.isHidden = !item.displayDecorations
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners || !item.displayDecorations
|
||||
}
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
@ -175,7 +175,7 @@ public enum AccountResult {
|
||||
case authorized(Account)
|
||||
}
|
||||
|
||||
public func accountWithId(accountManager: AccountManager<TelegramAccountManagerTypes>, networkArguments: NetworkInitializationArguments, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, supplementary: Bool, rootPath: String, beginWithTestingEnvironment: Bool, backupData: AccountBackupData?, auxiliaryMethods: AccountAuxiliaryMethods, shouldKeepAutoConnection: Bool = true) -> Signal<AccountResult, NoError> {
|
||||
public func accountWithId(accountManager: AccountManager<TelegramAccountManagerTypes>, networkArguments: NetworkInitializationArguments, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, supplementary: Bool, isSupportUser: Bool, rootPath: String, beginWithTestingEnvironment: Bool, backupData: AccountBackupData?, auxiliaryMethods: AccountAuxiliaryMethods, shouldKeepAutoConnection: Bool = true) -> Signal<AccountResult, NoError> {
|
||||
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
||||
|
||||
let postbox = openPostbox(
|
||||
@ -259,7 +259,7 @@ public func accountWithId(accountManager: AccountManager<TelegramAccountManagerT
|
||||
|> mapToSignal { phoneNumber in
|
||||
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: phoneNumber, useRequestTimeoutTimers: useRequestTimeoutTimers, appConfiguration: appConfig)
|
||||
|> map { network -> AccountResult in
|
||||
return .authorized(Account(accountManager: accountManager, id: id, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, postbox: postbox, network: network, networkArguments: networkArguments, peerId: authorizedState.peerId, auxiliaryMethods: auxiliaryMethods, supplementary: supplementary))
|
||||
return .authorized(Account(accountManager: accountManager, id: id, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, postbox: postbox, network: network, networkArguments: networkArguments, peerId: authorizedState.peerId, auxiliaryMethods: auxiliaryMethods, supplementary: supplementary, isSupportUser: isSupportUser))
|
||||
}
|
||||
}
|
||||
case _:
|
||||
@ -905,6 +905,7 @@ public class Account {
|
||||
public let basePath: String
|
||||
public let testingEnvironment: Bool
|
||||
public let supplementary: Bool
|
||||
public let isSupportUser: Bool
|
||||
public let postbox: Postbox
|
||||
public let network: Network
|
||||
public let networkArguments: NetworkInitializationArguments
|
||||
@ -988,7 +989,7 @@ public class Account {
|
||||
public let filteredStorySubscriptionsContext: StorySubscriptionsContext?
|
||||
public let hiddenStorySubscriptionsContext: StorySubscriptionsContext?
|
||||
|
||||
public init(accountManager: AccountManager<TelegramAccountManagerTypes>, id: AccountRecordId, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, networkArguments: NetworkInitializationArguments, peerId: PeerId, auxiliaryMethods: AccountAuxiliaryMethods, supplementary: Bool) {
|
||||
public init(accountManager: AccountManager<TelegramAccountManagerTypes>, id: AccountRecordId, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, networkArguments: NetworkInitializationArguments, peerId: PeerId, auxiliaryMethods: AccountAuxiliaryMethods, supplementary: Bool, isSupportUser: Bool) {
|
||||
self.accountManager = accountManager
|
||||
self.id = id
|
||||
self.basePath = basePath
|
||||
@ -1000,6 +1001,7 @@ public class Account {
|
||||
|
||||
self.auxiliaryMethods = auxiliaryMethods
|
||||
self.supplementary = supplementary
|
||||
self.isSupportUser = isSupportUser
|
||||
|
||||
self.networkStatsContext = NetworkStatsContext(postbox: postbox)
|
||||
|
||||
|
@ -9,12 +9,18 @@ private enum AccountKind {
|
||||
case unauthorized
|
||||
}
|
||||
|
||||
public struct AccountSupportUserInfo: Codable, Equatable {
|
||||
public init() {
|
||||
}
|
||||
}
|
||||
|
||||
public enum TelegramAccountRecordAttribute: AccountRecordAttribute, Equatable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case backupData
|
||||
case environment
|
||||
case sortOrder
|
||||
case loggedOut
|
||||
case supportUserInfo
|
||||
case legacyRootObject = "_"
|
||||
}
|
||||
|
||||
@ -22,6 +28,7 @@ public enum TelegramAccountRecordAttribute: AccountRecordAttribute, Equatable {
|
||||
case environment(AccountEnvironmentAttribute)
|
||||
case sortOrder(AccountSortOrderAttribute)
|
||||
case loggedOut(LoggedOutAccountAttribute)
|
||||
case supportUserInfo(AccountSupportUserInfo)
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
@ -34,6 +41,8 @@ public enum TelegramAccountRecordAttribute: AccountRecordAttribute, Equatable {
|
||||
self = .sortOrder(sortOrder)
|
||||
} else if let loggedOut = try? container.decodeIfPresent(LoggedOutAccountAttribute.self, forKey: .loggedOut) {
|
||||
self = .loggedOut(loggedOut)
|
||||
} else if let supportUserInfo = try? container.decodeIfPresent(AccountSupportUserInfo.self, forKey: .supportUserInfo) {
|
||||
self = .supportUserInfo(supportUserInfo)
|
||||
} else {
|
||||
let legacyRootObjectData = try! container.decode(AdaptedPostboxDecoder.RawObjectData.self, forKey: .legacyRootObject)
|
||||
if legacyRootObjectData.typeHash == postboxEncodableTypeHash(AccountBackupDataAttribute.self) {
|
||||
@ -62,6 +71,8 @@ public enum TelegramAccountRecordAttribute: AccountRecordAttribute, Equatable {
|
||||
try container.encode(sortOrder, forKey: .sortOrder)
|
||||
case let .loggedOut(loggedOut):
|
||||
try container.encode(loggedOut, forKey: .loggedOut)
|
||||
case let .supportUserInfo(supportUserInfo):
|
||||
try container.encode(supportUserInfo, forKey: .supportUserInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,7 +282,14 @@ public func currentAccount(allocateIfNotExists: Bool, networkArguments: NetworkI
|
||||
return false
|
||||
}
|
||||
})
|
||||
return accountWithId(accountManager: manager, networkArguments: networkArguments, id: record.0, encryptionParameters: encryptionParameters, supplementary: supplementary, rootPath: rootPath, beginWithTestingEnvironment: beginWithTestingEnvironment, backupData: nil, auxiliaryMethods: auxiliaryMethods)
|
||||
let isSupportUser = record.1.contains(where: { attribute in
|
||||
if case .supportUserInfo = attribute {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
return accountWithId(accountManager: manager, networkArguments: networkArguments, id: record.0, encryptionParameters: encryptionParameters, supplementary: supplementary, isSupportUser: isSupportUser, rootPath: rootPath, beginWithTestingEnvironment: beginWithTestingEnvironment, backupData: nil, auxiliaryMethods: auxiliaryMethods)
|
||||
|> mapToSignal { accountResult -> Signal<AccountResult?, NoError> in
|
||||
let postbox: Postbox
|
||||
let initialKind: AccountKind
|
||||
@ -441,7 +459,14 @@ private func cleanupAccount(networkArguments: NetworkInitializationArguments, ac
|
||||
return false
|
||||
}
|
||||
})
|
||||
return accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: id, encryptionParameters: encryptionParameters, supplementary: true, rootPath: rootPath, beginWithTestingEnvironment: beginWithTestingEnvironment, backupData: nil, auxiliaryMethods: auxiliaryMethods)
|
||||
let isSupportUser = attributes.contains(where: { attribute in
|
||||
if case .supportUserInfo = attribute {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
return accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: id, encryptionParameters: encryptionParameters, supplementary: true, isSupportUser: isSupportUser, rootPath: rootPath, beginWithTestingEnvironment: beginWithTestingEnvironment, backupData: nil, auxiliaryMethods: auxiliaryMethods)
|
||||
|> mapToSignal { account -> Signal<Void, NoError> in
|
||||
switch account {
|
||||
case .upgrading:
|
||||
|
@ -15,7 +15,7 @@ public enum AuthorizationCodeRequestError {
|
||||
case appOutdated
|
||||
}
|
||||
|
||||
func switchToAuthorizedAccount(transaction: AccountManagerModifier<TelegramAccountManagerTypes>, account: UnauthorizedAccount) {
|
||||
func switchToAuthorizedAccount(transaction: AccountManagerModifier<TelegramAccountManagerTypes>, account: UnauthorizedAccount, isSupportUser: Bool) {
|
||||
let nextSortOrder = (transaction.getRecords().map({ record -> Int32 in
|
||||
for attribute in record.attributes {
|
||||
if case let .sortOrder(sortOrder) = attribute {
|
||||
@ -25,10 +25,14 @@ func switchToAuthorizedAccount(transaction: AccountManagerModifier<TelegramAccou
|
||||
return 0
|
||||
}).max() ?? 0) + 1
|
||||
transaction.updateRecord(account.id, { _ in
|
||||
return AccountRecord(id: account.id, attributes: [
|
||||
var attributes: [TelegramAccountManagerTypes.Attribute] = [
|
||||
.environment(AccountEnvironmentAttribute(environment: account.testingEnvironment ? .test : .production)),
|
||||
.sortOrder(AccountSortOrderAttribute(order: nextSortOrder))
|
||||
], temporarySessionId: nil)
|
||||
]
|
||||
if isSupportUser {
|
||||
attributes.append(.supportUserInfo(AccountSupportUserInfo()))
|
||||
}
|
||||
return AccountRecord(id: account.id, attributes: attributes, temporarySessionId: nil)
|
||||
})
|
||||
transaction.setCurrentId(account.id)
|
||||
transaction.removeAuth()
|
||||
@ -323,6 +327,10 @@ public func sendAuthorizationCode(accountManager: AccountManager<TelegramAccount
|
||||
}
|
||||
|
||||
let user = TelegramUser(user: user)
|
||||
var isSupportUser = false
|
||||
if let phone = user.phone, phone.hasPrefix("42") {
|
||||
isSupportUser = true
|
||||
}
|
||||
let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil, invalidatedChannels: [])
|
||||
initializedAppSettingsAfterLogin(transaction: transaction, appVersion: account.networkArguments.appVersion, syncContacts: syncContacts)
|
||||
transaction.setState(state)
|
||||
@ -330,7 +338,7 @@ public func sendAuthorizationCode(accountManager: AccountManager<TelegramAccount
|
||||
transaction.setNoticeEntry(key: value.0, value: value.1)
|
||||
}
|
||||
return accountManager.transaction { transaction -> SendAuthorizationCodeResult in
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account)
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account, isSupportUser: isSupportUser)
|
||||
return .loggedIn
|
||||
}
|
||||
|> castError(AuthorizationCodeRequestError.self)
|
||||
@ -951,6 +959,10 @@ public func authorizeWithCode(accountManager: AccountManager<TelegramAccountMana
|
||||
}
|
||||
|
||||
let user = TelegramUser(user: user)
|
||||
var isSupportUser = false
|
||||
if let phone = user.phone, phone.hasPrefix("42") {
|
||||
isSupportUser = true
|
||||
}
|
||||
let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil, invalidatedChannels: [])
|
||||
initializedAppSettingsAfterLogin(transaction: transaction, appVersion: account.networkArguments.appVersion, syncContacts: syncContacts)
|
||||
transaction.setState(state)
|
||||
@ -958,7 +970,7 @@ public func authorizeWithCode(accountManager: AccountManager<TelegramAccountMana
|
||||
transaction.setNoticeEntry(key: value.0, value: value.1)
|
||||
}
|
||||
return accountManager.transaction { transaction -> AuthorizeWithCodeResult in
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account)
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account, isSupportUser: isSupportUser)
|
||||
return .loggedIn
|
||||
}
|
||||
case let .authorizationSignUpRequired(_, termsOfService):
|
||||
@ -1019,11 +1031,15 @@ public func authorizeWithPassword(accountManager: AccountManager<TelegramAccount
|
||||
/*transaction.updatePeersInternal([user], update: { current, peer -> Peer? in
|
||||
return peer
|
||||
})*/
|
||||
var isSupportUser = false
|
||||
if let phone = user.phone, phone.hasPrefix("42") {
|
||||
isSupportUser = true
|
||||
}
|
||||
initializedAppSettingsAfterLogin(transaction: transaction, appVersion: account.networkArguments.appVersion, syncContacts: syncContacts)
|
||||
transaction.setState(state)
|
||||
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account)
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account, isSupportUser: isSupportUser)
|
||||
}
|
||||
case .authorizationSignUpRequired:
|
||||
return .complete()
|
||||
@ -1085,12 +1101,16 @@ public func loginWithRecoveredAccountData(accountManager: AccountManager<Telegra
|
||||
}
|
||||
|
||||
let user = TelegramUser(user: user)
|
||||
var isSupportUser = false
|
||||
if let phone = user.phone, phone.hasPrefix("42") {
|
||||
isSupportUser = true
|
||||
}
|
||||
let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil, invalidatedChannels: [])
|
||||
|
||||
initializedAppSettingsAfterLogin(transaction: transaction, appVersion: account.networkArguments.appVersion, syncContacts: syncContacts)
|
||||
transaction.setState(state)
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account)
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account, isSupportUser: isSupportUser)
|
||||
}
|
||||
case .authorizationSignUpRequired:
|
||||
return .complete()
|
||||
@ -1237,6 +1257,10 @@ public func signUpWithName(accountManager: AccountManager<TelegramAccountManager
|
||||
}
|
||||
|
||||
let user = TelegramUser(user: user)
|
||||
var isSupportUser = false
|
||||
if let phone = user.phone, phone.hasPrefix("42") {
|
||||
isSupportUser = true
|
||||
}
|
||||
let appliedState = account.postbox.transaction { transaction -> Void in
|
||||
let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil, invalidatedChannels: [])
|
||||
if let hole = account.postbox.seedConfiguration.initializeChatListWithHole.topLevel {
|
||||
@ -1251,7 +1275,7 @@ public func signUpWithName(accountManager: AccountManager<TelegramAccountManager
|
||||
|> castError(SignUpError.self)
|
||||
|
||||
let switchedAccounts = accountManager.transaction { transaction -> Void in
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account)
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account, isSupportUser: isSupportUser)
|
||||
}
|
||||
|> castError(SignUpError.self)
|
||||
|
||||
|
@ -435,6 +435,22 @@ public func resendMessages(account: Account, messageIds: [MessageId]) -> Signal<
|
||||
}
|
||||
|
||||
func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, messages: [(Bool, EnqueueMessage)], disableAutoremove: Bool = false, transformGroupingKeysWithPeerId: Bool = false) -> [MessageId?] {
|
||||
/**
|
||||
* If it is a support account, mark messages as read here as they are
|
||||
* not marked as read when chat is opened.
|
||||
**/
|
||||
if account.isSupportUser {
|
||||
let namespace: MessageId.Namespace
|
||||
if peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
namespace = Namespaces.Message.SecretIncoming
|
||||
} else {
|
||||
namespace = Namespaces.Message.Cloud
|
||||
}
|
||||
if let index = transaction.getTopPeerMessageIndex(peerId: peerId, namespace: namespace) {
|
||||
let _ = transaction.applyInteractiveReadMaxIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
var forwardedMessageIds = Set<MessageId>()
|
||||
for (_, message) in messages {
|
||||
if case let .forward(sourceId, _, _, _, _) = message {
|
||||
|
@ -103,7 +103,7 @@ func _internal_exportAuthTransferToken(accountManager: AccountManager<TelegramAc
|
||||
initializedAppSettingsAfterLogin(transaction: transaction, appVersion: updatedAccount.networkArguments.appVersion, syncContacts: syncContacts)
|
||||
transaction.setState(state)
|
||||
return accountManager.transaction { transaction -> ExportAuthTransferTokenResult in
|
||||
switchToAuthorizedAccount(transaction: transaction, account: updatedAccount)
|
||||
switchToAuthorizedAccount(transaction: transaction, account: updatedAccount, isSupportUser: false)
|
||||
return .loggedIn
|
||||
}
|
||||
|> castError(ExportAuthTransferTokenError.self)
|
||||
@ -131,7 +131,7 @@ func _internal_exportAuthTransferToken(accountManager: AccountManager<TelegramAc
|
||||
initializedAppSettingsAfterLogin(transaction: transaction, appVersion: account.networkArguments.appVersion, syncContacts: syncContacts)
|
||||
transaction.setState(state)
|
||||
return accountManager.transaction { transaction -> ExportAuthTransferTokenResult in
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account)
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account, isSupportUser: false)
|
||||
return .loggedIn
|
||||
}
|
||||
|> castError(ExportAuthTransferTokenError.self)
|
||||
|
@ -15,6 +15,7 @@ final class PeerInfoScreenAddressItem: PeerInfoScreenItem {
|
||||
let action: (() -> Void)?
|
||||
let longTapAction: ((ASDisplayNode, String) -> Void)?
|
||||
let linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)?
|
||||
let contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
|
||||
init(
|
||||
id: AnyHashable,
|
||||
@ -23,7 +24,8 @@ final class PeerInfoScreenAddressItem: PeerInfoScreenItem {
|
||||
imageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?,
|
||||
action: (() -> Void)?,
|
||||
longTapAction: ((ASDisplayNode, String) -> Void)? = nil,
|
||||
linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)? = nil
|
||||
linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)? = nil,
|
||||
contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.label = label
|
||||
@ -32,6 +34,7 @@ final class PeerInfoScreenAddressItem: PeerInfoScreenItem {
|
||||
self.action = action
|
||||
self.longTapAction = longTapAction
|
||||
self.linkItemAction = linkItemAction
|
||||
self.contextAction = contextAction
|
||||
}
|
||||
|
||||
func node() -> PeerInfoScreenItemNode {
|
||||
@ -40,41 +43,130 @@ final class PeerInfoScreenAddressItem: PeerInfoScreenItem {
|
||||
}
|
||||
|
||||
private final class PeerInfoScreenAddressItemNode: PeerInfoScreenItemNode {
|
||||
private let selectionNode: PeerInfoScreenSelectableBackgroundNode
|
||||
private let bottomSeparatorNode: ASDisplayNode
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
private let contextSourceNode: ContextExtractedContentContainingNode
|
||||
|
||||
private let extractedBackgroundImageNode: ASImageNode
|
||||
|
||||
private var extractedRect: CGRect?
|
||||
private var nonExtractedRect: CGRect?
|
||||
|
||||
private let maskNode: ASImageNode
|
||||
private let bottomSeparatorNode: ASDisplayNode
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
private var item: PeerInfoScreenAddressItem?
|
||||
private var itemNode: ItemListAddressItemNode?
|
||||
|
||||
private var presentationData: PresentationData?
|
||||
|
||||
override init() {
|
||||
var bringToFrontForHighlightImpl: (() -> Void)?
|
||||
self.selectionNode = PeerInfoScreenSelectableBackgroundNode(bringToFrontForHighlight: { bringToFrontForHighlightImpl?() })
|
||||
self.contextSourceNode = ContextExtractedContentContainingNode()
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
|
||||
self.bottomSeparatorNode = ASDisplayNode()
|
||||
self.bottomSeparatorNode.isLayerBacked = true
|
||||
self.extractedBackgroundImageNode = ASImageNode()
|
||||
self.extractedBackgroundImageNode.displaysAsynchronously = false
|
||||
self.extractedBackgroundImageNode.alpha = 0.0
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
self.maskNode.isUserInteractionEnabled = false
|
||||
|
||||
self.bottomSeparatorNode = ASDisplayNode()
|
||||
self.bottomSeparatorNode.isLayerBacked = true
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init()
|
||||
|
||||
bringToFrontForHighlightImpl = { [weak self] in
|
||||
self?.bringToFrontForHighlight?()
|
||||
}
|
||||
|
||||
self.addSubnode(self.bottomSeparatorNode)
|
||||
self.addSubnode(self.selectionNode)
|
||||
|
||||
self.containerNode.addSubnode(self.contextSourceNode)
|
||||
self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode
|
||||
self.addSubnode(self.containerNode)
|
||||
|
||||
self.addSubnode(self.maskNode)
|
||||
|
||||
self.view.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.longPressGesture(_:))))
|
||||
self.contextSourceNode.contentNode.clipsToBounds = true
|
||||
|
||||
self.contextSourceNode.contentNode.addSubnode(self.extractedBackgroundImageNode)
|
||||
|
||||
self.addSubnode(self.activateArea)
|
||||
|
||||
self.containerNode.isGestureEnabled = false
|
||||
|
||||
self.containerNode.activated = { [weak self] gesture, _ in
|
||||
guard let strongSelf = self, let item = strongSelf.item, let contextAction = item.contextAction else {
|
||||
gesture.cancel()
|
||||
return
|
||||
}
|
||||
contextAction(strongSelf.contextSourceNode, gesture, nil)
|
||||
}
|
||||
|
||||
self.contextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in
|
||||
guard let strongSelf = self, let presentationData = strongSelf.presentationData else {
|
||||
return
|
||||
}
|
||||
let theme = presentationData.theme
|
||||
|
||||
if isExtracted {
|
||||
strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: theme.list.plainBackgroundColor)
|
||||
}
|
||||
|
||||
if let extractedRect = strongSelf.extractedRect, let nonExtractedRect = strongSelf.nonExtractedRect {
|
||||
let rect = isExtracted ? extractedRect : nonExtractedRect
|
||||
transition.updateFrame(node: strongSelf.extractedBackgroundImageNode, frame: rect)
|
||||
}
|
||||
|
||||
transition.updateAlpha(node: strongSelf.extractedBackgroundImageNode, alpha: isExtracted ? 1.0 : 0.0, completion: { _ in
|
||||
if !isExtracted {
|
||||
self?.extractedBackgroundImageNode.image = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func longPressGesture(_ recognizer: UILongPressGestureRecognizer) {
|
||||
if case .began = recognizer.state {
|
||||
if let item = self.item {
|
||||
item.longTapAction?(self, item.text)
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:)))
|
||||
recognizer.tapActionAtPoint = { [weak self] _ in
|
||||
guard let self, let item = self.item else {
|
||||
return .keepWithSingleTap
|
||||
}
|
||||
|
||||
if item.longTapAction != nil {
|
||||
return .waitForSingleTap
|
||||
}
|
||||
return .waitForSingleTap
|
||||
}
|
||||
recognizer.highlight = { [weak self] point in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateTouchesAtPoint(point)
|
||||
}
|
||||
self.view.addGestureRecognizer(recognizer)
|
||||
}
|
||||
|
||||
@objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .ended:
|
||||
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
|
||||
switch gesture {
|
||||
case .tap:
|
||||
if let item = self.item {
|
||||
item.action?()
|
||||
}
|
||||
case .longTap:
|
||||
if let item = self.item {
|
||||
item.longTapAction?(self, item.text)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,9 +176,10 @@ private final class PeerInfoScreenAddressItemNode: PeerInfoScreenItemNode {
|
||||
}
|
||||
|
||||
self.item = item
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.selectionNode.pressed = item.action
|
||||
|
||||
self.containerNode.isGestureEnabled = item.contextAction != nil
|
||||
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
@ -100,7 +193,7 @@ private final class PeerInfoScreenAddressItemNode: PeerInfoScreenItemNode {
|
||||
itemNode = current
|
||||
addressItem.updateNode(async: { $0() }, node: {
|
||||
return itemNode
|
||||
}, params: params, previousItem: topItem != nil ? addressItem : nil, nextItem: bottomItem != nil ? addressItem : nil, animation: .None, completion: { (layout, apply) in
|
||||
}, params: params, previousItem: addressItem, nextItem: addressItem, animation: .None, completion: { (layout, apply) in
|
||||
let nodeFrame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: layout.size.height))
|
||||
|
||||
itemNode.contentSize = layout.contentSize
|
||||
@ -118,28 +211,45 @@ private final class PeerInfoScreenAddressItemNode: PeerInfoScreenItemNode {
|
||||
itemNode = itemNodeValue as! ItemListAddressItemNode
|
||||
itemNode.isUserInteractionEnabled = false
|
||||
self.itemNode = itemNode
|
||||
self.addSubnode(itemNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(itemNode)
|
||||
}
|
||||
|
||||
let height = itemNode.contentSize.height
|
||||
|
||||
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: sideInset, y: height - UIScreenPixel), size: CGSize(width: width - sideInset, height: UIScreenPixel)))
|
||||
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0)
|
||||
|
||||
let hasCorners = hasCorners && (topItem == nil || bottomItem == nil)
|
||||
let hasTopCorners = hasCorners && topItem == nil
|
||||
let hasBottomCorners = hasCorners && bottomItem == nil
|
||||
|
||||
self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
self.maskNode.frame = CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height))
|
||||
self.bottomSeparatorNode.isHidden = hasBottomCorners || bottomItem != nil
|
||||
transition.updateFrame(node: self.maskNode, frame: CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height)))
|
||||
self.bottomSeparatorNode.isHidden = hasBottomCorners
|
||||
|
||||
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(), size: itemNode.bounds.size))
|
||||
self.activateArea.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: height))
|
||||
self.activateArea.accessibilityLabel = item.label
|
||||
|
||||
let highlightNodeOffset: CGFloat = topItem == nil ? 0.0 : UIScreenPixel
|
||||
self.selectionNode.update(size: CGSize(width: width, height: height + highlightNodeOffset), theme: presentationData.theme, transition: transition)
|
||||
transition.updateFrame(node: self.selectionNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -highlightNodeOffset), size: CGSize(width: width, height: height + highlightNodeOffset)))
|
||||
let contentSize = CGSize(width: width, height: height)
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
transition.updateFrame(node: self.contextSourceNode.contentNode, frame: CGRect(origin: CGPoint(), size: contentSize))
|
||||
|
||||
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: sideInset, y: height - UIScreenPixel), size: CGSize(width: width - sideInset, height: UIScreenPixel)))
|
||||
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0)
|
||||
let nonExtractedRect = CGRect(origin: CGPoint(), size: CGSize(width: contentSize.width, height: contentSize.height))
|
||||
let extractedRect = nonExtractedRect
|
||||
self.extractedRect = extractedRect
|
||||
self.nonExtractedRect = nonExtractedRect
|
||||
|
||||
if self.contextSourceNode.isExtractedToContextPreview {
|
||||
self.extractedBackgroundImageNode.frame = extractedRect
|
||||
} else {
|
||||
self.extractedBackgroundImageNode.frame = nonExtractedRect
|
||||
}
|
||||
self.contextSourceNode.contentRect = extractedRect
|
||||
|
||||
return height
|
||||
}
|
||||
|
||||
private func updateTouchesAtPoint(_ point: CGPoint?) {
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,54 @@ import MultilineTextComponent
|
||||
import BundleIconComponent
|
||||
import PlainButtonComponent
|
||||
|
||||
func businessHoursTextToCopy(businessHours: TelegramBusinessHours, presentationData: PresentationData, displayLocalTimezone: Bool) -> String {
|
||||
var text = ""
|
||||
|
||||
let cachedDays = businessHours.splitIntoWeekDays()
|
||||
|
||||
var timezoneOffsetMinutes: Int = 0
|
||||
if displayLocalTimezone {
|
||||
var currentCalendar = Calendar(identifier: .gregorian)
|
||||
currentCalendar.timeZone = TimeZone(identifier: businessHours.timezoneId) ?? TimeZone.current
|
||||
|
||||
let currentTimezone = TimeZone.current
|
||||
timezoneOffsetMinutes = (currentTimezone.secondsFromGMT() - currentCalendar.timeZone.secondsFromGMT()) / 60
|
||||
}
|
||||
|
||||
let businessDays: [TelegramBusinessHours.WeekDay] = cachedDays
|
||||
|
||||
for i in 0 ..< businessDays.count {
|
||||
let dayTitleValue: String
|
||||
switch i {
|
||||
case 0:
|
||||
dayTitleValue = presentationData.strings.Weekday_Monday
|
||||
case 1:
|
||||
dayTitleValue = presentationData.strings.Weekday_Tuesday
|
||||
case 2:
|
||||
dayTitleValue = presentationData.strings.Weekday_Wednesday
|
||||
case 3:
|
||||
dayTitleValue = presentationData.strings.Weekday_Thursday
|
||||
case 4:
|
||||
dayTitleValue = presentationData.strings.Weekday_Friday
|
||||
case 5:
|
||||
dayTitleValue = presentationData.strings.Weekday_Saturday
|
||||
case 6:
|
||||
dayTitleValue = presentationData.strings.Weekday_Sunday
|
||||
default:
|
||||
dayTitleValue = " "
|
||||
}
|
||||
|
||||
let businessHoursText = dayBusinessHoursText(presentationData: presentationData, day: businessDays[i], offsetMinutes: timezoneOffsetMinutes, formatAsPlainText: true)
|
||||
|
||||
if !text.isEmpty {
|
||||
text.append("\n")
|
||||
}
|
||||
text.append("\(dayTitleValue): \(businessHoursText)")
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
private func dayBusinessHoursText(presentationData: PresentationData, day: TelegramBusinessHours.WeekDay, offsetMinutes: Int, formatAsPlainText: Bool = false) -> String {
|
||||
var businessHoursText: String = ""
|
||||
switch day {
|
||||
@ -59,20 +107,23 @@ final class PeerInfoScreenBusinessHoursItem: PeerInfoScreenItem {
|
||||
let label: String
|
||||
let businessHours: TelegramBusinessHours
|
||||
let requestLayout: (Bool) -> Void
|
||||
let longTapAction: (ASDisplayNode, String) -> Void
|
||||
let longTapAction: ((ASDisplayNode, String) -> Void)?
|
||||
let contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
|
||||
init(
|
||||
id: AnyHashable,
|
||||
label: String,
|
||||
businessHours: TelegramBusinessHours,
|
||||
requestLayout: @escaping (Bool) -> Void,
|
||||
longTapAction: @escaping (ASDisplayNode, String) -> Void
|
||||
longTapAction: ((ASDisplayNode, String) -> Void)? = nil,
|
||||
contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.label = label
|
||||
self.businessHours = businessHours
|
||||
self.requestLayout = requestLayout
|
||||
self.longTapAction = longTapAction
|
||||
self.contextAction = contextAction
|
||||
}
|
||||
|
||||
func node() -> PeerInfoScreenItemNode {
|
||||
@ -154,6 +205,14 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
|
||||
self.containerNode.isGestureEnabled = false
|
||||
|
||||
self.containerNode.activated = { [weak self] gesture, _ in
|
||||
guard let strongSelf = self, let item = strongSelf.item, let contextAction = item.contextAction else {
|
||||
gesture.cancel()
|
||||
return
|
||||
}
|
||||
contextAction(strongSelf.contextSourceNode, gesture, nil)
|
||||
}
|
||||
|
||||
self.contextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in
|
||||
guard let strongSelf = self, let theme = strongSelf.theme else {
|
||||
return
|
||||
@ -180,7 +239,14 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
super.didLoad()
|
||||
|
||||
let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:)))
|
||||
recognizer.tapActionAtPoint = { _ in
|
||||
recognizer.tapActionAtPoint = { [weak self] _ in
|
||||
guard let self, let item = self.item else {
|
||||
return .keepWithSingleTap
|
||||
}
|
||||
|
||||
if item.longTapAction != nil {
|
||||
return .waitForSingleTap
|
||||
}
|
||||
return .waitForSingleTap
|
||||
}
|
||||
recognizer.highlight = { [weak self] point in
|
||||
@ -202,48 +268,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
self.item?.requestLayout(true)
|
||||
case .longTap:
|
||||
if let item = self.item, let presentationData = self.presentationData {
|
||||
var text = ""
|
||||
|
||||
var timezoneOffsetMinutes: Int = 0
|
||||
if self.displayLocalTimezone {
|
||||
var currentCalendar = Calendar(identifier: .gregorian)
|
||||
currentCalendar.timeZone = TimeZone(identifier: item.businessHours.timezoneId) ?? TimeZone.current
|
||||
|
||||
timezoneOffsetMinutes = (self.currentTimezone.secondsFromGMT() - currentCalendar.timeZone.secondsFromGMT()) / 60
|
||||
}
|
||||
|
||||
let businessDays: [TelegramBusinessHours.WeekDay] = self.cachedDays
|
||||
|
||||
for i in 0 ..< businessDays.count {
|
||||
let dayTitleValue: String
|
||||
switch i {
|
||||
case 0:
|
||||
dayTitleValue = presentationData.strings.Weekday_Monday
|
||||
case 1:
|
||||
dayTitleValue = presentationData.strings.Weekday_Tuesday
|
||||
case 2:
|
||||
dayTitleValue = presentationData.strings.Weekday_Wednesday
|
||||
case 3:
|
||||
dayTitleValue = presentationData.strings.Weekday_Thursday
|
||||
case 4:
|
||||
dayTitleValue = presentationData.strings.Weekday_Friday
|
||||
case 5:
|
||||
dayTitleValue = presentationData.strings.Weekday_Saturday
|
||||
case 6:
|
||||
dayTitleValue = presentationData.strings.Weekday_Sunday
|
||||
default:
|
||||
dayTitleValue = " "
|
||||
}
|
||||
|
||||
let businessHoursText = dayBusinessHoursText(presentationData: presentationData, day: businessDays[i], offsetMinutes: timezoneOffsetMinutes, formatAsPlainText: true)
|
||||
|
||||
if !text.isEmpty {
|
||||
text.append("\n")
|
||||
}
|
||||
text.append("\(dayTitleValue): \(businessHoursText)")
|
||||
}
|
||||
|
||||
item.longTapAction(self, text)
|
||||
item.longTapAction?(self, businessHoursTextToCopy(businessHours: item.businessHours, presentationData: presentationData, displayLocalTimezone: self.displayLocalTimezone))
|
||||
}
|
||||
default:
|
||||
break
|
||||
@ -271,6 +296,8 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
self.item = item
|
||||
self.presentationData = presentationData
|
||||
self.theme = presentationData.theme
|
||||
|
||||
self.containerNode.isGestureEnabled = item.contextAction != nil
|
||||
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
|
||||
@ -618,7 +645,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode
|
||||
let hasBottomCorners = hasCorners && bottomItem == nil
|
||||
|
||||
self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
self.maskNode.frame = CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height))
|
||||
transition.updateFrame(node: self.maskNode, frame: CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height)))
|
||||
self.bottomSeparatorNode.isHidden = hasBottomCorners
|
||||
|
||||
self.activateArea.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: height))
|
||||
|
@ -593,6 +593,10 @@ private final class PeerInfoInteraction {
|
||||
let openBirthdatePrivacy: () -> Void
|
||||
let openPremiumGift: () -> Void
|
||||
let editingOpenPersonalChannel: () -> Void
|
||||
let openUsernameContextMenu: (ASDisplayNode, ContextGesture?) -> Void
|
||||
let openBioContextMenu: (ASDisplayNode, ContextGesture?) -> Void
|
||||
let openWorkingHoursContextMenu: (ASDisplayNode, ContextGesture?) -> Void
|
||||
let openBusinessLocationContextMenu: (ASDisplayNode, ContextGesture?) -> Void
|
||||
let getController: () -> ViewController?
|
||||
|
||||
init(
|
||||
@ -655,6 +659,10 @@ private final class PeerInfoInteraction {
|
||||
openBirthdatePrivacy: @escaping () -> Void,
|
||||
openPremiumGift: @escaping () -> Void,
|
||||
editingOpenPersonalChannel: @escaping () -> Void,
|
||||
openUsernameContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
||||
openBioContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
||||
openWorkingHoursContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
||||
openBusinessLocationContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
||||
getController: @escaping () -> ViewController?
|
||||
) {
|
||||
self.openUsername = openUsername
|
||||
@ -716,6 +724,10 @@ private final class PeerInfoInteraction {
|
||||
self.openBirthdatePrivacy = openBirthdatePrivacy
|
||||
self.openPremiumGift = openPremiumGift
|
||||
self.editingOpenPersonalChannel = editingOpenPersonalChannel
|
||||
self.openUsernameContextMenu = openUsernameContextMenu
|
||||
self.openBioContextMenu = openBioContextMenu
|
||||
self.openWorkingHoursContextMenu = openWorkingHoursContextMenu
|
||||
self.openBusinessLocationContextMenu = openBusinessLocationContextMenu
|
||||
self.getController = getController
|
||||
}
|
||||
}
|
||||
@ -1018,7 +1030,7 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
|
||||
}
|
||||
|
||||
let ItemNameHelp = 0
|
||||
let ItemBio = 1
|
||||
let ItemBio: AnyHashable = AnyHashable("bio_edit")
|
||||
let ItemBioHelp = 2
|
||||
let ItemPhoneNumber = 3
|
||||
let ItemUsername = 4
|
||||
@ -1180,12 +1192,18 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
items[section] = []
|
||||
}
|
||||
|
||||
let bioContextAction: (ASDisplayNode) -> Void = { sourceNode in
|
||||
interaction.openPeerInfoContextMenu(.bio, sourceNode, nil)
|
||||
let bioContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in
|
||||
interaction.openBioContextMenu(node, gesture)
|
||||
}
|
||||
let bioLinkAction: (TextLinkItemActionType, TextLinkItem, ASDisplayNode, CGRect?, Promise<Bool>?) -> Void = { action, item, _, _, _ in
|
||||
interaction.performBioLinkAction(action, item)
|
||||
}
|
||||
let workingHoursContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in
|
||||
interaction.openWorkingHoursContextMenu(node, gesture)
|
||||
}
|
||||
let businessLocationContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in
|
||||
interaction.openBusinessLocationContextMenu(node, gesture)
|
||||
}
|
||||
|
||||
if let user = data.peer as? TelegramUser {
|
||||
if !callMessages.isEmpty {
|
||||
@ -1245,8 +1263,6 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
icon: .qrCode,
|
||||
action: { _, progress in
|
||||
interaction.openUsername(mainUsername, true, progress)
|
||||
}, longTapAction: { sourceNode in
|
||||
interaction.openPeerInfoContextMenu(.link(customLink: nil), sourceNode, nil)
|
||||
}, linkItemAction: { type, item, _, _, progress in
|
||||
if case .tap = type {
|
||||
if case let .mention(username) = item {
|
||||
@ -1255,6 +1271,8 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
}
|
||||
}, iconAction: {
|
||||
interaction.openQrCode()
|
||||
}, contextAction: { node, gesture, _ in
|
||||
interaction.openUsernameContextMenu(node, gesture)
|
||||
}, requestLayout: {
|
||||
interaction.requestLayout(false)
|
||||
}
|
||||
@ -1288,7 +1306,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
} else if let about = cachedData.about, !about.isEmpty {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.isPremium ? enabledPublicBioEntities : enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.isPremium ? enabledPublicBioEntities : enabledPrivateBioEntities), action: nil, linkItemAction: bioLinkAction, contextAction: bioContextAction, requestLayout: {
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
}
|
||||
@ -1296,11 +1314,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
if let businessHours = cachedData.businessHours {
|
||||
items[.peerInfo]!.append(PeerInfoScreenBusinessHoursItem(id: 300, label: presentationData.strings.PeerInfo_BusinessHours_Label, businessHours: businessHours, requestLayout: { animated in
|
||||
interaction.requestLayout(animated)
|
||||
}, longTapAction: { sourceNode, text in
|
||||
if !text.isEmpty {
|
||||
interaction.openPeerInfoContextMenu(.businessHours(text), sourceNode, nil)
|
||||
}
|
||||
}))
|
||||
}, longTapAction: nil, contextAction: workingHoursContextAction))
|
||||
}
|
||||
|
||||
if let businessLocation = cachedData.businessLocation {
|
||||
@ -1314,11 +1328,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
action: {
|
||||
interaction.openLocation()
|
||||
},
|
||||
longTapAction: { sourceNode, text in
|
||||
if !text.isEmpty {
|
||||
interaction.openPeerInfoContextMenu(.businessLocation(text), sourceNode, nil)
|
||||
}
|
||||
}
|
||||
contextAction: businessLocationContextAction
|
||||
))
|
||||
} else {
|
||||
items[.peerInfo]!.append(PeerInfoScreenAddressItem(
|
||||
@ -1327,11 +1337,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
text: businessLocation.address,
|
||||
imageSignal: nil,
|
||||
action: nil,
|
||||
longTapAction: { sourceNode, text in
|
||||
if !text.isEmpty {
|
||||
interaction.openPeerInfoContextMenu(.businessLocation(text), sourceNode, nil)
|
||||
}
|
||||
}
|
||||
contextAction: businessLocationContextAction
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -1534,7 +1540,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
if case .group = channel.info {
|
||||
enabledEntities = enabledPrivateBioEntities
|
||||
}
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledEntities), action: nil, linkItemAction: bioLinkAction, contextAction: bioContextAction, requestLayout: {
|
||||
interaction.requestLayout(true)
|
||||
}))
|
||||
}
|
||||
@ -1584,7 +1590,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
}
|
||||
|
||||
if let aboutText = aboutText {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, linkItemAction: bioLinkAction, contextAction: bioContextAction, requestLayout: {
|
||||
interaction.requestLayout(true)
|
||||
}))
|
||||
}
|
||||
@ -2748,6 +2754,26 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
return
|
||||
}
|
||||
self.editingOpenPersonalChannel()
|
||||
}, openUsernameContextMenu: { [weak self] node, gesture in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openUsernameContextMenu(node: node, gesture: gesture)
|
||||
}, openBioContextMenu: { [weak self] node, gesture in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openBioContextMenu(node: node, gesture: gesture)
|
||||
}, openWorkingHoursContextMenu: { [weak self] node, gesture in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openWorkingHoursContextMenu(node: node, gesture: gesture)
|
||||
}, openBusinessLocationContextMenu: { [weak self] node, gesture in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openBusinessLocationContextMenu(node: node, gesture: gesture)
|
||||
},
|
||||
getController: { [weak self] in
|
||||
return self?.controller
|
||||
@ -4585,6 +4611,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
var previousIsBlocked: Bool?
|
||||
var currentIsBlocked: Bool?
|
||||
|
||||
var previousBusinessHours: TelegramBusinessHours?
|
||||
var currentBusinessHours: TelegramBusinessHours?
|
||||
|
||||
var previousBusinessLocation: TelegramBusinessLocation?
|
||||
var currentBusinessLocation: TelegramBusinessLocation?
|
||||
|
||||
var previousPhotoIsPersonal: Bool?
|
||||
var currentPhotoIsPersonal: Bool?
|
||||
if let previousUser = previousData?.peer as? TelegramUser {
|
||||
@ -4613,6 +4645,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
currentAbout = cachedData.about
|
||||
previousIsBlocked = previousCachedData.isBlocked
|
||||
currentIsBlocked = cachedData.isBlocked
|
||||
previousBusinessHours = previousCachedData.businessHours
|
||||
currentBusinessHours = cachedData.businessHours
|
||||
previousBusinessLocation = previousCachedData.businessLocation
|
||||
currentBusinessLocation = cachedData.businessLocation
|
||||
}
|
||||
|
||||
if self.isSettings {
|
||||
@ -4641,6 +4677,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
if let previousIsBlocked, let currentIsBlocked, previousIsBlocked != currentIsBlocked {
|
||||
infoUpdated = true
|
||||
}
|
||||
|
||||
if previousData != nil {
|
||||
if (previousBusinessHours == nil) != (currentBusinessHours != nil) {
|
||||
infoUpdated = true
|
||||
}
|
||||
if (previousBusinessLocation == nil) != (currentBusinessLocation != nil) {
|
||||
infoUpdated = true
|
||||
}
|
||||
}
|
||||
|
||||
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: self.didSetReady && (membersUpdated || infoUpdated) ? .animated(duration: 0.3, curve: .spring) : .immediate)
|
||||
|
||||
if let cachedData = data.cachedData as? CachedUserData, let _ = cachedData.birthday {
|
||||
@ -6846,6 +6892,268 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}
|
||||
}
|
||||
|
||||
private func openUsernameContextMenu(node: ASDisplayNode, gesture: ContextGesture?) {
|
||||
guard let sourceNode = node as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
}
|
||||
guard let peer = self.data?.peer else {
|
||||
return
|
||||
}
|
||||
guard let username = peer.addressName else {
|
||||
return
|
||||
}
|
||||
|
||||
let copyAction = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
UIPasteboard.general.string = "@\(username)"
|
||||
|
||||
//TODO:localize
|
||||
self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: "Username copied"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
if self.isMyProfile {
|
||||
items.append(.action(ContextMenuActionItem(text: "Edit Username", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openSettings(section: .username)
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Copy Username", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
copyAction()
|
||||
}
|
||||
})))
|
||||
|
||||
let actions = ContextController.Items(content: .list(items))
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture)
|
||||
self.controller?.present(contextController, in: .window(.root))
|
||||
}
|
||||
|
||||
private func openBioContextMenu(node: ASDisplayNode, gesture: ContextGesture?) {
|
||||
guard let sourceNode = node as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
}
|
||||
guard let cachedData = self.data?.cachedData else {
|
||||
return
|
||||
}
|
||||
|
||||
var bioText: String?
|
||||
if let cachedData = cachedData as? CachedUserData {
|
||||
bioText = cachedData.about
|
||||
} else if let cachedData = cachedData as? CachedChannelData {
|
||||
bioText = cachedData.about
|
||||
} else if let cachedData = cachedData as? CachedGroupData {
|
||||
bioText = cachedData.about
|
||||
}
|
||||
|
||||
guard let bioText, !bioText.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
let copyAction = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
UIPasteboard.general.string = bioText
|
||||
|
||||
//TODO:localize
|
||||
self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: "Bio copied"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
if self.isMyProfile {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Edit Bio", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil)
|
||||
|
||||
for (_, section) in self.editingSections {
|
||||
for (id, itemNode) in section.itemNodes {
|
||||
if id == AnyHashable("bio_edit") {
|
||||
if let itemNode = itemNode as? PeerInfoScreenMultilineInputItemNode {
|
||||
itemNode.focus()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Copy Bio", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
copyAction()
|
||||
}
|
||||
})))
|
||||
|
||||
let actions = ContextController.Items(content: .list(items))
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture)
|
||||
self.controller?.present(contextController, in: .window(.root))
|
||||
}
|
||||
|
||||
private func openWorkingHoursContextMenu(node: ASDisplayNode, gesture: ContextGesture?) {
|
||||
guard let sourceNode = node as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
}
|
||||
guard let cachedData = self.data?.cachedData else {
|
||||
return
|
||||
}
|
||||
|
||||
var businessHours: TelegramBusinessHours?
|
||||
if let cachedData = cachedData as? CachedUserData {
|
||||
businessHours = cachedData.businessHours
|
||||
}
|
||||
|
||||
guard let businessHours else {
|
||||
return
|
||||
}
|
||||
|
||||
let copyAction = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
UIPasteboard.general.string = businessHoursTextToCopy(businessHours: businessHours, presentationData: self.presentationData, displayLocalTimezone: false)
|
||||
|
||||
//TODO:localize
|
||||
self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: "Working hours copied."), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
if self.isMyProfile {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Edit Hours", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let businessHoursSetupScreen = self.context.sharedContext.makeBusinessHoursSetupScreen(context: self.context, initialValue: businessHours, completion: { _ in })
|
||||
self.controller?.push(businessHoursSetupScreen)
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Copy Hours", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
copyAction()
|
||||
}
|
||||
})))
|
||||
|
||||
if self.isMyProfile {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Remove", textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self.context.engine.accountData.updateAccountBusinessHours(businessHours: nil).startStandalone()
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
let actions = ContextController.Items(content: .list(items))
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture)
|
||||
self.controller?.present(contextController, in: .window(.root))
|
||||
}
|
||||
|
||||
private func openBusinessLocationContextMenu(node: ASDisplayNode, gesture: ContextGesture?) {
|
||||
guard let sourceNode = node as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
}
|
||||
guard let cachedData = self.data?.cachedData else {
|
||||
return
|
||||
}
|
||||
|
||||
var businessLocation: TelegramBusinessLocation?
|
||||
if let cachedData = cachedData as? CachedUserData {
|
||||
businessLocation = cachedData.businessLocation
|
||||
}
|
||||
|
||||
guard let businessLocation else {
|
||||
return
|
||||
}
|
||||
|
||||
let copyAction = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
UIPasteboard.general.string = businessLocation.address
|
||||
|
||||
//TODO:localize
|
||||
self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: "Working hours copied."), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
if businessLocation.coordinates != nil {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Open in Maps", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Media Editor/LocationSmall"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss(completion: {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.interaction.openLocation()
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
if !businessLocation.address.isEmpty {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Copy Address", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
copyAction()
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
if self.isMyProfile {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Edit Location", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let businessLocationSetupScreen = self.context.sharedContext.makeBusinessLocationSetupScreen(context: self.context, initialValue: businessLocation, completion: { _ in })
|
||||
self.controller?.push(businessLocationSetupScreen)
|
||||
}
|
||||
})))
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Remove", textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self.context.engine.accountData.updateAccountBusinessLocation(businessLocation: nil).startStandalone()
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
let actions = ContextController.Items(content: .list(items))
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture)
|
||||
self.controller?.present(contextController, in: .window(.root))
|
||||
}
|
||||
|
||||
private func openPhone(value: String, node: ASDisplayNode, gesture: ContextGesture?, progress: Promise<Bool>?) {
|
||||
guard let sourceNode = node as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
@ -6919,45 +7227,66 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
|
||||
var isAnonymousNumber = false
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
if strongSelf.isMyProfile {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Change Number", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
c.dismiss {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openSettings(section: .phoneNumber)
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
if case let .user(peer) = peer, let peerPhoneNumber = peer.phone, formattedPhoneNumber == formatPhoneNumber(context: strongSelf.context, number: peerPhoneNumber) {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
telegramCallAction(false)
|
||||
}
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramVideoCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VideoCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
telegramCallAction(true)
|
||||
}
|
||||
})))
|
||||
if !formattedPhoneNumber.hasPrefix("+888") {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PhoneCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
if !strongSelf.isMyProfile {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
phoneCallAction()
|
||||
telegramCallAction(false)
|
||||
}
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramVideoCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VideoCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
telegramCallAction(true)
|
||||
}
|
||||
})))
|
||||
}
|
||||
if !formattedPhoneNumber.hasPrefix("+888") {
|
||||
if !strongSelf.isMyProfile {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PhoneCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
phoneCallAction()
|
||||
}
|
||||
})))
|
||||
}
|
||||
} else {
|
||||
isAnonymousNumber = true
|
||||
}
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Copy Number", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
copyAction()
|
||||
}
|
||||
})))
|
||||
} else {
|
||||
if !formattedPhoneNumber.hasPrefix("+888") {
|
||||
items.append(
|
||||
.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PhoneCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
phoneCallAction()
|
||||
}
|
||||
}))
|
||||
)
|
||||
if !strongSelf.isMyProfile {
|
||||
items.append(
|
||||
.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PhoneCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
phoneCallAction()
|
||||
}
|
||||
}))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
isAnonymousNumber = true
|
||||
}
|
||||
//TODO:localize
|
||||
items.append(
|
||||
.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
.action(ContextMenuActionItem(text: "Copy Number", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss {
|
||||
copyAction()
|
||||
}
|
||||
@ -9196,21 +9525,26 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
guard let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
var updatedControllers = navigationController.viewControllers
|
||||
for controller in navigationController.viewControllers.reversed() {
|
||||
if controller !== strongSelf && !(controller is TabBarController) {
|
||||
updatedControllers.removeLast()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
updatedControllers.append(c)
|
||||
|
||||
var animated = true
|
||||
if let validLayout = strongSelf.validLayout?.0, case .regular = validLayout.metrics.widthClass {
|
||||
animated = false
|
||||
if strongSelf.isMyProfile {
|
||||
navigationController.pushViewController(c)
|
||||
} else {
|
||||
var updatedControllers = navigationController.viewControllers
|
||||
for controller in navigationController.viewControllers.reversed() {
|
||||
if controller !== strongSelf && !(controller is TabBarController) {
|
||||
updatedControllers.removeLast()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
updatedControllers.append(c)
|
||||
|
||||
var animated = true
|
||||
if let validLayout = strongSelf.validLayout?.0, case .regular = validLayout.metrics.widthClass {
|
||||
animated = false
|
||||
}
|
||||
navigationController.setViewControllers(updatedControllers, animated: animated)
|
||||
}
|
||||
navigationController.setViewControllers(updatedControllers, animated: animated)
|
||||
}
|
||||
switch section {
|
||||
case .avatar:
|
||||
|
@ -36,7 +36,7 @@ final class PeerInfoScreenMultilineInputItemNode: PeerInfoScreenItemNode {
|
||||
private let bottomSeparatorNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
|
||||
private var item: PeerInfoScreenMultilineInputItem?
|
||||
private(set) var item: PeerInfoScreenMultilineInputItem?
|
||||
private var itemNode: ItemListMultilineInputItemNode?
|
||||
|
||||
override init() {
|
||||
@ -127,4 +127,8 @@ final class PeerInfoScreenMultilineInputItemNode: PeerInfoScreenItemNode {
|
||||
func animateErrorIfNeeded() {
|
||||
self.itemNode?.animateErrorIfNeeded()
|
||||
}
|
||||
|
||||
func focus() {
|
||||
self.itemNode?.focus()
|
||||
}
|
||||
}
|
||||
|
@ -2088,7 +2088,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
||||
if apply {
|
||||
switch strongSelf.chatLocation {
|
||||
case .peer, .replyThread:
|
||||
if !strongSelf.context.sharedContext.immediateExperimentalUISettings.skipReadHistory {
|
||||
if !strongSelf.context.sharedContext.immediateExperimentalUISettings.skipReadHistory && !strongSelf.context.account.isSupportUser {
|
||||
strongSelf.context.applyMaxReadIndex(for: strongSelf.chatLocation, contextHolder: strongSelf.chatLocationContextHolder, messageIndex: messageIndex)
|
||||
}
|
||||
case .customChatContents:
|
||||
|
@ -78,6 +78,7 @@ private struct AccountAttributes: Equatable {
|
||||
let sortIndex: Int32
|
||||
let isTestingEnvironment: Bool
|
||||
let backupData: AccountBackupData?
|
||||
let isSupportUser: Bool
|
||||
}
|
||||
|
||||
private enum AddedAccountResult {
|
||||
@ -516,14 +517,17 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
})
|
||||
var backupData: AccountBackupData?
|
||||
var sortIndex: Int32 = 0
|
||||
var isSupportUser = false
|
||||
for attribute in record.attributes {
|
||||
if case let .sortOrder(sortOrder) = attribute {
|
||||
sortIndex = sortOrder.order
|
||||
} else if case let .backupData(backupDataValue) = attribute {
|
||||
backupData = backupDataValue.data
|
||||
} else if case .supportUserInfo = attribute {
|
||||
isSupportUser = true
|
||||
}
|
||||
}
|
||||
result[record.id] = AccountAttributes(sortIndex: sortIndex, isTestingEnvironment: isTestingEnvironment, backupData: backupData)
|
||||
result[record.id] = AccountAttributes(sortIndex: sortIndex, isTestingEnvironment: isTestingEnvironment, backupData: backupData, isSupportUser: isSupportUser)
|
||||
}
|
||||
let authRecord: (AccountRecordId, Bool)? = view.currentAuthAccount.flatMap({ authAccount in
|
||||
let isTestingEnvironment = authAccount.attributes.contains(where: { attribute in
|
||||
@ -557,7 +561,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
var addedAuthSignal: Signal<UnauthorizedAccount?, NoError> = .single(nil)
|
||||
for (id, attributes) in records {
|
||||
if self.activeAccountsValue?.accounts.firstIndex(where: { $0.0 == id}) == nil {
|
||||
addedSignals.append(accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: id, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, rootPath: rootPath, beginWithTestingEnvironment: attributes.isTestingEnvironment, backupData: attributes.backupData, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: appDelegate?.uploadInBackround))
|
||||
addedSignals.append(accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: id, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, isSupportUser: attributes.isSupportUser, rootPath: rootPath, beginWithTestingEnvironment: attributes.isTestingEnvironment, backupData: attributes.backupData, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: appDelegate?.uploadInBackround))
|
||||
|> mapToSignal { result -> Signal<AddedAccountResult, NoError> in
|
||||
switch result {
|
||||
case let .authorized(account):
|
||||
@ -581,7 +585,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}
|
||||
}
|
||||
if let authRecord = authRecord, authRecord.0 != self.activeAccountsValue?.currentAuth?.id {
|
||||
addedAuthSignal = accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: authRecord.0, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, rootPath: rootPath, beginWithTestingEnvironment: authRecord.1, backupData: nil, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: appDelegate?.uploadInBackround))
|
||||
addedAuthSignal = accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: authRecord.0, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, isSupportUser: false, rootPath: rootPath, beginWithTestingEnvironment: authRecord.1, backupData: nil, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: appDelegate?.uploadInBackround))
|
||||
|> mapToSignal { result -> Signal<UnauthorizedAccount?, NoError> in
|
||||
switch result {
|
||||
case let .unauthorized(account):
|
||||
|
Loading…
x
Reference in New Issue
Block a user