Merge commit 'aca9fbcae8683d30919592494f076d51ef98d756'

This commit is contained in:
Ali 2022-09-05 03:57:40 +04:00
commit 229623491b
37 changed files with 475 additions and 205 deletions

View File

@ -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**:";
@ -8014,6 +8015,8 @@ 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.";
"SetTimeoutFor.Minutes_1" = "Set for 1 minute";
"SetTimeoutFor.Minutes_any" = "Set for %@ minutes";

View File

@ -504,12 +504,10 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
if #available(iOS 13.0, *) {
let appleIdProvider = ASAuthorizationAppleIDProvider()
let passwordProvider = ASAuthorizationPasswordProvider()
let request = appleIdProvider.createRequest()
request.user = number
let passwordRequest = passwordProvider.createRequest()
let authorizationController = ASAuthorizationController(authorizationRequests: [request, passwordRequest])
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = strongSelf
authorizationController.presentationContextProvider = strongSelf
authorizationController.performRequests()

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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))!

View 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()
}
}
}

View File

@ -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)
})
}

View File

@ -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
}

View File

@ -1,6 +1,6 @@
import Foundation
public struct ItemCollectionId: Comparable, Hashable {
public struct ItemCollectionId: Comparable, Hashable, Codable {
public typealias Namespace = Int32
public typealias Id = Int64

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -120,10 +120,9 @@ class EmojiHeaderComponent: Component {
self.statusView.center = targetPosition
animateFrom.alpha = 0.0
self.statusView.layer.animateScale(from: 0.24, to: 1.0, duration: 0.36, timingFunction: CAMediaTimingFunctionName.linear.rawValue)
let transition = ContainedViewLayoutTransition.animated(duration: 0.36, curve: .linear)
transition.animatePositionWithKeyframes(layer: self.statusView.layer, keyframes: generateParabollicMotionKeyframes(from: sourcePosition, to: targetPosition, elevation: 50.0))
self.statusView.layer.animateScale(from: 0.24, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.linear.rawValue)
self.statusView.layer.animatePosition(from: sourcePosition, to: targetPosition, duration: 0.55, timingFunction: kCAMediaTimingFunctionSpring)
Queue.mainQueue().after(0.55, {
self.addSubview(self.statusView)

View File

@ -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 {

View File

@ -208,15 +208,10 @@ class PremiumStarComponent: Component {
"rotate",
"tapRotate"
]
// if #available(iOS 11.0, *) {
// for key in keys {
// node.removeAnimation(forKey: key, blendOutDuration: 0.1)
// }
// } else {
for key in keys {
node.removeAnimation(forKey: key)
}
// }
for key in keys {
node.removeAnimation(forKey: key)
}
switch gesture.state {
case .began:
@ -454,12 +449,10 @@ class PremiumStarComponent: Component {
rightParticleSystem.birthRate = 60.0
rightParticleSystem.particleLifeSpan = 4.0
// leftBottomParticleSystem.speedFactor = 2.0
leftBottomParticleSystem.particleVelocity = 1.6
leftBottomParticleSystem.birthRate = 24.0
leftBottomParticleSystem.particleLifeSpan = 7.0
// rightBottomParticleSystem.speedFactor = 2.0
rightBottomParticleSystem.particleVelocity = 1.6
rightBottomParticleSystem.birthRate = 24.0
rightBottomParticleSystem.particleLifeSpan = 7.0

View File

@ -1199,12 +1199,10 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
emailController.signInWithApple = { [weak controller, weak emailController] in
if #available(iOS 13.0, *) {
let appleIdProvider = ASAuthorizationAppleIDProvider()
let passwordProvider = ASAuthorizationPasswordProvider()
let request = appleIdProvider.createRequest()
request.requestedScopes = [.email]
let passwordRequest = passwordProvider.createRequest()
let authorizationController = ASAuthorizationController(authorizationRequests: [request, passwordRequest])
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = controller
authorizationController.presentationContextProvider = controller
authorizationController.performRequests()

View File

@ -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

View File

@ -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)) }
}
}
}

View File

@ -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)
}

View File

@ -36,6 +36,9 @@ func cacheStickerPack(transaction: Transaction, info: StickerPackCollectionInfo,
case .iconStatusEmoji:
namespace = Namespaces.ItemCollection.CloudIconStatusEmoji
id = 0
case .premiumGifts:
namespace = Namespaces.ItemCollection.CloudPremiumGifts
id = 0
case .id:
break
default:

View File

@ -299,6 +299,13 @@ public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, edit
)
}
public let defaultDarkWallpaperGradientColors: [UIColor] = [
UIColor(rgb: 0x00b3dd),
UIColor(rgb: 0x3b59f2),
UIColor(rgb: 0x358be2),
UIColor(rgb: 0xa434cf)
]
public func makeDefaultDarkPresentationTheme(extendingThemeReference: PresentationThemeReference? = nil, preview: Bool) -> PresentationTheme {
let rootNavigationBar = PresentationThemeRootNavigationBar(
buttonColor: UIColor(rgb: 0xffffff),
@ -309,7 +316,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
accentTextColor: UIColor(rgb: 0xffffff),
blurredBackgroundColor: UIColor(rgb: 0x1d1d1d, alpha: 0.9),
opaqueBackgroundColor: UIColor(rgb: 0x1d1d1d).mixedWith(UIColor(rgb: 0x000000), alpha: 0.1),
separatorColor: UIColor(rgb: 0x3d3d40),
separatorColor: UIColor(rgb: 0x545458, alpha: 0.65),
badgeBackgroundColor: UIColor(rgb: 0xffffff),
badgeStrokeColor: UIColor(rgb: 0x1c1c1d),
badgeTextColor: UIColor(rgb: 0x000000),
@ -486,7 +493,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
withWallpaper: PresentationThemeBubbleColorComponents(
fill: [UIColor(rgb: 0x1D1D1D, alpha: incomingBubbleAlpha)],
highlightedFill: UIColor(rgb: 0x353539),
stroke: UIColor(rgb: 0x262628),
stroke: .clear,
shadow: nil,
reactionInactiveBackground: UIColor(rgb: 0xffffff, alpha: 0.1),
reactionInactiveForeground: UIColor(rgb: 0xffffff),
@ -496,7 +503,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
withoutWallpaper: PresentationThemeBubbleColorComponents(
fill: [UIColor(rgb: 0x1D1D1D, alpha: incomingBubbleAlpha)],
highlightedFill: UIColor(rgb: 0x353539),
stroke: UIColor(rgb: 0x262628),
stroke: .clear,
shadow: nil,
reactionInactiveBackground: UIColor(rgb: 0xffffff, alpha: 0.1),
reactionInactiveForeground: UIColor(rgb: 0xffffff),
@ -556,7 +563,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)),
@ -643,8 +650,10 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
badgeTextColor: UIColor(rgb: 0x000000)
)
let defaultPatternWallpaper: TelegramWallpaper = defaultBuiltinWallpaper(data: .default, colors: defaultDarkWallpaperGradientColors.map(\.rgb), intensity: -35)
let chat = PresentationThemeChat(
defaultWallpaper: .color(0x000000),
defaultWallpaper: defaultPatternWallpaper,
animateMessageColors: false,
message: message,
serviceMessage: serviceMessage,

View File

@ -791,7 +791,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),

View File

@ -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)),

View 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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -5750,16 +5750,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
@ -5772,7 +5779,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 }
@ -5782,8 +5789,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)
})
})
}
@ -8936,6 +8953,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
@ -8950,7 +8969,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()

View File

@ -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)
}

View File

@ -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:

View File

@ -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

View File

@ -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()
}

View File

@ -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()
}
}
}

View File

@ -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

View File

@ -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)

View File

@ -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