mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35:19 +00:00
1599 lines
84 KiB
Swift
1599 lines
84 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AsyncDisplayKit
|
|
import Display
|
|
import SwiftSignalKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import SyncCore
|
|
import LegacyComponents
|
|
import TelegramPresentationData
|
|
import ItemListUI
|
|
import PresentationDataUtils
|
|
import AccountContext
|
|
import TextFormat
|
|
import OverlayStatusController
|
|
import TelegramStringFormatting
|
|
import AccountContext
|
|
import ShareController
|
|
import AlertUI
|
|
import PresentationDataUtils
|
|
import TelegramNotices
|
|
import GalleryUI
|
|
import ItemListAvatarAndNameInfoItem
|
|
import PeerAvatarGalleryUI
|
|
import NotificationMuteSettingsUI
|
|
import NotificationSoundSelectionUI
|
|
import Markdown
|
|
import LocalizedPeerData
|
|
import PhoneNumberFormat
|
|
import TelegramIntents
|
|
|
|
private final class UserInfoControllerArguments {
|
|
let context: AccountContext
|
|
let avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext
|
|
let updateEditingName: (ItemListAvatarAndNameInfoItemName) -> Void
|
|
let tapAvatarAction: () -> Void
|
|
let openChat: () -> Void
|
|
let addContact: () -> Void
|
|
let shareContact: () -> Void
|
|
let shareMyContact: () -> Void
|
|
let startSecretChat: () -> Void
|
|
let changeNotificationMuteSettings: () -> Void
|
|
let openSharedMedia: () -> Void
|
|
let openGroupsInCommon: () -> Void
|
|
let updatePeerBlocked: (Bool) -> Void
|
|
let deleteContact: () -> Void
|
|
let displayUsernameContextMenu: (String) -> Void
|
|
let displayCopyContextMenu: (UserInfoEntryTag, String) -> Void
|
|
let call: () -> Void
|
|
let openCallMenu: (String) -> Void
|
|
let requestPhoneNumber: () -> Void
|
|
let aboutLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void
|
|
let displayAboutContextMenu: (String) -> Void
|
|
let openEncryptionKey: (SecretChatKeyFingerprint) -> Void
|
|
let addBotToGroup: () -> Void
|
|
let shareBot: () -> Void
|
|
let botSettings: () -> Void
|
|
let botHelp: () -> Void
|
|
let botPrivacy: () -> Void
|
|
let report: () -> Void
|
|
|
|
init(context: AccountContext, avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, tapAvatarAction: @escaping () -> Void, openChat: @escaping () -> Void, addContact: @escaping () -> Void, shareContact: @escaping () -> Void, shareMyContact: @escaping () -> Void, startSecretChat: @escaping () -> Void, changeNotificationMuteSettings: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openGroupsInCommon: @escaping () -> Void, updatePeerBlocked: @escaping (Bool) -> Void, deleteContact: @escaping () -> Void, displayUsernameContextMenu: @escaping (String) -> Void, displayCopyContextMenu: @escaping (UserInfoEntryTag, String) -> Void, call: @escaping () -> Void, openCallMenu: @escaping (String) -> Void, requestPhoneNumber: @escaping () -> Void, aboutLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, displayAboutContextMenu: @escaping (String) -> Void, openEncryptionKey: @escaping (SecretChatKeyFingerprint) -> Void, addBotToGroup: @escaping () -> Void, shareBot: @escaping () -> Void, botSettings: @escaping () -> Void, botHelp: @escaping () -> Void, botPrivacy: @escaping () -> Void, report: @escaping () -> Void) {
|
|
self.context = context
|
|
self.avatarAndNameInfoContext = avatarAndNameInfoContext
|
|
self.updateEditingName = updateEditingName
|
|
self.tapAvatarAction = tapAvatarAction
|
|
self.openChat = openChat
|
|
self.addContact = addContact
|
|
|
|
self.shareContact = shareContact
|
|
self.shareMyContact = shareMyContact
|
|
self.startSecretChat = startSecretChat
|
|
self.changeNotificationMuteSettings = changeNotificationMuteSettings
|
|
self.openSharedMedia = openSharedMedia
|
|
self.openGroupsInCommon = openGroupsInCommon
|
|
self.updatePeerBlocked = updatePeerBlocked
|
|
self.deleteContact = deleteContact
|
|
self.displayUsernameContextMenu = displayUsernameContextMenu
|
|
self.displayCopyContextMenu = displayCopyContextMenu
|
|
self.call = call
|
|
self.openCallMenu = openCallMenu
|
|
self.requestPhoneNumber = requestPhoneNumber
|
|
self.aboutLinkAction = aboutLinkAction
|
|
self.displayAboutContextMenu = displayAboutContextMenu
|
|
self.openEncryptionKey = openEncryptionKey
|
|
self.addBotToGroup = addBotToGroup
|
|
self.shareBot = shareBot
|
|
self.botSettings = botSettings
|
|
self.botHelp = botHelp
|
|
self.botPrivacy = botPrivacy
|
|
self.report = report
|
|
}
|
|
}
|
|
|
|
private enum UserInfoSection: ItemListSectionId {
|
|
case info
|
|
case actions
|
|
case sharedMediaAndNotifications
|
|
case bot
|
|
case block
|
|
}
|
|
|
|
private enum UserInfoEntryTag {
|
|
case about
|
|
case phoneNumber
|
|
case username
|
|
}
|
|
|
|
private func areMessagesEqual(_ lhsMessage: Message, _ rhsMessage: Message) -> Bool {
|
|
if lhsMessage.stableVersion != rhsMessage.stableVersion {
|
|
return false
|
|
}
|
|
if lhsMessage.id != rhsMessage.id || lhsMessage.flags != rhsMessage.flags {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
private enum UserInfoEntry: ItemListNodeEntry {
|
|
case info(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, peer: Peer?, presence: PeerPresence?, cachedData: CachedPeerData?, state: ItemListAvatarAndNameInfoItemState, displayCall: Bool)
|
|
case calls(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, messages: [Message])
|
|
case about(PresentationTheme, Peer, String, String)
|
|
case phoneNumber(PresentationTheme, Int, String, String, Bool)
|
|
case requestPhoneNumber(PresentationTheme, String, String)
|
|
case userName(PresentationTheme, String, String)
|
|
case sendMessage(PresentationTheme, String)
|
|
case addContact(PresentationTheme, String)
|
|
case shareContact(PresentationTheme, String)
|
|
case shareMyContact(PresentationTheme, String)
|
|
case startSecretChat(PresentationTheme, String)
|
|
case sharedMedia(PresentationTheme, String)
|
|
case notifications(PresentationTheme, String, String)
|
|
case groupsInCommon(PresentationTheme, String, String)
|
|
case secretEncryptionKey(PresentationTheme, String, SecretChatKeyFingerprint)
|
|
case botAddToGroup(PresentationTheme, String)
|
|
case botShare(PresentationTheme, String)
|
|
case botSettings(PresentationTheme, String)
|
|
case botHelp(PresentationTheme, String)
|
|
case botPrivacy(PresentationTheme, String)
|
|
case botReport(PresentationTheme, String)
|
|
case block(PresentationTheme, String, DestructiveUserInfoAction)
|
|
|
|
var section: ItemListSectionId {
|
|
switch self {
|
|
case .info, .calls, .about, .phoneNumber, .requestPhoneNumber, .userName:
|
|
return UserInfoSection.info.rawValue
|
|
case .sendMessage, .addContact, .shareContact, .shareMyContact, .startSecretChat, .botAddToGroup, .botShare:
|
|
return UserInfoSection.actions.rawValue
|
|
case .botSettings, .botHelp, .botPrivacy:
|
|
return UserInfoSection.bot.rawValue
|
|
case .sharedMedia, .notifications, .groupsInCommon, .secretEncryptionKey:
|
|
return UserInfoSection.sharedMediaAndNotifications.rawValue
|
|
case .botReport, .block:
|
|
return UserInfoSection.block.rawValue
|
|
}
|
|
}
|
|
|
|
var stableId: Int {
|
|
return self.sortIndex
|
|
}
|
|
|
|
static func ==(lhs: UserInfoEntry, rhs: UserInfoEntry) -> Bool {
|
|
switch lhs {
|
|
case let .info(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsPresence, lhsCachedData, lhsState, lhsDisplayCall):
|
|
switch rhs {
|
|
case let .info(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsPresence, rhsCachedData, rhsState, rhsDisplayCall):
|
|
if lhsTheme !== rhsTheme {
|
|
return false
|
|
}
|
|
if lhsStrings !== rhsStrings {
|
|
return false
|
|
}
|
|
if lhsDateTimeFormat != rhsDateTimeFormat {
|
|
return false
|
|
}
|
|
if let lhsPeer = lhsPeer, let rhsPeer = rhsPeer {
|
|
if !lhsPeer.isEqual(rhsPeer) {
|
|
return false
|
|
}
|
|
} else if (lhsPeer != nil) != (rhsPeer != nil) {
|
|
return false
|
|
}
|
|
if let lhsPresence = lhsPresence, let rhsPresence = rhsPresence {
|
|
if !lhsPresence.isEqual(to: rhsPresence) {
|
|
return false
|
|
}
|
|
} else if (lhsPresence != nil) != (rhsPresence != nil) {
|
|
return false
|
|
}
|
|
if let lhsCachedData = lhsCachedData, let rhsCachedData = rhsCachedData {
|
|
if !lhsCachedData.isEqual(to: rhsCachedData) {
|
|
return false
|
|
}
|
|
} else if (lhsCachedData != nil) != (rhsCachedData != nil) {
|
|
return false
|
|
}
|
|
if lhsState != rhsState {
|
|
return false
|
|
}
|
|
if lhsDisplayCall != rhsDisplayCall {
|
|
return false
|
|
}
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
case let .calls(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsMessages):
|
|
if case let .calls(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsMessages) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat {
|
|
if lhsMessages.count != rhsMessages.count {
|
|
return false
|
|
}
|
|
for i in 0 ..< lhsMessages.count {
|
|
if !areMessagesEqual(lhsMessages[i], rhsMessages[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .about(lhsTheme, lhsPeer, lhsText, lhsValue):
|
|
if case let .about(rhsTheme, rhsPeer, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsPeer.isEqual(rhsPeer), lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .phoneNumber(lhsTheme, lhsIndex, lhsLabel, lhsValue, lhsMain):
|
|
if case let .phoneNumber(rhsTheme, rhsIndex, rhsLabel, rhsValue, rhsMain) = rhs, lhsTheme === rhsTheme, lhsIndex == rhsIndex, lhsLabel == rhsLabel, lhsValue == rhsValue, lhsMain == rhsMain {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .requestPhoneNumber(lhsTheme, lhsLabel, lhsValue):
|
|
if case let .requestPhoneNumber(rhsTheme, rhsLabel, rhsValue) = rhs, lhsTheme === rhsTheme, lhsLabel == rhsLabel, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .userName(lhsTheme, lhsText, lhsValue):
|
|
if case let .userName(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .sendMessage(lhsTheme, lhsText):
|
|
if case let .sendMessage(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .addContact(lhsTheme, lhsText):
|
|
if case let .addContact(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .shareContact(lhsTheme, lhsText):
|
|
if case let .shareContact(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .shareMyContact(lhsTheme, lhsText):
|
|
if case let .shareMyContact(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .startSecretChat(lhsTheme, lhsText):
|
|
if case let .startSecretChat(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .sharedMedia(lhsTheme, lhsText):
|
|
if case let .sharedMedia(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .notifications(lhsTheme, lhsText, lhsValue):
|
|
if case let .notifications(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .groupsInCommon(lhsTheme, lhsText, lhsValue):
|
|
if case let .groupsInCommon(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .secretEncryptionKey(lhsTheme, lhsText, lhsValue):
|
|
if case let .secretEncryptionKey(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .botAddToGroup(lhsTheme, lhsText):
|
|
if case let .botAddToGroup(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .botShare(lhsTheme, lhsText):
|
|
if case let .botShare(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .botSettings(lhsTheme, lhsText):
|
|
if case let .botSettings(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .botHelp(lhsTheme, lhsText):
|
|
if case let .botHelp(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .botPrivacy(lhsTheme, lhsText):
|
|
if case let .botPrivacy(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .botReport(lhsTheme, lhsText):
|
|
if case let .botReport(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .block(lhsTheme, lhsText, lhsAction):
|
|
if case let .block(rhsTheme, rhsText, rhsAction) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsAction == rhsAction {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
private var sortIndex: Int {
|
|
switch self {
|
|
case .info:
|
|
return 0
|
|
case .calls:
|
|
return 1
|
|
case let .phoneNumber(_, index, _, _, _):
|
|
return 2 + index
|
|
case .requestPhoneNumber:
|
|
return 998
|
|
case .about:
|
|
return 999
|
|
case .userName:
|
|
return 1000
|
|
case .sendMessage:
|
|
return 1001
|
|
case .addContact:
|
|
return 1002
|
|
case .shareContact:
|
|
return 1003
|
|
case .shareMyContact:
|
|
return 1004
|
|
case .startSecretChat:
|
|
return 1005
|
|
case .botAddToGroup:
|
|
return 1006
|
|
case .botShare:
|
|
return 1007
|
|
case .botSettings:
|
|
return 1008
|
|
case .botHelp:
|
|
return 1009
|
|
case .botPrivacy:
|
|
return 1010
|
|
case .sharedMedia:
|
|
return 1011
|
|
case .notifications:
|
|
return 1012
|
|
case .secretEncryptionKey:
|
|
return 1014
|
|
case .groupsInCommon:
|
|
return 1015
|
|
case .botReport:
|
|
return 1016
|
|
case .block:
|
|
return 1017
|
|
}
|
|
}
|
|
|
|
static func <(lhs: UserInfoEntry, rhs: UserInfoEntry) -> Bool {
|
|
return lhs.sortIndex < rhs.sortIndex
|
|
}
|
|
|
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
|
let arguments = arguments as! UserInfoControllerArguments
|
|
switch self {
|
|
case let .info(theme, strings, dateTimeFormat, peer, presence, cachedData, state, displayCall):
|
|
return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, cachedData: cachedData, state: state, sectionId: self.section, style: .plain, editingNameUpdated: { editingName in
|
|
arguments.updateEditingName(editingName)
|
|
}, avatarTapped: {
|
|
arguments.tapAvatarAction()
|
|
}, context: arguments.avatarAndNameInfoContext, call: displayCall ? {
|
|
arguments.call()
|
|
} : nil)
|
|
case let .calls(theme, strings, dateTimeFormat, messages):
|
|
return ItemListCallListItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, messages: messages, sectionId: self.section, style: .plain)
|
|
case let .about(theme, peer, text, value):
|
|
var enabledEntityTypes: EnabledEntityTypes = []
|
|
if let peer = peer as? TelegramUser, let _ = peer.botInfo {
|
|
enabledEntityTypes = [.allUrl, .mention, .hashtag]
|
|
}
|
|
return ItemListTextWithLabelItem(presentationData: presentationData, label: text, text: foldMultipleLineBreaks(value), enabledEntityTypes: enabledEntityTypes, multiline: true, sectionId: self.section, action: nil, longTapAction: {
|
|
arguments.displayAboutContextMenu(value)
|
|
}, linkItemAction: { action, itemLink in
|
|
arguments.aboutLinkAction(action, itemLink)
|
|
}, tag: UserInfoEntryTag.about)
|
|
case let .phoneNumber(theme, _, label, value, isMain):
|
|
return ItemListTextWithLabelItem(presentationData: presentationData, label: label, text: value, textColor: isMain ? .highlighted : .accent, enabledEntityTypes: [], multiline: false, sectionId: self.section, action: {
|
|
arguments.openCallMenu(value)
|
|
}, longTapAction: {
|
|
arguments.displayCopyContextMenu(.phoneNumber, value)
|
|
}, tag: UserInfoEntryTag.phoneNumber)
|
|
case let .requestPhoneNumber(theme, label, value):
|
|
return ItemListTextWithLabelItem(presentationData: presentationData, label: label, text: value, textColor: .accent, enabledEntityTypes: [], multiline: false, sectionId: self.section, action: {
|
|
arguments.requestPhoneNumber()
|
|
})
|
|
case let .userName(theme, text, value):
|
|
return ItemListTextWithLabelItem(presentationData: presentationData, label: text, text: "@\(value)", textColor: .accent, enabledEntityTypes: [], multiline: false, sectionId: self.section, action: {
|
|
arguments.displayUsernameContextMenu("@\(value)")
|
|
}, longTapAction: {
|
|
arguments.displayCopyContextMenu(.username, "@\(value)")
|
|
}, tag: UserInfoEntryTag.username)
|
|
case let .sendMessage(theme, text):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.openChat()
|
|
})
|
|
case let .addContact(theme, text):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.addContact()
|
|
})
|
|
case let .shareContact(theme, text):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.shareContact()
|
|
})
|
|
case let .shareMyContact(theme, text):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.shareMyContact()
|
|
})
|
|
case let .startSecretChat(theme, text):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.startSecretChat()
|
|
})
|
|
case let .sharedMedia(theme, text):
|
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .plain, action: {
|
|
arguments.openSharedMedia()
|
|
})
|
|
case let .notifications(theme, text, value):
|
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .plain, action: {
|
|
arguments.changeNotificationMuteSettings()
|
|
})
|
|
case let .groupsInCommon(theme, text, value):
|
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .plain, action: {
|
|
arguments.openGroupsInCommon()
|
|
})
|
|
case let .secretEncryptionKey(theme, text, fingerprint):
|
|
return ItemListSecretChatKeyItem(presentationData: presentationData, title: text, fingerprint: fingerprint, sectionId: self.section, style: .plain, action: {
|
|
arguments.openEncryptionKey(fingerprint)
|
|
})
|
|
case let .botAddToGroup(theme, text):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.addBotToGroup()
|
|
})
|
|
case let .botShare(theme, text):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.shareBot()
|
|
})
|
|
case let .botSettings(theme, text):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.botSettings()
|
|
})
|
|
case let .botHelp(theme, text):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.botHelp()
|
|
})
|
|
case let .botPrivacy(theme, text):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.botPrivacy()
|
|
})
|
|
case let .botReport(theme, text):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.report()
|
|
})
|
|
case let .block(theme, text, action):
|
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .destructive, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
switch action {
|
|
case .block:
|
|
arguments.updatePeerBlocked(true)
|
|
case .unblock:
|
|
arguments.updatePeerBlocked(false)
|
|
case .removeContact:
|
|
arguments.deleteContact()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
private enum DestructiveUserInfoAction {
|
|
case block
|
|
case removeContact
|
|
case unblock
|
|
}
|
|
|
|
private struct UserInfoEditingState: Equatable {
|
|
let editingName: ItemListAvatarAndNameInfoItemName?
|
|
|
|
static func ==(lhs: UserInfoEditingState, rhs: UserInfoEditingState) -> Bool {
|
|
if lhs.editingName != rhs.editingName {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
private struct UserInfoState: Equatable {
|
|
let savingData: Bool
|
|
let editingState: UserInfoEditingState?
|
|
|
|
init() {
|
|
self.savingData = false
|
|
self.editingState = nil
|
|
}
|
|
|
|
init(savingData: Bool, editingState: UserInfoEditingState?) {
|
|
self.savingData = savingData
|
|
self.editingState = editingState
|
|
}
|
|
|
|
static func ==(lhs: UserInfoState, rhs: UserInfoState) -> Bool {
|
|
if lhs.savingData != rhs.savingData {
|
|
return false
|
|
}
|
|
if lhs.editingState != rhs.editingState {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func withUpdatedSavingData(_ savingData: Bool) -> UserInfoState {
|
|
return UserInfoState(savingData: savingData, editingState: self.editingState)
|
|
}
|
|
|
|
func withUpdatedEditingState(_ editingState: UserInfoEditingState?) -> UserInfoState {
|
|
return UserInfoState(savingData: self.savingData, editingState: editingState)
|
|
}
|
|
}
|
|
|
|
private func stringForBlockAction(strings: PresentationStrings, action: DestructiveUserInfoAction, peer: Peer) -> String {
|
|
switch action {
|
|
case .block:
|
|
if let user = peer as? TelegramUser, user.botInfo != nil {
|
|
return strings.Bot_Stop
|
|
} else {
|
|
return strings.Conversation_BlockUser
|
|
}
|
|
case .unblock:
|
|
if let user = peer as? TelegramUser, user.botInfo != nil {
|
|
return strings.Bot_Unblock
|
|
} else {
|
|
return strings.Conversation_UnblockUser
|
|
}
|
|
case .removeContact:
|
|
return strings.UserInfo_DeleteContact
|
|
}
|
|
}
|
|
|
|
private func userInfoEntries(account: Account, presentationData: PresentationData, view: PeerView, cachedPeerData: CachedPeerData?, deviceContacts: [(DeviceContactStableId, DeviceContactBasicData)], mode: PeerInfoControllerMode, state: UserInfoState, peerChatState: PostboxCoding?, globalNotificationSettings: GlobalNotificationSettings) -> [UserInfoEntry] {
|
|
var entries: [UserInfoEntry] = []
|
|
|
|
guard let peer = view.peers[view.peerId], let user = peerViewMainPeer(view) as? TelegramUser else {
|
|
return []
|
|
}
|
|
|
|
var editingName: ItemListAvatarAndNameInfoItemName?
|
|
|
|
var isEditing = false
|
|
if let editingState = state.editingState {
|
|
isEditing = true
|
|
|
|
if view.peerIsContact {
|
|
editingName = editingState.editingName
|
|
}
|
|
}
|
|
|
|
var callsAvailable = true
|
|
if let cachedUserData = cachedPeerData as? CachedUserData {
|
|
callsAvailable = cachedUserData.voiceCallsAvailable
|
|
}
|
|
|
|
entries.append(UserInfoEntry.info(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: user, presence: view.peerPresences[user.id], cachedData: cachedPeerData, state: ItemListAvatarAndNameInfoItemState(editingName: editingName, updatingName: nil), displayCall: user.botInfo == nil && callsAvailable))
|
|
|
|
if case let .calls(messages) = mode, !isEditing {
|
|
entries.append(UserInfoEntry.calls(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, messages: messages))
|
|
}
|
|
|
|
if let phoneNumber = user.phone, !phoneNumber.isEmpty {
|
|
let formattedNumber = formatPhoneNumber(phoneNumber)
|
|
let normalizedNumber = DeviceContactNormalizedPhoneNumber(rawValue: formattedNumber)
|
|
|
|
var index = 0
|
|
var found = false
|
|
|
|
var existingNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
|
var phoneNumbers: [(String, DeviceContactNormalizedPhoneNumber, Bool)] = []
|
|
|
|
for (_, contact) in deviceContacts {
|
|
inner: for number in contact.phoneNumbers {
|
|
var isMain = false
|
|
let normalizedContactNumber = DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(number.value))
|
|
if !existingNumbers.contains(normalizedContactNumber) {
|
|
existingNumbers.insert(normalizedContactNumber)
|
|
} else {
|
|
continue inner
|
|
}
|
|
if normalizedContactNumber == normalizedNumber {
|
|
found = true
|
|
isMain = true
|
|
}
|
|
|
|
phoneNumbers.append((number.label, normalizedContactNumber, isMain))
|
|
}
|
|
}
|
|
if !found {
|
|
entries.append(UserInfoEntry.phoneNumber(presentationData.theme, index, presentationData.strings.ContactInfo_PhoneLabelMobile, formattedNumber, false))
|
|
index += 1
|
|
} else {
|
|
for (label, number, isMain) in phoneNumbers {
|
|
entries.append(UserInfoEntry.phoneNumber(presentationData.theme, index, localizedPhoneNumberLabel(label: label, strings: presentationData.strings), number.rawValue, isMain && phoneNumbers.count != 1))
|
|
index += 1
|
|
}
|
|
}
|
|
} else {
|
|
//entries.append(UserInfoEntry.requestPhoneNumber(presentationData.theme, "phone", "Request Number"))
|
|
}
|
|
|
|
let aboutTitle: String
|
|
if let _ = user.botInfo {
|
|
aboutTitle = presentationData.strings.Profile_BotInfo
|
|
} else {
|
|
aboutTitle = presentationData.strings.Profile_About
|
|
}
|
|
if user.isFake {
|
|
let aboutValue: String
|
|
if let _ = user.botInfo {
|
|
aboutValue = presentationData.strings.UserInfo_FakeBotWarning
|
|
} else {
|
|
aboutValue = presentationData.strings.UserInfo_FakeUserWarning
|
|
}
|
|
entries.append(UserInfoEntry.about(presentationData.theme, peer, aboutTitle, aboutValue))
|
|
} else if user.isScam {
|
|
let aboutValue: String
|
|
if let _ = user.botInfo {
|
|
aboutValue = presentationData.strings.UserInfo_ScamBotWarning
|
|
} else {
|
|
aboutValue = presentationData.strings.UserInfo_ScamUserWarning
|
|
}
|
|
entries.append(UserInfoEntry.about(presentationData.theme, peer, aboutTitle, aboutValue))
|
|
} else if let cachedUserData = cachedPeerData as? CachedUserData, let about = cachedUserData.about, !about.isEmpty {
|
|
entries.append(UserInfoEntry.about(presentationData.theme, peer, aboutTitle, about))
|
|
}
|
|
|
|
if !isEditing {
|
|
if let username = user.username, !username.isEmpty {
|
|
entries.append(UserInfoEntry.userName(presentationData.theme, presentationData.strings.Profile_Username, username))
|
|
}
|
|
|
|
if !(peer is TelegramSecretChat) {
|
|
entries.append(UserInfoEntry.sendMessage(presentationData.theme, presentationData.strings.UserInfo_SendMessage))
|
|
}
|
|
|
|
if user.botInfo == nil {
|
|
if view.peerIsContact {
|
|
if let phone = user.phone, !phone.isEmpty {
|
|
entries.append(UserInfoEntry.shareContact(presentationData.theme, presentationData.strings.UserInfo_ShareContact))
|
|
}
|
|
} else {
|
|
entries.append(UserInfoEntry.addContact(presentationData.theme, presentationData.strings.Conversation_AddToContacts))
|
|
}
|
|
}
|
|
|
|
if let cachedUserData = cachedPeerData as? CachedUserData, let peerStatusSettings = cachedUserData.peerStatusSettings, peerStatusSettings.contains(.canShareContact) {
|
|
entries.append(UserInfoEntry.shareMyContact(presentationData.theme, presentationData.strings.UserInfo_ShareMyContactInfo))
|
|
}
|
|
|
|
if let peer = peer as? TelegramUser, peer.botInfo == nil {
|
|
entries.append(UserInfoEntry.startSecretChat(presentationData.theme, presentationData.strings.UserInfo_StartSecretChat))
|
|
}
|
|
|
|
if let peer = peer as? TelegramUser, let botInfo = peer.botInfo {
|
|
if botInfo.flags.contains(.worksWithGroups) {
|
|
entries.append(UserInfoEntry.botAddToGroup(presentationData.theme, presentationData.strings.UserInfo_InviteBotToGroup))
|
|
}
|
|
entries.append(UserInfoEntry.botShare(presentationData.theme, presentationData.strings.UserInfo_ShareBot))
|
|
|
|
if let cachedUserData = cachedPeerData as? CachedUserData, let botInfo = cachedUserData.botInfo {
|
|
for command in botInfo.commands {
|
|
if command.text == "settings" {
|
|
entries.append(UserInfoEntry.botSettings(presentationData.theme, presentationData.strings.UserInfo_BotSettings))
|
|
} else if command.text == "help" {
|
|
entries.append(UserInfoEntry.botHelp(presentationData.theme, presentationData.strings.UserInfo_BotHelp))
|
|
} else if command.text == "privacy" {
|
|
entries.append(UserInfoEntry.botPrivacy(presentationData.theme, presentationData.strings.UserInfo_BotPrivacy))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
entries.append(UserInfoEntry.sharedMedia(presentationData.theme, presentationData.strings.GroupInfo_SharedMedia))
|
|
}
|
|
let notificationsLabel: String
|
|
let notificationSettings = view.notificationSettings as? TelegramPeerNotificationSettings ?? TelegramPeerNotificationSettings.defaultSettings
|
|
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
|
if until < Int32.max - 1 {
|
|
notificationsLabel = stringForRemainingMuteInterval(strings: presentationData.strings, muteInterval: until)
|
|
} else {
|
|
notificationsLabel = presentationData.strings.UserInfo_NotificationsDisabled
|
|
}
|
|
} else if case .default = notificationSettings.messageSound {
|
|
notificationsLabel = presentationData.strings.UserInfo_NotificationsEnabled
|
|
} else {
|
|
notificationsLabel = localizedPeerNotificationSoundString(strings: presentationData.strings, sound: notificationSettings.messageSound, default: globalNotificationSettings.effective.channels.sound)
|
|
}
|
|
entries.append(UserInfoEntry.notifications(presentationData.theme, presentationData.strings.GroupInfo_Notifications, notificationsLabel))
|
|
|
|
if isEditing {
|
|
if view.peerIsContact {
|
|
entries.append(UserInfoEntry.block(presentationData.theme, stringForBlockAction(strings: presentationData.strings, action: .removeContact, peer: user), .removeContact))
|
|
}
|
|
} else {
|
|
if peer is TelegramSecretChat, let peerChatState = peerChatState as? SecretChatKeyState, let keyFingerprint = peerChatState.keyFingerprint {
|
|
entries.append(UserInfoEntry.secretEncryptionKey(presentationData.theme, presentationData.strings.Profile_EncryptionKey, keyFingerprint))
|
|
}
|
|
|
|
if let groupsInCommon = (cachedPeerData as? CachedUserData)?.commonGroupCount, groupsInCommon != 0 {
|
|
entries.append(UserInfoEntry.groupsInCommon(presentationData.theme, presentationData.strings.UserInfo_GroupsInCommon, presentationStringsFormattedNumber(groupsInCommon, presentationData.dateTimeFormat.groupingSeparator)))
|
|
}
|
|
|
|
if let peer = peer as? TelegramUser, let _ = peer.botInfo {
|
|
entries.append(UserInfoEntry.botReport(presentationData.theme, presentationData.strings.ReportPeer_Report))
|
|
}
|
|
|
|
if let cachedData = cachedPeerData as? CachedUserData {
|
|
if cachedData.isBlocked {
|
|
entries.append(UserInfoEntry.block(presentationData.theme, stringForBlockAction(strings: presentationData.strings, action: .unblock, peer: user), .unblock))
|
|
} else {
|
|
if let peer = peer as? TelegramUser, peer.flags.contains(.isSupport) {
|
|
} else {
|
|
entries.append(UserInfoEntry.block(presentationData.theme, stringForBlockAction(strings: presentationData.strings, action: .block, peer: user), .block))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return entries
|
|
}
|
|
|
|
private func getUserPeer(postbox: Postbox, peerId: PeerId) -> Signal<(Peer?, CachedPeerData?), NoError> {
|
|
return postbox.transaction { transaction -> (Peer?, CachedPeerData?) in
|
|
guard let peer = transaction.getPeer(peerId) else {
|
|
return (nil, nil)
|
|
}
|
|
var resultPeer: Peer?
|
|
if let peer = peer as? TelegramSecretChat {
|
|
resultPeer = transaction.getPeer(peer.regularPeerId)
|
|
} else {
|
|
resultPeer = peer
|
|
}
|
|
return (resultPeer, resultPeer.flatMap({ transaction.getPeerCachedData(peerId: $0.id) }))
|
|
}
|
|
}
|
|
|
|
public func openAddPersonContactImpl(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
|
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|
|
|> deliverOnMainQueue).start(next: { peer, cachedData in
|
|
guard let user = peer as? TelegramUser, let contactData = DeviceContactExtendedData(peer: user) else {
|
|
return
|
|
}
|
|
|
|
var shareViaException = false
|
|
if let cachedData = cachedData as? CachedUserData, let peerStatusSettings = cachedData.peerStatusSettings {
|
|
shareViaException = peerStatusSettings.contains(.addExceptionWhenAddingContact)
|
|
}
|
|
|
|
pushController(deviceContactInfoController(context: context, subject: .create(peer: user, contactData: contactData, isSharing: true, shareViaException: shareViaException, completion: { peer, stableId, contactData in
|
|
if let peer = peer as? TelegramUser {
|
|
if let phone = peer.phone, !phone.isEmpty {
|
|
}
|
|
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
present(OverlayStatusController(theme: presentationData.theme, type: .genericSuccess(presentationData.strings.AddContact_StatusSuccess(peer.compactDisplayTitle).0, true)), nil)
|
|
}
|
|
}), completed: nil, cancelled: nil))
|
|
})
|
|
}
|
|
|
|
public func userInfoController(context: AccountContext, peerId: PeerId, mode: PeerInfoControllerMode = .generic) -> ViewController {
|
|
let statePromise = ValuePromise(UserInfoState(), ignoreRepeated: true)
|
|
let stateValue = Atomic(value: UserInfoState())
|
|
let updateState: ((UserInfoState) -> UserInfoState) -> Void = { f in
|
|
statePromise.set(stateValue.modify { f($0) })
|
|
}
|
|
|
|
var pushControllerImpl: ((ViewController) -> Void)?
|
|
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
|
var openChatImpl: (() -> Void)?
|
|
var shareContactImpl: (() -> Void)?
|
|
var shareMyContactImpl: (() -> Void)?
|
|
var startSecretChatImpl: (() -> Void)?
|
|
var botAddToGroupImpl: (() -> Void)?
|
|
var shareBotImpl: (() -> Void)?
|
|
var dismissInputImpl: (() -> Void)?
|
|
var dismissImpl: (() -> Void)?
|
|
|
|
let actionsDisposable = DisposableSet()
|
|
|
|
let updatePeerNameDisposable = MetaDisposable()
|
|
actionsDisposable.add(updatePeerNameDisposable)
|
|
|
|
let updatePeerBlockedDisposable = MetaDisposable()
|
|
actionsDisposable.add(updatePeerBlockedDisposable)
|
|
|
|
let changeMuteSettingsDisposable = MetaDisposable()
|
|
actionsDisposable.add(changeMuteSettingsDisposable)
|
|
|
|
let hiddenAvatarRepresentationDisposable = MetaDisposable()
|
|
actionsDisposable.add(hiddenAvatarRepresentationDisposable)
|
|
|
|
let createSecretChatDisposable = MetaDisposable()
|
|
actionsDisposable.add(createSecretChatDisposable)
|
|
|
|
let navigateDisposable = MetaDisposable()
|
|
actionsDisposable.add(navigateDisposable)
|
|
|
|
var avatarGalleryTransitionArguments: ((AvatarGalleryEntry) -> GalleryTransitionArguments?)?
|
|
let avatarAndNameInfoContext = ItemListAvatarAndNameInfoItemContext()
|
|
var updateHiddenAvatarImpl: (() -> Void)?
|
|
|
|
var aboutLinkActionImpl: ((TextLinkItemActionType, TextLinkItem) -> Void)?
|
|
var displayAboutContextMenuImpl: ((String) -> Void)?
|
|
var displayCopyContextMenuImpl: ((UserInfoEntryTag, String) -> Void)?
|
|
var popToRootImpl: (() -> Void)?
|
|
|
|
let cachedAvatarEntries = Atomic<Promise<[AvatarGalleryEntry]>?>(value: nil)
|
|
|
|
let peerView = Promise<(PeerView, CachedPeerData?)>()
|
|
peerView.set(context.account.viewTracker.peerView(peerId, updateData: true) |> mapToSignal({ view -> Signal<(PeerView, CachedPeerData?), NoError> in
|
|
if peerId.namespace == Namespaces.Peer.SecretChat {
|
|
if let peer = peerViewMainPeer(view) {
|
|
return context.account.viewTracker.peerView(peer.id) |> map({ secretChatView -> (PeerView, CachedPeerData?) in
|
|
return (view, secretChatView.cachedData)
|
|
})
|
|
}
|
|
}
|
|
return .single((view, view.cachedData))
|
|
}))
|
|
|
|
let requestCallImpl: (Bool) -> Void = { isVideo in
|
|
let _ = (peerView.get()
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { view in
|
|
guard let peer = peerViewMainPeer(view.0) else {
|
|
return
|
|
}
|
|
|
|
if let cachedUserData = view.1 as? CachedUserData, cachedUserData.callsPrivate {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Call_ConnectionErrorTitle, text: presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
|
return
|
|
}
|
|
|
|
context.requestCall(peerId: peer.id, isVideo: isVideo, completion: {})
|
|
})
|
|
}
|
|
|
|
let arguments = UserInfoControllerArguments(context: context, avatarAndNameInfoContext: avatarAndNameInfoContext, updateEditingName: { editingName in
|
|
updateState { state in
|
|
if let _ = state.editingState {
|
|
return state.withUpdatedEditingState(UserInfoEditingState(editingName: editingName))
|
|
} else {
|
|
return state
|
|
}
|
|
}
|
|
}, tapAvatarAction: {
|
|
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId) |> deliverOnMainQueue).start(next: { peer, _ in
|
|
guard let peer = peer else {
|
|
return
|
|
}
|
|
|
|
if peer.profileImageRepresentations.isEmpty {
|
|
return
|
|
}
|
|
|
|
let galleryController = AvatarGalleryController(context: context, peer: peer, remoteEntries: cachedAvatarEntries.with { $0 }, replaceRootController: { controller, ready in
|
|
})
|
|
hiddenAvatarRepresentationDisposable.set((galleryController.hiddenMedia |> deliverOnMainQueue).start(next: { entry in
|
|
avatarAndNameInfoContext.hiddenAvatarRepresentation = entry?.representations.first?.representation
|
|
updateHiddenAvatarImpl?()
|
|
}))
|
|
presentControllerImpl?(galleryController, AvatarGalleryControllerPresentationArguments(transitionArguments: { entry in
|
|
return avatarGalleryTransitionArguments?(entry)
|
|
}))
|
|
})
|
|
}, openChat: {
|
|
openChatImpl?()
|
|
}, addContact: {
|
|
openAddPersonContactImpl(context: context, peerId: peerId, pushController: { c in
|
|
pushControllerImpl?(c)
|
|
}, present: { c, a in
|
|
presentControllerImpl?(c, a)
|
|
})
|
|
}, shareContact: {
|
|
shareContactImpl?()
|
|
}, shareMyContact: {
|
|
shareMyContactImpl?()
|
|
}, startSecretChat: {
|
|
startSecretChatImpl?()
|
|
}, changeNotificationMuteSettings: {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let _ = (context.account.postbox.transaction { transaction -> (TelegramPeerNotificationSettings, GlobalNotificationSettings) in
|
|
let peerSettings: TelegramPeerNotificationSettings = (transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings) ?? TelegramPeerNotificationSettings.defaultSettings
|
|
let globalSettings: GlobalNotificationSettings = (transaction.getPreferencesEntry(key: PreferencesKeys.globalNotifications) as? GlobalNotificationSettings) ?? GlobalNotificationSettings.defaultSettings
|
|
return (peerSettings, globalSettings)
|
|
}
|
|
|> deliverOnMainQueue).start(next: { peerSettings, globalSettings in
|
|
let soundSettings: NotificationSoundSettings?
|
|
if case .default = peerSettings.messageSound {
|
|
soundSettings = NotificationSoundSettings(value: nil)
|
|
} else {
|
|
soundSettings = NotificationSoundSettings(value: peerSettings.messageSound)
|
|
}
|
|
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()
|
|
})
|
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
}, updateSettings: { value in
|
|
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: context.account, peerId: peerId, muteInterval: value).start())
|
|
})
|
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
})
|
|
}, openSharedMedia: {
|
|
if let controller = context.sharedContext.makePeerSharedMediaController(context: context, peerId: peerId) {
|
|
pushControllerImpl?(controller)
|
|
}
|
|
}, openGroupsInCommon: {
|
|
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { peer, _ in
|
|
guard let peer = peer else {
|
|
return
|
|
}
|
|
|
|
pushControllerImpl?(groupsInCommonController(context: context, peerId: peer.id))
|
|
})
|
|
}, updatePeerBlocked: { value in
|
|
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|
|
|> take(1)
|
|
|> deliverOnMainQueue).start(next: { peer, _ in
|
|
guard let peer = peer else {
|
|
return
|
|
}
|
|
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
if let peer = peer as? TelegramUser, let _ = peer.botInfo {
|
|
updatePeerBlockedDisposable.set(requestUpdatePeerIsBlocked(account: context.account, peerId: peer.id, isBlocked: value).start())
|
|
if !value {
|
|
let _ = enqueueMessages(account: context.account, peerId: peer.id, messages: [.message(text: "/start", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]).start()
|
|
openChatImpl?()
|
|
}
|
|
} else {
|
|
if value {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let controller = ActionSheetController(presentationData: presentationData)
|
|
let dismissAction: () -> Void = { [weak controller] in
|
|
controller?.dismissAnimated()
|
|
}
|
|
var reportSpam = false
|
|
var deleteChat = false
|
|
controller.setItemGroups([
|
|
ActionSheetItemGroup(items: [
|
|
ActionSheetTextItem(title: presentationData.strings.UserInfo_BlockConfirmationTitle(peer.compactDisplayTitle).0),
|
|
/*ActionSheetCheckboxItem(title: presentationData.strings.Conversation_Moderate_Report, label: "", value: reportSpam, action: { [weak controller] checkValue in
|
|
reportSpam = checkValue
|
|
controller?.updateItem(groupIndex: 0, itemIndex: 1, { item in
|
|
if let item = item as? ActionSheetCheckboxItem {
|
|
return ActionSheetCheckboxItem(title: item.title, label: item.label, value: !item.value, action: item.action)
|
|
}
|
|
return item
|
|
})
|
|
}),
|
|
ActionSheetCheckboxItem(title: presentationData.strings.ReportSpam_DeleteThisChat, label: "", value: deleteChat, action: { [weak controller] checkValue in
|
|
deleteChat = checkValue
|
|
controller?.updateItem(groupIndex: 0, itemIndex: 2, { item in
|
|
if let item = item as? ActionSheetCheckboxItem {
|
|
return ActionSheetCheckboxItem(title: item.title, label: item.label, value: !item.value, action: item.action)
|
|
}
|
|
return item
|
|
})
|
|
}),*/
|
|
ActionSheetButtonItem(title: presentationData.strings.UserInfo_BlockActionTitle(peer.compactDisplayTitle).0, color: .destructive, action: {
|
|
dismissAction()
|
|
updatePeerBlockedDisposable.set(requestUpdatePeerIsBlocked(account: context.account, peerId: peer.id, isBlocked: true).start())
|
|
if deleteChat {
|
|
let _ = removePeerChat(account: context.account, peerId: peerId, reportChatSpam: reportSpam).start()
|
|
popToRootImpl?()
|
|
} else if reportSpam {
|
|
let _ = reportPeer(account: context.account, peerId: peerId, reason: .spam, message: "").start()
|
|
}
|
|
|
|
deleteSendMessageIntents(peerId: peerId)
|
|
})
|
|
]),
|
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
|
])
|
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
} else {
|
|
let text: String
|
|
if value {
|
|
text = presentationData.strings.UserInfo_BlockConfirmation(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).0
|
|
} else {
|
|
text = presentationData.strings.UserInfo_UnblockConfirmation(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).0
|
|
}
|
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Yes, action: {
|
|
updatePeerBlockedDisposable.set(requestUpdatePeerIsBlocked(account: context.account, peerId: peer.id, isBlocked: value).start())
|
|
})]), nil)
|
|
}
|
|
}
|
|
})
|
|
}, deleteContact: {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let controller = ActionSheetController(presentationData: presentationData)
|
|
let dismissAction: () -> Void = { [weak controller] in
|
|
controller?.dismissAnimated()
|
|
}
|
|
controller.setItemGroups([
|
|
ActionSheetItemGroup(items: [
|
|
ActionSheetButtonItem(title: presentationData.strings.UserInfo_DeleteContact, color: .destructive, action: {
|
|
dismissAction()
|
|
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|
|
|> deliverOnMainQueue).start(next: { peer, _ in
|
|
guard let peer = peer else {
|
|
return
|
|
}
|
|
let deleteContactFromDevice: Signal<Never, NoError>
|
|
if let contactDataManager = context.sharedContext.contactDataManager {
|
|
deleteContactFromDevice = contactDataManager.deleteContactWithAppSpecificReference(peerId: peer.id)
|
|
} else {
|
|
deleteContactFromDevice = .complete()
|
|
}
|
|
|
|
var deleteSignal = deleteContactPeerInteractively(account: context.account, peerId: peer.id)
|
|
|> then(deleteContactFromDevice)
|
|
|
|
let progressSignal = Signal<Never, NoError> { subscriber in
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
|
presentControllerImpl?(controller, nil)
|
|
return ActionDisposable { [weak controller] in
|
|
Queue.mainQueue().async() {
|
|
controller?.dismiss()
|
|
}
|
|
}
|
|
}
|
|
|> runOn(Queue.mainQueue())
|
|
|> delay(0.15, queue: Queue.mainQueue())
|
|
let progressDisposable = progressSignal.start()
|
|
|
|
deleteSignal = deleteSignal
|
|
|> afterDisposed {
|
|
Queue.mainQueue().async {
|
|
progressDisposable.dispose()
|
|
}
|
|
}
|
|
|
|
updatePeerBlockedDisposable.set((deleteSignal
|
|
|> deliverOnMainQueue).start(completed: {
|
|
dismissImpl?()
|
|
}))
|
|
|
|
deleteSendMessageIntents(peerId: peerId)
|
|
})
|
|
})
|
|
]),
|
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
|
])
|
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
}, displayUsernameContextMenu: { text in
|
|
let shareController = ShareController(context: context, subject: .url("\(text)"))
|
|
presentControllerImpl?(shareController, nil)
|
|
}, displayCopyContextMenu: { tag, phone in
|
|
displayCopyContextMenuImpl?(tag, phone)
|
|
}, call: {
|
|
requestCallImpl(false)
|
|
}, openCallMenu: { number in
|
|
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|
|
|> deliverOnMainQueue).start(next: { peer, _ in
|
|
if let peer = peer as? TelegramUser, let peerPhoneNumber = peer.phone, formatPhoneNumber(number) == formatPhoneNumber(peerPhoneNumber) {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let controller = ActionSheetController(presentationData: presentationData)
|
|
let dismissAction: () -> Void = { [weak controller] in
|
|
controller?.dismissAnimated()
|
|
}
|
|
controller.setItemGroups([
|
|
ActionSheetItemGroup(items: [
|
|
ActionSheetButtonItem(title: presentationData.strings.UserInfo_TelegramCall, action: {
|
|
dismissAction()
|
|
requestCallImpl(false)
|
|
}),
|
|
ActionSheetButtonItem(title: presentationData.strings.UserInfo_PhoneCall, action: {
|
|
dismissAction()
|
|
context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))")
|
|
}),
|
|
]),
|
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
|
])
|
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
} else {
|
|
context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))")
|
|
}
|
|
})
|
|
}, requestPhoneNumber: {
|
|
let _ = (requestPhoneNumber(account: context.account, peerId: peerId)
|
|
|> deliverOnMainQueue).start(completed: {
|
|
|
|
})
|
|
}, aboutLinkAction: { action, itemLink in
|
|
aboutLinkActionImpl?(action, itemLink)
|
|
}, displayAboutContextMenu: { text in
|
|
displayAboutContextMenuImpl?(text)
|
|
}, openEncryptionKey: { fingerprint in
|
|
let _ = (context.account.postbox.transaction { transaction -> Peer? in
|
|
if let peer = transaction.getPeer(peerId) as? TelegramSecretChat {
|
|
if let userPeer = transaction.getPeer(peer.regularPeerId) {
|
|
return userPeer
|
|
}
|
|
}
|
|
return nil
|
|
} |> deliverOnMainQueue).start(next: { peer in
|
|
if let peer = peer {
|
|
pushControllerImpl?(SecretChatKeyController(context: context, fingerprint: fingerprint, peer: peer))
|
|
}
|
|
})
|
|
}, addBotToGroup: {
|
|
botAddToGroupImpl?()
|
|
}, shareBot: {
|
|
shareBotImpl?()
|
|
}, botSettings: {
|
|
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|
|
|> deliverOnMainQueue).start(next: { peer in
|
|
let _ = enqueueMessages(account: context.account, peerId: peer.id, messages: [.message(text: "/settings", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]).start()
|
|
openChatImpl?()
|
|
})
|
|
}, botHelp: {
|
|
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|
|
|> deliverOnMainQueue).start(next: { peer in
|
|
let _ = enqueueMessages(account: context.account, peerId: peer.id, messages: [.message(text: "/help", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]).start()
|
|
openChatImpl?()
|
|
})
|
|
}, botPrivacy: {
|
|
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|
|
|> deliverOnMainQueue).start(next: { peer in
|
|
let _ = enqueueMessages(account: context.account, peerId: peer.id, messages: [.message(text: "/privacy", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]).start()
|
|
openChatImpl?()
|
|
})
|
|
}, report: {
|
|
presentControllerImpl?(peerReportOptionsController(context: context, subject: .peer(peerId), passthrough: false, present: { c, a in
|
|
presentControllerImpl?(c, a)
|
|
}, push: { c in
|
|
pushControllerImpl?(c)
|
|
}, completion: { _, _ in }), nil)
|
|
})
|
|
|
|
let deviceContacts: Signal<[(DeviceContactStableId, DeviceContactBasicData)], NoError> = peerView.get()
|
|
|> map { peerView -> String in
|
|
if let peer = peerView.0.peers[peerId] as? TelegramUser {
|
|
return peer.phone ?? ""
|
|
}
|
|
return ""
|
|
}
|
|
|> distinctUntilChanged
|
|
|> mapToSignal { number -> Signal<[(DeviceContactStableId, DeviceContactBasicData)], NoError> in
|
|
if number.isEmpty {
|
|
return .single([])
|
|
} else {
|
|
return context.sharedContext.contactDataManager?.basicDataForNormalizedPhoneNumber(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(number))) ?? .single([])
|
|
}
|
|
}
|
|
|
|
let globalNotificationsKey: PostboxViewKey = .preferences(keys: Set<ValueBoxKey>([PreferencesKeys.globalNotifications]))
|
|
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), peerView.get(), deviceContacts, context.account.postbox.combinedView(keys: [.peerChatState(peerId: peerId), globalNotificationsKey]))
|
|
|> map { presentationData, state, view, deviceContacts, combinedView -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|
let peer = peerViewMainPeer(view.0)
|
|
|
|
var globalNotificationSettings: GlobalNotificationSettings = .defaultSettings
|
|
if let preferencesView = combinedView.views[globalNotificationsKey] as? PreferencesView {
|
|
if let settings = preferencesView.values[PreferencesKeys.globalNotifications] as? GlobalNotificationSettings {
|
|
globalNotificationSettings = settings
|
|
}
|
|
}
|
|
|
|
if let peer = peer {
|
|
let _ = cachedAvatarEntries.modify { value in
|
|
if value != nil {
|
|
return value
|
|
} else {
|
|
let promise = Promise<[AvatarGalleryEntry]>()
|
|
promise.set(fetchedAvatarGalleryEntries(account: context.account, peer: peer))
|
|
return promise
|
|
}
|
|
}
|
|
}
|
|
var leftNavigationButton: ItemListNavigationButton?
|
|
let rightNavigationButton: ItemListNavigationButton
|
|
if let editingState = state.editingState {
|
|
leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
|
updateState {
|
|
$0.withUpdatedEditingState(nil)
|
|
}
|
|
})
|
|
|
|
var doneEnabled = true
|
|
if let editingName = editingState.editingName, editingName.isEmpty {
|
|
doneEnabled = false
|
|
}
|
|
|
|
if state.savingData {
|
|
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: doneEnabled, action: {})
|
|
} else {
|
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: doneEnabled, action: {
|
|
var updateName: ItemListAvatarAndNameInfoItemName?
|
|
updateState { state in
|
|
if let editingState = state.editingState, let editingName = editingState.editingName {
|
|
if let user = peer {
|
|
if ItemListAvatarAndNameInfoItemName(user) != editingName {
|
|
updateName = editingName
|
|
}
|
|
}
|
|
}
|
|
if updateName != nil {
|
|
return state.withUpdatedSavingData(true)
|
|
} else {
|
|
return state.withUpdatedEditingState(nil)
|
|
}
|
|
}
|
|
|
|
if let updateName = updateName, case let .personName(firstName, lastName, _) = updateName {
|
|
updatePeerNameDisposable.set((updateContactName(account: context.account, peerId: peerId, firstName: firstName, lastName: lastName)
|
|
|> deliverOnMainQueue).start(error: { _ in
|
|
updateState { state in
|
|
return state.withUpdatedSavingData(false)
|
|
}
|
|
}, completed: {
|
|
updateState { state in
|
|
return state.withUpdatedSavingData(false).withUpdatedEditingState(nil)
|
|
}
|
|
|
|
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|
|
|> mapToSignal { peer, _ -> Signal<Void, NoError> in
|
|
guard let peer = peer as? TelegramUser, let phone = peer.phone, !phone.isEmpty else {
|
|
return .complete()
|
|
}
|
|
return (context.sharedContext.contactDataManager?.basicDataForNormalizedPhoneNumber(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone))) ?? .single([]))
|
|
|> take(1)
|
|
|> mapToSignal { records -> Signal<Void, NoError> in
|
|
var signals: [Signal<DeviceContactExtendedData?, NoError>] = []
|
|
if let contactDataManager = context.sharedContext.contactDataManager {
|
|
for (id, basicData) in records {
|
|
signals.append(contactDataManager.appendContactData(DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: basicData.phoneNumbers), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: ""), to: id))
|
|
}
|
|
}
|
|
return combineLatest(signals)
|
|
|> mapToSignal { _ -> Signal<Void, NoError> in
|
|
return .complete()
|
|
}
|
|
}
|
|
}).start()
|
|
}))
|
|
}
|
|
})
|
|
}
|
|
} else {
|
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: {
|
|
if let user = peer {
|
|
updateState { state in
|
|
return state.withUpdatedEditingState(UserInfoEditingState(editingName: ItemListAvatarAndNameInfoItemName(user)))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.UserInfo_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: nil)
|
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: userInfoEntries(account: context.account, presentationData: presentationData, view: view.0, cachedPeerData: view.1, deviceContacts: deviceContacts, mode: mode, state: state, peerChatState: (combinedView.views[.peerChatState(peerId: peerId)] as? PeerChatStateView)?.chatState, globalNotificationSettings: globalNotificationSettings), style: .plain)
|
|
|
|
return (controllerState, (listState, arguments))
|
|
}
|
|
|> afterDisposed {
|
|
actionsDisposable.dispose()
|
|
}
|
|
|
|
let controller = ItemListController(context: context, state: signal)
|
|
|
|
pushControllerImpl = { [weak controller] value in
|
|
(controller?.navigationController as? NavigationController)?.pushViewController(value)
|
|
}
|
|
dismissImpl = { [weak controller] in
|
|
guard let controller = controller else {
|
|
return
|
|
}
|
|
(controller.navigationController as? NavigationController)?.filterController(controller, animated: true)
|
|
}
|
|
presentControllerImpl = { [weak controller] value, presentationArguments in
|
|
controller?.present(value, in: .window(.root), with: presentationArguments, blockInteraction: true)
|
|
}
|
|
dismissInputImpl = { [weak controller] in
|
|
controller?.view.endEditing(true)
|
|
}
|
|
openChatImpl = { [weak controller] in
|
|
if let navigationController = (controller?.navigationController as? NavigationController) {
|
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
|
|
}
|
|
}
|
|
shareContactImpl = { [weak controller] in
|
|
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|
|
|> deliverOnMainQueue).start(next: { peer, _ in
|
|
if let peer = peer as? TelegramUser, let phone = peer.phone {
|
|
let contact = TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)
|
|
let shareController = ShareController(context: context, subject: .media(.standalone(media: contact)))
|
|
controller?.present(shareController, in: .window(.root))
|
|
}
|
|
})
|
|
}
|
|
shareMyContactImpl = { [weak controller] in
|
|
let _ = (getUserPeer(postbox: context.account.postbox, peerId: context.account.peerId)
|
|
|> deliverOnMainQueue).start(next: { peer, _ in
|
|
guard let peer = peer as? TelegramUser, let phone = peer.phone else {
|
|
return
|
|
}
|
|
let contact = TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)
|
|
|
|
let _ = (enqueueMessages(account: context.account, peerId: peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: contact), replyToMessageId: nil, localGroupingKey: nil)])
|
|
|> deliverOnMainQueue).start(next: { [weak controller] _ in
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
controller?.present(OverlayStatusController(theme: presentationData.theme, type: .success), in: .window(.root))
|
|
})
|
|
})
|
|
}
|
|
startSecretChatImpl = { [weak controller] in
|
|
let _ = (context.account.postbox.transaction { transaction -> (Peer?, PeerId?) in
|
|
let peer = transaction.getPeer(peerId)
|
|
let filteredPeerIds = Array(transaction.getAssociatedPeerIds(peerId)).filter { $0.namespace == Namespaces.Peer.SecretChat }
|
|
var activeIndices: [ChatListIndex] = []
|
|
for associatedId in filteredPeerIds {
|
|
if let state = (transaction.getPeer(associatedId) as? TelegramSecretChat)?.embeddedState {
|
|
switch state {
|
|
case .active, .handshake:
|
|
if let (_, index) = transaction.getPeerChatListIndex(associatedId) {
|
|
activeIndices.append(index)
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
activeIndices.sort()
|
|
if let index = activeIndices.last {
|
|
return (peer, index.messageIndex.id.peerId)
|
|
} else {
|
|
return (peer, nil)
|
|
}
|
|
} |> deliverOnMainQueue).start(next: { peer, currentPeerId in
|
|
if let currentPeerId = currentPeerId {
|
|
if let navigationController = (controller?.navigationController as? NavigationController) {
|
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(currentPeerId)))
|
|
}
|
|
} else if let controller = controller {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let displayTitle = peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) ?? ""
|
|
controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.UserInfo_StartSecretChatConfirmation(displayTitle).0, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.UserInfo_StartSecretChatStart, action: {
|
|
var createSignal = createSecretChat(account: context.account, peerId: peerId)
|
|
var cancelImpl: (() -> Void)?
|
|
let progressSignal = Signal<Never, NoError> { subscriber in
|
|
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
|
cancelImpl?()
|
|
}))
|
|
presentControllerImpl?(controller, nil)
|
|
return ActionDisposable { [weak controller] in
|
|
Queue.mainQueue().async() {
|
|
controller?.dismiss()
|
|
}
|
|
}
|
|
}
|
|
|> runOn(Queue.mainQueue())
|
|
|> delay(0.15, queue: Queue.mainQueue())
|
|
let progressDisposable = progressSignal.start()
|
|
|
|
createSignal = createSignal
|
|
|> afterDisposed {
|
|
Queue.mainQueue().async {
|
|
progressDisposable.dispose()
|
|
}
|
|
}
|
|
cancelImpl = {
|
|
createSecretChatDisposable.set(nil)
|
|
}
|
|
|
|
createSecretChatDisposable.set((createSignal |> deliverOnMainQueue).start(next: { [weak controller] peerId in
|
|
if let navigationController = (controller?.navigationController as? NavigationController) {
|
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
|
|
}
|
|
}, error: { [weak controller] _ in
|
|
if let controller = controller {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
|
}
|
|
}))
|
|
})]), in: .window(.root))
|
|
}
|
|
})
|
|
}
|
|
botAddToGroupImpl = { [weak controller] in
|
|
guard let controller = controller else {
|
|
return
|
|
}
|
|
context.sharedContext.openResolvedUrl(.groupBotStart(peerId: peerId, payload: ""), context: context, urlContext: .generic, navigationController: controller.navigationController as? NavigationController, openPeer: { id, navigation in
|
|
|
|
}, sendFile: nil,
|
|
sendSticker: nil,
|
|
present: { c, a in
|
|
presentControllerImpl?(c, a)
|
|
}, dismissInput: {
|
|
dismissInputImpl?()
|
|
}, contentContext: nil)
|
|
}
|
|
shareBotImpl = { [weak controller] in
|
|
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|
|
|> deliverOnMainQueue).start(next: { peer, _ in
|
|
if let peer = peer as? TelegramUser, let username = peer.username {
|
|
let shareController = ShareController(context: context, subject: .url("https://t.me/\(username)"))
|
|
controller?.present(shareController, in: .window(.root))
|
|
}
|
|
})
|
|
}
|
|
avatarGalleryTransitionArguments = { [weak controller] entry in
|
|
if let controller = controller {
|
|
var result: ((ASDisplayNode, CGRect, () -> (UIView?, UIView?)), CGRect)?
|
|
controller.forEachItemNode { itemNode in
|
|
if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode {
|
|
result = itemNode.avatarTransitionNode()
|
|
}
|
|
}
|
|
if let (node, _) = result {
|
|
return GalleryTransitionArguments(transitionNode: node, addToTransitionSurface: { _ in
|
|
})
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
updateHiddenAvatarImpl = { [weak controller] in
|
|
if let controller = controller {
|
|
controller.forEachItemNode { itemNode in
|
|
if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode {
|
|
itemNode.updateAvatarHidden()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
aboutLinkActionImpl = { [weak context, weak controller] action, itemLink in
|
|
if let controller = controller, let context = context {
|
|
context.sharedContext.handleTextLinkAction(context: context, peerId: peerId, navigateDisposable: navigateDisposable, controller: controller, action: action, itemLink: itemLink)
|
|
}
|
|
}
|
|
displayAboutContextMenuImpl = { [weak controller] text in
|
|
if let strongController = controller {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
var resultItemNode: ListViewItemNode?
|
|
let _ = strongController.frameForItemNode({ itemNode in
|
|
if let itemNode = itemNode as? ItemListTextWithLabelItemNode {
|
|
if let tag = itemNode.tag as? UserInfoEntryTag {
|
|
if tag == .about {
|
|
resultItemNode = itemNode
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
if let resultItemNode = resultItemNode {
|
|
let contextMenuController = ContextMenuController(actions: [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: {
|
|
UIPasteboard.general.string = text
|
|
})])
|
|
strongController.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak resultItemNode] in
|
|
if let strongController = controller, let resultItemNode = resultItemNode {
|
|
return (resultItemNode, resultItemNode.contentBounds.insetBy(dx: 0.0, dy: -2.0), strongController.displayNode, strongController.view.bounds)
|
|
} else {
|
|
return nil
|
|
}
|
|
}))
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
displayCopyContextMenuImpl = { [weak controller] tag, value in
|
|
if let strongController = controller {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
var resultItemNode: ListViewItemNode?
|
|
let _ = strongController.frameForItemNode({ itemNode in
|
|
if let itemNode = itemNode as? ItemListTextWithLabelItemNode {
|
|
if let itemTag = itemNode.tag as? UserInfoEntryTag {
|
|
if itemTag == tag && itemNode.item?.text == value {
|
|
resultItemNode = itemNode
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
if let resultItemNode = resultItemNode {
|
|
let contextMenuController = ContextMenuController(actions: [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: {
|
|
UIPasteboard.general.string = value
|
|
})])
|
|
strongController.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak resultItemNode] in
|
|
if let strongController = controller, let resultItemNode = resultItemNode {
|
|
return (resultItemNode, resultItemNode.contentBounds.insetBy(dx: 0.0, dy: -2.0), strongController.displayNode, strongController.view.bounds)
|
|
} else {
|
|
return nil
|
|
}
|
|
}))
|
|
}
|
|
}
|
|
}
|
|
|
|
popToRootImpl = { [weak controller] in
|
|
(controller?.navigationController as? NavigationController)?.popToRoot(animated: true)
|
|
}
|
|
|
|
controller.didAppear = { [weak controller] firstTime in
|
|
guard let controller = controller, firstTime else {
|
|
return
|
|
}
|
|
|
|
var resultItemNode: ItemListAvatarAndNameInfoItemNode?
|
|
let _ = controller.frameForItemNode({ itemNode in
|
|
if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode {
|
|
resultItemNode = itemNode
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
if let resultItemNode = resultItemNode, let callButtonFrame = resultItemNode.callButtonFrame {
|
|
let _ = (ApplicationSpecificNotice.getProfileCallTips(accountManager: context.sharedContext.accountManager)
|
|
|> deliverOnMainQueue).start(next: { [weak controller] counter in
|
|
guard let controller = controller else {
|
|
return
|
|
}
|
|
|
|
var displayTip = false
|
|
if counter == 0 {
|
|
displayTip = true
|
|
} else if counter < 3 && arc4random_uniform(4) == 1 {
|
|
displayTip = true
|
|
}
|
|
if !displayTip {
|
|
return
|
|
}
|
|
let _ = ApplicationSpecificNotice.incrementProfileCallTips(accountManager: context.sharedContext.accountManager).start()
|
|
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let text: String = presentationData.strings.UserInfo_TapToCall
|
|
|
|
let tooltipController = TooltipController(content: .text(text), baseFontSize: presentationData.listsFontSize.baseDisplaySize, dismissByTapOutside: true)
|
|
controller.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { [weak resultItemNode] in
|
|
if let resultItemNode = resultItemNode {
|
|
return (resultItemNode, callButtonFrame)
|
|
}
|
|
return nil
|
|
}))
|
|
})
|
|
}
|
|
}
|
|
|
|
controller.navigationItem.backBarButtonItem = UIBarButtonItem(title: context.sharedContext.currentPresentationData.with{ $0 }.strings.Common_Back, style: .plain, target: nil, action: nil)
|
|
|
|
return controller
|
|
}
|