mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Phone numbers improvements
This commit is contained in:
parent
55c38c8590
commit
6de753b14a
@ -12190,3 +12190,19 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"EmojiPacks.UnarchiveEmojiPacksConfirmation_1" = "Unarchive %@ Pack";
|
"EmojiPacks.UnarchiveEmojiPacksConfirmation_1" = "Unarchive %@ Pack";
|
||||||
"EmojiPacks.UnarchiveEmojiPacksConfirmation_any" = "Unarchive %@ Packs";
|
"EmojiPacks.UnarchiveEmojiPacksConfirmation_any" = "Unarchive %@ Packs";
|
||||||
|
|
||||||
|
"HashtagSearch.ThisChat" = "This Chat";
|
||||||
|
"HashtagSearch.MyMessages" = "My Messages";
|
||||||
|
"HashtagSearch.PublicPosts" = "Public Posts";
|
||||||
|
|
||||||
|
"Chat.Context.Phone.AddToContacts" = "Add to Contacts";
|
||||||
|
"Chat.Context.Phone.CreateNewContact" = "Create New Contact";
|
||||||
|
"Chat.Context.Phone.AddToExistingContact" = "Add to Existing Contact";
|
||||||
|
"Chat.Context.Phone.SendMessage" = "Send Message";
|
||||||
|
"Chat.Context.Phone.TelegramVoiceCall" = "Telegram Voice Call";
|
||||||
|
"Chat.Context.Phone.TelegramVideoCall" = "Telegram Video Call";
|
||||||
|
"Chat.Context.Phone.InviteToTelegram" = "Invite to Telegram";
|
||||||
|
"Chat.Context.Phone.CallViaCarrier" = "Call via Carrier";
|
||||||
|
"Chat.Context.Phone.CopyNumber" = "Copy Number";
|
||||||
|
"Chat.Context.Phone.NotOnTelegram" = "This number is not on Telegram.";
|
||||||
|
"Chat.Context.Phone.ViewProfile" = "View Profile";
|
||||||
|
@ -932,7 +932,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
func makeOverlayAudioPlayerController(context: AccountContext, chatLocation: ChatLocation, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation?, parentNavigationController: NavigationController?) -> ViewController & OverlayAudioPlayerController
|
func makeOverlayAudioPlayerController(context: AccountContext, chatLocation: ChatLocation, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation?, parentNavigationController: NavigationController?) -> ViewController & OverlayAudioPlayerController
|
||||||
func makePeerInfoController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool, requestsContext: PeerInvitationImportersContext?) -> ViewController?
|
func makePeerInfoController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool, requestsContext: PeerInvitationImportersContext?) -> ViewController?
|
||||||
func makeChannelAdminController(context: AccountContext, peerId: PeerId, adminId: PeerId, initialParticipant: ChannelParticipant) -> ViewController?
|
func makeChannelAdminController(context: AccountContext, peerId: PeerId, adminId: PeerId, initialParticipant: ChannelParticipant) -> ViewController?
|
||||||
func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController
|
func makeDeviceContactInfoController(context: ShareControllerAccountContext, environment: ShareControllerEnvironment, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController
|
||||||
func makePeersNearbyController(context: AccountContext) -> ViewController
|
func makePeersNearbyController(context: AccountContext) -> ViewController
|
||||||
func makeComposeController(context: AccountContext) -> ViewController
|
func makeComposeController(context: AccountContext) -> ViewController
|
||||||
func makeChatListController(context: AccountContext, location: ChatListControllerLocation, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController
|
func makeChatListController(context: AccountContext, location: ChatListControllerLocation, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController
|
||||||
@ -1224,3 +1224,28 @@ public struct StickersSearchConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public protocol ShareControllerAccountContext: AnyObject {
|
||||||
|
var accountId: AccountRecordId { get }
|
||||||
|
var accountPeerId: EnginePeer.Id { get }
|
||||||
|
var stateManager: AccountStateManager { get }
|
||||||
|
var engineData: TelegramEngine.EngineData { get }
|
||||||
|
var animationCache: AnimationCache { get }
|
||||||
|
var animationRenderer: MultiAnimationRenderer { get }
|
||||||
|
var contentSettings: ContentSettings { get }
|
||||||
|
var appConfiguration: AppConfiguration { get }
|
||||||
|
|
||||||
|
func resolveInlineStickers(fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol ShareControllerEnvironment: AnyObject {
|
||||||
|
var presentationData: PresentationData { get }
|
||||||
|
var updatedPresentationData: Signal<PresentationData, NoError> { get }
|
||||||
|
var isMainApp: Bool { get }
|
||||||
|
var energyUsageSettings: EnergyUsageSettings { get }
|
||||||
|
|
||||||
|
var mediaManager: MediaManager? { get }
|
||||||
|
|
||||||
|
func setAccountUserInterfaceInUse(id: AccountRecordId) -> Disposable
|
||||||
|
func donateSendMessageIntent(account: ShareControllerAccountContext, peerIds: [EnginePeer.Id])
|
||||||
|
}
|
||||||
|
@ -971,6 +971,7 @@ public protocol ChatController: ViewController {
|
|||||||
var chatLocation: ChatLocation { get }
|
var chatLocation: ChatLocation { get }
|
||||||
var canReadHistory: ValuePromise<Bool> { get }
|
var canReadHistory: ValuePromise<Bool> { get }
|
||||||
var parentController: ViewController? { get set }
|
var parentController: ViewController? { get set }
|
||||||
|
var customNavigationController: NavigationController? { get set }
|
||||||
|
|
||||||
var purposefulAction: (() -> Void)? { get set }
|
var purposefulAction: (() -> Void)? { get set }
|
||||||
|
|
||||||
|
@ -28,13 +28,22 @@ public final class BotCheckoutController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static func fetch(context: AccountContext, source: BotPaymentInvoiceSource) -> Signal<InputData, FetchError> {
|
public static func fetch(context: AccountContext, source: BotPaymentInvoiceSource) -> Signal<InputData, FetchError> {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let theme = context.sharedContext.currentPresentationData.with { $0 }.theme
|
||||||
let themeParams: [String: Any] = [
|
let themeParams: [String: Any] = [
|
||||||
"bg_color": Int32(bitPattern: presentationData.theme.list.plainBackgroundColor.argb),
|
"bg_color": Int32(bitPattern: theme.list.plainBackgroundColor.rgb),
|
||||||
"text_color": Int32(bitPattern: presentationData.theme.list.itemPrimaryTextColor.argb),
|
"secondary_bg_color": Int32(bitPattern: theme.list.blocksBackgroundColor.rgb),
|
||||||
"link_color": Int32(bitPattern: presentationData.theme.list.itemAccentColor.argb),
|
"text_color": Int32(bitPattern: theme.list.itemPrimaryTextColor.rgb),
|
||||||
"button_color": Int32(bitPattern: presentationData.theme.list.itemCheckColors.fillColor.argb),
|
"hint_color": Int32(bitPattern: theme.list.itemSecondaryTextColor.rgb),
|
||||||
"button_text_color": Int32(bitPattern: presentationData.theme.list.itemCheckColors.foregroundColor.argb)
|
"link_color": Int32(bitPattern: theme.list.itemAccentColor.rgb),
|
||||||
|
"button_color": Int32(bitPattern: theme.list.itemCheckColors.fillColor.rgb),
|
||||||
|
"button_text_color": Int32(bitPattern: theme.list.itemCheckColors.foregroundColor.rgb),
|
||||||
|
"header_bg_color": Int32(bitPattern: theme.rootController.navigationBar.opaqueBackgroundColor.rgb),
|
||||||
|
"accent_text_color": Int32(bitPattern: theme.list.itemAccentColor.rgb),
|
||||||
|
"section_bg_color": Int32(bitPattern: theme.list.itemBlocksBackgroundColor.rgb),
|
||||||
|
"section_header_text_color": Int32(bitPattern: theme.list.freeTextColor.rgb),
|
||||||
|
"subtitle_text_color": Int32(bitPattern: theme.list.itemSecondaryTextColor.rgb),
|
||||||
|
"destructive_text_color": Int32(bitPattern: theme.list.itemDestructiveColor.rgb),
|
||||||
|
"section_separator_color": Int32(bitPattern: theme.list.itemBlocksSeparatorColor.rgb)
|
||||||
]
|
]
|
||||||
|
|
||||||
return context.engine.payments.fetchBotPaymentForm(source: source, themeParams: themeParams)
|
return context.engine.payments.fetchBotPaymentForm(source: source, themeParams: themeParams)
|
||||||
|
@ -23,6 +23,7 @@ import StoryContainerScreen
|
|||||||
import ChatListHeaderComponent
|
import ChatListHeaderComponent
|
||||||
import TelegramIntents
|
import TelegramIntents
|
||||||
import UndoUI
|
import UndoUI
|
||||||
|
import ShareController
|
||||||
|
|
||||||
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
||||||
private let controller: ViewController
|
private let controller: ViewController
|
||||||
@ -309,7 +310,7 @@ public class ContactsController: ViewController {
|
|||||||
guard let strongSelf = self, let value = value else {
|
guard let strongSelf = self, let value = value else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, id, value), completed: nil, cancelled: nil), completion: { [weak self] in
|
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: strongSelf.context), environment: ShareControllerAppEnvironment(sharedContext: strongSelf.context.sharedContext), subject: .vcard(nil, id, value), completed: nil, cancelled: nil), completion: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
|
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
|
||||||
}
|
}
|
||||||
@ -741,7 +742,7 @@ public class ContactsController: ViewController {
|
|||||||
case .allowed:
|
case .allowed:
|
||||||
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: "", lastName: "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: "+")]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: "", lastName: "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: "+")]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
||||||
if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController {
|
if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController {
|
||||||
navigationController.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
navigationController.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: strongSelf.context), environment: ShareControllerAppEnvironment(sharedContext: strongSelf.context.sharedContext), subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -755,7 +756,7 @@ public class ContactsController: ViewController {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController {
|
if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController {
|
||||||
navigationController.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil))
|
navigationController.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: strongSelf.context), environment: ShareControllerAppEnvironment(sharedContext: strongSelf.context.sharedContext), subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}), completed: nil, cancelled: nil))
|
}), completed: nil, cancelled: nil))
|
||||||
|
@ -121,6 +121,19 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
|||||||
statusNode.attributedText = NSAttributedString(string: value, font: subtitleFont, textColor: presentationData.theme.contextMenu.secondaryColor)
|
statusNode.attributedText = NSAttributedString(string: value, font: subtitleFont, textColor: presentationData.theme.contextMenu.secondaryColor)
|
||||||
statusNode.maximumNumberOfLines = 1
|
statusNode.maximumNumberOfLines = 1
|
||||||
self.statusNode = statusNode
|
self.statusNode = statusNode
|
||||||
|
case let .secondLineWithAttributedValue(value):
|
||||||
|
self.textNode.maximumNumberOfLines = 1
|
||||||
|
let statusNode = ImmediateTextNode()
|
||||||
|
statusNode.isAccessibilityElement = false
|
||||||
|
statusNode.isUserInteractionEnabled = false
|
||||||
|
statusNode.displaysAsynchronously = false
|
||||||
|
|
||||||
|
let mutableString = value.mutableCopy() as! NSMutableAttributedString
|
||||||
|
mutableString.addAttribute(.foregroundColor, value: presentationData.theme.contextMenu.secondaryColor, range: NSRange(location: 0, length: mutableString.length))
|
||||||
|
mutableString.addAttribute(.font, value: subtitleFont, range: NSRange(location: 0, length: mutableString.length))
|
||||||
|
statusNode.attributedText = mutableString
|
||||||
|
statusNode.maximumNumberOfLines = 1
|
||||||
|
self.statusNode = statusNode
|
||||||
case .multiline:
|
case .multiline:
|
||||||
self.textNode.maximumNumberOfLines = 0
|
self.textNode.maximumNumberOfLines = 0
|
||||||
self.statusNode = nil
|
self.statusNode = nil
|
||||||
@ -350,10 +363,15 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
|||||||
|
|
||||||
self.textNode.attributedText = NSAttributedString(string: self.action.text, font: titleFont, textColor: textColor)
|
self.textNode.attributedText = NSAttributedString(string: self.action.text, font: titleFont, textColor: textColor)
|
||||||
|
|
||||||
|
let subtitleFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0)
|
||||||
switch self.action.textLayout {
|
switch self.action.textLayout {
|
||||||
case let .secondLineWithValue(value):
|
case let .secondLineWithValue(value):
|
||||||
let subtitleFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0)
|
|
||||||
self.statusNode?.attributedText = NSAttributedString(string: value, font: subtitleFont, textColor: presentationData.theme.contextMenu.secondaryColor)
|
self.statusNode?.attributedText = NSAttributedString(string: value, font: subtitleFont, textColor: presentationData.theme.contextMenu.secondaryColor)
|
||||||
|
case let .secondLineWithAttributedValue(value):
|
||||||
|
let mutableString = value.mutableCopy() as! NSMutableAttributedString
|
||||||
|
mutableString.addAttribute(.foregroundColor, value: presentationData.theme.contextMenu.secondaryColor, range: NSRange(location: 0, length: mutableString.length))
|
||||||
|
mutableString.addAttribute(.font, value: subtitleFont, range: NSRange(location: 0, length: mutableString.length))
|
||||||
|
self.statusNode?.attributedText = mutableString
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ public enum ContextMenuActionItemTextLayout {
|
|||||||
case singleLine
|
case singleLine
|
||||||
case twoLinesMax
|
case twoLinesMax
|
||||||
case secondLineWithValue(String)
|
case secondLineWithValue(String)
|
||||||
|
case secondLineWithAttributedValue(NSAttributedString)
|
||||||
case multiline
|
case multiline
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2150,6 +2151,7 @@ public protocol ContextExtractedContentSource: AnyObject {
|
|||||||
var initialAppearanceOffset: CGPoint { get }
|
var initialAppearanceOffset: CGPoint { get }
|
||||||
var centerVertically: Bool { get }
|
var centerVertically: Bool { get }
|
||||||
var keepInPlace: Bool { get }
|
var keepInPlace: Bool { get }
|
||||||
|
var adjustContentHorizontally: Bool { get }
|
||||||
var adjustContentForSideInset: Bool { get }
|
var adjustContentForSideInset: Bool { get }
|
||||||
var ignoreContentTouches: Bool { get }
|
var ignoreContentTouches: Bool { get }
|
||||||
var blurBackground: Bool { get }
|
var blurBackground: Bool { get }
|
||||||
@ -2170,6 +2172,10 @@ public extension ContextExtractedContentSource {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var adjustContentHorizontally: Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
var adjustContentForSideInset: Bool {
|
var adjustContentForSideInset: Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -229,20 +229,6 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking
|
|||||||
|
|
||||||
self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
|
self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
|
||||||
|
|
||||||
var subtitle: String?
|
|
||||||
switch self.item.textLayout {
|
|
||||||
case .singleLine:
|
|
||||||
self.titleLabelNode.maximumNumberOfLines = 1
|
|
||||||
case .twoLinesMax:
|
|
||||||
self.titleLabelNode.maximumNumberOfLines = 2
|
|
||||||
case let .secondLineWithValue(subtitleValue):
|
|
||||||
self.titleLabelNode.maximumNumberOfLines = 1
|
|
||||||
subtitle = subtitleValue
|
|
||||||
case .multiline:
|
|
||||||
self.titleLabelNode.maximumNumberOfLines = 0
|
|
||||||
self.titleLabelNode.lineSpacing = 0.1
|
|
||||||
}
|
|
||||||
|
|
||||||
var forcedHeight: CGFloat?
|
var forcedHeight: CGFloat?
|
||||||
var titleVerticalOffset: CGFloat?
|
var titleVerticalOffset: CGFloat?
|
||||||
let titleFont: UIFont
|
let titleFont: UIFont
|
||||||
@ -265,6 +251,30 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking
|
|||||||
let subtitleFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0)
|
let subtitleFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0)
|
||||||
let subtitleColor = presentationData.theme.contextMenu.secondaryColor
|
let subtitleColor = presentationData.theme.contextMenu.secondaryColor
|
||||||
|
|
||||||
|
var subtitle: NSAttributedString?
|
||||||
|
switch self.item.textLayout {
|
||||||
|
case .singleLine:
|
||||||
|
self.titleLabelNode.maximumNumberOfLines = 1
|
||||||
|
case .twoLinesMax:
|
||||||
|
self.titleLabelNode.maximumNumberOfLines = 2
|
||||||
|
case let .secondLineWithValue(subtitleValue):
|
||||||
|
self.titleLabelNode.maximumNumberOfLines = 1
|
||||||
|
subtitle = NSAttributedString(
|
||||||
|
string: subtitleValue,
|
||||||
|
font: subtitleFont,
|
||||||
|
textColor: subtitleColor
|
||||||
|
)
|
||||||
|
case let .secondLineWithAttributedValue(subtitleValue):
|
||||||
|
self.titleLabelNode.maximumNumberOfLines = 1
|
||||||
|
let mutableString = subtitleValue.mutableCopy() as! NSMutableAttributedString
|
||||||
|
mutableString.addAttribute(.foregroundColor, value: subtitleColor, range: NSRange(location: 0, length: mutableString.length))
|
||||||
|
mutableString.addAttribute(.font, value: subtitleFont, range: NSRange(location: 0, length: mutableString.length))
|
||||||
|
subtitle = mutableString
|
||||||
|
case .multiline:
|
||||||
|
self.titleLabelNode.maximumNumberOfLines = 0
|
||||||
|
self.titleLabelNode.lineSpacing = 0.1
|
||||||
|
}
|
||||||
|
|
||||||
let titleColor: UIColor
|
let titleColor: UIColor
|
||||||
switch self.item.textColor {
|
switch self.item.textColor {
|
||||||
case .primary:
|
case .primary:
|
||||||
@ -308,13 +318,7 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking
|
|||||||
|
|
||||||
self.titleLabelNode.isUserInteractionEnabled = self.titleLabelNode.tapAttributeAction != nil && self.item.action == nil
|
self.titleLabelNode.isUserInteractionEnabled = self.titleLabelNode.tapAttributeAction != nil && self.item.action == nil
|
||||||
|
|
||||||
self.subtitleNode.attributedText = subtitle.flatMap { subtitle in
|
self.subtitleNode.attributedText = subtitle
|
||||||
return NSAttributedString(
|
|
||||||
string: subtitle,
|
|
||||||
font: subtitleFont,
|
|
||||||
textColor: subtitleColor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var iconSize: CGSize?
|
var iconSize: CGSize?
|
||||||
if let iconSource = self.item.iconSource {
|
if let iconSource = self.item.iconSource {
|
||||||
|
@ -1064,6 +1064,9 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
if let contentNode = itemContentNode {
|
if let contentNode = itemContentNode {
|
||||||
var contentFrame = CGRect(origin: CGPoint(x: contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingItem.contentRect.minX, y: contentRect.minY - contentNode.containingItem.contentRect.minY + contentVerticalOffset + additionalVisibleOffsetY), size: contentNode.containingItem.view.bounds.size)
|
var contentFrame = CGRect(origin: CGPoint(x: contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingItem.contentRect.minX, y: contentRect.minY - contentNode.containingItem.contentRect.minY + contentVerticalOffset + additionalVisibleOffsetY), size: contentNode.containingItem.view.bounds.size)
|
||||||
if case let .extracted(extracted) = self.source {
|
if case let .extracted(extracted) = self.source {
|
||||||
|
if extracted.adjustContentHorizontally {
|
||||||
|
contentFrame.origin.x = combinedActionsFrame.minX
|
||||||
|
}
|
||||||
if extracted.centerVertically {
|
if extracted.centerVertically {
|
||||||
if combinedActionsFrame.height.isZero {
|
if combinedActionsFrame.height.isZero {
|
||||||
contentFrame.origin.y = floorToScreenPixels((layout.size.height - contentFrame.height) / 2.0)
|
contentFrame.origin.y = floorToScreenPixels((layout.size.height - contentFrame.height) / 2.0)
|
||||||
@ -1160,7 +1163,10 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
let contentX = contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingItem.contentRect.minX
|
let contentX = contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingItem.contentRect.minX
|
||||||
let contentWidth = contentNode.containingItem.view.bounds.size.width
|
let contentWidth = contentNode.containingItem.view.bounds.size.width
|
||||||
let contentHeight = contentNode.containingItem.view.bounds.size.height
|
let contentHeight = contentNode.containingItem.view.bounds.size.height
|
||||||
if case let .extracted(extracted) = self.source, extracted.centerVertically {
|
if case let .extracted(extracted) = self.source, extracted.adjustContentHorizontally {
|
||||||
|
let fixedContentX = self.actionsContainerNode.frame.minX
|
||||||
|
animationInContentXDistance = fixedContentX - contentX
|
||||||
|
} else if case let .extracted(extracted) = self.source, extracted.centerVertically {
|
||||||
if actionsSize.height.isZero {
|
if actionsSize.height.isZero {
|
||||||
var initialContentRect = contentRect
|
var initialContentRect = contentRect
|
||||||
initialContentRect.origin.y += extracted.initialAppearanceOffset.y
|
initialContentRect.origin.y += extracted.initialAppearanceOffset.y
|
||||||
@ -1429,7 +1435,10 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
var animationInContentXDistance: CGFloat = 0.0
|
var animationInContentXDistance: CGFloat = 0.0
|
||||||
let contentX = contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingItem.contentRect.minX
|
let contentX = contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingItem.contentRect.minX
|
||||||
let contentWidth = contentNode.containingItem.view.bounds.size.width
|
let contentWidth = contentNode.containingItem.view.bounds.size.width
|
||||||
if case let .extracted(extracted) = self.source, extracted.centerVertically {
|
if case let .extracted(extracted) = self.source, extracted.adjustContentHorizontally {
|
||||||
|
let fixedContentX = self.actionsContainerNode.frame.minX
|
||||||
|
animationInContentXDistance = contentX - fixedContentX
|
||||||
|
} else if case let .extracted(extracted) = self.source, extracted.centerVertically {
|
||||||
if actionsSize.height.isZero {
|
if actionsSize.height.isZero {
|
||||||
// let fixedContentY = floorToScreenPixels((layout.size.height - contentHeight) / 2.0)
|
// let fixedContentY = floorToScreenPixels((layout.size.height - contentHeight) / 2.0)
|
||||||
animationInContentYDistance = 0.0 //contentY - fixedContentY
|
animationInContentYDistance = 0.0 //contentY - fixedContentY
|
||||||
|
@ -330,7 +330,7 @@ public func generateTintedImage(image: UIImage?, color: UIColor, backgroundColor
|
|||||||
return tintedImage
|
return tintedImage
|
||||||
}
|
}
|
||||||
|
|
||||||
public func generateGradientTintedImage(image: UIImage?, colors: [UIColor]) -> UIImage? {
|
public func generateGradientTintedImage(image: UIImage?, colors: [UIColor], direction: GradientImageDirection = .vertical) -> UIImage? {
|
||||||
guard let image = image else {
|
guard let image = image else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -357,7 +357,21 @@ public func generateGradientTintedImage(image: UIImage?, colors: [UIColor]) -> U
|
|||||||
let colorSpace = DeviceGraphicsContextSettings.shared.colorSpace
|
let colorSpace = DeviceGraphicsContextSettings.shared.colorSpace
|
||||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
||||||
|
|
||||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: imageRect.height), end: CGPoint(x: 0.0, y: 0.0), options: CGGradientDrawingOptions())
|
let start: CGPoint
|
||||||
|
let end: CGPoint
|
||||||
|
switch direction {
|
||||||
|
case .horizontal:
|
||||||
|
start = .zero
|
||||||
|
end = CGPoint(x: imageRect.width, y: 0.0)
|
||||||
|
case .vertical:
|
||||||
|
start = CGPoint(x: 0.0, y: imageRect.height)
|
||||||
|
end = .zero
|
||||||
|
case .diagonal:
|
||||||
|
start = CGPoint(x: 0.0, y: 0.0)
|
||||||
|
end = CGPoint(x: imageRect.width, y: imageRect.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.drawLinearGradient(gradient, start: start, end: end, options: CGGradientDrawingOptions())
|
||||||
} else if !colors.isEmpty {
|
} else if !colors.isEmpty {
|
||||||
context.setFillColor(colors[0].cgColor)
|
context.setFillColor(colors[0].cgColor)
|
||||||
context.fill(imageRect)
|
context.fill(imageRect)
|
||||||
|
@ -1327,8 +1327,6 @@ private extension UIBezierPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
extension UIImageView {
|
extension UIImageView {
|
||||||
func setDrawingAnimatedImage(data: Data) {
|
func setDrawingAnimatedImage(data: Data) {
|
||||||
DispatchQueue.global().async {
|
DispatchQueue.global().async {
|
||||||
@ -1354,48 +1352,3 @@ extension UIImageView {
|
|||||||
self.animationRepeatCount = 0
|
self.animationRepeatCount = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//private func prerenderEntityTransformations(entity: DrawingEntity, image: UIImage, colorSpace: CGColorSpace) -> UIImage {
|
|
||||||
// let imageSize = image.size
|
|
||||||
//
|
|
||||||
// let angle: CGFloat
|
|
||||||
// var scale: CGFloat
|
|
||||||
// let position: CGPoint
|
|
||||||
//
|
|
||||||
// if let entity = entity as? DrawingStickerEntity {
|
|
||||||
// angle = -entity.rotation
|
|
||||||
// scale = entity.scale
|
|
||||||
// position = entity.position
|
|
||||||
// } else {
|
|
||||||
// fatalError()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let rotatedSize = CGSize(
|
|
||||||
// width: abs(imageSize.width * cos(angle)) + abs(imageSize.height * sin(angle)),
|
|
||||||
// height: abs(imageSize.width * sin(angle)) + abs(imageSize.height * cos(angle))
|
|
||||||
// )
|
|
||||||
// let newSize = CGSize(width: rotatedSize.width * scale, height: rotatedSize.height * scale)
|
|
||||||
//
|
|
||||||
// let newImage = generateImage(newSize, contextGenerator: { size, context in
|
|
||||||
// context.setAllowsAntialiasing(true)
|
|
||||||
// context.setShouldAntialias(true)
|
|
||||||
// context.interpolationQuality = .high
|
|
||||||
// context.clear(CGRect(origin: .zero, size: size))
|
|
||||||
// context.translateBy(x: newSize.width * 0.5, y: newSize.height * 0.5)
|
|
||||||
// context.rotate(by: angle)
|
|
||||||
// context.scaleBy(x: scale, y: scale)
|
|
||||||
// let drawRect = CGRect(
|
|
||||||
// x: -imageSize.width * 0.5,
|
|
||||||
// y: -imageSize.height * 0.5,
|
|
||||||
// width: imageSize.width,
|
|
||||||
// height: imageSize.height
|
|
||||||
// )
|
|
||||||
// if let cgImage = image.cgImage {
|
|
||||||
// context.draw(cgImage, in: drawRect)
|
|
||||||
// }
|
|
||||||
// }, scale: 1.0)!
|
|
||||||
//
|
|
||||||
// let _ = position
|
|
||||||
//
|
|
||||||
// return newImage
|
|
||||||
//}
|
|
||||||
|
@ -1995,7 +1995,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, ASS
|
|||||||
let changeEmailActionButtonFrame: CGRect
|
let changeEmailActionButtonFrame: CGRect
|
||||||
let resendCodeActionFrame: CGRect
|
let resendCodeActionFrame: CGRect
|
||||||
let resendCodeActionButtonFrame: CGRect
|
let resendCodeActionButtonFrame: CGRect
|
||||||
if changeEmailActionSize.width + resendCodeActionSize.width > layout.size.width - buttonFrame.minX * 2.0 {
|
if changeEmailActionSize.width + resendCodeActionSize.width > layout.size.width - buttonFrame.minX * 2.0 - 32.0 {
|
||||||
changeEmailActionButtonFrame = CGRect(origin: CGPoint(x: buttonFrame.minX, y: buttonFrame.minY), size: CGSize(width: buttonFrame.width, height: buttonFrame.height))
|
changeEmailActionButtonFrame = CGRect(origin: CGPoint(x: buttonFrame.minX, y: buttonFrame.minY), size: CGSize(width: buttonFrame.width, height: buttonFrame.height))
|
||||||
changeEmailActionFrame = CGRect(origin: CGPoint(x: changeEmailActionButtonFrame.minX + floor((changeEmailActionButtonFrame.width - changeEmailActionSize.width) / 2.0), y: changeEmailActionButtonFrame.minY + floor((changeEmailActionButtonFrame.height - changeEmailActionSize.height) / 2.0)), size: changeEmailActionSize)
|
changeEmailActionFrame = CGRect(origin: CGPoint(x: changeEmailActionButtonFrame.minX + floor((changeEmailActionButtonFrame.width - changeEmailActionSize.width) / 2.0), y: changeEmailActionButtonFrame.minY + floor((changeEmailActionButtonFrame.height - changeEmailActionSize.height) / 2.0)), size: changeEmailActionSize)
|
||||||
resendCodeActionButtonFrame = CGRect(origin: CGPoint(x: buttonFrame.minX, y: buttonFrame.maxY), size: CGSize(width: buttonFrame.width, height: buttonFrame.height))
|
resendCodeActionButtonFrame = CGRect(origin: CGPoint(x: buttonFrame.minX, y: buttonFrame.maxY), size: CGSize(width: buttonFrame.width, height: buttonFrame.height))
|
||||||
|
@ -24,6 +24,8 @@ import UndoUI
|
|||||||
import GalleryUI
|
import GalleryUI
|
||||||
import PeerAvatarGalleryUI
|
import PeerAvatarGalleryUI
|
||||||
import Postbox
|
import Postbox
|
||||||
|
import ShareController
|
||||||
|
import ContextUI
|
||||||
|
|
||||||
private enum DeviceContactInfoAction {
|
private enum DeviceContactInfoAction {
|
||||||
case sendMessage
|
case sendMessage
|
||||||
@ -33,7 +35,7 @@ private enum DeviceContactInfoAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class DeviceContactInfoControllerArguments {
|
private final class DeviceContactInfoControllerArguments {
|
||||||
let context: AccountContext
|
let accountContext: AccountContext?
|
||||||
let isPlain: Bool
|
let isPlain: Bool
|
||||||
let updateEditingName: (ItemListAvatarAndNameInfoItemName) -> Void
|
let updateEditingName: (ItemListAvatarAndNameInfoItemName) -> Void
|
||||||
let updatePhone: (Int64, String) -> Void
|
let updatePhone: (Int64, String) -> Void
|
||||||
@ -50,8 +52,8 @@ private final class DeviceContactInfoControllerArguments {
|
|||||||
let updateShareViaException: (Bool) -> Void
|
let updateShareViaException: (Bool) -> Void
|
||||||
let openAvatar: (EnginePeer) -> Void
|
let openAvatar: (EnginePeer) -> Void
|
||||||
|
|
||||||
init(context: AccountContext, isPlain: Bool, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, updatePhone: @escaping (Int64, String) -> Void, updatePhoneLabel: @escaping (Int64, String) -> Void, deletePhone: @escaping (Int64) -> Void, setPhoneIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, addPhoneNumber: @escaping () -> Void, performAction: @escaping (DeviceContactInfoAction) -> Void, toggleSelection: @escaping (DeviceContactInfoDataId) -> Void, callPhone: @escaping (String) -> Void, openUrl: @escaping (String) -> Void, openAddress: @escaping (DeviceContactAddressData) -> Void, displayCopyContextMenu: @escaping (DeviceContactInfoEntryTag, String) -> Void, updateShareViaException: @escaping (Bool) -> Void, openAvatar: @escaping (EnginePeer) -> Void) {
|
init(accountContext: AccountContext?, isPlain: Bool, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, updatePhone: @escaping (Int64, String) -> Void, updatePhoneLabel: @escaping (Int64, String) -> Void, deletePhone: @escaping (Int64) -> Void, setPhoneIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, addPhoneNumber: @escaping () -> Void, performAction: @escaping (DeviceContactInfoAction) -> Void, toggleSelection: @escaping (DeviceContactInfoDataId) -> Void, callPhone: @escaping (String) -> Void, openUrl: @escaping (String) -> Void, openAddress: @escaping (DeviceContactAddressData) -> Void, displayCopyContextMenu: @escaping (DeviceContactInfoEntryTag, String) -> Void, updateShareViaException: @escaping (Bool) -> Void, openAvatar: @escaping (EnginePeer) -> Void) {
|
||||||
self.context = context
|
self.accountContext = accountContext
|
||||||
self.isPlain = isPlain
|
self.isPlain = isPlain
|
||||||
self.updateEditingName = updateEditingName
|
self.updateEditingName = updateEditingName
|
||||||
self.updatePhone = updatePhone
|
self.updatePhone = updatePhone
|
||||||
@ -404,7 +406,10 @@ private enum DeviceContactInfoEntry: ItemListNodeEntry {
|
|||||||
let arguments = arguments as! DeviceContactInfoControllerArguments
|
let arguments = arguments as! DeviceContactInfoControllerArguments
|
||||||
switch self {
|
switch self {
|
||||||
case let .info(_, _, _, dateTimeFormat, peer, state, jobSummary, _, hiddenAvatar):
|
case let .info(_, _, _, dateTimeFormat, peer, state, jobSummary, _, hiddenAvatar):
|
||||||
return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .contact, peer: peer, presence: nil, label: jobSummary, memberCount: nil, state: state, sectionId: self.section, style: arguments.isPlain ? .plain : .blocks(withTopInset: false, withExtendedBottomInset: true), editingNameUpdated: { editingName in
|
guard let accountContext = arguments.accountContext else {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
return ItemListAvatarAndNameInfoItem(accountContext: accountContext, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .contact, peer: peer, presence: nil, label: jobSummary, memberCount: nil, state: state, sectionId: self.section, style: arguments.isPlain ? .plain : .blocks(withTopInset: false, withExtendedBottomInset: true), editingNameUpdated: { editingName in
|
||||||
arguments.updateEditingName(editingName)
|
arguments.updateEditingName(editingName)
|
||||||
}, avatarTapped: {
|
}, avatarTapped: {
|
||||||
if peer.smallProfileImage != nil {
|
if peer.smallProfileImage != nil {
|
||||||
@ -625,7 +630,7 @@ private func filteredContactData(contactData: DeviceContactExtendedData, exclude
|
|||||||
return DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumbers: phoneNumbers), middleName: contactData.middleName, prefix: contactData.prefix, suffix: contactData.suffix, organization: includeJob ? contactData.organization : "", jobTitle: includeJob ? contactData.jobTitle : "", department: includeJob ? contactData.department : "", emailAddresses: emailAddresses, urls: urls, addresses: addresses, birthdayDate: includeBirthday ? contactData.birthdayDate : nil, socialProfiles: socialProfiles, instantMessagingProfiles: instantMessagingProfiles, note: includeNote ? contactData.note : "")
|
return DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumbers: phoneNumbers), middleName: contactData.middleName, prefix: contactData.prefix, suffix: contactData.suffix, organization: includeJob ? contactData.organization : "", jobTitle: includeJob ? contactData.jobTitle : "", department: includeJob ? contactData.department : "", emailAddresses: emailAddresses, urls: urls, addresses: addresses, birthdayDate: includeBirthday ? contactData.birthdayDate : nil, socialProfiles: socialProfiles, instantMessagingProfiles: instantMessagingProfiles, note: includeNote ? contactData.note : "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func deviceContactInfoEntries(account: Account, engine: TelegramEngine, presentationData: PresentationData, peer: EnginePeer?, isShare: Bool, shareViaException: Bool, contactData: DeviceContactExtendedData, isContact: Bool, state: DeviceContactInfoState, selecting: Bool, editingPhoneNumbers: Bool, hiddenAvatar: TelegramMediaImageRepresentation?) -> [DeviceContactInfoEntry] {
|
private func deviceContactInfoEntries(context: ShareControllerAccountContext, presentationData: PresentationData, peer: EnginePeer?, isShare: Bool, shareViaException: Bool, contactData: DeviceContactExtendedData, isContact: Bool, state: DeviceContactInfoState, selecting: Bool, editingPhoneNumbers: Bool, hiddenAvatar: TelegramMediaImageRepresentation?) -> [DeviceContactInfoEntry] {
|
||||||
var entries: [DeviceContactInfoEntry] = []
|
var entries: [DeviceContactInfoEntry] = []
|
||||||
|
|
||||||
var editingName: ItemListAvatarAndNameInfoItemName?
|
var editingName: ItemListAvatarAndNameInfoItemName?
|
||||||
@ -738,16 +743,20 @@ private func deviceContactInfoEntries(account: Account, engine: TelegramEngine,
|
|||||||
|
|
||||||
var addressIndex = 0
|
var addressIndex = 0
|
||||||
for address in contactData.addresses {
|
for address in contactData.addresses {
|
||||||
let signal = geocodeLocation(address: address.asPostalAddress)
|
let signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>
|
||||||
|
if let context = context as? ShareControllerAppAccountContext {
|
||||||
|
signal = geocodeLocation(address: address.asPostalAddress)
|
||||||
|> mapToSignal { coordinates -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
|
|> mapToSignal { coordinates -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
|
||||||
if let (latitude, longitude) = coordinates {
|
if let (latitude, longitude) = coordinates {
|
||||||
let resource = MapSnapshotMediaResource(latitude: latitude, longitude: longitude, width: 90, height: 90)
|
let resource = MapSnapshotMediaResource(latitude: latitude, longitude: longitude, width: 90, height: 90)
|
||||||
return chatMapSnapshotImage(engine: engine, resource: resource)
|
return chatMapSnapshotImage(engine: context.context.engine, resource: resource)
|
||||||
} else {
|
} else {
|
||||||
return .single({ _ in return nil })
|
return .single({ _ in return nil })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
signal = .single({ _ in return nil })
|
||||||
|
}
|
||||||
entries.append(.address(entries.count, addressIndex, presentationData.theme, localizedGenericContactFieldLabel(label: address.label, strings: presentationData.strings), address, signal, selecting ? !state.excludedComponents.contains(.address(address)) : nil))
|
entries.append(.address(entries.count, addressIndex, presentationData.theme, localizedGenericContactFieldLabel(label: address.label, strings: presentationData.strings), address, signal, selecting ? !state.excludedComponents.contains(.address(address)) : nil))
|
||||||
addressIndex += 1
|
addressIndex += 1
|
||||||
}
|
}
|
||||||
@ -828,7 +837,7 @@ private final class DeviceContactInfoController: ItemListController, MFMessageCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func deviceContactInfoController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController {
|
public func deviceContactInfoController(context: ShareControllerAccountContext, environment: ShareControllerEnvironment, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController {
|
||||||
var initialState = DeviceContactInfoState()
|
var initialState = DeviceContactInfoState()
|
||||||
if case let .create(peer, contactData, _, _, _) = subject {
|
if case let .create(peer, contactData, _, _, _) = subject {
|
||||||
var peerPhoneNumber: String?
|
var peerPhoneNumber: String?
|
||||||
@ -876,7 +885,11 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
|
|
||||||
var displayCopyContextMenuImpl: ((DeviceContactInfoEntryTag, String) -> Void)?
|
var displayCopyContextMenuImpl: ((DeviceContactInfoEntryTag, String) -> Void)?
|
||||||
|
|
||||||
|
let presentationData = environment.presentationData
|
||||||
let callImpl: (String) -> Void = { number in
|
let callImpl: (String) -> Void = { number in
|
||||||
|
guard let context = (context as? ShareControllerAppAccountContext)?.context else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let user: Signal<TelegramUser?, NoError>
|
let user: Signal<TelegramUser?, NoError>
|
||||||
if let peer = subject.peer {
|
if let peer = subject.peer {
|
||||||
user = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id))
|
user = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id))
|
||||||
@ -890,10 +903,10 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
} else {
|
} else {
|
||||||
user = .single(nil)
|
user = .single(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = (user
|
let _ = (user
|
||||||
|> deliverOnMainQueue).start(next: { user in
|
|> deliverOnMainQueue).start(next: { user in
|
||||||
if let user = user, let phone = user.phone, formatPhoneNumber(phone) == formatPhoneNumber(number) {
|
if let user = user, let phone = user.phone, formatPhoneNumber(phone) == formatPhoneNumber(number) {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
let controller = ActionSheetController(presentationData: presentationData)
|
let controller = ActionSheetController(presentationData: presentationData)
|
||||||
let dismissAction: () -> Void = { [weak controller] in
|
let dismissAction: () -> Void = { [weak controller] in
|
||||||
controller?.dismissAnimated()
|
controller?.dismissAnimated()
|
||||||
@ -932,7 +945,13 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
shareViaException = shareViaExceptionValue
|
shareViaException = shareViaExceptionValue
|
||||||
}
|
}
|
||||||
|
|
||||||
let arguments = DeviceContactInfoControllerArguments(context: context, isPlain: !isShare, updateEditingName: { editingName in
|
let accountContext: AccountContext?
|
||||||
|
if let context = context as? ShareControllerAppAccountContext {
|
||||||
|
accountContext = context.context
|
||||||
|
} else {
|
||||||
|
accountContext = nil
|
||||||
|
}
|
||||||
|
let arguments = DeviceContactInfoControllerArguments(accountContext: accountContext, isPlain: !isShare, updateEditingName: { editingName in
|
||||||
updateState { state in
|
updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
if let _ = state.editingState {
|
if let _ = state.editingState {
|
||||||
@ -952,6 +971,9 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}, updatePhoneLabel: { id, currentLabel in
|
}, updatePhoneLabel: { id, currentLabel in
|
||||||
|
guard let context = (context as? ShareControllerAppAccountContext)?.context else {
|
||||||
|
return
|
||||||
|
}
|
||||||
pushControllerImpl?(phoneLabelController(context: context, currentLabel: currentLabel, completion: { value in
|
pushControllerImpl?(phoneLabelController(context: context, currentLabel: currentLabel, completion: { value in
|
||||||
updateState { state in
|
updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
@ -1000,7 +1022,6 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
if subject.contactData.basicData.phoneNumbers.count == 1 {
|
if subject.contactData.basicData.phoneNumbers.count == 1 {
|
||||||
inviteAction(subject.contactData.basicData.phoneNumbers[0].value)
|
inviteAction(subject.contactData.basicData.phoneNumbers[0].value)
|
||||||
} else {
|
} else {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
let controller = ActionSheetController(presentationData: presentationData)
|
let controller = ActionSheetController(presentationData: presentationData)
|
||||||
let dismissAction: () -> Void = { [weak controller] in
|
let dismissAction: () -> Void = { [weak controller] in
|
||||||
controller?.dismissAnimated()
|
controller?.dismissAnimated()
|
||||||
@ -1020,7 +1041,7 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
}
|
}
|
||||||
case .createContact:
|
case .createContact:
|
||||||
pushControllerImpl?(deviceContactInfoController(context: context, subject: .create(peer: subject.peer, contactData: subject.contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
pushControllerImpl?(deviceContactInfoController(context: context, environment: environment, subject: .create(peer: subject.peer, contactData: subject.contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
||||||
dismissImpl?(false)
|
dismissImpl?(false)
|
||||||
}), completed: nil, cancelled: nil))
|
}), completed: nil, cancelled: nil))
|
||||||
case .addToExisting:
|
case .addToExisting:
|
||||||
@ -1059,9 +1080,9 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
})
|
})
|
||||||
|
|
||||||
let hiddenAvatarPromise = Promise<TelegramMediaImageRepresentation?>(nil)
|
let hiddenAvatarPromise = Promise<TelegramMediaImageRepresentation?>(nil)
|
||||||
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
let updatedPresentationData = updatedPresentationData?.signal ?? environment.updatedPresentationData
|
||||||
let previousEditingPhoneIds = Atomic<Set<Int64>?>(value: nil)
|
let previousEditingPhoneIds = Atomic<Set<Int64>?>(value: nil)
|
||||||
let signal = combineLatest(presentationData, statePromise.get(), contactData, hiddenAvatarPromise.get())
|
let signal = combineLatest(updatedPresentationData, statePromise.get(), contactData, hiddenAvatarPromise.get())
|
||||||
|> map { presentationData, state, peerAndContactData, hiddenAvatar -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> map { presentationData, state, peerAndContactData, hiddenAvatar -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
var presentationData = presentationData
|
var presentationData = presentationData
|
||||||
let updatedTheme = presentationData.theme.withModalBlocksBackground()
|
let updatedTheme = presentationData.theme.withModalBlocksBackground()
|
||||||
@ -1116,6 +1137,9 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
|
|
||||||
rightNavigationButton = ItemListNavigationButton(content: .text(isShare ? presentationData.strings.Common_Done : presentationData.strings.Compose_Create), style: .bold, enabled: (isShare || !filteredPhoneNumbers.isEmpty) && composedContactData != nil, action: {
|
rightNavigationButton = ItemListNavigationButton(content: .text(isShare ? presentationData.strings.Common_Done : presentationData.strings.Compose_Create), style: .bold, enabled: (isShare || !filteredPhoneNumbers.isEmpty) && composedContactData != nil, action: {
|
||||||
if let composedContactData = composedContactData {
|
if let composedContactData = composedContactData {
|
||||||
|
guard let context = (context as? ShareControllerAppAccountContext)?.context else {
|
||||||
|
return
|
||||||
|
}
|
||||||
var addToPrivacyExceptions = false
|
var addToPrivacyExceptions = false
|
||||||
updateState { state in
|
updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
@ -1129,7 +1153,7 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
if share, filteredPhoneNumbers.count <= 1, let peer = peer {
|
if share, filteredPhoneNumbers.count <= 1, let peer = peer {
|
||||||
addContactDisposable.set((context.engine.contacts.addContactInteractively(peerId: peer.id, firstName: composedContactData.basicData.firstName, lastName: composedContactData.basicData.lastName, phoneNumber: filteredPhoneNumbers.first?.value ?? "", addToPrivacyExceptions: shareViaException && addToPrivacyExceptions)
|
addContactDisposable.set((context.engine.contacts.addContactInteractively(peerId: peer.id, firstName: composedContactData.basicData.firstName, lastName: composedContactData.basicData.lastName, phoneNumber: filteredPhoneNumbers.first?.value ?? "", addToPrivacyExceptions: shareViaException && addToPrivacyExceptions)
|
||||||
|> deliverOnMainQueue).start(error: { _ in
|
|> deliverOnMainQueue).start(error: { _ in
|
||||||
presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
presentControllerImpl?(textAlertController(context: context, updatedPresentationData: (environment.presentationData, updatedPresentationData), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||||
}, completed: {
|
}, completed: {
|
||||||
let _ = (contactDataManager.createContactWithData(composedContactData)
|
let _ = (contactDataManager.createContactWithData(composedContactData)
|
||||||
|> deliverOnMainQueue).start(next: { contactIdAndData in
|
|> deliverOnMainQueue).start(next: { contactIdAndData in
|
||||||
@ -1250,7 +1274,7 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
focusItemTag = DeviceContactInfoEntryTag.editingPhone(insertedPhoneId)
|
focusItemTag = DeviceContactInfoEntryTag.editingPhone(insertedPhoneId)
|
||||||
}
|
}
|
||||||
|
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: deviceContactInfoEntries(account: context.account, engine: context.engine, presentationData: presentationData, peer: peerAndContactData.0, isShare: isShare, shareViaException: shareViaException, contactData: peerAndContactData.2, isContact: peerAndContactData.1 != nil, state: state, selecting: selecting, editingPhoneNumbers: editingPhones, hiddenAvatar: hiddenAvatar), style: isShare ? .blocks : .plain, focusItemTag: focusItemTag)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: deviceContactInfoEntries(context: context, presentationData: presentationData, peer: peerAndContactData.0, isShare: isShare, shareViaException: shareViaException, contactData: peerAndContactData.2, isContact: peerAndContactData.1 != nil, state: state, selecting: selecting, editingPhoneNumbers: editingPhones, hiddenAvatar: hiddenAvatar), style: isShare ? .blocks : .plain, focusItemTag: focusItemTag)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
@ -1258,24 +1282,27 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
actionsDisposable.dispose()
|
actionsDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = DeviceContactInfoController(context: context, state: signal)
|
let controller = DeviceContactInfoController(presentationData: ItemListPresentationData(environment.presentationData), updatedPresentationData: environment.updatedPresentationData |> map { ItemListPresentationData($0) }, state: signal, tabBarItem: nil)
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
addToExistingImpl = { [weak controller] in
|
addToExistingImpl = { [weak controller] in
|
||||||
guard let controller = controller else {
|
guard let controller, let accountContext = (context as? ShareControllerAppAccountContext)?.context else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
addContactToExisting(context: context, parentController: controller, contactData: subject.contactData, completion: { peer, contactId, contactData in
|
addContactToExisting(context: accountContext, parentController: controller, contactData: subject.contactData, completion: { peer, contactId, contactData in
|
||||||
replaceControllerImpl?(deviceContactInfoController(context: context, subject: .vcard(peer?._asPeer(), contactId, contactData), completed: nil, cancelled: nil))
|
replaceControllerImpl?(deviceContactInfoController(context: context, environment: environment, subject: .vcard(peer?._asPeer(), contactId, contactData), completed: nil, cancelled: nil))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
openChatImpl = { [weak controller] peerId in
|
openChatImpl = { [weak controller] peerId in
|
||||||
|
guard let controller, let context = (context as? ShareControllerAppAccountContext)?.context else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||||
|> deliverOnMainQueue).start(next: { peer in
|
|> deliverOnMainQueue).start(next: { [weak controller] peer in
|
||||||
guard let peer = peer else {
|
guard let peer, let controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let navigationController = (controller?.navigationController as? NavigationController) {
|
if let navigationController = (controller.navigationController as? NavigationController) {
|
||||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer)))
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1301,7 +1328,7 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
inviteImpl = { [weak controller] numbers in
|
inviteImpl = { [weak controller] numbers in
|
||||||
controller?.inviteContact(presentationData: context.sharedContext.currentPresentationData.with { $0 }, numbers: numbers)
|
controller?.inviteContact(presentationData: environment.presentationData, numbers: numbers)
|
||||||
}
|
}
|
||||||
openAddressImpl = { [weak controller] address in
|
openAddressImpl = { [weak controller] address in
|
||||||
guard let _ = controller else {
|
guard let _ = controller else {
|
||||||
@ -1309,7 +1336,7 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
openUrlImpl = { [weak controller] url in
|
openUrlImpl = { [weak controller] url in
|
||||||
guard let controller = controller else {
|
guard let controller, let context = (context as? ShareControllerAppAccountContext)?.context else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: controller.navigationController as? NavigationController, dismissInput: { [weak controller] in
|
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: controller.navigationController as? NavigationController, dismissInput: { [weak controller] in
|
||||||
@ -1319,7 +1346,7 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
|
|
||||||
displayCopyContextMenuImpl = { [weak controller] tag, value in
|
displayCopyContextMenuImpl = { [weak controller] tag, value in
|
||||||
if let strongController = controller {
|
if let strongController = controller {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = environment.presentationData
|
||||||
var resultItemNode: ListViewItemNode?
|
var resultItemNode: ListViewItemNode?
|
||||||
let _ = strongController.frameForItemNode({ itemNode in
|
let _ = strongController.frameForItemNode({ itemNode in
|
||||||
if let itemNode = itemNode as? ItemListTextWithLabelItemNode {
|
if let itemNode = itemNode as? ItemListTextWithLabelItemNode {
|
||||||
@ -1350,6 +1377,9 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
openAvatarImpl = { [weak controller] peer in
|
openAvatarImpl = { [weak controller] peer in
|
||||||
|
guard let context = (context as? ShareControllerAppAccountContext)?.context else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let avatarController = AvatarGalleryController(context: context, peer: peer, replaceRootController: { _, _ in
|
let avatarController = AvatarGalleryController(context: context, peer: peer, replaceRootController: { _, _ in
|
||||||
})
|
})
|
||||||
hiddenAvatarPromise.set(
|
hiddenAvatarPromise.set(
|
||||||
@ -1413,7 +1443,7 @@ private func addContactToExisting(context: AccountContext, parentController: Vie
|
|||||||
let _ = (dataSignal
|
let _ = (dataSignal
|
||||||
|> deliverOnMainQueue).start(next: { peer, stableId in
|
|> deliverOnMainQueue).start(next: { peer, stableId in
|
||||||
guard let stableId = stableId else {
|
guard let stableId = stableId else {
|
||||||
parentController.present(deviceContactInfoController(context: context, subject: .create(peer: peer?._asPeer(), contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
parentController.present(deviceContactInfoController(context: ShareControllerAppAccountContext(context: context), environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext), subject: .create(peer: peer?._asPeer(), contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
||||||
}), completed: nil, cancelled: nil), in: .window(.root))
|
}), completed: nil, cancelled: nil), in: .window(.root))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1459,7 +1489,7 @@ func addContactOptionsController(context: AccountContext, peer: EnginePeer?, con
|
|||||||
controller.setItemGroups([
|
controller.setItemGroups([
|
||||||
ActionSheetItemGroup(items: [
|
ActionSheetItemGroup(items: [
|
||||||
ActionSheetButtonItem(title: presentationData.strings.Profile_CreateNewContact, action: { [weak controller] in
|
ActionSheetButtonItem(title: presentationData.strings.Profile_CreateNewContact, action: { [weak controller] in
|
||||||
controller?.present(context.sharedContext.makeDeviceContactInfoController(context: context, subject: .create(peer: peer?._asPeer(), contactData: contactData, isSharing: peer != nil, shareViaException: false, completion: { _, _, _ in
|
controller?.present(context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: context), environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext), subject: .create(peer: peer?._asPeer(), contactData: contactData, isSharing: peer != nil, shareViaException: false, completion: { _, _, _ in
|
||||||
}), completed: nil, cancelled: nil), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
}), completed: nil, cancelled: nil), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
dismissAction()
|
dismissAction()
|
||||||
}),
|
}),
|
||||||
@ -1476,3 +1506,32 @@ func addContactOptionsController(context: AccountContext, peer: EnginePeer?, con
|
|||||||
])
|
])
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func pushContactContextOptionsController(context: AccountContext, contextController: ContextControllerProtocol, presentationData: PresentationData, peer: EnginePeer?, contactData: DeviceContactExtendedData, parentController: ViewController, push: @escaping (ViewController) -> Void) {
|
||||||
|
var items: [ContextMenuItem] = []
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in
|
||||||
|
c?.popItems()
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
items.append(.separator)
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: presentationData.strings.Chat_Context_Phone_CreateNewContact, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||||
|
f(.default)
|
||||||
|
|
||||||
|
push(context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: context), environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext), subject: .create(peer: peer?._asPeer(), contactData: contactData, isSharing: peer != nil, shareViaException: false, completion: { _, _, _ in
|
||||||
|
}), completed: nil, cancelled: nil))
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: presentationData.strings.Chat_Context_Phone_AddToExistingContact, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MoveToContacts"), color: theme.contextMenu.primaryColor) }, action: { [weak parentController] _, f in
|
||||||
|
f(.default)
|
||||||
|
guard let parentController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addContactToExisting(context: context, parentController: parentController, contactData: contactData, completion: { peer, contactId, contactData in
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
contextController.pushItems(items: .single(ContextController.Items(content: .list(items))))
|
||||||
|
}
|
||||||
|
@ -59,7 +59,7 @@ public func openAddPersonContactImpl(context: AccountContext, updatedPresentatio
|
|||||||
shareViaException = statusSettings.contains(.addExceptionWhenAddingContact)
|
shareViaException = statusSettings.contains(.addExceptionWhenAddingContact)
|
||||||
}
|
}
|
||||||
|
|
||||||
pushController(deviceContactInfoController(context: context, updatedPresentationData: updatedPresentationData, subject: .create(peer: user, contactData: contactData, isSharing: true, shareViaException: shareViaException, completion: { peer, stableId, contactData in
|
pushController(deviceContactInfoController(context: ShareControllerAppAccountContext(context: context), environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext), updatedPresentationData: updatedPresentationData, subject: .create(peer: user, contactData: contactData, isSharing: true, shareViaException: shareViaException, completion: { peer, stableId, contactData in
|
||||||
if let peer = peer as? TelegramUser {
|
if let peer = peer as? TelegramUser {
|
||||||
completion()
|
completion()
|
||||||
|
|
||||||
|
@ -304,18 +304,6 @@ private func collectExternalShareItems(strings: PresentationStrings, dateTimeFor
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol ShareControllerEnvironment: AnyObject {
|
|
||||||
var presentationData: PresentationData { get }
|
|
||||||
var updatedPresentationData: Signal<PresentationData, NoError> { get }
|
|
||||||
var isMainApp: Bool { get }
|
|
||||||
var energyUsageSettings: EnergyUsageSettings { get }
|
|
||||||
|
|
||||||
var mediaManager: MediaManager? { get }
|
|
||||||
|
|
||||||
func setAccountUserInterfaceInUse(id: AccountRecordId) -> Disposable
|
|
||||||
func donateSendMessageIntent(account: ShareControllerAccountContext, peerIds: [EnginePeer.Id])
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class ShareControllerAppEnvironment: ShareControllerEnvironment {
|
public final class ShareControllerAppEnvironment: ShareControllerEnvironment {
|
||||||
let sharedContext: SharedAccountContext
|
let sharedContext: SharedAccountContext
|
||||||
|
|
||||||
@ -353,19 +341,6 @@ public final class ShareControllerAppEnvironment: ShareControllerEnvironment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol ShareControllerAccountContext: AnyObject {
|
|
||||||
var accountId: AccountRecordId { get }
|
|
||||||
var accountPeerId: EnginePeer.Id { get }
|
|
||||||
var stateManager: AccountStateManager { get }
|
|
||||||
var engineData: TelegramEngine.EngineData { get }
|
|
||||||
var animationCache: AnimationCache { get }
|
|
||||||
var animationRenderer: MultiAnimationRenderer { get }
|
|
||||||
var contentSettings: ContentSettings { get }
|
|
||||||
var appConfiguration: AppConfiguration { get }
|
|
||||||
|
|
||||||
func resolveInlineStickers(fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class ShareControllerAppAccountContext: ShareControllerAccountContext {
|
public final class ShareControllerAppAccountContext: ShareControllerAccountContext {
|
||||||
public let context: AccountContext
|
public let context: AccountContext
|
||||||
|
|
||||||
|
@ -349,7 +349,6 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
|||||||
var disabledPeerSelected: ((EnginePeer) -> Void)?
|
var disabledPeerSelected: ((EnginePeer) -> Void)?
|
||||||
|
|
||||||
let ready = Promise<Bool>()
|
let ready = Promise<Bool>()
|
||||||
private var didSetReady = false
|
|
||||||
|
|
||||||
private var controllerInteraction: ShareControllerInteraction?
|
private var controllerInteraction: ShareControllerInteraction?
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ public final class ChatBotInfoItemNode: ListViewItemNode {
|
|||||||
break
|
break
|
||||||
case .ignore:
|
case .ignore:
|
||||||
return .fail
|
return .fail
|
||||||
case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji:
|
case .url, .phone, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji:
|
||||||
return .waitForSingleTap
|
return .waitForSingleTap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,7 @@ public struct ChatMessageBubbleContentTapAction {
|
|||||||
public enum Content {
|
public enum Content {
|
||||||
case none
|
case none
|
||||||
case url(Url)
|
case url(Url)
|
||||||
|
case phone(String)
|
||||||
case textMention(String)
|
case textMention(String)
|
||||||
case peerMention(peerId: PeerId, mention: String, openProfile: Bool)
|
case peerMention(peerId: PeerId, mention: String, openProfile: Bool)
|
||||||
case botCommand(String)
|
case botCommand(String)
|
||||||
|
@ -770,24 +770,24 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.mainContextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtractedToContextPreview, _ in
|
self.mainContextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtractedToContextPreview, _ in
|
||||||
guard let strongSelf = self, let _ = strongSelf.item else {
|
guard let self, let _ = self.item else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for contentNode in strongSelf.contentNodes {
|
for contentNode in self.contentNodes {
|
||||||
contentNode.willUpdateIsExtractedToContextPreview(isExtractedToContextPreview)
|
contentNode.willUpdateIsExtractedToContextPreview(isExtractedToContextPreview)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.mainContextSourceNode.isExtractedToContextPreviewUpdated = { [weak self] isExtractedToContextPreview in
|
self.mainContextSourceNode.isExtractedToContextPreviewUpdated = { [weak self] isExtractedToContextPreview in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.backgroundWallpaperNode.setMaskMode(strongSelf.backgroundMaskMode)
|
self.backgroundWallpaperNode.setMaskMode(self.backgroundMaskMode)
|
||||||
strongSelf.backgroundNode.setMaskMode(strongSelf.backgroundMaskMode)
|
self.backgroundNode.setMaskMode(self.backgroundMaskMode)
|
||||||
if !isExtractedToContextPreview, let (rect, size) = strongSelf.absoluteRect {
|
if !isExtractedToContextPreview, let (rect, size) = self.absoluteRect {
|
||||||
strongSelf.updateAbsoluteRect(rect, within: size)
|
self.updateAbsoluteRect(rect, within: size)
|
||||||
}
|
}
|
||||||
|
|
||||||
for contentNode in strongSelf.contentNodes {
|
for contentNode in self.contentNodes {
|
||||||
contentNode.updateIsExtractedToContextPreview(isExtractedToContextPreview)
|
contentNode.updateIsExtractedToContextPreview(isExtractedToContextPreview)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1157,7 +1157,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
break
|
break
|
||||||
case .ignore:
|
case .ignore:
|
||||||
return .fail
|
return .fail
|
||||||
case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji:
|
case .url, .phone, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji:
|
||||||
return .waitForSingleTap
|
return .waitForSingleTap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4589,6 +4589,16 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
item.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url.url, concealed: url.concealed, message: item.content.firstMessage, allowInlineWebpageResolution: url.allowInlineWebpageResolution, progress: tapAction.activate?()))
|
item.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url.url, concealed: url.concealed, message: item.content.firstMessage, allowInlineWebpageResolution: url.allowInlineWebpageResolution, progress: tapAction.activate?()))
|
||||||
}, contextMenuOnLongPress: !tapAction.hasLongTapAction))
|
}, contextMenuOnLongPress: !tapAction.hasLongTapAction))
|
||||||
}
|
}
|
||||||
|
case let .phone(number):
|
||||||
|
return .action(InternalBubbleTapAction.Action({ [weak self] in
|
||||||
|
guard let self, let item = self.item, let contentNode = self.contextContentNodeForPhoneNumber(number) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addSubnode(contentNode)
|
||||||
|
|
||||||
|
item.controllerInteraction.openPhoneContextMenu(ChatControllerInteraction.OpenPhone(number: number, message: item.content.firstMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?()))
|
||||||
|
}, contextMenuOnLongPress: !tapAction.hasLongTapAction))
|
||||||
case let .peerMention(peerId, _, openProfile):
|
case let .peerMention(peerId, _, openProfile):
|
||||||
return .action(InternalBubbleTapAction.Action { [weak self] in
|
return .action(InternalBubbleTapAction.Action { [weak self] in
|
||||||
if let item = self?.item {
|
if let item = self?.item {
|
||||||
@ -4751,6 +4761,16 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
} else {
|
} else {
|
||||||
disableDefaultPressAnimation = true
|
disableDefaultPressAnimation = true
|
||||||
}
|
}
|
||||||
|
case let .phone(number):
|
||||||
|
return .action(InternalBubbleTapAction.Action({ [weak self] in
|
||||||
|
guard let self, let item = self.item, let contentNode = self.contextContentNodeForPhoneNumber(number) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addSubnode(contentNode)
|
||||||
|
|
||||||
|
item.controllerInteraction.openPhoneContextMenu(ChatControllerInteraction.OpenPhone(number: number, message: item.content.firstMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?()))
|
||||||
|
}, contextMenuOnLongPress: !tapAction.hasLongTapAction))
|
||||||
case let .peerMention(peerId, mention, _):
|
case let .peerMention(peerId, mention, _):
|
||||||
return .action(InternalBubbleTapAction.Action {
|
return .action(InternalBubbleTapAction.Action {
|
||||||
item.controllerInteraction.longTap(.peerMention(peerId, mention), message)
|
item.controllerInteraction.longTap(.peerMention(peerId, mention), message)
|
||||||
@ -4818,6 +4838,39 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func contextContentNodeForPhoneNumber(_ number: String) -> ContextExtractedContentContainingNode? {
|
||||||
|
guard let item = self.item else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let containingNode = ContextExtractedContentContainingNode()
|
||||||
|
|
||||||
|
let incoming = item.content.effectivelyIncoming(item.context.account.peerId, associatedData: item.associatedData)
|
||||||
|
|
||||||
|
let textNode = ImmediateTextNode()
|
||||||
|
textNode.attributedText = NSAttributedString(string: number, font: Font.regular(item.presentationData.fontSize.baseDisplaySize), textColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.linkTextColor : item.presentationData.theme.theme.chat.message.outgoing.linkTextColor)
|
||||||
|
let textSize = textNode.updateLayout(CGSize(width: 1000.0, height: 100.0))
|
||||||
|
|
||||||
|
let backgroundNode = ASDisplayNode()
|
||||||
|
backgroundNode.backgroundColor = (incoming ? item.presentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper.fill : item.presentationData.theme.theme.chat.message.outgoing.bubble.withoutWallpaper.fill).first ?? .black
|
||||||
|
backgroundNode.clipsToBounds = true
|
||||||
|
backgroundNode.cornerRadius = 10.0
|
||||||
|
|
||||||
|
let insets = UIEdgeInsets(top: 5.0, left: 8.0, bottom: 5.0, right: 8.0)
|
||||||
|
let backgroundSize = CGSize(width: textSize.width + insets.left + insets.right, height: textSize.height + insets.top + insets.bottom)
|
||||||
|
backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backgroundSize)
|
||||||
|
textNode.frame = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: textSize)
|
||||||
|
backgroundNode.addSubnode(textNode)
|
||||||
|
|
||||||
|
containingNode.frame = CGRect(origin: CGPoint(x: self.backgroundNode.frame.minX + 3.0, y: 1.0), size: CGSize(width: backgroundSize.width, height: backgroundSize.height + 20.0))
|
||||||
|
containingNode.contentNode.frame = CGRect(origin: .zero, size: backgroundSize)
|
||||||
|
containingNode.contentRect = CGRect(origin: .zero, size: backgroundSize)
|
||||||
|
containingNode.contentNode.addSubnode(backgroundNode)
|
||||||
|
|
||||||
|
containingNode.contentNode.alpha = 0.0
|
||||||
|
|
||||||
|
return containingNode
|
||||||
|
}
|
||||||
|
|
||||||
private func traceSelectionNodes(parent: ASDisplayNode, point: CGPoint) -> ASDisplayNode? {
|
private func traceSelectionNodes(parent: ASDisplayNode, point: CGPoint) -> ASDisplayNode? {
|
||||||
if let parent = parent as? FileMessageSelectionNode, parent.bounds.contains(point) {
|
if let parent = parent as? FileMessageSelectionNode, parent.bounds.contains(point) {
|
||||||
return parent
|
return parent
|
||||||
|
@ -857,7 +857,15 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
urlRange = urlRangeValue
|
urlRange = urlRangeValue
|
||||||
concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText)
|
concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText)
|
||||||
}
|
}
|
||||||
return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: concealed)), activate: { [weak self] in
|
|
||||||
|
var content: ChatMessageBubbleContentTapAction.Content
|
||||||
|
if url.hasPrefix("tel:") {
|
||||||
|
content = .phone(url.replacingOccurrences(of: "tel:", with: ""))
|
||||||
|
} else {
|
||||||
|
content = .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: concealed))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ChatMessageBubbleContentTapAction(content: content, activate: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -614,6 +614,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}, openRecommendedChannelContextMenu: { _, _, _ in
|
}, openRecommendedChannelContextMenu: { _, _, _ in
|
||||||
}, openGroupBoostInfo: { _, _ in
|
}, openGroupBoostInfo: { _, _ in
|
||||||
}, openStickerEditor: {
|
}, openStickerEditor: {
|
||||||
|
}, openPhoneContextMenu: { _ in
|
||||||
|
}, openAgeRestrictedMessageMedia: { _, _ in
|
||||||
}, requestMessageUpdate: { _, _ in
|
}, requestMessageUpdate: { _, _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, dismissTextInput: {
|
}, dismissTextInput: {
|
||||||
|
@ -148,6 +148,22 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct OpenPhone {
|
||||||
|
public var number: String
|
||||||
|
public var message: Message
|
||||||
|
public var contentNode: ContextExtractedContentContainingNode
|
||||||
|
public var messageNode: ASDisplayNode
|
||||||
|
public var progress: Promise<Bool>?
|
||||||
|
|
||||||
|
public init(number: String, message: Message, contentNode: ContextExtractedContentContainingNode, messageNode: ASDisplayNode, progress: Promise<Bool>? = nil) {
|
||||||
|
self.number = number
|
||||||
|
self.message = message
|
||||||
|
self.contentNode = contentNode
|
||||||
|
self.messageNode = messageNode
|
||||||
|
self.progress = progress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public let openMessage: (Message, OpenMessageParams) -> Bool
|
public let openMessage: (Message, OpenMessageParams) -> Bool
|
||||||
public let openPeer: (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, OpenPeerSource) -> Void
|
public let openPeer: (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, OpenPeerSource) -> Void
|
||||||
public let openPeerMention: (String, Promise<Bool>?) -> Void
|
public let openPeerMention: (String, Promise<Bool>?) -> Void
|
||||||
@ -242,6 +258,8 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
public let openRecommendedChannelContextMenu: (EnginePeer, UIView, ContextGesture?) -> Void
|
public let openRecommendedChannelContextMenu: (EnginePeer, UIView, ContextGesture?) -> Void
|
||||||
public let openGroupBoostInfo: (EnginePeer.Id?, Int) -> Void
|
public let openGroupBoostInfo: (EnginePeer.Id?, Int) -> Void
|
||||||
public let openStickerEditor: () -> Void
|
public let openStickerEditor: () -> Void
|
||||||
|
public let openPhoneContextMenu: (OpenPhone) -> Void
|
||||||
|
public let openAgeRestrictedMessageMedia: (Message, @escaping () -> Void) -> Void
|
||||||
|
|
||||||
public let requestMessageUpdate: (MessageId, Bool) -> Void
|
public let requestMessageUpdate: (MessageId, Bool) -> Void
|
||||||
public let cancelInteractiveKeyboardGestures: () -> Void
|
public let cancelInteractiveKeyboardGestures: () -> Void
|
||||||
@ -367,6 +385,8 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
openRecommendedChannelContextMenu: @escaping (EnginePeer, UIView, ContextGesture?) -> Void,
|
openRecommendedChannelContextMenu: @escaping (EnginePeer, UIView, ContextGesture?) -> Void,
|
||||||
openGroupBoostInfo: @escaping (EnginePeer.Id?, Int) -> Void,
|
openGroupBoostInfo: @escaping (EnginePeer.Id?, Int) -> Void,
|
||||||
openStickerEditor: @escaping () -> Void,
|
openStickerEditor: @escaping () -> Void,
|
||||||
|
openPhoneContextMenu: @escaping (OpenPhone) -> Void,
|
||||||
|
openAgeRestrictedMessageMedia: @escaping (Message, @escaping () -> Void) -> Void,
|
||||||
requestMessageUpdate: @escaping (MessageId, Bool) -> Void,
|
requestMessageUpdate: @escaping (MessageId, Bool) -> Void,
|
||||||
cancelInteractiveKeyboardGestures: @escaping () -> Void,
|
cancelInteractiveKeyboardGestures: @escaping () -> Void,
|
||||||
dismissTextInput: @escaping () -> Void,
|
dismissTextInput: @escaping () -> Void,
|
||||||
@ -472,6 +492,8 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
self.openRecommendedChannelContextMenu = openRecommendedChannelContextMenu
|
self.openRecommendedChannelContextMenu = openRecommendedChannelContextMenu
|
||||||
self.openGroupBoostInfo = openGroupBoostInfo
|
self.openGroupBoostInfo = openGroupBoostInfo
|
||||||
self.openStickerEditor = openStickerEditor
|
self.openStickerEditor = openStickerEditor
|
||||||
|
self.openPhoneContextMenu = openPhoneContextMenu
|
||||||
|
self.openAgeRestrictedMessageMedia = openAgeRestrictedMessageMedia
|
||||||
|
|
||||||
self.requestMessageUpdate = requestMessageUpdate
|
self.requestMessageUpdate = requestMessageUpdate
|
||||||
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures
|
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures
|
||||||
|
@ -3326,6 +3326,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
}, openRecommendedChannelContextMenu: { _, _, _ in
|
}, openRecommendedChannelContextMenu: { _, _, _ in
|
||||||
}, openGroupBoostInfo: { _, _ in
|
}, openGroupBoostInfo: { _, _ in
|
||||||
}, openStickerEditor: {
|
}, openStickerEditor: {
|
||||||
|
}, openPhoneContextMenu: { _ in
|
||||||
|
}, openAgeRestrictedMessageMedia: { _, _ in
|
||||||
}, requestMessageUpdate: { _, _ in
|
}, requestMessageUpdate: { _, _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, dismissTextInput: {
|
}, dismissTextInput: {
|
||||||
|
@ -492,19 +492,13 @@ public class ShareRootControllerImpl {
|
|||||||
|
|
||||||
let displayShare: () -> Void = {
|
let displayShare: () -> Void = {
|
||||||
var cancelImpl: (() -> Void)?
|
var cancelImpl: (() -> Void)?
|
||||||
let _ = cancelImpl
|
|
||||||
|
|
||||||
let beginShare: () -> Void = {
|
let beginShare: () -> Void = {
|
||||||
let requestUserInteraction: ([UnpreparedShareItemContent]) -> Signal<[PreparedShareItemContent], NoError> = { content in
|
let requestUserInteraction: ([UnpreparedShareItemContent]) -> Signal<[PreparedShareItemContent], NoError> = { content in
|
||||||
return Signal { [weak self] subscriber in
|
return Signal { [weak self] subscriber in
|
||||||
switch content[0] {
|
switch content[0] {
|
||||||
case let .contact(data):
|
case let .contact(data):
|
||||||
#if !DEBUG
|
let controller = deviceContactInfoController(context: context, environment: environment, subject: .filter(peer: nil, contactId: nil, contactData: data, completion: { peer, contactData in
|
||||||
//qwefqwfqwefw
|
|
||||||
#endif
|
|
||||||
let _ = data
|
|
||||||
let _ = self
|
|
||||||
/*let controller = deviceContactInfoController(context: context, subject: .filter(peer: nil, contactId: nil, contactData: data, completion: { peer, contactData in
|
|
||||||
let phone = contactData.basicData.phoneNumbers[0].value
|
let phone = contactData.basicData.phoneNumbers[0].value
|
||||||
if let vCardData = contactData.serializedVCard() {
|
if let vCardData = contactData.serializedVCard() {
|
||||||
subscriber.putNext([.media(.media(.standalone(media: TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: nil, vCardData: vCardData))))])
|
subscriber.putNext([.media(.media(.standalone(media: TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: nil, vCardData: vCardData))))])
|
||||||
@ -517,7 +511,7 @@ public class ShareRootControllerImpl {
|
|||||||
if let strongSelf = self, let window = strongSelf.mainWindow {
|
if let strongSelf = self, let window = strongSelf.mainWindow {
|
||||||
controller.presentationArguments = ViewControllerPresentationArguments(presentationAnimation: .modalSheet)
|
controller.presentationArguments = ViewControllerPresentationArguments(presentationAnimation: .modalSheet)
|
||||||
window.present(controller, on: .root)
|
window.present(controller, on: .root)
|
||||||
}*/
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
|
@ -1762,7 +1762,7 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
|
|
||||||
self.sendMessages(view: view, peer: targetPeer, messages: enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime)
|
self.sendMessages(view: view, peer: targetPeer, messages: enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime)
|
||||||
} else {
|
} else {
|
||||||
let contactController = component.context.sharedContext.makeDeviceContactInfoController(context: component.context, subject: .filter(peer: peerAndContactData.0?._asPeer(), contactId: nil, contactData: contactData, completion: { [weak self, weak view] peer, contactData in
|
let contactController = component.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: component.context), environment: ShareControllerAppEnvironment(sharedContext: component.context.sharedContext), subject: .filter(peer: peerAndContactData.0?._asPeer(), contactId: nil, contactData: contactData, completion: { [weak self, weak view] peer, contactData in
|
||||||
guard let self, let view else {
|
guard let self, let view else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"filename" : "ic_lt_adduser.pdf",
|
||||||
"filename" : "ic_lt_adduser.pdf"
|
"idiom" : "universal"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "xcode",
|
||||||
"author" : "xcode"
|
"version" : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AgeMark.imageset/18on_24.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AgeMark.imageset/18on_24.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AgeMark.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AgeMark.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "18on_24.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AgeUnmark.imageset/18off_24.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AgeUnmark.imageset/18off_24.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AgeUnmark.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AgeUnmark.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "18off_24.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Telegram.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Telegram.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "tlogo_24.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Telegram.imageset/tlogo_24.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Telegram.imageset/tlogo_24.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Message/AgeRestricted.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Message/AgeRestricted.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "eyeoff_30.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
143
submodules/TelegramUI/Images.xcassets/Chat/Message/AgeRestricted.imageset/eyeoff_30.pdf
vendored
Normal file
143
submodules/TelegramUI/Images.xcassets/Chat/Message/AgeRestricted.imageset/eyeoff_30.pdf
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Filter /FlateDecode
|
||||||
|
/Type /XObject
|
||||||
|
/Length 2 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
xm•Í®E…÷ý½F¢R¶Ë.×<16>X<03>0
|
||||||
|
é&ŠÄóçs§§§ós7S÷Œ<C592><7F>=ï~{ÿÿ¿<C3BF>÷þþËþë_Û»×<C397>OÛ›´~üíýùøùz]ßY±Ôrmtµ1öLJí2ûþñéñ‘À™ÃÄwm¹¨ôý`äên»x“c*Ilê˜d»A2§ö¼¹>H·RÖˆ§û“|/hز ÙÍUÇ<55>®¸Vä/h‚o‰°j•ªà,ïñYm\®Ä‹ÖçòYñžyoÐYÝÝõêBŒ¬kЧî)g)ÝWˆÀ¯›çht–Ùwokyï« ìêš{´%@^ñf“è±rŸÄ›šP4"ø:yøÐQ®3,`¶˜´v¸Òk'žôJ†å.ÚVg¸NŠ‹[<5B>äËzl“QÛ ·H¸‰'®ÚÒdRSµ3R±Ò¦«Ï.¸þ@oÛ?›·¹¬;#;ŠŽD/ÙÌ^PĘALyFæš`<60>‡UŸg…Ù–¥Èâq"ëKuFÞF|"9zsff1@L‘J!+3"?ƒ•Â
|
||||||
|
À<EFBFBD><EFBFBD>µ„èrÄ/ä±–Bˆƒg
|
||||||
|
fD¡jR\Ðl^VEîÄ(R<>U&£cøS‚9÷b×X·€\QúÀUÚ¢(-ƒ]ƒ}Ÿ64jF"¬r¦ˆæ¡s¢¯º—r<E28094>¤ë:éðZÑ'ÄW¶²¼ð‚J$c9R¸<52>dc(p
|
||||||
|
F7¨þÁàè<>}[,m<>þ”l<E2809D>œW/š- U±±±jQYEéE7‹Òä´ôtZ{lƒÆQb
ƒur¼<72>‘Ö¼Ñ;wm™T5t ¬Æ…Ü8¹0ÎJLgGYV˜¶:”‡ÜJ"'R‚ t¥œCƒ£QWA<57>õ
·¥ÌÏèì<C3A8>"C ËÁ5Xôæ“ÕwªÇéÚr@4;´äVsyÒ}–Áî>‘*¿Ž
T\<ù“†4i¨.«z&‘n§¦~Ö2pqžš‚ÒÕjáÎSƒç7c«aB<61>-zÀ5{@:ÃDµsvw0‚)tDƒaÉ'_u-<2D>K†¤b#9›µ<E280BA>\}áHS:Òu(–û\…X¤Ï¨Š!U—…\”͉”BµsëÊžXUŠUí…NJ¾<4A>›nXDvÄVˆÕ]§›A…|Ýs1ñ÷OÛÇí<C387>í3Ü|¨
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
868
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 4 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
0.000000 12.800001 m
|
||||||
|
0.000000 16.720367 0.000000 18.680552 0.762954 20.177933 c
|
||||||
|
1.434068 21.495068 2.504932 22.565931 3.822066 23.237045 c
|
||||||
|
5.319448 24.000000 7.279633 24.000000 11.200000 24.000000 c
|
||||||
|
12.800001 24.000000 l
|
||||||
|
16.720367 24.000000 18.680552 24.000000 20.177933 23.237045 c
|
||||||
|
21.495068 22.565931 22.565931 21.495068 23.237045 20.177933 c
|
||||||
|
24.000000 18.680552 24.000000 16.720367 24.000000 12.800000 c
|
||||||
|
24.000000 11.199999 l
|
||||||
|
24.000000 7.279633 24.000000 5.319448 23.237045 3.822067 c
|
||||||
|
22.565931 2.504932 21.495068 1.434069 20.177933 0.762955 c
|
||||||
|
18.680552 0.000000 16.720367 0.000000 12.800000 0.000000 c
|
||||||
|
11.199999 0.000000 l
|
||||||
|
7.279632 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||||
|
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||||
|
0.000000 5.319448 0.000000 7.279633 0.000000 11.200000 c
|
||||||
|
0.000000 12.800001 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
944
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /XObject << /X1 1 0 R >>
|
||||||
|
/ExtGState << /E1 << /SMask << /Type /Mask
|
||||||
|
/G 3 0 R
|
||||||
|
/S /Alpha
|
||||||
|
>>
|
||||||
|
/Type /ExtGState
|
||||||
|
>> >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Length 7 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
/E1 gs
|
||||||
|
/X1 Do
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
46
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||||
|
/Resources 5 0 R
|
||||||
|
/Contents 6 0 R
|
||||||
|
/Parent 9 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
<< /Kids [ 8 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Pages 9 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 11
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000001152 00000 n
|
||||||
|
0000001174 00000 n
|
||||||
|
0000002366 00000 n
|
||||||
|
0000002388 00000 n
|
||||||
|
0000002686 00000 n
|
||||||
|
0000002788 00000 n
|
||||||
|
0000002809 00000 n
|
||||||
|
0000002982 00000 n
|
||||||
|
0000003056 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 10 0 R
|
||||||
|
/Size 11
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
3116
|
||||||
|
%%EOF
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "xcode",
|
||||||
"author" : "xcode"
|
"version" : 1
|
||||||
},
|
},
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"provides-namespace" : true
|
"provides-namespace" : true
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Components/Search Bar/Hashtag.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Components/Search Bar/Hashtag.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "tagsearch_24.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Components/Search Bar/Hashtag.imageset/tagsearch_24.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Components/Search Bar/Hashtag.imageset/tagsearch_24.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/Mock.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Mock.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "mock.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Premium/Mock.imageset/mock.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Mock.imageset/mock.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
12
submodules/TelegramUI/Images.xcassets/Premium/Mock2.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Mock2.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Mock2.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Premium/Mock2.imageset/Mock2.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Mock2.imageset/Mock2.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 110 KiB |
@ -0,0 +1,241 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import TelegramNotices
|
||||||
|
import ContextUI
|
||||||
|
import AccountContext
|
||||||
|
import ChatMessageItemView
|
||||||
|
import ChatMessageItemCommon
|
||||||
|
import AvatarNode
|
||||||
|
import UndoUI
|
||||||
|
import MessageUI
|
||||||
|
import PeerInfoUI
|
||||||
|
|
||||||
|
extension ChatControllerImpl: MFMessageComposeViewControllerDelegate {
|
||||||
|
func openPhoneContextMenu(number: String, peer: EnginePeer?, message: Message, contentNode: ContextExtractedContentContainingNode, messageNode: ASDisplayNode, frame: CGRect, anyRecognizer: UIGestureRecognizer?, location: CGPoint?) -> Void {
|
||||||
|
if self.presentationInterfaceState.interfaceState.selectionState != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dismissAllTooltips()
|
||||||
|
|
||||||
|
let recognizer: TapLongTapOrDoubleTapGestureRecognizer? = anyRecognizer as? TapLongTapOrDoubleTapGestureRecognizer
|
||||||
|
let gesture: ContextGesture? = anyRecognizer as? ContextGesture
|
||||||
|
|
||||||
|
if let messages = self.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) {
|
||||||
|
(self.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures()
|
||||||
|
self.chatDisplayNode.cancelInteractiveKeyboardGestures()
|
||||||
|
var updatedMessages = messages
|
||||||
|
for i in 0 ..< updatedMessages.count {
|
||||||
|
if updatedMessages[i].id == message.id {
|
||||||
|
let message = updatedMessages.remove(at: i)
|
||||||
|
updatedMessages.insert(message, at: 0)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||||
|
|
||||||
|
let source: ContextContentSource
|
||||||
|
if let location = location {
|
||||||
|
source = .location(ChatMessageContextLocationContentSource(controller: self, location: messageNode.view.convert(messageNode.bounds, to: nil).origin.offsetBy(dx: location.x, dy: location.y)))
|
||||||
|
} else {
|
||||||
|
source = .extracted(ChatMessagePhoneContextExtractedContentSource(chatNode: self.chatDisplayNode, contentNode: contentNode))
|
||||||
|
// source = .extracted(ChatMessageContextExtractedContentSource(chatController: self, chatNode: self.chatDisplayNode, engine: self.context.engine, message: message, selectAll: false))
|
||||||
|
}
|
||||||
|
|
||||||
|
var items: [ContextMenuItem] = []
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Phone_AddToContacts, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||||
|
guard let self, let c else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let basicData = DeviceContactBasicData(firstName: "", lastName: "", phoneNumbers: [
|
||||||
|
DeviceContactPhoneNumberData(label: "", value: number)
|
||||||
|
])
|
||||||
|
let contactData = DeviceContactExtendedData(basicData: basicData, middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
||||||
|
|
||||||
|
pushContactContextOptionsController(context: self.context, contextController: c, presentationData: self.presentationData, peer: nil, contactData: contactData, parentController: self, push: { [weak self] c in
|
||||||
|
self?.push(c)
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
items.append(.separator)
|
||||||
|
if let peer {
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Phone_SendMessage, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
|
f(.default)
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.openPeer(peer: peer, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Phone_TelegramVoiceCall, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
|
f(.default)
|
||||||
|
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.controllerInteraction?.callPeer(peer.id, false)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Phone_TelegramVideoCall, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VideoCall"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
|
f(.default)
|
||||||
|
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.controllerInteraction?.callPeer(peer.id, true)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Phone_InviteToTelegram, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Telegram"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
|
f(.default)
|
||||||
|
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.inviteToTelegram(numbers: [number])
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if number.hasPrefix("+888") {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Phone_CallViaCarrier, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PhoneCall"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
|
f(.default)
|
||||||
|
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.openUrl("tel:\(number)", concealed: false)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Phone_CopyNumber, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
|
f(.default)
|
||||||
|
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
UIPasteboard.general.string = number
|
||||||
|
|
||||||
|
self.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: presentationData.strings.Conversation_PhoneCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
items.append(.separator)
|
||||||
|
if let peer {
|
||||||
|
let avatarSize = CGSize(width: 28.0, height: 28.0)
|
||||||
|
let avatarSignal = peerAvatarCompleteImage(account: self.context.account, peer: peer, size: avatarSize)
|
||||||
|
|
||||||
|
let subtitle = NSMutableAttributedString(string: self.presentationData.strings.Chat_Context_Phone_ViewProfile + " >")
|
||||||
|
if let range = subtitle.string.range(of: ">"), let arrowImage = UIImage(bundleImageName: "Item List/InlineTextRightArrow") {
|
||||||
|
subtitle.addAttribute(.attachment, value: arrowImage, range: NSRange(range, in: subtitle.string))
|
||||||
|
subtitle.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: subtitle.string))
|
||||||
|
}
|
||||||
|
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), textLayout: .secondLineWithAttributedValue(subtitle), icon: { theme in return nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: avatarSignal), iconPosition: .left, action: { [weak self] _, f in
|
||||||
|
f(.default)
|
||||||
|
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.openPeer(peer: peer, navigation: .info(nil), fromMessage: nil)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let emptyAction: ((ContextMenuActionItem.Action) -> Void)? = nil
|
||||||
|
items.append(
|
||||||
|
.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Phone_NotOnTelegram, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.canReadHistory.set(false)
|
||||||
|
|
||||||
|
let controller = ContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(items))), recognizer: recognizer, gesture: gesture, disableScreenshots: false)
|
||||||
|
controller.dismissed = { [weak self] in
|
||||||
|
self?.canReadHistory.set(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.window?.presentInGlobalOverlay(controller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func inviteToTelegram(numbers: [String]) {
|
||||||
|
if MFMessageComposeViewController.canSendText() {
|
||||||
|
let composer = MFMessageComposeViewController()
|
||||||
|
composer.messageComposeDelegate = self
|
||||||
|
composer.recipients = Array(Set(numbers))
|
||||||
|
let url = self.presentationData.strings.InviteText_URL
|
||||||
|
let body = self.presentationData.strings.InviteText_SingleContact(url).string
|
||||||
|
composer.body = body
|
||||||
|
self.messageComposeController = composer
|
||||||
|
if let window = self.view.window {
|
||||||
|
window.rootViewController?.present(composer, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc public func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
|
||||||
|
self.messageComposeController = nil
|
||||||
|
|
||||||
|
controller.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class ChatMessagePhoneContextExtractedContentSource: ContextExtractedContentSource {
|
||||||
|
let keepInPlace: Bool = false
|
||||||
|
let ignoreContentTouches: Bool = true
|
||||||
|
let blurBackground: Bool = true
|
||||||
|
let adjustContentHorizontally = true
|
||||||
|
|
||||||
|
private weak var chatNode: ChatControllerNode?
|
||||||
|
private let contentNode: ContextExtractedContentContainingNode
|
||||||
|
|
||||||
|
var shouldBeDismissed: Signal<Bool, NoError> {
|
||||||
|
return .single(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(chatNode: ChatControllerNode, contentNode: ContextExtractedContentContainingNode) {
|
||||||
|
self.chatNode = chatNode
|
||||||
|
self.contentNode = contentNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func takeView() -> ContextControllerTakeViewInfo? {
|
||||||
|
guard let chatNode = self.chatNode else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
||||||
|
transition.updateAlpha(node: self.contentNode.contentNode, alpha: 1.0)
|
||||||
|
|
||||||
|
return ContextControllerTakeViewInfo(containingItem: .node(self.contentNode), contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
guard let chatNode = self.chatNode else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
||||||
|
transition.updateAlpha(node: self.contentNode.contentNode, alpha: 0.0, completion: { _ in
|
||||||
|
self.contentNode.removeFromSupernode()
|
||||||
|
})
|
||||||
|
|
||||||
|
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil))
|
||||||
|
}
|
||||||
|
}
|
@ -123,6 +123,7 @@ import PeerNameColorScreen
|
|||||||
import ChatEmptyNode
|
import ChatEmptyNode
|
||||||
import ChatMediaInputStickerGridItem
|
import ChatMediaInputStickerGridItem
|
||||||
import AdsInfoScreen
|
import AdsInfoScreen
|
||||||
|
import MessageUI
|
||||||
|
|
||||||
public enum ChatControllerPeekActions {
|
public enum ChatControllerPeekActions {
|
||||||
case standard
|
case standard
|
||||||
@ -224,6 +225,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var validLayout: ContainerViewLayout?
|
var validLayout: ContainerViewLayout?
|
||||||
|
|
||||||
public weak var parentController: ViewController?
|
public weak var parentController: ViewController?
|
||||||
|
public weak var customNavigationController: NavigationController?
|
||||||
|
|
||||||
let currentChatListFilter: Int32?
|
let currentChatListFilter: Int32?
|
||||||
let chatNavigationStack: [ChatNavigationStackItem]
|
let chatNavigationStack: [ChatNavigationStackItem]
|
||||||
@ -593,6 +595,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
var networkSpeedEventsDisposable: Disposable?
|
var networkSpeedEventsDisposable: Disposable?
|
||||||
|
|
||||||
|
var messageComposeController: MFMessageComposeViewController?
|
||||||
|
|
||||||
public var alwaysShowSearchResultsAsList: Bool = false {
|
public var alwaysShowSearchResultsAsList: Bool = false {
|
||||||
didSet {
|
didSet {
|
||||||
self.presentationInterfaceState = self.presentationInterfaceState.updatedDisplayHistoryFilterAsList(self.alwaysShowSearchResultsAsList)
|
self.presentationInterfaceState = self.presentationInterfaceState.updatedDisplayHistoryFilterAsList(self.alwaysShowSearchResultsAsList)
|
||||||
@ -2295,7 +2299,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, openUrl: { [weak self] urlData in
|
}, openUrl: { [weak self] urlData in
|
||||||
if let strongSelf = self {
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let url = urlData.url
|
let url = urlData.url
|
||||||
let concealed = urlData.concealed
|
let concealed = urlData.concealed
|
||||||
let message = urlData.message
|
let message = urlData.message
|
||||||
@ -2316,7 +2322,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
} else {
|
} else {
|
||||||
strongSelf.openUrl(url, concealed: concealed, forceExternal: forceExternal, skipConcealedAlert: skipConcealedAlert, message: message, allowInlineWebpageResolution: urlData.allowInlineWebpageResolution, progress: progress)
|
strongSelf.openUrl(url, concealed: concealed, forceExternal: forceExternal, skipConcealedAlert: skipConcealedAlert, message: message, allowInlineWebpageResolution: urlData.allowInlineWebpageResolution, progress: progress)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, shareCurrentLocation: { [weak self] in
|
}, shareCurrentLocation: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if case .pinnedMessages = strongSelf.presentationInterfaceState.subject {
|
if case .pinnedMessages = strongSelf.presentationInterfaceState.subject {
|
||||||
@ -4629,6 +4634,34 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.openStickerEditor()
|
self.openStickerEditor()
|
||||||
|
}, openPhoneContextMenu: { [weak self] phoneData in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
phoneData.progress?.set(.single(true))
|
||||||
|
let _ = (self.context.engine.peers.resolvePeerByPhone(phone: phoneData.number)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
phoneData.progress?.set(.single(false))
|
||||||
|
|
||||||
|
self.openPhoneContextMenu(number: phoneData.number, peer: peer, message: phoneData.message, contentNode: phoneData.contentNode, messageNode: phoneData.messageNode, frame: phoneData.messageNode.bounds, anyRecognizer: nil, location: nil)
|
||||||
|
})
|
||||||
|
}, openAgeRestrictedMessageMedia: { [weak self] message, reveal in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let controller = chatAgeRestrictionAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, completion: { [weak self] alwaysShow in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if alwaysShow {
|
||||||
|
self.present(UndoOverlayController(presentationData: self.presentationData, content: .info(title: nil, text: "You can update the visibility of sensitive media in [Data and Storage > Show 18+ Content]().", timeout: nil, customUndoText: nil), elevatedLayout: false, position: .top, action: { _ in return false }), in: .current)
|
||||||
|
}
|
||||||
|
reveal()
|
||||||
|
})
|
||||||
|
self.present(controller, in: .window(.root))
|
||||||
}, requestMessageUpdate: { [weak self] id, scroll in
|
}, requestMessageUpdate: { [weak self] id, scroll in
|
||||||
if let self {
|
if let self {
|
||||||
self.chatDisplayNode.historyNode.requestMessageUpdate(id, andScrollToItem: scroll)
|
self.chatDisplayNode.historyNode.requestMessageUpdate(id, andScrollToItem: scroll)
|
||||||
@ -9511,7 +9544,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
func addPeerContact() {
|
func addPeerContact() {
|
||||||
if let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramUser, let peerStatusSettings = self.presentationInterfaceState.contactStatus?.peerStatusSettings, let contactData = DeviceContactExtendedData(peer: EnginePeer(peer)) {
|
if let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramUser, let peerStatusSettings = self.presentationInterfaceState.contactStatus?.peerStatusSettings, let contactData = DeviceContactExtendedData(peer: EnginePeer(peer)) {
|
||||||
self.present(context.sharedContext.makeDeviceContactInfoController(context: context, subject: .create(peer: peer, contactData: contactData, isSharing: true, shareViaException: peerStatusSettings.contains(.addExceptionWhenAddingContact), completion: { [weak self] peer, stableId, contactData in
|
self.present(context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: self.context), environment: ShareControllerAppEnvironment(sharedContext: self.context.sharedContext), subject: .create(peer: peer, contactData: contactData, isSharing: true, shareViaException: peerStatusSettings.contains(.addExceptionWhenAddingContact), completion: { [weak self] peer, stableId, contactData in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -10556,6 +10589,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
} else if case let .overlay(navigationController) = self.presentationInterfaceState.mode {
|
} else if case let .overlay(navigationController) = self.presentationInterfaceState.mode {
|
||||||
return navigationController
|
return navigationController
|
||||||
} else {
|
} else {
|
||||||
|
if let navigationController = self.customNavigationController {
|
||||||
|
return navigationController
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ import TelegramCallsUI
|
|||||||
import AutomaticBusinessMessageSetupScreen
|
import AutomaticBusinessMessageSetupScreen
|
||||||
import MediaEditorScreen
|
import MediaEditorScreen
|
||||||
import CameraScreen
|
import CameraScreen
|
||||||
|
import ShareController
|
||||||
|
|
||||||
extension ChatControllerImpl {
|
extension ChatControllerImpl {
|
||||||
enum AttachMenuSubject {
|
enum AttachMenuSubject {
|
||||||
@ -520,7 +521,7 @@ extension ChatControllerImpl {
|
|||||||
enqueueMessages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
enqueueMessages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
||||||
strongSelf.sendMessages(strongSelf.transformEnqueueMessages(enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime))
|
strongSelf.sendMessages(strongSelf.transformEnqueueMessages(enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime))
|
||||||
} else {
|
} else {
|
||||||
let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
|
let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: strongSelf.context), environment: ShareControllerAppEnvironment(sharedContext: strongSelf.context.sharedContext), subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
|
||||||
guard let strongSelf = self, !contactData.basicData.phoneNumbers.isEmpty else {
|
guard let strongSelf = self, !contactData.basicData.phoneNumbers.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1590,7 +1591,7 @@ extension ChatControllerImpl {
|
|||||||
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
||||||
strongSelf.sendMessages([message])
|
strongSelf.sendMessages([message])
|
||||||
} else {
|
} else {
|
||||||
let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
|
let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: strongSelf.context), environment: ShareControllerAppEnvironment(sharedContext: strongSelf.context.sharedContext), subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
|
||||||
guard let strongSelf = self, !contactData.basicData.phoneNumbers.isEmpty else {
|
guard let strongSelf = self, !contactData.basicData.phoneNumbers.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,8 @@ extension ChatControllerImpl {
|
|||||||
}), in: .current)
|
}), in: .current)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.push(calendarScreen)
|
self.effectiveNavigationController?.pushViewController(calendarScreen)
|
||||||
|
|
||||||
dismissCalendarScreen = { [weak calendarScreen] in
|
dismissCalendarScreen = { [weak calendarScreen] in
|
||||||
calendarScreen?.dismiss(completion: nil)
|
calendarScreen?.dismiss(completion: nil)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import SearchUI
|
|||||||
import TelegramPermissionsUI
|
import TelegramPermissionsUI
|
||||||
import AppBundle
|
import AppBundle
|
||||||
import DeviceAccess
|
import DeviceAccess
|
||||||
|
import ShareController
|
||||||
|
|
||||||
public class ComposeControllerImpl: ViewController, ComposeController {
|
public class ComposeControllerImpl: ViewController, ComposeController {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
@ -200,7 +201,7 @@ public class ComposeControllerImpl: ViewController, ComposeController {
|
|||||||
switch status {
|
switch status {
|
||||||
case .allowed:
|
case .allowed:
|
||||||
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: "", lastName: "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: "+")]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: "", lastName: "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: "+")]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
||||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: strongSelf.context), environment: ShareControllerAppEnvironment(sharedContext: strongSelf.context.sharedContext), subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -211,7 +212,7 @@ public class ComposeControllerImpl: ViewController, ComposeController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(strongSelf.navigationController as? NavigationController)?.replaceAllButRootController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil), animated: true)
|
(strongSelf.navigationController as? NavigationController)?.replaceAllButRootController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: strongSelf.context), environment: ShareControllerAppEnvironment(sharedContext: strongSelf.context.sharedContext), subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil), animated: true)
|
||||||
}
|
}
|
||||||
}), completed: nil, cancelled: nil))
|
}), completed: nil, cancelled: nil))
|
||||||
case .notDetermined:
|
case .notDetermined:
|
||||||
|
@ -7,6 +7,7 @@ import AccountContext
|
|||||||
import AlertUI
|
import AlertUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import PeerInfoUI
|
import PeerInfoUI
|
||||||
|
import ShareController
|
||||||
|
|
||||||
func openAddContactImpl(context: AccountContext, firstName: String = "", lastName: String = "", phoneNumber: String, label: String = "_$!<Mobile>!$_", present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void = {}) {
|
func openAddContactImpl(context: AccountContext, firstName: String = "", lastName: String = "", phoneNumber: String, label: String = "_$!<Mobile>!$_", present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void = {}) {
|
||||||
let _ = (DeviceAccess.authorizationStatus(subject: .contacts)
|
let _ = (DeviceAccess.authorizationStatus(subject: .contacts)
|
||||||
@ -15,13 +16,13 @@ func openAddContactImpl(context: AccountContext, firstName: String = "", lastNam
|
|||||||
switch value {
|
switch value {
|
||||||
case .allowed:
|
case .allowed:
|
||||||
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: label, value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: label, value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
||||||
present(deviceContactInfoController(context: context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
present(deviceContactInfoController(context: ShareControllerAppAccountContext(context: context), environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext), subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
|
||||||
if let peer = peer {
|
if let peer = peer {
|
||||||
if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||||
pushController(infoController)
|
pushController(infoController)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pushController(deviceContactInfoController(context: context, subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil))
|
pushController(deviceContactInfoController(context: ShareControllerAppAccountContext(context: context), environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext), subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil))
|
||||||
}
|
}
|
||||||
}), completed: completed, cancelled: nil), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
}), completed: completed, cancelled: nil), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
case .notDetermined:
|
case .notDetermined:
|
||||||
|
@ -330,7 +330,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
|||||||
} else {
|
} else {
|
||||||
contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contact.firstName, lastName: contact.lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: contact.phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contact.firstName, lastName: contact.lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: contact.phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
||||||
}
|
}
|
||||||
let controller = deviceContactInfoController(context: params.context, updatedPresentationData: params.updatedPresentationData, subject: .vcard(peer?._asPeer(), nil, contactData), completed: nil, cancelled: nil)
|
let controller = deviceContactInfoController(context: ShareControllerAppAccountContext(context: params.context), environment: ShareControllerAppEnvironment(sharedContext: params.context.sharedContext), updatedPresentationData: params.updatedPresentationData, subject: .vcard(peer?._asPeer(), nil, contactData), completed: nil, cancelled: nil)
|
||||||
params.navigationController?.pushViewController(controller)
|
params.navigationController?.pushViewController(controller)
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
|
@ -175,6 +175,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
}, openRecommendedChannelContextMenu: { _, _, _ in
|
}, openRecommendedChannelContextMenu: { _, _, _ in
|
||||||
}, openGroupBoostInfo: { _, _ in
|
}, openGroupBoostInfo: { _, _ in
|
||||||
}, openStickerEditor: {
|
}, openStickerEditor: {
|
||||||
|
}, openPhoneContextMenu: { _ in
|
||||||
|
}, openAgeRestrictedMessageMedia: { _, _ in
|
||||||
}, requestMessageUpdate: { _, _ in
|
}, requestMessageUpdate: { _, _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, dismissTextInput: {
|
}, dismissTextInput: {
|
||||||
|
@ -1598,8 +1598,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
openResolvedUrlImpl(resolvedUrl, context: context, urlContext: urlContext, navigationController: navigationController, forceExternal: forceExternal, openPeer: openPeer, sendFile: sendFile, sendSticker: sendSticker, sendEmoji: sendEmoji, requestMessageActionUrlAuth: requestMessageActionUrlAuth, joinVoiceChat: joinVoiceChat, present: present, dismissInput: dismissInput, contentContext: contentContext, progress: progress, completion: completion)
|
openResolvedUrlImpl(resolvedUrl, context: context, urlContext: urlContext, navigationController: navigationController, forceExternal: forceExternal, openPeer: openPeer, sendFile: sendFile, sendSticker: sendSticker, sendEmoji: sendEmoji, requestMessageActionUrlAuth: requestMessageActionUrlAuth, joinVoiceChat: joinVoiceChat, present: present, dismissInput: dismissInput, contentContext: contentContext, progress: progress, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController {
|
public func makeDeviceContactInfoController(context: ShareControllerAccountContext, environment: ShareControllerEnvironment, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController {
|
||||||
return deviceContactInfoController(context: context, subject: subject, completed: completed, cancelled: cancelled)
|
return deviceContactInfoController(context: context, environment: environment, subject: subject, completed: completed, cancelled: cancelled)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makePeersNearbyController(context: AccountContext) -> ViewController {
|
public func makePeersNearbyController(context: AccountContext) -> ViewController {
|
||||||
@ -1769,6 +1769,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}, openRecommendedChannelContextMenu: { _, _, _ in
|
}, openRecommendedChannelContextMenu: { _, _, _ in
|
||||||
}, openGroupBoostInfo: { _, _ in
|
}, openGroupBoostInfo: { _, _ in
|
||||||
}, openStickerEditor: {
|
}, openStickerEditor: {
|
||||||
|
}, openPhoneContextMenu: { _ in
|
||||||
|
}, openAgeRestrictedMessageMedia: { _, _ in
|
||||||
}, requestMessageUpdate: { _, _ in
|
}, requestMessageUpdate: { _, _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, dismissTextInput: {
|
}, dismissTextInput: {
|
||||||
|
@ -234,24 +234,22 @@ public struct WebAppParameters {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func generateWebAppThemeParams(_ presentationTheme: PresentationTheme) -> [String: Any] {
|
public func generateWebAppThemeParams(_ theme: PresentationTheme) -> [String: Any] {
|
||||||
let backgroundColor = presentationTheme.list.plainBackgroundColor.rgb
|
|
||||||
let secondaryBackgroundColor = presentationTheme.list.blocksBackgroundColor.rgb
|
|
||||||
return [
|
return [
|
||||||
"bg_color": Int32(bitPattern: backgroundColor),
|
"bg_color": Int32(bitPattern: theme.list.plainBackgroundColor.rgb),
|
||||||
"secondary_bg_color": Int32(bitPattern: secondaryBackgroundColor),
|
"secondary_bg_color": Int32(bitPattern: theme.list.blocksBackgroundColor.rgb),
|
||||||
"text_color": Int32(bitPattern: presentationTheme.list.itemPrimaryTextColor.rgb),
|
"text_color": Int32(bitPattern: theme.list.itemPrimaryTextColor.rgb),
|
||||||
"hint_color": Int32(bitPattern: presentationTheme.list.itemSecondaryTextColor.rgb),
|
"hint_color": Int32(bitPattern: theme.list.itemSecondaryTextColor.rgb),
|
||||||
"link_color": Int32(bitPattern: presentationTheme.list.itemAccentColor.rgb),
|
"link_color": Int32(bitPattern: theme.list.itemAccentColor.rgb),
|
||||||
"button_color": Int32(bitPattern: presentationTheme.list.itemCheckColors.fillColor.rgb),
|
"button_color": Int32(bitPattern: theme.list.itemCheckColors.fillColor.rgb),
|
||||||
"button_text_color": Int32(bitPattern: presentationTheme.list.itemCheckColors.foregroundColor.rgb),
|
"button_text_color": Int32(bitPattern: theme.list.itemCheckColors.foregroundColor.rgb),
|
||||||
"header_bg_color": Int32(bitPattern: presentationTheme.rootController.navigationBar.opaqueBackgroundColor.rgb),
|
"header_bg_color": Int32(bitPattern: theme.rootController.navigationBar.opaqueBackgroundColor.rgb),
|
||||||
"accent_text_color": Int32(bitPattern: presentationTheme.list.itemAccentColor.rgb),
|
"accent_text_color": Int32(bitPattern: theme.list.itemAccentColor.rgb),
|
||||||
"section_bg_color": Int32(bitPattern: presentationTheme.list.itemBlocksBackgroundColor.rgb),
|
"section_bg_color": Int32(bitPattern: theme.list.itemBlocksBackgroundColor.rgb),
|
||||||
"section_header_text_color": Int32(bitPattern: presentationTheme.list.freeTextColor.rgb),
|
"section_header_text_color": Int32(bitPattern: theme.list.freeTextColor.rgb),
|
||||||
"subtitle_text_color": Int32(bitPattern: presentationTheme.list.itemSecondaryTextColor.rgb),
|
"subtitle_text_color": Int32(bitPattern: theme.list.itemSecondaryTextColor.rgb),
|
||||||
"destructive_text_color": Int32(bitPattern: presentationTheme.list.itemDestructiveColor.rgb),
|
"destructive_text_color": Int32(bitPattern: theme.list.itemDestructiveColor.rgb),
|
||||||
"section_separator_color": Int32(bitPattern: presentationTheme.list.itemBlocksSeparatorColor.rgb)
|
"section_separator_color": Int32(bitPattern: theme.list.itemBlocksSeparatorColor.rgb)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user