Merge branch 'master' into gradient-messages

This commit is contained in:
Peter 2019-07-30 16:58:09 +03:00
commit ad7bda3182
23 changed files with 170 additions and 54 deletions

View File

@ -698,13 +698,13 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
switch self.contentNodes { switch self.contentNodes {
case let .standard(node): case let .standard(node):
if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled { if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled, handleNodeContainer.frame.insetBy(dx: 0.0, dy: -5.0).contains(point) {
return handleNodeContainer.view return handleNodeContainer.view
} else { } else {
return nil return nil
} }
case let .custom(node): case let .custom(node):
if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled { if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled, handleNodeContainer.frame.insetBy(dx: 0.0, dy: -5.0).contains(point) {
return handleNodeContainer.view return handleNodeContainer.view
} else { } else {
return nil return nil

View File

@ -31,7 +31,7 @@
_dimensions = CGSizeMake(width, height); _dimensions = CGSizeMake(width, height);
if ((_frameRate > 60) || _animation->duration() > 4.5) { if ((_frameRate > 60) || _animation->duration() > 7.0) {
return nil; return nil;
} }
} }

View File

@ -38,7 +38,7 @@ private final class SubscriberDisposable<T, E> : Disposable {
} }
} }
public struct Signal<T, E> { public final class Signal<T, E> {
private let generator: (Subscriber<T, E>) -> Disposable private let generator: (Subscriber<T, E>) -> Disposable
public init(_ generator: @escaping(Subscriber<T, E>) -> Disposable) { public init(_ generator: @escaping(Subscriber<T, E>) -> Disposable) {

View File

@ -191,7 +191,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
} }
return .update(StoreMessage(id: updatedId, globallyUniqueId: nil, groupingKey: currentMessage.groupingKey, timestamp: updatedTimestamp ?? currentMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: forwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media)) return .update(StoreMessage(id: updatedId, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: updatedTimestamp ?? currentMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: forwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media))
}) })
for file in sentStickers { for file in sentStickers {
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 20) transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 20)

View File

@ -71,4 +71,31 @@ public extension Message {
return false return false
} }
public var secretMediaDuration: Int32? {
var found = false
for attribute in self.attributes {
if let attribute = attribute as? AutoremoveTimeoutMessageAttribute {
found = true
break
}
}
if !found {
return nil
}
for media in self.media {
switch media {
case _ as TelegramMediaImage:
return nil
case let file as TelegramMediaFile:
return file.duration
default:
break
}
}
return nil
}
} }

View File

@ -11,7 +11,7 @@ public struct CacheStorageSettings: PreferencesEntry, Equatable {
public let defaultCacheStorageTimeout: Int32 public let defaultCacheStorageTimeout: Int32
public static var defaultSettings: CacheStorageSettings { public static var defaultSettings: CacheStorageSettings {
return CacheStorageSettings(defaultCacheStorageTimeout: Int32.max) return CacheStorageSettings(defaultCacheStorageTimeout: 7 * 60 * 60 * 24)
} }
init(defaultCacheStorageTimeout: Int32) { init(defaultCacheStorageTimeout: Int32) {

View File

@ -20,8 +20,25 @@ public enum InteractiveMessagesDeletionType: Int32 {
case forEveryone = 1 case forEveryone = 1
} }
public func deleteMessagesInteractively(postbox: Postbox, messageIds: [MessageId], type: InteractiveMessagesDeletionType) -> Signal<Void, NoError> { public func deleteMessagesInteractively(postbox: Postbox, messageIds initialMessageIds: [MessageId], type: InteractiveMessagesDeletionType, deleteAllInGroup: Bool = false) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Void in return postbox.transaction { transaction -> Void in
var messageIds: [MessageId] = []
if deleteAllInGroup {
for id in initialMessageIds {
if let group = transaction.getMessageGroup(id) ?? transaction.getMessageForwardedGroup(id) {
for message in group {
if !messageIds.contains(message.id) {
messageIds.append(message.id)
}
}
} else {
messageIds.append(id)
}
}
} else {
messageIds = initialMessageIds
}
var messageIdsByPeerId: [PeerId: [MessageId]] = [:] var messageIdsByPeerId: [PeerId: [MessageId]] = [:]
for id in messageIds { for id in messageIds {
if messageIdsByPeerId[id.peerId] == nil { if messageIdsByPeerId[id.peerId] == nil {

View File

@ -57,10 +57,14 @@ public func markMessageContentAsConsumedInteractively(postbox: Postbox, messageI
for i in 0 ..< updatedAttributes.count { for i in 0 ..< updatedAttributes.count {
if let attribute = updatedAttributes[i] as? AutoremoveTimeoutMessageAttribute { if let attribute = updatedAttributes[i] as? AutoremoveTimeoutMessageAttribute {
if attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0 { if attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0 {
updatedAttributes[i] = AutoremoveTimeoutMessageAttribute(timeout: attribute.timeout, countdownBeginTime: timestamp) var timeout = attribute.timeout
if let duration = message.secretMediaDuration {
timeout = max(timeout, duration)
}
updatedAttributes[i] = AutoremoveTimeoutMessageAttribute(timeout: timeout, countdownBeginTime: timestamp)
updateMessage = true updateMessage = true
transaction.addTimestampBasedMessageAttribute(tag: 0, timestamp: timestamp + attribute.timeout, messageId: messageId) transaction.addTimestampBasedMessageAttribute(tag: 0, timestamp: timestamp + timeout, messageId: messageId)
if messageId.peerId.namespace == Namespaces.Peer.SecretChat { if messageId.peerId.namespace == Namespaces.Peer.SecretChat {
var layer: SecretChatLayer? var layer: SecretChatLayer?

View File

@ -7,19 +7,22 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
let constructiveColor: UIColor = UIColor(rgb: 0x08a723) let constructiveColor: UIColor = UIColor(rgb: 0x08a723)
let secretColor: UIColor = UIColor(rgb: 0x00b12c) let secretColor: UIColor = UIColor(rgb: 0x00b12c)
let badgeFillColor: UIColor
let badgeTextColor: UIColor let badgeTextColor: UIColor
let outgoingBubbleFillColor: UIColor let outgoingBubbleFillColor: UIColor
let outgoingBubbleHighlightedFillColor: UIColor let outgoingBubbleHighlightedFillColor: UIColor
let outgoingScamColor: UIColor let outgoingScamColor: UIColor
if accentColor.rgb == UIColor.white.rgb { if accentColor.rgb == UIColor.white.rgb {
badgeFillColor = .white
badgeTextColor = .black badgeTextColor = .black
outgoingBubbleFillColor = UIColor(rgb: 0x313131) outgoingBubbleFillColor = UIColor(rgb: 0x313131)
outgoingBubbleHighlightedFillColor = UIColor(rgb: 0x464646) outgoingBubbleHighlightedFillColor = UIColor(rgb: 0x464646)
outgoingScamColor = destructiveColor outgoingScamColor = destructiveColor
} else { } else {
outgoingBubbleFillColor = accentColor badgeFillColor = destructiveColor
badgeTextColor = .white badgeTextColor = .white
outgoingBubbleFillColor = accentColor
outgoingBubbleHighlightedFillColor = accentColor.withMultipliedBrightnessBy(1.421) outgoingBubbleHighlightedFillColor = accentColor.withMultipliedBrightnessBy(1.421)
outgoingScamColor = .white outgoingScamColor = .white
} }
@ -31,9 +34,9 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
selectedIconColor: accentColor, selectedIconColor: accentColor,
textColor: UIColor(rgb: 0x828282), textColor: UIColor(rgb: 0x828282),
selectedTextColor: accentColor, selectedTextColor: accentColor,
badgeBackgroundColor: destructiveColor, badgeBackgroundColor: badgeFillColor,
badgeStrokeColor: UIColor(rgb: 0x1c1c1d), badgeStrokeColor: UIColor(rgb: 0x1c1c1d),
badgeTextColor: UIColor(rgb: 0xffffff) badgeTextColor: badgeTextColor
) )
let rootNavigationBar = PresentationThemeRootNavigationBar( let rootNavigationBar = PresentationThemeRootNavigationBar(
@ -45,9 +48,9 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
accentTextColor: accentColor, accentTextColor: accentColor,
backgroundColor: UIColor(rgb: 0x1c1c1d), backgroundColor: UIColor(rgb: 0x1c1c1d),
separatorColor: UIColor(rgb: 0x3d3d40), separatorColor: UIColor(rgb: 0x3d3d40),
badgeBackgroundColor: destructiveColor, badgeBackgroundColor: badgeFillColor,
badgeStrokeColor: UIColor(rgb: 0x1c1c1d), badgeStrokeColor: UIColor(rgb: 0x1c1c1d),
badgeTextColor: UIColor(rgb: 0xffffff) badgeTextColor: badgeTextColor
) )
let navigationSearchBar = PresentationThemeNavigationSearchBar( let navigationSearchBar = PresentationThemeNavigationSearchBar(
@ -253,7 +256,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
foregroundColor: .white, foregroundColor: .white,
badgeBackgroundColor: accentColor, badgeBackgroundColor: accentColor,
badgeStrokeColor: accentColor, badgeStrokeColor: accentColor,
badgeTextColor: .white badgeTextColor: badgeTextColor
) )
let chat = PresentationThemeChat( let chat = PresentationThemeChat(

View File

@ -82,12 +82,14 @@ private final class AnimatedStickerFrame {
let type: AnimationRendererFrameType let type: AnimationRendererFrameType
let width: Int let width: Int
let height: Int let height: Int
let isLastFrame: Bool
init(data: Data, type: AnimationRendererFrameType, width: Int, height: Int) { init(data: Data, type: AnimationRendererFrameType, width: Int, height: Int, isLastFrame: Bool) {
self.data = data self.data = data
self.type = type self.type = type
self.width = width self.width = width
self.height = height self.height = height
self.isLastFrame = isLastFrame
} }
} }
@ -162,6 +164,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
func takeFrame() -> AnimatedStickerFrame { func takeFrame() -> AnimatedStickerFrame {
var frameData: Data? var frameData: Data?
var isLastFrame = false
let dataLength = self.data.count let dataLength = self.data.count
let decodeBufferLength = self.decodeBuffer.count let decodeBufferLength = self.decodeBuffer.count
@ -192,6 +195,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
self.offset += Int(frameLength) self.offset += Int(frameLength)
if self.offset == dataLength { if self.offset == dataLength {
isLastFrame = true
self.offset = self.initialOffset self.offset = self.initialOffset
self.frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in self.frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
memset(bytes, 0, frameBufferLength) memset(bytes, 0, frameBufferLength)
@ -199,7 +203,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
} }
} }
return AnimatedStickerFrame(data: frameData!, type: .yuva, width: self.width, height: self.height) return AnimatedStickerFrame(data: frameData!, type: .yuva, width: self.width, height: self.height, isLastFrame: isLastFrame)
} }
} }
@ -243,7 +247,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
memset(bytes, 0, self.width * self.height * 4) memset(bytes, 0, self.width * self.height * 4)
self.animation.renderFrame(with: Int32(frameIndex), into: bytes, width: Int32(self.width), height: Int32(self.height)) self.animation.renderFrame(with: Int32(frameIndex), into: bytes, width: Int32(self.width), height: Int32(self.height))
} }
return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height) return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height, isLastFrame: frameIndex == self.frameCount)
} }
} }
@ -434,6 +438,10 @@ final class AnimatedStickerNode: ASDisplayNode {
strongSelf.started() strongSelf.started()
} }
}) })
if case .once = strongSelf.playbackMode, frame.isLastFrame {
strongSelf.stop()
strongSelf.isPlaying = false
}
} }
} }
frameQueue.with { frameQueue in frameQueue.with { frameQueue in
@ -450,6 +458,12 @@ final class AnimatedStickerNode: ASDisplayNode {
self.timer.swap(nil)?.invalidate() self.timer.swap(nil)?.invalidate()
} }
func playIfNeeded() {
if !self.isPlaying {
self.play()
}
}
func updateLayout(size: CGSize) { func updateLayout(size: CGSize) {
self.renderer?.frame = CGRect(origin: CGPoint(), size: size) self.renderer?.frame = CGRect(origin: CGPoint(), size: size)
} }

View File

@ -892,7 +892,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
return current return current
} }
if let updateRank = updateRank, updateRank.count > rankMaxLength { if let updateRank = updateRank, updateRank.count > rankMaxLength || updateRank.containsEmoji {
errorImpl?() errorImpl?()
return return
} }
@ -928,6 +928,10 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
} }
let effectiveRank = updateRank ?? currentRank let effectiveRank = updateRank ?? currentRank
if effectiveRank?.containsEmoji ?? false {
errorImpl?()
return
}
if let updateFlags = updateFlags { if let updateFlags = updateFlags {
updateState { current in updateState { current in
@ -961,7 +965,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
return current return current
} }
if let updateRank = updateRank, updateRank.count > rankMaxLength { if let updateRank = updateRank, updateRank.count > rankMaxLength || updateRank.containsEmoji {
errorImpl?() errorImpl?()
return return
} }
@ -1014,7 +1018,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
return current return current
} }
if let updateRank = updateRank, updateRank.count > rankMaxLength { if let updateRank = updateRank, updateRank.count > rankMaxLength || updateRank.containsEmoji {
errorImpl?() errorImpl?()
return return
} }

View File

@ -2680,7 +2680,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
if isAction && (actions.options == .deleteGlobally || actions.options == .deleteLocally) { if isAction && (actions.options == .deleteGlobally || actions.options == .deleteLocally) {
let _ = deleteMessagesInteractively(postbox: strongSelf.context.account.postbox, messageIds: Array(messageIds), type: actions.options == .deleteLocally ? .forLocalPeer : .forEveryone).start() let _ = deleteMessagesInteractively(postbox: strongSelf.context.account.postbox, messageIds: Array(messageIds), type: actions.options == .deleteLocally ? .forLocalPeer : .forEveryone).start()
} else if (messages.first?.flags.isSending ?? false) { } else if (messages.first?.flags.isSending ?? false) {
let _ = deleteMessagesInteractively(postbox: strongSelf.context.account.postbox, messageIds: Array(messageIds), type: .forEveryone).start() let _ = deleteMessagesInteractively(postbox: strongSelf.context.account.postbox, messageIds: Array(messageIds), type: .forEveryone, deleteAllInGroup: true).start()
} else { } else {
strongSelf.presentDeleteMessageOptions(messageIds: messageIds, options: actions.options) strongSelf.presentDeleteMessageOptions(messageIds: messageIds, options: actions.options)
} }

View File

@ -274,9 +274,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self?.interfaceInteraction?.presentController(controller, nil) self?.interfaceInteraction?.presentController(controller, nil)
}) })
self.textInputPanelNode?.storedInputLanguage = chatPresentationInterfaceState.interfaceState.inputLanguage self.textInputPanelNode?.storedInputLanguage = chatPresentationInterfaceState.interfaceState.inputLanguage
self.textInputPanelNode?.updateHeight = { [weak self] in self.textInputPanelNode?.updateHeight = { [weak self] animated in
if let strongSelf = self, let _ = strongSelf.inputPanelNode as? ChatTextInputPanelNode, !strongSelf.ignoreUpdateHeight { if let strongSelf = self, let _ = strongSelf.inputPanelNode as? ChatTextInputPanelNode, !strongSelf.ignoreUpdateHeight {
strongSelf.requestLayout(.animated(duration: 0.1, curve: .easeInOut)) strongSelf.requestLayout(animated ? .animated(duration: 0.1, curve: .easeInOut) : .immediate)
} }
} }

View File

@ -222,6 +222,7 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState
let panel = ChatTextInputPanelNode(presentationInterfaceState: chatPresentationInterfaceState, presentController: { [weak interfaceInteraction] controller in let panel = ChatTextInputPanelNode(presentationInterfaceState: chatPresentationInterfaceState, presentController: { [weak interfaceInteraction] controller in
interfaceInteraction?.presentController(controller, nil) interfaceInteraction?.presentController(controller, nil)
}) })
panel.interfaceInteraction = interfaceInteraction panel.interfaceInteraction = interfaceInteraction
panel.context = context panel.context = context
return panel return panel

View File

@ -611,10 +611,13 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
} }
self.recentListNode.isHidden = filter.contains(.excludeRecent) self.recentListNode.isHidden = filter.contains(.excludeRecent)
let currentRemotePeers = Atomic<([FoundPeer], [FoundPeer])?>(value: nil)
let presentationDataPromise = self.presentationDataPromise let presentationDataPromise = self.presentationDataPromise
let foundItems = self.searchQuery.get() let foundItems = self.searchQuery.get()
|> mapToSignal { query -> Signal<([ChatListSearchEntry], Bool)?, NoError> in |> mapToSignal { query -> Signal<([ChatListSearchEntry], Bool)?, NoError> in
guard let query = query, !query.isEmpty else { guard let query = query, !query.isEmpty else {
let _ = currentRemotePeers.swap(nil)
return .single(nil) return .single(nil)
} }
@ -651,8 +654,9 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
} }
let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError> let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError>
let currentRemotePeersValue = currentRemotePeers.with { $0 } ?? ([], [])
foundRemotePeers = ( foundRemotePeers = (
.single(([], [], true)) .single((currentRemotePeersValue.0, currentRemotePeersValue.1, true))
|> then( |> then(
searchPeers(account: context.account, query: query) searchPeers(account: context.account, query: query)
|> map { ($0.0, $0.1, false) } |> map { ($0.0, $0.1, false) }
@ -717,6 +721,8 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
let isSearching = foundRemotePeers.2 || foundRemoteMessages.1 let isSearching = foundRemotePeers.2 || foundRemoteMessages.1
var index = 0 var index = 0
let _ = currentRemotePeers.swap((foundRemotePeers.0, foundRemotePeers.1))
let filteredPeer:(Peer, Peer) -> Bool = { peer, accountPeer in let filteredPeer:(Peer, Peer) -> Bool = { peer, accountPeer in
guard !filter.contains(.excludeSavedMessages) || peer.id != accountPeer.id else { return false } guard !filter.contains(.excludeSavedMessages) || peer.id != accountPeer.id else { return false }
guard !filter.contains(.excludeSecretChats) || peer.id.namespace != Namespaces.Peer.SecretChat else { return false } guard !filter.contains(.excludeSecretChats) || peer.id.namespace != Namespaces.Peer.SecretChat else { return false }

View File

@ -153,11 +153,11 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
return return
} }
let isPlaying = self.visibilityStatus && item.controllerInteraction.stickerSettings.loopAnimatedStickers let isPlaying = self.visibilityStatus
if self.isPlaying != isPlaying { if self.isPlaying != isPlaying {
self.isPlaying = isPlaying self.isPlaying = isPlaying
self.animationNode.visibility = isPlaying self.animationNode.visibility = isPlaying
if let item = self.item, isPlaying, !self.didSetUpAnimationNode { if self.isPlaying && !self.didSetUpAnimationNode {
self.didSetUpAnimationNode = true self.didSetUpAnimationNode = true
var telegramFile: TelegramMediaFile? var telegramFile: TelegramMediaFile?
for media in item.message.media { for media in item.message.media {
@ -166,11 +166,19 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
break break
} }
} }
if let telegramFile = telegramFile { if let telegramFile = telegramFile {
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, mode: .cached) var playbackMode: AnimatedStickerPlaybackMode = .loop
if !item.controllerInteraction.stickerSettings.loopAnimatedStickers {
playbackMode = .once
}
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, playbackMode: playbackMode, mode: .cached)
} else if let emojiResource = self.emojiResource { } else if let emojiResource = self.emojiResource {
self.animationNode.setup(account: item.context.account, resource: emojiResource, width: 384, height: 384, mode: .cached) var playbackMode: AnimatedStickerPlaybackMode = .loop
if item.context.sharedContext.immediateExperimentalUISettings.playAnimatedEmojiOnce {
playbackMode = .once
}
self.animationNode.setup(account: item.context.account, resource: emojiResource, width: 384, height: 384, playbackMode: playbackMode, mode: .cached)
} }
} }
} }
@ -646,26 +654,32 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if let item = self.item, self.imageNode.frame.contains(location) { if let item = self.item, self.imageNode.frame.contains(location) {
if self.telegramFile != nil { if self.telegramFile != nil {
let _ = item.controllerInteraction.openMessage(item.message, .default) let _ = item.controllerInteraction.openMessage(item.message, .default)
} else if let emoji = self.emojiResource, emoji.name == "heart" { } else if let emoji = self.emojiResource {
let hapticFeedback: HapticFeedback if item.context.sharedContext.immediateExperimentalUISettings.playAnimatedEmojiOnce {
if let currentHapticFeedback = self.hapticFeedback { self.animationNode.playIfNeeded()
hapticFeedback = currentHapticFeedback
} else {
hapticFeedback = HapticFeedback()
self.hapticFeedback = hapticFeedback
} }
hapticFeedback.prepareImpact()
hapticFeedback.impact(.heavy) if emoji.name == "heart" {
Queue.mainQueue().after(0.2) { let hapticFeedback: HapticFeedback
hapticFeedback.impact(.medium) if let currentHapticFeedback = self.hapticFeedback {
Queue.mainQueue().after(0.68) { hapticFeedback = currentHapticFeedback
} else {
hapticFeedback = HapticFeedback()
self.hapticFeedback = hapticFeedback
}
hapticFeedback.prepareImpact()
hapticFeedback.impact(.heavy)
Queue.mainQueue().after(0.2) {
hapticFeedback.impact(.medium) hapticFeedback.impact(.medium)
Queue.mainQueue().after(0.2) { Queue.mainQueue().after(0.74) {
hapticFeedback.impact(.medium) hapticFeedback.impact(.medium)
Queue.mainQueue().after(0.68) { Queue.mainQueue().after(0.2) {
hapticFeedback.impact(.medium) hapticFeedback.impact(.medium)
Queue.mainQueue().after(0.2) { Queue.mainQueue().after(0.74) {
hapticFeedback.impact(.medium) hapticFeedback.impact(.medium)
Queue.mainQueue().after(0.2) {
hapticFeedback.impact(.medium)
}
} }
} }
} }

View File

@ -1,6 +1,7 @@
import Foundation import Foundation
import UIKit import UIKit
import AsyncDisplayKit import AsyncDisplayKit
import SwiftSignalKit
import Display import Display
import Postbox import Postbox
import TelegramCore import TelegramCore
@ -44,7 +45,6 @@ private final class ActionSheetItemNode: ASDisplayNode {
super.init() super.init()
self.addSubnode(self.separatorNode)
self.addSubnode(self.highlightedBackgroundNode) self.addSubnode(self.highlightedBackgroundNode)
self.addSubnode(self.titleNode) self.addSubnode(self.titleNode)
self.addSubnode(self.iconNode) self.addSubnode(self.iconNode)
@ -391,6 +391,15 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
completedEffect = true completedEffect = true
intermediateCompletion() intermediateCompletion()
}) })
Queue.mainQueue().after(0.7) {
completedAlpha = true
completedButton = true
completedBubble = true
completedEffect = true
intermediateCompletion()
}
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in }) self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in })

View File

@ -66,7 +66,7 @@ private func generateKnobImage() -> UIImage? {
}) })
} }
private let allowedValues: [Int32] = [0, 30, 60 * 1, 60 * 5, 60 * 15, 60 * 30, 60 * 60] private let allowedValues: [Int32] = [0, 10, 30, 60, 300, 900, 3600]
class ChatSlowmodeItemNode: ListViewItemNode { class ChatSlowmodeItemNode: ListViewItemNode {
private let backgroundNode: ASDisplayNode private let backgroundNode: ASDisplayNode

View File

@ -223,7 +223,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
var displayAttachmentMenu: () -> Void = { } var displayAttachmentMenu: () -> Void = { }
var sendMessage: () -> Void = { } var sendMessage: () -> Void = { }
var paste: (ChatTextInputPanelPasteData) -> Void = { _ in } var paste: (ChatTextInputPanelPasteData) -> Void = { _ in }
var updateHeight: () -> Void = { } var updateHeight: (Bool) -> Void = { _ in }
var updateActivity: () -> Void = { } var updateActivity: () -> Void = { }
@ -1199,7 +1199,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
} }
self.updateActionButtons(hasText: inputHasText, hideMicButton: hideMicButton, animated: animated) self.updateActionButtons(hasText: inputHasText, hideMicButton: hideMicButton, animated: animated)
self.updateTextHeight() self.updateTextHeight(animated: animated)
} }
private func updateActionButtons(hasText: Bool, hideMicButton: Bool, animated: Bool) { private func updateActionButtons(hasText: Bool, hideMicButton: Bool, animated: Bool) {
@ -1345,12 +1345,12 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.actionButtons.updateAccessibility() self.actionButtons.updateAccessibility()
} }
private func updateTextHeight() { private func updateTextHeight(animated: Bool) {
if let (width, leftInset, rightInset, maxHeight, metrics) = self.validLayout { if let (width, leftInset, rightInset, maxHeight, metrics) = self.validLayout {
let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset, maxHeight: maxHeight, metrics: metrics) let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset, maxHeight: maxHeight, metrics: metrics)
let panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics) let panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics)
if !self.bounds.size.height.isEqual(to: panelHeight) { if !self.bounds.size.height.isEqual(to: panelHeight) {
self.updateHeight() self.updateHeight(animated)
} }
} }
} }

View File

@ -3,5 +3,4 @@ import Foundation
public struct GlobalExperimentalSettings { public struct GlobalExperimentalSettings {
public static var isAppStoreBuild: Bool = false public static var isAppStoreBuild: Bool = false
public static var enableFeed: Bool = false public static var enableFeed: Bool = false
public static var animatedStickers: Bool = false
} }

View File

@ -31,7 +31,7 @@ func stickerFromLegacyDocument(_ documentAttachment: TGDocumentMediaAttachment)
func legacyComponentsStickers(postbox: Postbox, namespace: Int32) -> SSignal { func legacyComponentsStickers(postbox: Postbox, namespace: Int32) -> SSignal {
return SSignal { subscriber in return SSignal { subscriber in
let disposable = (postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [namespace], aroundIndex: nil, count: 1000)).start(next: { view in let disposable = (postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [namespace], aroundIndex: nil, count: 200 * 200)).start(next: { view in
var stickerPackDocuments: [ItemCollectionId: [Any]] = [:] var stickerPackDocuments: [ItemCollectionId: [Any]] = [:]
for entry in view.entries { for entry in view.entries {

View File

@ -51,8 +51,25 @@ func presentLegacySecureIdAttachmentMenu(context: AccountContext, present: @esca
mappedIntent = TGPassportAttachIntentSelfie mappedIntent = TGPassportAttachIntentSelfie
} }
var uploadStarted = false
guard let attachMenu = TGPassportAttachMenu.present(with: legacyController.context, parentController: emptyController, menuController: nil, title: "", intent: mappedIntent, uploadAction: { signal, completed in guard let attachMenu = TGPassportAttachMenu.present(with: legacyController.context, parentController: emptyController, menuController: nil, title: "", intent: mappedIntent, uploadAction: { signal, completed in
if let signal = signal { if let signal = signal {
if uploadStarted {
completed?()
return
}
uploadStarted = true
let statusController = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
let statusDisposable = (Signal<Never, NoError> { subscriber in
present(statusController)
return ActionDisposable {
statusController.dismiss()
}
}
|> runOn(.mainQueue())
|> delay(0.1, queue: .mainQueue())).start()
let _ = (processedLegacySecureIdAttachmentItems(postbox: context.account.postbox, signal: signal) let _ = (processedLegacySecureIdAttachmentItems(postbox: context.account.postbox, signal: signal)
|> mapToSignal { resources -> Signal<([TelegramMediaResource], SecureIdRecognizedDocumentData?), NoError> in |> mapToSignal { resources -> Signal<([TelegramMediaResource], SecureIdRecognizedDocumentData?), NoError> in
switch type { switch type {
@ -70,6 +87,7 @@ func presentLegacySecureIdAttachmentMenu(context: AccountContext, present: @esca
} }
|> deliverOnMainQueue).start(next: { resourcesAndData in |> deliverOnMainQueue).start(next: { resourcesAndData in
completion(resourcesAndData.0, resourcesAndData.1) completion(resourcesAndData.0, resourcesAndData.1)
statusDisposable.dispose()
completed?() completed?()
}) })
} else { } else {

View File

@ -114,7 +114,7 @@ public func phoneLabelController(context: AccountContext, currentLabel: String,
arguments.complete() arguments.complete()
}) })
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Notifications_TextTone), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.PhoneLabel_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(entries: phoneLabelEntries(presentationData: presentationData, state: state), style: .blocks) let listState = ItemListNodeState(entries: phoneLabelEntries(presentationData: presentationData, state: state), style: .blocks)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))