mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
95bbe5a518
commit
84679a71cd
@ -7973,6 +7973,7 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Settings.ChangeProfilePhoto" = "Change Profile Photo";
|
||||
|
||||
"Premium.EmojiStatusShortTitle" = "This is %1$@'s current status.";
|
||||
"Premium.EmojiStatusTitle" = "This is %1$@'s current status from #[%2$@]().";
|
||||
"Premium.EmojiStatusText" = "Emoji status is a premium feature.\n Other features included in **Telegram Premium**:";
|
||||
|
||||
@ -8013,3 +8014,5 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Premium.PricePerMonth" = "%@/month";
|
||||
"Premium.PricePerYear" = "%@/year";
|
||||
|
||||
"Conversation.SendMesageAsPremiumInfo" = "Subscribe to **Telegram Premium** to be able to comment on behalf your channels in group chats.";
|
||||
|
@ -25,7 +25,7 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
||||
var selectCountryCode: (() -> Void)?
|
||||
var checkPhone: (() -> Void)?
|
||||
var hasNumberUpdated: ((Bool) -> Void)?
|
||||
var numberUpdated: (() -> Void)?
|
||||
var keyPressed: ((Int) -> Void)?
|
||||
|
||||
var preferredCountryIdForCode: [String: String] = [:]
|
||||
|
||||
@ -146,9 +146,7 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
||||
self.phoneInputNode.numberTextUpdated = { [weak self] number in
|
||||
if let strongSelf = self {
|
||||
let _ = processNumberChange(strongSelf.phoneInputNode.number)
|
||||
|
||||
strongSelf.numberUpdated?()
|
||||
|
||||
|
||||
if strongSelf.hasCountry {
|
||||
strongSelf.hasNumberUpdated?(!strongSelf.phoneInputNode.codeAndNumber.2.isEmpty)
|
||||
} else {
|
||||
@ -162,9 +160,7 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
||||
if let name = name {
|
||||
strongSelf.preferredCountryIdForCode[code] = name
|
||||
}
|
||||
|
||||
strongSelf.numberUpdated?()
|
||||
|
||||
|
||||
if processNumberChange(strongSelf.phoneInputNode.number) {
|
||||
} else if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
|
||||
let flagString = emojiFlagForISOCountryCode(name)
|
||||
@ -211,6 +207,10 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
||||
self.phoneInputNode.returnAction = { [weak self] in
|
||||
self?.checkPhone?()
|
||||
}
|
||||
|
||||
self.phoneInputNode.keyPressed = { [weak self] num in
|
||||
self?.keyPressed?(num)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func countryPressed() {
|
||||
@ -410,11 +410,9 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
self.phoneAndCountryNode.hasNumberUpdated = { [weak self] hasNumber in
|
||||
self?.proceedNode.isEnabled = hasNumber
|
||||
}
|
||||
self.phoneAndCountryNode.numberUpdated = { [weak self] in
|
||||
self.phoneAndCountryNode.keyPressed = { [weak self] num in
|
||||
if let strongSelf = self, !strongSelf.managedAnimationNode.isHidden {
|
||||
if let state = ManagedPhoneAnimationState.allCases.randomElement() {
|
||||
strongSelf.managedAnimationNode.enqueue(state)
|
||||
}
|
||||
strongSelf.managedAnimationNode.animate(num: num)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1049,40 +1047,113 @@ final class PhoneConfirmationController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private enum ManagedPhoneAnimationState: CaseIterable, Equatable {
|
||||
case keypad1
|
||||
case keypad2
|
||||
case keypad3
|
||||
case keypad4
|
||||
private final class PhoneKeyNode: ASDisplayNode {
|
||||
private let imageNode: ASImageNode
|
||||
private var highlightedNode: ASImageNode?
|
||||
|
||||
private let image: UIImage?
|
||||
private let highlightedImage: UIImage?
|
||||
|
||||
init(offset: CGPoint, image: UIImage?, highlightedImage: UIImage?) {
|
||||
self.image = image
|
||||
self.highlightedImage = highlightedImage
|
||||
|
||||
self.imageNode = ASImageNode()
|
||||
self.imageNode.displaysAsynchronously = false
|
||||
self.imageNode.image = image
|
||||
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
if let imageSize = self.imageNode.image?.size {
|
||||
self.imageNode.frame = CGRect(origin: CGPoint(x: -offset.x, y: -offset.y), size: imageSize)
|
||||
}
|
||||
|
||||
self.addSubnode(self.imageNode)
|
||||
}
|
||||
|
||||
func animatePress() {
|
||||
guard self.highlightedNode == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
let highlightedNode = ASImageNode()
|
||||
highlightedNode.displaysAsynchronously = false
|
||||
highlightedNode.image = self.highlightedImage
|
||||
highlightedNode.frame = self.imageNode.frame
|
||||
self.addSubnode(highlightedNode)
|
||||
self.highlightedNode = highlightedNode
|
||||
|
||||
highlightedNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.16, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
self?.highlightedNode?.removeFromSupernode()
|
||||
self?.highlightedNode = nil
|
||||
})
|
||||
|
||||
let values: [NSNumber] = [0.75, 0.5, 0.75, 1.0]
|
||||
self.layer.animateKeyframes(values: values, duration: 0.16, keyPath: "transform.scale")
|
||||
}
|
||||
}
|
||||
|
||||
private final class ManagedPhoneAnimationNode: ManagedAnimationNode {
|
||||
private var phoneState: ManagedPhoneAnimationState = .keypad1
|
||||
private var timer: SwiftSignalKit.Timer?
|
||||
|
||||
private let plateNode: ASDisplayNode
|
||||
private var nodes: [PhoneKeyNode]
|
||||
|
||||
init() {
|
||||
self.plateNode = ASDisplayNode()
|
||||
self.plateNode.backgroundColor = UIColor(rgb: 0xc30023)
|
||||
self.plateNode.frame = CGRect(x: 27.0, y: 38.0, width: 46.0, height: 32.0)
|
||||
|
||||
let image = UIImage(bundleImageName: "Settings/Keypad")
|
||||
let highlightedImage = generateTintedImage(image: image, color: UIColor(rgb: 0x000000, alpha: 0.4))
|
||||
|
||||
var nodes: [PhoneKeyNode] = []
|
||||
for i in 0 ..< 9 {
|
||||
let offset: CGPoint
|
||||
switch i {
|
||||
case 1:
|
||||
offset = CGPoint(x: 15.0, y: 0.0)
|
||||
case 2:
|
||||
offset = CGPoint(x: 30.0, y: 0.0)
|
||||
case 3:
|
||||
offset = CGPoint(x: 0.0, y: 10.0)
|
||||
case 4:
|
||||
offset = CGPoint(x: 15.0, y: 10.0)
|
||||
case 5:
|
||||
offset = CGPoint(x: 30.0, y: 10.0)
|
||||
case 6:
|
||||
offset = CGPoint(x: 0.0, y: 21.0)
|
||||
case 7:
|
||||
offset = CGPoint(x: 15.0, y: 21.0)
|
||||
case 8:
|
||||
offset = CGPoint(x: 30.0, y: 21.0)
|
||||
default:
|
||||
offset = CGPoint(x: 0.0, y: 0.0)
|
||||
}
|
||||
let node = PhoneKeyNode(offset: offset, image: image, highlightedImage: highlightedImage)
|
||||
node.frame = CGRect(origin: offset.offsetBy(dx: 28.0, dy: 38.0), size: CGSize(width: 15.0, height: 10.0))
|
||||
nodes.append(node)
|
||||
}
|
||||
self.nodes = nodes
|
||||
|
||||
super.init(size: CGSize(width: 100.0, height: 100.0))
|
||||
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.3))
|
||||
}
|
||||
|
||||
func enqueue(_ state: ManagedPhoneAnimationState) {
|
||||
switch state {
|
||||
case .keypad1:
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 0, endFrame: 10), duration: 0.15))
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 10, endFrame: 0), duration: 0.15))
|
||||
case .keypad2:
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 24, endFrame: 34), duration: 0.15))
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 34, endFrame: 24), duration: 0.15))
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.1))
|
||||
case .keypad3:
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 36, endFrame: 46), duration: 0.15))
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 46, endFrame: 36), duration: 0.15))
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.1))
|
||||
case .keypad4:
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 0, endFrame: 10), duration: 0.15))
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 10, endFrame: 0), duration: 0.15))
|
||||
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.001))
|
||||
|
||||
self.addSubnode(self.plateNode)
|
||||
|
||||
for node in nodes {
|
||||
self.addSubnode(node)
|
||||
}
|
||||
}
|
||||
|
||||
func animate(num: Int) {
|
||||
guard num != 0 else {
|
||||
return
|
||||
}
|
||||
let index = max(0, min(self.nodes.count - 1, num - 1))
|
||||
self.nodes[index].animatePress()
|
||||
}
|
||||
}
|
||||
|
@ -384,7 +384,7 @@ public final class ChatPresentationInterfaceState: Equatable {
|
||||
public let showCommands: Bool
|
||||
public let hasBotCommands: Bool
|
||||
public let showSendAsPeers: Bool
|
||||
public let sendAsPeers: [FoundPeer]?
|
||||
public let sendAsPeers: [SendAsPeer]?
|
||||
public let botMenuButton: BotMenuButton
|
||||
public let showWebView: Bool
|
||||
public let currentSendAsPeerId: PeerId?
|
||||
@ -462,7 +462,7 @@ public final class ChatPresentationInterfaceState: Equatable {
|
||||
self.customEmojiAvailable = true
|
||||
}
|
||||
|
||||
public init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: ChatPinnedMessage?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, presentationReady: Bool, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, autoremoveTimeout: Int32?, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, importState: ChatPresentationImportState?, reportReason: ReportReason?, showCommands: Bool, hasBotCommands: Bool, showSendAsPeers: Bool, sendAsPeers: [FoundPeer]?, botMenuButton: BotMenuButton, showWebView: Bool, currentSendAsPeerId: PeerId?, copyProtectionEnabled: Bool, hasPlentyOfMessages: Bool, isPremium: Bool, forceInputCommandsHidden: Bool, voiceMessagesAvailable: Bool, customEmojiAvailable: Bool) {
|
||||
public init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: ChatPinnedMessage?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, presentationReady: Bool, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, autoremoveTimeout: Int32?, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, importState: ChatPresentationImportState?, reportReason: ReportReason?, showCommands: Bool, hasBotCommands: Bool, showSendAsPeers: Bool, sendAsPeers: [SendAsPeer]?, botMenuButton: BotMenuButton, showWebView: Bool, currentSendAsPeerId: PeerId?, copyProtectionEnabled: Bool, hasPlentyOfMessages: Bool, isPremium: Bool, forceInputCommandsHidden: Bool, voiceMessagesAvailable: Bool, customEmojiAvailable: Bool) {
|
||||
self.interfaceState = interfaceState
|
||||
self.chatLocation = chatLocation
|
||||
self.renderedPeer = renderedPeer
|
||||
@ -929,7 +929,7 @@ public final class ChatPresentationInterfaceState: Equatable {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable)
|
||||
}
|
||||
|
||||
public func updatedSendAsPeers(_ sendAsPeers: [FoundPeer]?) -> ChatPresentationInterfaceState {
|
||||
public func updatedSendAsPeers(_ sendAsPeers: [SendAsPeer]?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable)
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
||||
private let textNode: ImmediateTextNode
|
||||
private let statusNode: ImmediateTextNode?
|
||||
private let iconNode: ASImageNode
|
||||
private let titleIconNode: ASImageNode
|
||||
private let badgeBackgroundNode: ASImageNode
|
||||
private let badgeTextNode: ImmediateTextNode
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
@ -134,6 +135,13 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
||||
self.iconNode.image = action.icon(presentationData.theme)
|
||||
}
|
||||
|
||||
self.titleIconNode = ASImageNode()
|
||||
self.titleIconNode.isAccessibilityElement = false
|
||||
self.titleIconNode.displaysAsynchronously = false
|
||||
self.titleIconNode.displayWithoutProcessing = true
|
||||
self.titleIconNode.isUserInteractionEnabled = false
|
||||
self.titleIconNode.image = action.textIcon(presentationData.theme)
|
||||
|
||||
self.badgeBackgroundNode = ASImageNode()
|
||||
self.badgeBackgroundNode.isAccessibilityElement = false
|
||||
self.badgeBackgroundNode.displaysAsynchronously = false
|
||||
@ -173,6 +181,9 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
||||
self.addSubnode(self.badgeBackgroundNode)
|
||||
self.addSubnode(self.badgeTextNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
if let _ = self.titleIconNode.image {
|
||||
self.addSubnode(self.titleIconNode)
|
||||
}
|
||||
|
||||
self.buttonNode.highligthedChanged = { [weak self] highligted in
|
||||
guard let strongSelf = self else {
|
||||
@ -272,6 +283,10 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
|
||||
if let iconSize = self.titleIconNode.image?.size {
|
||||
transition.updateFrame(node: self.titleIconNode, frame: CGRect(origin: CGPoint(x: self.textNode.frame.maxX + 7.0, y: floorToScreenPixels(self.textNode.frame.midY - iconSize.height / 2.0)), size: iconSize))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return (CGSize(width: textSize.width + sideInset + rightTextInset + badgeWidthSpace, height: verticalInset * 2.0 + textSize.height), { size, transition in
|
||||
@ -290,6 +305,10 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
|
||||
if let iconSize = self.titleIconNode.image?.size {
|
||||
transition.updateFrame(node: self.titleIconNode, frame: CGRect(origin: CGPoint(x: self.textNode.frame.maxX + 7.0, y: floorToScreenPixels(self.textNode.frame.midY - iconSize.height / 2.0)), size: iconSize))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -378,13 +397,15 @@ public final class ContextActionNode: ASDisplayNode, ContextActionNodeProtocol {
|
||||
self.requestLayout()
|
||||
}
|
||||
|
||||
private var performedAction = false
|
||||
public func performAction() {
|
||||
guard let controller = self.getController() else {
|
||||
guard let controller = self.getController(), !self.performedAction else {
|
||||
return
|
||||
}
|
||||
self.action.action?(ContextMenuActionItem.Action(
|
||||
controller: controller,
|
||||
dismissWithResult: { [weak self] result in
|
||||
self?.performedAction = true
|
||||
self?.actionSelected(result)
|
||||
},
|
||||
updateAction: { [weak self] id, updatedAction in
|
||||
|
@ -102,6 +102,7 @@ public final class ContextMenuActionItem {
|
||||
public let badge: ContextMenuActionBadge?
|
||||
public let icon: (PresentationTheme) -> UIImage?
|
||||
public let iconSource: ContextMenuActionItemIconSource?
|
||||
public let textIcon: (PresentationTheme) -> UIImage?
|
||||
public let action: ((Action) -> Void)?
|
||||
|
||||
convenience public init(
|
||||
@ -114,6 +115,7 @@ public final class ContextMenuActionItem {
|
||||
badge: ContextMenuActionBadge? = nil,
|
||||
icon: @escaping (PresentationTheme) -> UIImage?,
|
||||
iconSource: ContextMenuActionItemIconSource? = nil,
|
||||
textIcon: @escaping (PresentationTheme) -> UIImage? = { _ in return nil },
|
||||
action: ((ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void)?
|
||||
) {
|
||||
self.init(
|
||||
@ -126,6 +128,7 @@ public final class ContextMenuActionItem {
|
||||
badge: badge,
|
||||
icon: icon,
|
||||
iconSource: iconSource,
|
||||
textIcon: textIcon,
|
||||
action: action.flatMap { action in
|
||||
return { impl in
|
||||
action(impl.controller, impl.dismissWithResult)
|
||||
@ -144,6 +147,7 @@ public final class ContextMenuActionItem {
|
||||
badge: ContextMenuActionBadge? = nil,
|
||||
icon: @escaping (PresentationTheme) -> UIImage?,
|
||||
iconSource: ContextMenuActionItemIconSource? = nil,
|
||||
textIcon: @escaping (PresentationTheme) -> UIImage? = { _ in return nil },
|
||||
action: ((Action) -> Void)?
|
||||
) {
|
||||
self.id = id
|
||||
@ -155,6 +159,7 @@ public final class ContextMenuActionItem {
|
||||
self.badge = badge
|
||||
self.icon = icon
|
||||
self.iconSource = iconSource
|
||||
self.textIcon = textIcon
|
||||
self.action = action
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import Display
|
||||
import AppBundle
|
||||
import LegacyComponents
|
||||
|
||||
private func createEmitterBehavior(type: String) -> NSObject {
|
||||
func createEmitterBehavior(type: String) -> NSObject {
|
||||
let selector = ["behaviorWith", "Type:"].joined(separator: "")
|
||||
let behaviorClass = NSClassFromString(["CA", "Emitter", "Behavior"].joined(separator: "")) as! NSObject.Type
|
||||
let behaviorWithType = behaviorClass.method(for: NSSelectorFromString(selector))!
|
||||
|
97
submodules/InvisibleInkDustNode/Sources/MediaDustNode.swift
Normal file
97
submodules/InvisibleInkDustNode/Sources/MediaDustNode.swift
Normal file
@ -0,0 +1,97 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SwiftSignalKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import AppBundle
|
||||
import LegacyComponents
|
||||
|
||||
public class MediaDustNode: ASDisplayNode {
|
||||
private var currentParams: (size: CGSize, color: UIColor)?
|
||||
private var animColor: CGColor?
|
||||
|
||||
private var emitterNode: ASDisplayNode
|
||||
private var emitter: CAEmitterCell?
|
||||
private var emitterLayer: CAEmitterLayer?
|
||||
|
||||
public override init() {
|
||||
self.emitterNode = ASDisplayNode()
|
||||
self.emitterNode.isUserInteractionEnabled = false
|
||||
self.emitterNode.clipsToBounds = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.emitterNode)
|
||||
}
|
||||
|
||||
public override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let emitter = CAEmitterCell()
|
||||
emitter.color = UIColor(rgb: 0xffffff, alpha: 0.0).cgColor
|
||||
emitter.contents = UIImage(bundleImageName: "Components/TextSpeckle")?.cgImage
|
||||
emitter.contentsScale = 1.8
|
||||
emitter.emissionRange = .pi * 2.0
|
||||
emitter.lifetime = 8.0
|
||||
emitter.scale = 0.5
|
||||
emitter.velocityRange = 0.0
|
||||
emitter.name = "dustCell"
|
||||
emitter.alphaRange = 1.0
|
||||
emitter.setValue("point", forKey: "particleType")
|
||||
emitter.setValue(1.0, forKey: "mass")
|
||||
emitter.setValue(0.01, forKey: "massRange")
|
||||
self.emitter = emitter
|
||||
|
||||
let alphaBehavior = createEmitterBehavior(type: "valueOverLife")
|
||||
alphaBehavior.setValue("color.alpha", forKey: "keyPath")
|
||||
alphaBehavior.setValue([0, 0, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 1, 0, -1], forKey: "values")
|
||||
alphaBehavior.setValue(true, forKey: "additive")
|
||||
|
||||
let scaleBehavior = createEmitterBehavior(type: "valueOverLife")
|
||||
scaleBehavior.setValue("scale", forKey: "keyPath")
|
||||
scaleBehavior.setValue([0.0, 0.5], forKey: "values")
|
||||
scaleBehavior.setValue([0.0, 0.05], forKey: "locations")
|
||||
|
||||
let behaviors = [alphaBehavior, scaleBehavior]
|
||||
|
||||
let emitterLayer = CAEmitterLayer()
|
||||
emitterLayer.masksToBounds = true
|
||||
emitterLayer.allowsGroupOpacity = true
|
||||
emitterLayer.lifetime = 1
|
||||
emitterLayer.emitterCells = [emitter]
|
||||
emitterLayer.seed = arc4random()
|
||||
emitterLayer.emitterShape = .rectangle
|
||||
emitterLayer.setValue(behaviors, forKey: "emitterBehaviors")
|
||||
|
||||
self.emitterLayer = emitterLayer
|
||||
|
||||
self.emitterNode.layer.addSublayer(emitterLayer)
|
||||
|
||||
self.updateEmitter()
|
||||
}
|
||||
|
||||
private func updateEmitter() {
|
||||
guard let (size, _) = self.currentParams else {
|
||||
return
|
||||
}
|
||||
|
||||
self.emitterLayer?.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.emitterLayer?.emitterSize = size
|
||||
self.emitterLayer?.emitterPosition = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
|
||||
|
||||
let square = Float(size.width * size.height)
|
||||
Queue.mainQueue().async {
|
||||
self.emitter?.birthRate = min(100000.0, square * 0.016)
|
||||
}
|
||||
}
|
||||
|
||||
public func update(size: CGSize, color: UIColor) {
|
||||
self.currentParams = (size, color)
|
||||
|
||||
self.emitterNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
if self.isNodeLoaded {
|
||||
self.updateEmitter()
|
||||
}
|
||||
}
|
||||
}
|
@ -198,7 +198,7 @@ public func inviteRequestsController(context: AccountContext, updatedPresentatio
|
||||
} else {
|
||||
string = presentationData.strings.MemberRequests_UserAddedToGroup(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
|
||||
}
|
||||
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .invitedToVoiceChat(context: context, peer: peer, text: string), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .invitedToVoiceChat(context: context, peer: peer, text: string, action: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -167,6 +167,8 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
public var countryCodeTextUpdated: ((String) -> Void)?
|
||||
public var numberTextUpdated: ((String) -> Void)?
|
||||
|
||||
public var keyPressed: ((Int) -> Void)?
|
||||
|
||||
public var returnAction: (() -> Void)?
|
||||
|
||||
private let phoneFormatter = InteractivePhoneFormatter()
|
||||
@ -245,7 +247,7 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
@objc private func numberTextChanged(_ textField: UITextField) {
|
||||
self.updateNumberFromTextFields()
|
||||
}
|
||||
|
||||
|
||||
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
if !self.enableEditing {
|
||||
return false
|
||||
@ -254,6 +256,11 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
self.updateNumber(cleanPhoneNumber(string), tryRestoringInputPosition: false)
|
||||
return false
|
||||
}
|
||||
|
||||
if string.count == 1, let num = Int(string) {
|
||||
self.keyPressed?(num)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -1271,7 +1271,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
let layoutOptions = {
|
||||
if state.isPremium == true {
|
||||
|
||||
} else if let products = state.products {
|
||||
} else if let products = state.products, products.count > 1 {
|
||||
var optionsItems: [SectionGroupComponent.Item] = []
|
||||
let gradientColors: [UIColor] = [
|
||||
UIColor(rgb: 0x8e77ff),
|
||||
@ -2113,9 +2113,22 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
var highlightableLinks = false
|
||||
let secondaryTitleText: String
|
||||
if let otherPeerName = state.otherPeerName {
|
||||
if case let .emojiStatus(_, _, _, emojiPackTitle) = context.component.source {
|
||||
if case let .emojiStatus(_, _, file, emojiPackTitle) = context.component.source {
|
||||
highlightableLinks = true
|
||||
secondaryTitleText = environment.strings.Premium_EmojiStatusTitle(otherPeerName, emojiPackTitle ?? "").string.replacingOccurrences(of: "#", with: " # ")
|
||||
|
||||
var packReference: StickerPackReference?
|
||||
if let file = file {
|
||||
for attribute in file.attributes {
|
||||
if case let .CustomEmoji(_, _, reference) = attribute {
|
||||
packReference = reference
|
||||
}
|
||||
}
|
||||
}
|
||||
if let packReference = packReference, case let .id(id, _) = packReference, id == 773947703670341676 {
|
||||
secondaryTitleText = environment.strings.Premium_EmojiStatusShortTitle(otherPeerName).string
|
||||
} else {
|
||||
secondaryTitleText = environment.strings.Premium_EmojiStatusTitle(otherPeerName, emojiPackTitle ?? "").string.replacingOccurrences(of: "#", with: " # ")
|
||||
}
|
||||
} else if case .profile = context.component.source {
|
||||
secondaryTitleText = environment.strings.Premium_PersonalTitle(otherPeerName).string
|
||||
} else if case let .gift(fromPeerId, _, duration) = context.component.source {
|
||||
|
@ -205,6 +205,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1275374751] = { return Api.EmojiLanguage.parse_emojiLanguage($0) }
|
||||
dict[-1835310691] = { return Api.EmojiStatus.parse_emojiStatus($0) }
|
||||
dict[769727150] = { return Api.EmojiStatus.parse_emojiStatusEmpty($0) }
|
||||
dict[-97474361] = { return Api.EmojiStatus.parse_emojiStatusUntil($0) }
|
||||
dict[-1519029347] = { return Api.EmojiURL.parse_emojiURL($0) }
|
||||
dict[1643173063] = { return Api.EncryptedChat.parse_encryptedChat($0) }
|
||||
dict[505183301] = { return Api.EncryptedChat.parse_encryptedChatDiscarded($0) }
|
||||
@ -354,6 +355,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[42402760] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmoji($0) }
|
||||
dict[215889721] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmojiAnimations($0) }
|
||||
dict[-427863538] = { return Api.InputStickerSet.parse_inputStickerSetDice($0) }
|
||||
dict[701560302] = { return Api.InputStickerSet.parse_inputStickerSetEmojiDefaultStatuses($0) }
|
||||
dict[80008398] = { return Api.InputStickerSet.parse_inputStickerSetEmojiGenericAnimations($0) }
|
||||
dict[-4838507] = { return Api.InputStickerSet.parse_inputStickerSetEmpty($0) }
|
||||
dict[-1645763991] = { return Api.InputStickerSet.parse_inputStickerSetID($0) }
|
||||
@ -696,6 +698,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1954007928] = { return Api.SecureValueType.parse_secureValueTypeRentalAgreement($0) }
|
||||
dict[-368907213] = { return Api.SecureValueType.parse_secureValueTypeTemporaryRegistration($0) }
|
||||
dict[-63531698] = { return Api.SecureValueType.parse_secureValueTypeUtilityBill($0) }
|
||||
dict[-1206095820] = { return Api.SendAsPeer.parse_sendAsPeer($0) }
|
||||
dict[-44119819] = { return Api.SendMessageAction.parse_sendMessageCancelAction($0) }
|
||||
dict[1653390447] = { return Api.SendMessageAction.parse_sendMessageChooseContactAction($0) }
|
||||
dict[-1336228175] = { return Api.SendMessageAction.parse_sendMessageChooseStickerAction($0) }
|
||||
@ -805,6 +808,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1398708869] = { return Api.Update.parse_updateMessagePoll($0) }
|
||||
dict[274961865] = { return Api.Update.parse_updateMessagePollVote($0) }
|
||||
dict[357013699] = { return Api.Update.parse_updateMessageReactions($0) }
|
||||
dict[-2030252155] = { return Api.Update.parse_updateMoveStickerSetToTop($0) }
|
||||
dict[1656358105] = { return Api.Update.parse_updateNewChannelMessage($0) }
|
||||
dict[314359194] = { return Api.Update.parse_updateNewEncryptedMessage($0) }
|
||||
dict[522914557] = { return Api.Update.parse_updateNewMessage($0) }
|
||||
@ -936,7 +940,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-541588713] = { return Api.channels.ChannelParticipant.parse_channelParticipant($0) }
|
||||
dict[-1699676497] = { return Api.channels.ChannelParticipants.parse_channelParticipants($0) }
|
||||
dict[-266911767] = { return Api.channels.ChannelParticipants.parse_channelParticipantsNotModified($0) }
|
||||
dict[-2091463255] = { return Api.channels.SendAsPeers.parse_sendAsPeers($0) }
|
||||
dict[-191450938] = { return Api.channels.SendAsPeers.parse_sendAsPeers($0) }
|
||||
dict[182326673] = { return Api.contacts.Blocked.parse_blocked($0) }
|
||||
dict[-513392236] = { return Api.contacts.Blocked.parse_blockedSlice($0) }
|
||||
dict[-353862078] = { return Api.contacts.Contacts.parse_contacts($0) }
|
||||
@ -1554,6 +1558,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.SecureValueType:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.SendAsPeer:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.SendMessageAction:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ShippingOption:
|
||||
|
@ -541,7 +541,7 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum MessageExtendedMedia: TypeConstructorDescription {
|
||||
indirect enum MessageExtendedMedia: TypeConstructorDescription {
|
||||
case messageExtendedMedia(media: Api.MessageMedia)
|
||||
case messageExtendedMediaPreview(flags: Int32, w: Int32?, h: Int32?, thumb: Api.PhotoSize?, videoDuration: Int32?)
|
||||
|
||||
|
@ -1,3 +1,45 @@
|
||||
public extension Api {
|
||||
enum SendAsPeer: TypeConstructorDescription {
|
||||
case sendAsPeer(flags: Int32, peer: Api.Peer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .sendAsPeer(let flags, let peer):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1206095820)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .sendAsPeer(let flags, let peer):
|
||||
return ("sendAsPeer", [("flags", String(describing: flags)), ("peer", String(describing: peer))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_sendAsPeer(_ reader: BufferReader) -> SendAsPeer? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.SendAsPeer.sendAsPeer(flags: _1!, peer: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SendMessageAction: TypeConstructorDescription {
|
||||
case sendMessageCancelAction
|
||||
@ -842,49 +884,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StickerPack: TypeConstructorDescription {
|
||||
case stickerPack(emoticon: String, documents: [Int64])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stickerPack(let emoticon, let documents):
|
||||
if boxed {
|
||||
buffer.appendInt32(313694676)
|
||||
}
|
||||
serializeString(emoticon, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(documents.count))
|
||||
for item in documents {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stickerPack(let emoticon, let documents):
|
||||
return ("stickerPack", [("emoticon", String(describing: emoticon)), ("documents", String(describing: documents))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_stickerPack(_ reader: BufferReader) -> StickerPack? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: [Int64]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StickerPack.stickerPack(emoticon: _1!, documents: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,49 @@
|
||||
public extension Api {
|
||||
enum StickerPack: TypeConstructorDescription {
|
||||
case stickerPack(emoticon: String, documents: [Int64])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stickerPack(let emoticon, let documents):
|
||||
if boxed {
|
||||
buffer.appendInt32(313694676)
|
||||
}
|
||||
serializeString(emoticon, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(documents.count))
|
||||
for item in documents {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stickerPack(let emoticon, let documents):
|
||||
return ("stickerPack", [("emoticon", String(describing: emoticon)), ("documents", String(describing: documents))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_stickerPack(_ reader: BufferReader) -> StickerPack? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: [Int64]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StickerPack.stickerPack(emoticon: _1!, documents: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StickerSet: TypeConstructorDescription {
|
||||
case stickerSet(flags: Int32, installedDate: Int32?, id: Int64, accessHash: Int64, title: String, shortName: String, thumbs: [Api.PhotoSize]?, thumbDcId: Int32?, thumbVersion: Int32?, thumbDocumentId: Int64?, count: Int32, hash: Int32)
|
||||
@ -613,6 +659,7 @@ public extension Api {
|
||||
case updateMessagePoll(flags: Int32, pollId: Int64, poll: Api.Poll?, results: Api.PollResults)
|
||||
case updateMessagePollVote(pollId: Int64, userId: Int64, options: [Buffer], qts: Int32)
|
||||
case updateMessageReactions(peer: Api.Peer, msgId: Int32, reactions: Api.MessageReactions)
|
||||
case updateMoveStickerSetToTop(flags: Int32, stickerset: Int64)
|
||||
case updateNewChannelMessage(message: Api.Message, pts: Int32, ptsCount: Int32)
|
||||
case updateNewEncryptedMessage(message: Api.EncryptedMessage, qts: Int32)
|
||||
case updateNewMessage(message: Api.Message, pts: Int32, ptsCount: Int32)
|
||||
@ -1195,6 +1242,13 @@ public extension Api {
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
reactions.serialize(buffer, true)
|
||||
break
|
||||
case .updateMoveStickerSetToTop(let flags, let stickerset):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2030252155)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(stickerset, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateNewChannelMessage(let message, let pts, let ptsCount):
|
||||
if boxed {
|
||||
buffer.appendInt32(1656358105)
|
||||
@ -1698,6 +1752,8 @@ public extension Api {
|
||||
return ("updateMessagePollVote", [("pollId", String(describing: pollId)), ("userId", String(describing: userId)), ("options", String(describing: options)), ("qts", String(describing: qts))])
|
||||
case .updateMessageReactions(let peer, let msgId, let reactions):
|
||||
return ("updateMessageReactions", [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("reactions", String(describing: reactions))])
|
||||
case .updateMoveStickerSetToTop(let flags, let stickerset):
|
||||
return ("updateMoveStickerSetToTop", [("flags", String(describing: flags)), ("stickerset", String(describing: stickerset))])
|
||||
case .updateNewChannelMessage(let message, let pts, let ptsCount):
|
||||
return ("updateNewChannelMessage", [("message", String(describing: message)), ("pts", String(describing: pts)), ("ptsCount", String(describing: ptsCount))])
|
||||
case .updateNewEncryptedMessage(let message, let qts):
|
||||
@ -2918,6 +2974,20 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateMoveStickerSetToTop(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.Update.updateMoveStickerSetToTop(flags: _1!, stickerset: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateNewChannelMessage(_ reader: BufferReader) -> Update? {
|
||||
var _1: Api.Message?
|
||||
if let signature = reader.readInt32() {
|
||||
|
@ -692,13 +692,13 @@ public extension Api.channels {
|
||||
}
|
||||
public extension Api.channels {
|
||||
enum SendAsPeers: TypeConstructorDescription {
|
||||
case sendAsPeers(peers: [Api.Peer], chats: [Api.Chat], users: [Api.User])
|
||||
case sendAsPeers(peers: [Api.SendAsPeer], chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .sendAsPeers(let peers, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2091463255)
|
||||
buffer.appendInt32(-191450938)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(peers.count))
|
||||
@ -727,9 +727,9 @@ public extension Api.channels {
|
||||
}
|
||||
|
||||
public static func parse_sendAsPeers(_ reader: BufferReader) -> SendAsPeers? {
|
||||
var _1: [Api.Peer]?
|
||||
var _1: [Api.SendAsPeer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SendAsPeer.self)
|
||||
}
|
||||
var _2: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
|
@ -390,6 +390,7 @@ public extension Api {
|
||||
enum EmojiStatus: TypeConstructorDescription {
|
||||
case emojiStatus(documentId: Int64)
|
||||
case emojiStatusEmpty
|
||||
case emojiStatusUntil(documentId: Int64, until: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -404,6 +405,13 @@ public extension Api {
|
||||
buffer.appendInt32(769727150)
|
||||
}
|
||||
|
||||
break
|
||||
case .emojiStatusUntil(let documentId, let until):
|
||||
if boxed {
|
||||
buffer.appendInt32(-97474361)
|
||||
}
|
||||
serializeInt64(documentId, buffer: buffer, boxed: false)
|
||||
serializeInt32(until, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -414,6 +422,8 @@ public extension Api {
|
||||
return ("emojiStatus", [("documentId", String(describing: documentId))])
|
||||
case .emojiStatusEmpty:
|
||||
return ("emojiStatusEmpty", [])
|
||||
case .emojiStatusUntil(let documentId, let until):
|
||||
return ("emojiStatusUntil", [("documentId", String(describing: documentId)), ("until", String(describing: until))])
|
||||
}
|
||||
}
|
||||
|
||||
@ -431,6 +441,20 @@ public extension Api {
|
||||
public static func parse_emojiStatusEmpty(_ reader: BufferReader) -> EmojiStatus? {
|
||||
return Api.EmojiStatus.emojiStatusEmpty
|
||||
}
|
||||
public static func parse_emojiStatusUntil(_ reader: BufferReader) -> EmojiStatus? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.EmojiStatus.emojiStatusUntil(documentId: _1!, until: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1136,67 +1160,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum GeoPoint: TypeConstructorDescription {
|
||||
case geoPoint(flags: Int32, long: Double, lat: Double, accessHash: Int64, accuracyRadius: Int32?)
|
||||
case geoPointEmpty
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .geoPoint(let flags, let long, let lat, let accessHash, let accuracyRadius):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1297942941)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeDouble(long, buffer: buffer, boxed: false)
|
||||
serializeDouble(lat, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(accuracyRadius!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .geoPointEmpty:
|
||||
if boxed {
|
||||
buffer.appendInt32(286776671)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .geoPoint(let flags, let long, let lat, let accessHash, let accuracyRadius):
|
||||
return ("geoPoint", [("flags", String(describing: flags)), ("long", String(describing: long)), ("lat", String(describing: lat)), ("accessHash", String(describing: accessHash)), ("accuracyRadius", String(describing: accuracyRadius))])
|
||||
case .geoPointEmpty:
|
||||
return ("geoPointEmpty", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_geoPoint(_ reader: BufferReader) -> GeoPoint? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Double?
|
||||
_2 = reader.readDouble()
|
||||
var _3: Double?
|
||||
_3 = reader.readDouble()
|
||||
var _4: Int64?
|
||||
_4 = reader.readInt64()
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.GeoPoint.geoPoint(flags: _1!, long: _2!, lat: _3!, accessHash: _4!, accuracyRadius: _5)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_geoPointEmpty(_ reader: BufferReader) -> GeoPoint? {
|
||||
return Api.GeoPoint.geoPointEmpty
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,67 @@
|
||||
public extension Api {
|
||||
enum GeoPoint: TypeConstructorDescription {
|
||||
case geoPoint(flags: Int32, long: Double, lat: Double, accessHash: Int64, accuracyRadius: Int32?)
|
||||
case geoPointEmpty
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .geoPoint(let flags, let long, let lat, let accessHash, let accuracyRadius):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1297942941)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeDouble(long, buffer: buffer, boxed: false)
|
||||
serializeDouble(lat, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(accuracyRadius!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .geoPointEmpty:
|
||||
if boxed {
|
||||
buffer.appendInt32(286776671)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .geoPoint(let flags, let long, let lat, let accessHash, let accuracyRadius):
|
||||
return ("geoPoint", [("flags", String(describing: flags)), ("long", String(describing: long)), ("lat", String(describing: lat)), ("accessHash", String(describing: accessHash)), ("accuracyRadius", String(describing: accuracyRadius))])
|
||||
case .geoPointEmpty:
|
||||
return ("geoPointEmpty", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_geoPoint(_ reader: BufferReader) -> GeoPoint? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Double?
|
||||
_2 = reader.readDouble()
|
||||
var _3: Double?
|
||||
_3 = reader.readDouble()
|
||||
var _4: Int64?
|
||||
_4 = reader.readInt64()
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.GeoPoint.geoPoint(flags: _1!, long: _2!, lat: _3!, accessHash: _4!, accuracyRadius: _5)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_geoPointEmpty(_ reader: BufferReader) -> GeoPoint? {
|
||||
return Api.GeoPoint.geoPointEmpty
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum GlobalPrivacySettings: TypeConstructorDescription {
|
||||
case globalPrivacySettings(flags: Int32, archiveAndMuteNewNoncontactPeers: Api.Bool?)
|
||||
|
@ -677,6 +677,7 @@ public extension Api {
|
||||
case inputStickerSetAnimatedEmoji
|
||||
case inputStickerSetAnimatedEmojiAnimations
|
||||
case inputStickerSetDice(emoticon: String)
|
||||
case inputStickerSetEmojiDefaultStatuses
|
||||
case inputStickerSetEmojiGenericAnimations
|
||||
case inputStickerSetEmpty
|
||||
case inputStickerSetID(id: Int64, accessHash: Int64)
|
||||
@ -702,6 +703,12 @@ public extension Api {
|
||||
buffer.appendInt32(-427863538)
|
||||
}
|
||||
serializeString(emoticon, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputStickerSetEmojiDefaultStatuses:
|
||||
if boxed {
|
||||
buffer.appendInt32(701560302)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputStickerSetEmojiGenericAnimations:
|
||||
if boxed {
|
||||
@ -745,6 +752,8 @@ public extension Api {
|
||||
return ("inputStickerSetAnimatedEmojiAnimations", [])
|
||||
case .inputStickerSetDice(let emoticon):
|
||||
return ("inputStickerSetDice", [("emoticon", String(describing: emoticon))])
|
||||
case .inputStickerSetEmojiDefaultStatuses:
|
||||
return ("inputStickerSetEmojiDefaultStatuses", [])
|
||||
case .inputStickerSetEmojiGenericAnimations:
|
||||
return ("inputStickerSetEmojiGenericAnimations", [])
|
||||
case .inputStickerSetEmpty:
|
||||
@ -775,6 +784,9 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputStickerSetEmojiDefaultStatuses(_ reader: BufferReader) -> InputStickerSet? {
|
||||
return Api.InputStickerSet.inputStickerSetEmojiDefaultStatuses
|
||||
}
|
||||
public static func parse_inputStickerSetEmojiGenericAnimations(_ reader: BufferReader) -> InputStickerSet? {
|
||||
return Api.InputStickerSet.inputStickerSetEmojiGenericAnimations
|
||||
}
|
||||
|
@ -1254,7 +1254,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
||||
}
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(participant.peer), text: text), action: { _ in return false })
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(participant.peer), text: text, action: nil), action: { _ in return false })
|
||||
}
|
||||
} else {
|
||||
if case let .channel(groupPeer) = groupPeer, let listenerLink = inviteLinks?.listenerLink, !groupPeer.hasPermission(.inviteMembers) {
|
||||
@ -1362,7 +1362,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
||||
}
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text), action: { _ in return false })
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text, action: nil), action: { _ in return false })
|
||||
}
|
||||
}))
|
||||
} else if case let .legacyGroup(groupPeer) = groupPeer {
|
||||
@ -1430,7 +1430,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
||||
}
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text), action: { _ in return false })
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text, action: nil), action: { _ in return false })
|
||||
}
|
||||
}))
|
||||
}
|
||||
@ -2262,7 +2262,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
return
|
||||
}
|
||||
let text = strongSelf.presentationData.strings.VoiceChat_PeerJoinedText(EnginePeer(event.peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(event.peer), text: text), action: { _ in return false })
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(event.peer), text: text, action: nil), action: { _ in return false })
|
||||
}
|
||||
}))
|
||||
|
||||
@ -2277,7 +2277,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.VoiceChat_DisplayAsSuccess(EnginePeer(peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
||||
}
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(peer), text: text), action: { _ in return false })
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(peer), text: text, action: nil), action: { _ in return false })
|
||||
}))
|
||||
|
||||
self.stateVersionDisposable.set((self.call.stateVersion
|
||||
|
@ -66,6 +66,8 @@ extension StickerPackReference {
|
||||
self = .premiumGifts
|
||||
case .inputStickerSetEmojiGenericAnimations:
|
||||
self = .emojiGenericAnimations
|
||||
case .inputStickerSetEmojiDefaultStatuses:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +101,8 @@ extension PeerEmojiStatus {
|
||||
self.init(fileId: documentId)
|
||||
case .emojiStatusEmpty:
|
||||
return nil
|
||||
case .emojiStatusUntil:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,30 @@ import SwiftSignalKit
|
||||
import TelegramApi
|
||||
import MtProtoKit
|
||||
|
||||
public struct SendAsPeer: Equatable {
|
||||
public let peer: Peer
|
||||
public let subscribers: Int32?
|
||||
public let isPremiumRequired: Bool
|
||||
|
||||
public init(peer: Peer, subscribers: Int32?, isPremiumRequired: Bool) {
|
||||
self.peer = peer
|
||||
self.subscribers = subscribers
|
||||
self.isPremiumRequired = isPremiumRequired
|
||||
}
|
||||
|
||||
public static func ==(lhs: SendAsPeer, rhs: SendAsPeer) -> Bool {
|
||||
return lhs.peer.isEqual(rhs.peer) && lhs.subscribers == rhs.subscribers && lhs.isPremiumRequired == rhs.isPremiumRequired
|
||||
}
|
||||
}
|
||||
|
||||
public final class CachedSendAsPeers: Codable {
|
||||
public let peerIds: [PeerId]
|
||||
public let premiumRequiredPeerIds: Set<PeerId>
|
||||
public let timestamp: Int32
|
||||
|
||||
public init(peerIds: [PeerId], timestamp: Int32) {
|
||||
public init(peerIds: [PeerId], premiumRequiredPeerIds: Set<PeerId>, timestamp: Int32) {
|
||||
self.peerIds = peerIds
|
||||
self.premiumRequiredPeerIds = premiumRequiredPeerIds
|
||||
self.timestamp = timestamp
|
||||
}
|
||||
|
||||
@ -17,6 +35,7 @@ public final class CachedSendAsPeers: Codable {
|
||||
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
self.peerIds = (try container.decode([Int64].self, forKey: "peerIds")).map(PeerId.init)
|
||||
self.premiumRequiredPeerIds = Set((try container.decodeIfPresent([Int64].self, forKey: "premiumRequiredPeerIds") ?? []).map(PeerId.init))
|
||||
self.timestamp = try container.decode(Int32.self, forKey: "timestamp")
|
||||
}
|
||||
|
||||
@ -24,24 +43,25 @@ public final class CachedSendAsPeers: Codable {
|
||||
var container = encoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
try container.encode(self.peerIds.map { $0.toInt64() }, forKey: "peerIds")
|
||||
try container.encode(Array(self.premiumRequiredPeerIds).map { $0.toInt64() }, forKey: "premiumRequiredPeerIds")
|
||||
try container.encode(self.timestamp, forKey: "timestamp")
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_cachedPeerSendAsAvailablePeers(account: Account, peerId: PeerId) -> Signal<[FoundPeer], NoError> {
|
||||
func _internal_cachedPeerSendAsAvailablePeers(account: Account, peerId: PeerId) -> Signal<[SendAsPeer], NoError> {
|
||||
let key = ValueBoxKey(length: 8)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
return account.postbox.transaction { transaction -> ([FoundPeer], Int32)? in
|
||||
return account.postbox.transaction { transaction -> ([SendAsPeer], Int32)? in
|
||||
let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedSendAsPeers, key: key))?.get(CachedSendAsPeers.self)
|
||||
if let cached = cached {
|
||||
var peers: [FoundPeer] = []
|
||||
var peers: [SendAsPeer] = []
|
||||
for peerId in cached.peerIds {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
var subscribers: Int32?
|
||||
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData {
|
||||
subscribers = cachedData.participantsSummary.memberCount
|
||||
}
|
||||
peers.append(FoundPeer(peer: peer, subscribers: subscribers))
|
||||
peers.append(SendAsPeer(peer: peer, subscribers: subscribers, isPremiumRequired: cached.premiumRequiredPeerIds.contains(peer.id)))
|
||||
}
|
||||
}
|
||||
return (peers, cached.timestamp)
|
||||
@ -49,8 +69,8 @@ func _internal_cachedPeerSendAsAvailablePeers(account: Account, peerId: PeerId)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|> mapToSignal { cachedPeersAndTimestamp -> Signal<[FoundPeer], NoError> in
|
||||
let initialSignal: Signal<[FoundPeer], NoError>
|
||||
|> mapToSignal { cachedPeersAndTimestamp -> Signal<[SendAsPeer], NoError> in
|
||||
let initialSignal: Signal<[SendAsPeer], NoError>
|
||||
if let (cachedPeers, _) = cachedPeersAndTimestamp {
|
||||
initialSignal = .single(cachedPeers)
|
||||
} else {
|
||||
@ -58,10 +78,16 @@ func _internal_cachedPeerSendAsAvailablePeers(account: Account, peerId: PeerId)
|
||||
}
|
||||
return initialSignal
|
||||
|> then(_internal_peerSendAsAvailablePeers(network: account.network, postbox: account.postbox, peerId: peerId)
|
||||
|> mapToSignal { peers -> Signal<[FoundPeer], NoError> in
|
||||
return account.postbox.transaction { transaction -> [FoundPeer] in
|
||||
|> mapToSignal { peers -> Signal<[SendAsPeer], NoError> in
|
||||
return account.postbox.transaction { transaction -> [SendAsPeer] in
|
||||
let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
if let entry = CodableEntry(CachedSendAsPeers(peerIds: peers.map { $0.peer.id }, timestamp: currentTimestamp)) {
|
||||
var premiumRequiredPeerIds = Set<PeerId>()
|
||||
for peer in peers {
|
||||
if peer.isPremiumRequired {
|
||||
premiumRequiredPeerIds.insert(peer.peer.id)
|
||||
}
|
||||
}
|
||||
if let entry = CodableEntry(CachedSendAsPeers(peerIds: peers.map { $0.peer.id }, premiumRequiredPeerIds: premiumRequiredPeerIds, timestamp: currentTimestamp)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedSendAsPeers, key: key), entry: entry)
|
||||
}
|
||||
return peers
|
||||
@ -71,7 +97,7 @@ func _internal_cachedPeerSendAsAvailablePeers(account: Account, peerId: PeerId)
|
||||
}
|
||||
|
||||
|
||||
func _internal_peerSendAsAvailablePeers(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<[FoundPeer], NoError> {
|
||||
func _internal_peerSendAsAvailablePeers(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<[SendAsPeer], NoError> {
|
||||
return postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
} |> mapToSignal { inputPeer in
|
||||
@ -88,9 +114,15 @@ func _internal_peerSendAsAvailablePeers(network: Network, postbox: Postbox, peer
|
||||
return .single([])
|
||||
}
|
||||
switch result {
|
||||
case let .sendAsPeers(_, chats, _):
|
||||
case let .sendAsPeers(sendAsPeers, chats, _):
|
||||
var subscribers: [PeerId: Int32] = [:]
|
||||
let peers = chats.compactMap(parseTelegramGroupOrChannel)
|
||||
var premiumRequiredPeerIds = Set<PeerId>()
|
||||
for sendAsPeer in sendAsPeers {
|
||||
if case let .sendAsPeer(flags, peer) = sendAsPeer, (flags & (1 << 0)) != 0 {
|
||||
premiumRequiredPeerIds.insert(peer.peerId)
|
||||
}
|
||||
}
|
||||
for chat in chats {
|
||||
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
|
||||
switch chat {
|
||||
@ -110,8 +142,8 @@ func _internal_peerSendAsAvailablePeers(network: Network, postbox: Postbox, peer
|
||||
return updated
|
||||
})
|
||||
return peers
|
||||
} |> map { peers -> [FoundPeer] in
|
||||
return peers.map { FoundPeer(peer: $0, subscribers: subscribers[$0.id]) }
|
||||
} |> map { peers -> [SendAsPeer] in
|
||||
return peers.map { SendAsPeer(peer: $0, subscribers: subscribers[$0.id], isPremiumRequired: premiumRequiredPeerIds.contains($0.id)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -690,7 +690,7 @@ public extension TelegramEngine {
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
public func sendAsAvailablePeers(peerId: PeerId) -> Signal<[FoundPeer], NoError> {
|
||||
public func sendAsAvailablePeers(peerId: PeerId) -> Signal<[SendAsPeer], NoError> {
|
||||
return _internal_cachedPeerSendAsAvailablePeers(account: self.account, peerId: peerId)
|
||||
}
|
||||
|
||||
|
@ -556,7 +556,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
||||
infoPrimaryTextColor: UIColor(rgb: 0xffffff),
|
||||
infoLinkTextColor: UIColor(rgb: 0xffffff),
|
||||
outgoingCheckColor: UIColor(rgb: 0xffffff),
|
||||
mediaDateAndStatusFillColor: UIColor(white: 0.0, alpha: 0.5),
|
||||
mediaDateAndStatusFillColor: UIColor(white: 0.0, alpha: 0.3),
|
||||
mediaDateAndStatusTextColor: UIColor(rgb: 0xffffff),
|
||||
shareButtonFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)),
|
||||
shareButtonStrokeColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0xb2b2b2, alpha: 0.18), withoutWallpaper: UIColor(rgb: 0xb2b2b2, alpha: 0.18)),
|
||||
|
@ -789,7 +789,7 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres
|
||||
infoPrimaryTextColor: UIColor(rgb: 0xffffff),
|
||||
infoLinkTextColor: accentColor,
|
||||
outgoingCheckColor: outgoingCheckColor,
|
||||
mediaDateAndStatusFillColor: UIColor(white: 0.0, alpha: 0.5),
|
||||
mediaDateAndStatusFillColor: UIColor(white: 0.0, alpha: 0.3),
|
||||
mediaDateAndStatusTextColor: UIColor(rgb: 0xffffff),
|
||||
shareButtonFillColor: PresentationThemeVariableColor(color: additionalBackgroundColor.withAlphaComponent(0.5)),
|
||||
shareButtonStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor),
|
||||
|
@ -671,7 +671,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
infoPrimaryTextColor: UIColor(rgb: 0x000000),
|
||||
infoLinkTextColor: UIColor(rgb: 0x004bad),
|
||||
outgoingCheckColor: UIColor(rgb: 0x19c700),
|
||||
mediaDateAndStatusFillColor: UIColor(white: 0.0, alpha: 0.5),
|
||||
mediaDateAndStatusFillColor: UIColor(white: 0.0, alpha: 0.3),
|
||||
mediaDateAndStatusTextColor: UIColor(rgb: 0xffffff),
|
||||
shareButtonFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x748391, alpha: 0.45)),
|
||||
shareButtonStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: .clear),
|
||||
@ -802,7 +802,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
infoPrimaryTextColor: UIColor(rgb: 0x000000),
|
||||
infoLinkTextColor: UIColor(rgb: 0x004bad),
|
||||
outgoingCheckColor: UIColor(rgb: 0xffffff),
|
||||
mediaDateAndStatusFillColor: UIColor(rgb: 0x000000, alpha: 0.5),
|
||||
mediaDateAndStatusFillColor: UIColor(rgb: 0x000000, alpha: 0.3),
|
||||
mediaDateAndStatusTextColor: UIColor(rgb: 0xffffff),
|
||||
shareButtonFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)),
|
||||
shareButtonStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: UIColor(rgb: 0xe5e5ea)),
|
||||
|
21
submodules/TelegramUI/Images.xcassets/Settings/Keypad.imageset/Contents.json
vendored
Normal file
21
submodules/TelegramUI/Images.xcassets/Settings/Keypad.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Keyboard.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Settings/Keypad.imageset/Keyboard.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Settings/Keypad.imageset/Keyboard.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.7 KiB |
@ -5749,16 +5749,23 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let currentAccountPeer = self.context.account.postbox.loadedPeerWithId(self.context.account.peerId)
|
||||
|> map { peer in
|
||||
return FoundPeer(peer: peer, subscribers: nil)
|
||||
return SendAsPeer(peer: peer, subscribers: nil, isPremiumRequired: false)
|
||||
}
|
||||
|
||||
if let peerId = self.chatLocation.peerId, [Namespaces.Peer.CloudChannel, Namespaces.Peer.CloudGroup].contains(peerId.namespace) {
|
||||
self.sendAsPeersDisposable = (combineLatest(queue: Queue.mainQueue(), currentAccountPeer, self.context.account.postbox.peerView(id: peerId), self.context.engine.peers.sendAsAvailablePeers(peerId: peerId)))
|
||||
.start(next: { [weak self] currentAccountPeer, peerView, peers in
|
||||
self.sendAsPeersDisposable = (combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
currentAccountPeer,
|
||||
self.context.account.postbox.peerView(id: peerId),
|
||||
self.context.engine.peers.sendAsAvailablePeers(peerId: peerId))
|
||||
).start(next: { [weak self] currentAccountPeer, peerView, peers in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var allPeers: [FoundPeer]?
|
||||
|
||||
let isPremium = strongSelf.presentationInterfaceState.isPremium
|
||||
|
||||
var allPeers: [SendAsPeer]?
|
||||
if !peers.isEmpty {
|
||||
if let channel = peerViewMainPeer(peerView) as? TelegramChannel, case .group = channel.info, channel.hasPermission(.canBeAnonymous) {
|
||||
allPeers = peers
|
||||
@ -5771,7 +5778,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
if !hasAnonymousPeer {
|
||||
allPeers?.insert(FoundPeer(peer: channel, subscribers: 0), at: 0)
|
||||
allPeers?.insert(SendAsPeer(peer: channel, subscribers: 0, isPremiumRequired: false), at: 0)
|
||||
}
|
||||
} else {
|
||||
allPeers = peers.filter { $0.peer.id != peerViewMainPeer(peerView)?.id }
|
||||
@ -5781,8 +5788,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if allPeers?.count == 1 {
|
||||
allPeers = nil
|
||||
}
|
||||
|
||||
var currentSendAsPeerId = strongSelf.presentationInterfaceState.currentSendAsPeerId
|
||||
if let peerId = currentSendAsPeerId, let peer = allPeers?.first(where: { $0.peer.id == peerId }) {
|
||||
if !isPremium && peer.isPremiumRequired {
|
||||
currentSendAsPeerId = nil
|
||||
}
|
||||
} else {
|
||||
currentSendAsPeerId = nil
|
||||
}
|
||||
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
return $0.updatedSendAsPeers(allPeers)
|
||||
return $0.updatedSendAsPeers(allPeers).updatedCurrentSendAsPeerId(currentSendAsPeerId)
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -8935,6 +8952,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
let isPremium = strongSelf.presentationInterfaceState.isPremium
|
||||
|
||||
let cleanInsets = layout.intrinsicInsets
|
||||
let insets = layout.insets(options: .input)
|
||||
let bottomInset = max(insets.bottom, cleanInsets.bottom) + 43.0
|
||||
@ -8949,7 +8968,23 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
items.append(.custom(ChatSendAsPeerTitleContextItem(text: strongSelf.presentationInterfaceState.strings.Conversation_SendMesageAs.uppercased()), false))
|
||||
items.append(.custom(ChatSendAsPeerListContextItem(context: strongSelf.context, chatPeerId: peerId, peers: peers, selectedPeerId: myPeerId), false))
|
||||
items.append(.custom(ChatSendAsPeerListContextItem(context: strongSelf.context, chatPeerId: peerId, peers: peers, selectedPeerId: myPeerId, isPremium: isPremium, presentToast: { [weak self] peer in
|
||||
if let strongSelf = self {
|
||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.Conversation_SendMesageAsPremiumInfo, action: strongSelf.presentationData.strings.EmojiInput_PremiumEmojiToast_Action), elevatedLayout: false, action: { [weak self] action in
|
||||
guard let strongSelf = self else {
|
||||
return true
|
||||
}
|
||||
if case .undo = action {
|
||||
strongSelf.chatDisplayNode.dismissTextInput()
|
||||
|
||||
let controller = PremiumIntroScreen(context: strongSelf.context, source: .settings)
|
||||
strongSelf.push(controller)
|
||||
}
|
||||
return true
|
||||
}), in: .current)
|
||||
}
|
||||
|
||||
}), false))
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
|
@ -22,6 +22,7 @@ import LocalMediaResources
|
||||
import WallpaperResources
|
||||
import ChatMessageInteractiveMediaBadge
|
||||
import ContextUI
|
||||
import InvisibleInkDustNode
|
||||
|
||||
private struct FetchControls {
|
||||
let fetch: (Bool) -> Void
|
||||
@ -76,6 +77,7 @@ struct ChatMessageDateAndStatus {
|
||||
}
|
||||
|
||||
private class ExtendedMediaOverlayNode: ASDisplayNode {
|
||||
private let dustNode: MediaDustNode
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
private let blurNode: NavigationBackgroundNode
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
@ -83,6 +85,8 @@ private class ExtendedMediaOverlayNode: ASDisplayNode {
|
||||
private let textNode: ImmediateTextNode
|
||||
|
||||
override init() {
|
||||
self.dustNode = MediaDustNode()
|
||||
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
self.buttonNode.clipsToBounds = true
|
||||
self.buttonNode.cornerRadius = 16.0
|
||||
@ -105,6 +109,7 @@ private class ExtendedMediaOverlayNode: ASDisplayNode {
|
||||
self.cornerRadius = 16.0
|
||||
self.isUserInteractionEnabled = false
|
||||
|
||||
self.addSubnode(self.dustNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
self.buttonNode.addSubnode(self.blurNode)
|
||||
self.buttonNode.addSubnode(self.highlightedBackgroundNode)
|
||||
@ -142,6 +147,9 @@ private class ExtendedMediaOverlayNode: ASDisplayNode {
|
||||
let spacing: CGFloat = 2.0
|
||||
let padding: CGFloat = 10.0
|
||||
|
||||
self.dustNode.frame = CGRect(origin: .zero, size: size)
|
||||
self.dustNode.update(size: size, color: .white)
|
||||
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.semibold(14.0), textColor: .white, paragraphAlignment: .center)
|
||||
let textSize = self.textNode.updateLayout(size)
|
||||
if let iconSize = self.iconNode.image?.size {
|
||||
@ -150,8 +158,8 @@ private class ExtendedMediaOverlayNode: ASDisplayNode {
|
||||
self.highlightedBackgroundNode.frame = CGRect(origin: .zero, size: contentSize)
|
||||
self.blurNode.frame = self.highlightedBackgroundNode.frame
|
||||
self.blurNode.update(size: self.blurNode.frame.size, transition: .immediate)
|
||||
self.blurNode.updateColor(color: UIColor(rgb: 0x000000, alpha: 0.5), enableBlur: true, transition: .immediate)
|
||||
|
||||
self.blurNode.updateColor(color: UIColor(rgb: 0x000000, alpha: 0.3), enableBlur: true, transition: .immediate)
|
||||
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(x: self.buttonNode.frame.minX + padding, y: self.buttonNode.frame.minY + floorToScreenPixels((contentSize.height - iconSize.height) / 2.0) + 1.0), size: iconSize)
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: self.iconNode.frame.maxX + spacing, y: self.buttonNode.frame.minY + floorToScreenPixels((contentSize.height - textSize.height) / 2.0)), size: textSize)
|
||||
}
|
||||
|
@ -808,7 +808,11 @@ public class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol
|
||||
case .text:
|
||||
item.controllerInteraction.sendMessage(button.title)
|
||||
case let .url(url):
|
||||
item.controllerInteraction.openUrl(url, true, nil, nil)
|
||||
var concealed = true
|
||||
if url.hasPrefix("tg://") {
|
||||
concealed = false
|
||||
}
|
||||
item.controllerInteraction.openUrl(url, concealed, nil, nil)
|
||||
case .requestMap:
|
||||
item.controllerInteraction.shareCurrentLocation()
|
||||
case .requestPhone:
|
||||
|
@ -363,8 +363,13 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
override func transitionNode(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||
if self.item?.message.id == messageId, let currentMedia = self.media, currentMedia.isSemanticallyEqual(to: media) {
|
||||
return self.interactiveImageNode.transitionNode()
|
||||
if self.item?.message.id == messageId, var currentMedia = self.media {
|
||||
if let invoice = currentMedia as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia, case let .full(fullMedia) = extendedMedia {
|
||||
currentMedia = fullMedia
|
||||
}
|
||||
if currentMedia.isSemanticallyEqual(to: media) {
|
||||
return self.interactiveImageNode.transitionNode()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -380,7 +385,13 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
override func updateHiddenMedia(_ media: [Media]?) -> Bool {
|
||||
var mediaHidden = false
|
||||
if let currentMedia = self.media, let media = media {
|
||||
|
||||
var currentMedia = self.media
|
||||
if let invoice = currentMedia as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia, case let .full(fullMedia) = extendedMedia {
|
||||
currentMedia = fullMedia
|
||||
}
|
||||
|
||||
if let currentMedia = currentMedia, let media = media {
|
||||
for item in media {
|
||||
if item.isSemanticallyEqual(to: currentMedia) {
|
||||
mediaHidden = true
|
||||
|
@ -11,18 +11,23 @@ import ContextUI
|
||||
import TelegramStringFormatting
|
||||
import AvatarNode
|
||||
import AccountContext
|
||||
import UndoUI
|
||||
|
||||
final class ChatSendAsPeerListContextItem: ContextMenuCustomItem {
|
||||
let context: AccountContext
|
||||
let chatPeerId: PeerId
|
||||
let peers: [FoundPeer]
|
||||
let peers: [SendAsPeer]
|
||||
let selectedPeerId: PeerId?
|
||||
let isPremium: Bool
|
||||
let presentToast: (EnginePeer) -> Void
|
||||
|
||||
init(context: AccountContext, chatPeerId: PeerId, peers: [FoundPeer], selectedPeerId: PeerId?) {
|
||||
init(context: AccountContext, chatPeerId: PeerId, peers: [SendAsPeer], selectedPeerId: PeerId?, isPremium: Bool, presentToast: @escaping (EnginePeer) -> Void) {
|
||||
self.context = context
|
||||
self.chatPeerId = chatPeerId
|
||||
self.peers = peers
|
||||
self.selectedPeerId = selectedPeerId
|
||||
self.isPremium = isPremium
|
||||
self.presentToast = presentToast
|
||||
}
|
||||
|
||||
func node(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) -> ContextMenuCustomNode {
|
||||
@ -99,9 +104,16 @@ private final class ChatSendAsPeerListContextItemNode: ASDisplayNode, ContextMen
|
||||
}
|
||||
}
|
||||
|
||||
let action = ContextMenuActionItem(text: EnginePeer(peer.peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), textLayout: subtitle.flatMap { .secondLineWithValue($0) } ?? .singleLine, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: isSelected ? extendedAvatarSize : avatarSize, signal: avatarSignal), action: { _, f in
|
||||
let action = ContextMenuActionItem(text: EnginePeer(peer.peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), textLayout: subtitle.flatMap { .secondLineWithValue($0) } ?? .singleLine, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: isSelected ? extendedAvatarSize : avatarSize, signal: avatarSignal), textIcon: { theme in
|
||||
return !item.isPremium && peer.isPremiumRequired ? generateTintedImage(image: UIImage(bundleImageName: "Notification/SecretLock"), color: theme.contextMenu.badgeInactiveFillColor) : nil
|
||||
}, action: { _, f in
|
||||
f(.default)
|
||||
|
||||
if !item.isPremium && peer.isPremiumRequired {
|
||||
item.presentToast(EnginePeer(peer.peer))
|
||||
return
|
||||
}
|
||||
|
||||
if peer.peer.id != item.selectedPeerId {
|
||||
let _ = item.context.engine.peers.updatePeerSendAsPeer(peerId: item.chatPeerId, sendAs: peer.peer.id).start()
|
||||
}
|
||||
|
@ -174,79 +174,3 @@ public func canTranslateText(context: AccountContext, text: String, showTranslat
|
||||
return (false, nil)
|
||||
}
|
||||
}
|
||||
|
||||
public struct TextTranslationResult: Equatable {
|
||||
let text: String
|
||||
let detectedLanguage: String?
|
||||
}
|
||||
|
||||
public enum TextTranslationError {
|
||||
case generic
|
||||
}
|
||||
|
||||
private let userAgents: [String] = [
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", // 13.5%
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36", // 6.6%
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0", // 6.4%
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0", // 6.2%
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36", // 5.2%
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Safari/537.36" // 4.8%
|
||||
]
|
||||
|
||||
public func translateText(context: AccountContext, text: String, from: String?, to: String) -> Signal<TextTranslationResult, TextTranslationError> {
|
||||
return Signal { subscriber in
|
||||
var uri = "https://translate.goo";
|
||||
uri += "gleapis.com/transl";
|
||||
uri += "ate_a";
|
||||
uri += "/singl";
|
||||
uri += "e?client=gtx&sl=" + (from ?? "auto") + "&tl=" + to + "&dt=t" + "&ie=UTF-8&oe=UTF-8&otf=1&ssel=0&tsel=0&kc=7&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&q=";
|
||||
uri += text.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
|
||||
|
||||
var request = URLRequest(url: URL(string: uri)!)
|
||||
request.httpMethod = "GET"
|
||||
request.setValue(userAgents[Int.random(in: 0 ..< userAgents.count)], forHTTPHeaderField: "User-Agent")
|
||||
let session = URLSession.shared
|
||||
let task = session.dataTask(with: request, completionHandler: { data, response, error in
|
||||
if let _ = error {
|
||||
subscriber.putError(.generic)
|
||||
} else if let data = data {
|
||||
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? NSArray
|
||||
if let json = json, json.count > 0 {
|
||||
let array = json[0] as? NSArray ?? NSArray()
|
||||
var result: String = ""
|
||||
for i in 0 ..< array.count {
|
||||
let blockText = array[i] as? NSArray
|
||||
if let blockText = blockText, blockText.count > 0 {
|
||||
let value = blockText[0] as? String
|
||||
if let value = value, value != "null" {
|
||||
result += value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let translationResult = TextTranslationResult(text: result, detectedLanguage: json[2] as? String)
|
||||
|
||||
var fromLang: String?
|
||||
if let lang = translationResult.detectedLanguage {
|
||||
fromLang = lang
|
||||
} else if let lang = from {
|
||||
fromLang = lang
|
||||
}
|
||||
if let fromLang = fromLang {
|
||||
let _ = context.engine.messages.translate(text: text, fromLang: fromLang, toLang: to).start()
|
||||
}
|
||||
|
||||
subscriber.putNext(translationResult)
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
}
|
||||
})
|
||||
task.resume()
|
||||
|
||||
return ActionDisposable {
|
||||
task.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,15 +98,15 @@ private final class TranslateScreenComponent: CombinedComponent {
|
||||
self.availableSpeakLanguages = supportedSpeakLanguages()
|
||||
|
||||
super.init()
|
||||
|
||||
self.translationDisposable.set((translateText(context: context, text: text, from: fromLanguage, to: toLanguage) |> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
|
||||
self.translationDisposable.set((context.engine.messages.translate(text: text, fromLang: fromLanguage, toLang: toLanguage) |> deliverOnMainQueue).start(next: { [weak self] text in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.translatedText = result.text
|
||||
if strongSelf.fromLanguage == nil {
|
||||
strongSelf.fromLanguage = result.detectedLanguage
|
||||
}
|
||||
strongSelf.translatedText = text
|
||||
// if strongSelf.fromLanguage == nil {
|
||||
// strongSelf.fromLanguage = result.detectedLanguage
|
||||
// }
|
||||
strongSelf.updated(transition: .immediate)
|
||||
}, error: { error in
|
||||
|
||||
@ -127,14 +127,14 @@ private final class TranslateScreenComponent: CombinedComponent {
|
||||
self.translatedText = nil
|
||||
self.updated(transition: .immediate)
|
||||
|
||||
self.translationDisposable.set((translateText(context: self.context, text: text, from: fromLanguage, to: toLanguage) |> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
self.translationDisposable.set((self.context.engine.messages.translate(text: text, fromLang: fromLanguage, toLang: toLanguage) |> deliverOnMainQueue).start(next: { [weak self] text in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.translatedText = result.text
|
||||
if strongSelf.fromLanguage == nil {
|
||||
strongSelf.fromLanguage = result.detectedLanguage
|
||||
}
|
||||
strongSelf.translatedText = text
|
||||
// if strongSelf.fromLanguage == nil {
|
||||
// strongSelf.fromLanguage = result.detectedLanguage
|
||||
// }
|
||||
strongSelf.updated(transition: .immediate)
|
||||
}, error: { error in
|
||||
|
||||
|
@ -22,7 +22,7 @@ public enum UndoOverlayContent {
|
||||
case chatRemovedFromFolder(chatTitle: String, folderTitle: String)
|
||||
case messagesUnpinned(title: String, text: String, undo: Bool, isHidden: Bool)
|
||||
case setProximityAlert(title: String, text: String, cancelled: Bool)
|
||||
case invitedToVoiceChat(context: AccountContext, peer: EnginePeer, text: String)
|
||||
case invitedToVoiceChat(context: AccountContext, peer: EnginePeer, text: String, action: String?)
|
||||
case linkCopied(text: String)
|
||||
case banned(text: String)
|
||||
case importedMessage(text: String)
|
||||
|
@ -515,7 +515,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
displayUndo = false
|
||||
self.originalRemainingSeconds = 3
|
||||
case let .invitedToVoiceChat(context, peer, text):
|
||||
case let .invitedToVoiceChat(context, peer, text, action):
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 15.0))
|
||||
self.iconNode = nil
|
||||
self.iconCheckNode = nil
|
||||
@ -530,8 +530,14 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
self.avatarNode?.setPeer(context: context, theme: presentationData.theme, peer: peer, overrideImage: nil, emptyColor: presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: true)
|
||||
|
||||
displayUndo = false
|
||||
self.originalRemainingSeconds = 3
|
||||
if let action = action {
|
||||
displayUndo = true
|
||||
undoText = action
|
||||
self.originalRemainingSeconds = 5
|
||||
} else {
|
||||
displayUndo = false
|
||||
self.originalRemainingSeconds = 3
|
||||
}
|
||||
case let .audioRate(slowdown, text):
|
||||
self.avatarNode = nil
|
||||
self.iconNode = nil
|
||||
|
Loading…
x
Reference in New Issue
Block a user