mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-04 21:41:45 +00:00
Merge branch 'master' into gradient-messages
This commit is contained in:
commit
ad7bda3182
@ -698,13 +698,13 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
switch self.contentNodes {
|
||||
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
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
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
|
||||
} else {
|
||||
return nil
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
|
||||
_dimensions = CGSizeMake(width, height);
|
||||
|
||||
if ((_frameRate > 60) || _animation->duration() > 4.5) {
|
||||
if ((_frameRate > 60) || _animation->duration() > 7.0) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
public init(_ generator: @escaping(Subscriber<T, E>) -> Disposable) {
|
||||
|
||||
@ -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 {
|
||||
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 20)
|
||||
|
||||
@ -71,4 +71,31 @@ public extension Message {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ public struct CacheStorageSettings: PreferencesEntry, Equatable {
|
||||
public let defaultCacheStorageTimeout: Int32
|
||||
|
||||
public static var defaultSettings: CacheStorageSettings {
|
||||
return CacheStorageSettings(defaultCacheStorageTimeout: Int32.max)
|
||||
return CacheStorageSettings(defaultCacheStorageTimeout: 7 * 60 * 60 * 24)
|
||||
}
|
||||
|
||||
init(defaultCacheStorageTimeout: Int32) {
|
||||
|
||||
@ -20,8 +20,25 @@ public enum InteractiveMessagesDeletionType: Int32 {
|
||||
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
|
||||
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]] = [:]
|
||||
for id in messageIds {
|
||||
if messageIdsByPeerId[id.peerId] == nil {
|
||||
|
||||
@ -57,10 +57,14 @@ public func markMessageContentAsConsumedInteractively(postbox: Postbox, messageI
|
||||
for i in 0 ..< updatedAttributes.count {
|
||||
if let attribute = updatedAttributes[i] as? AutoremoveTimeoutMessageAttribute {
|
||||
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
|
||||
|
||||
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 {
|
||||
var layer: SecretChatLayer?
|
||||
|
||||
@ -7,19 +7,22 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
||||
let constructiveColor: UIColor = UIColor(rgb: 0x08a723)
|
||||
let secretColor: UIColor = UIColor(rgb: 0x00b12c)
|
||||
|
||||
let badgeFillColor: UIColor
|
||||
let badgeTextColor: UIColor
|
||||
let outgoingBubbleFillColor: UIColor
|
||||
let outgoingBubbleHighlightedFillColor: UIColor
|
||||
let outgoingScamColor: UIColor
|
||||
|
||||
if accentColor.rgb == UIColor.white.rgb {
|
||||
badgeFillColor = .white
|
||||
badgeTextColor = .black
|
||||
outgoingBubbleFillColor = UIColor(rgb: 0x313131)
|
||||
outgoingBubbleHighlightedFillColor = UIColor(rgb: 0x464646)
|
||||
outgoingScamColor = destructiveColor
|
||||
} else {
|
||||
outgoingBubbleFillColor = accentColor
|
||||
badgeFillColor = destructiveColor
|
||||
badgeTextColor = .white
|
||||
outgoingBubbleFillColor = accentColor
|
||||
outgoingBubbleHighlightedFillColor = accentColor.withMultipliedBrightnessBy(1.421)
|
||||
outgoingScamColor = .white
|
||||
}
|
||||
@ -31,9 +34,9 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
||||
selectedIconColor: accentColor,
|
||||
textColor: UIColor(rgb: 0x828282),
|
||||
selectedTextColor: accentColor,
|
||||
badgeBackgroundColor: destructiveColor,
|
||||
badgeBackgroundColor: badgeFillColor,
|
||||
badgeStrokeColor: UIColor(rgb: 0x1c1c1d),
|
||||
badgeTextColor: UIColor(rgb: 0xffffff)
|
||||
badgeTextColor: badgeTextColor
|
||||
)
|
||||
|
||||
let rootNavigationBar = PresentationThemeRootNavigationBar(
|
||||
@ -45,9 +48,9 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
||||
accentTextColor: accentColor,
|
||||
backgroundColor: UIColor(rgb: 0x1c1c1d),
|
||||
separatorColor: UIColor(rgb: 0x3d3d40),
|
||||
badgeBackgroundColor: destructiveColor,
|
||||
badgeBackgroundColor: badgeFillColor,
|
||||
badgeStrokeColor: UIColor(rgb: 0x1c1c1d),
|
||||
badgeTextColor: UIColor(rgb: 0xffffff)
|
||||
badgeTextColor: badgeTextColor
|
||||
)
|
||||
|
||||
let navigationSearchBar = PresentationThemeNavigationSearchBar(
|
||||
@ -253,7 +256,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
||||
foregroundColor: .white,
|
||||
badgeBackgroundColor: accentColor,
|
||||
badgeStrokeColor: accentColor,
|
||||
badgeTextColor: .white
|
||||
badgeTextColor: badgeTextColor
|
||||
)
|
||||
|
||||
let chat = PresentationThemeChat(
|
||||
|
||||
@ -82,12 +82,14 @@ private final class AnimatedStickerFrame {
|
||||
let type: AnimationRendererFrameType
|
||||
let width: 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.type = type
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.isLastFrame = isLastFrame
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,6 +164,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
||||
|
||||
func takeFrame() -> AnimatedStickerFrame {
|
||||
var frameData: Data?
|
||||
var isLastFrame = false
|
||||
|
||||
let dataLength = self.data.count
|
||||
let decodeBufferLength = self.decodeBuffer.count
|
||||
@ -192,6 +195,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
||||
|
||||
self.offset += Int(frameLength)
|
||||
if self.offset == dataLength {
|
||||
isLastFrame = true
|
||||
self.offset = self.initialOffset
|
||||
self.frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
|
||||
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)
|
||||
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()
|
||||
}
|
||||
})
|
||||
if case .once = strongSelf.playbackMode, frame.isLastFrame {
|
||||
strongSelf.stop()
|
||||
strongSelf.isPlaying = false
|
||||
}
|
||||
}
|
||||
}
|
||||
frameQueue.with { frameQueue in
|
||||
@ -450,6 +458,12 @@ final class AnimatedStickerNode: ASDisplayNode {
|
||||
self.timer.swap(nil)?.invalidate()
|
||||
}
|
||||
|
||||
func playIfNeeded() {
|
||||
if !self.isPlaying {
|
||||
self.play()
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize) {
|
||||
self.renderer?.frame = CGRect(origin: CGPoint(), size: size)
|
||||
}
|
||||
|
||||
@ -892,7 +892,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
||||
return current
|
||||
}
|
||||
|
||||
if let updateRank = updateRank, updateRank.count > rankMaxLength {
|
||||
if let updateRank = updateRank, updateRank.count > rankMaxLength || updateRank.containsEmoji {
|
||||
errorImpl?()
|
||||
return
|
||||
}
|
||||
@ -928,6 +928,10 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
||||
}
|
||||
|
||||
let effectiveRank = updateRank ?? currentRank
|
||||
if effectiveRank?.containsEmoji ?? false {
|
||||
errorImpl?()
|
||||
return
|
||||
}
|
||||
|
||||
if let updateFlags = updateFlags {
|
||||
updateState { current in
|
||||
@ -961,7 +965,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
||||
return current
|
||||
}
|
||||
|
||||
if let updateRank = updateRank, updateRank.count > rankMaxLength {
|
||||
if let updateRank = updateRank, updateRank.count > rankMaxLength || updateRank.containsEmoji {
|
||||
errorImpl?()
|
||||
return
|
||||
}
|
||||
@ -1014,7 +1018,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
||||
return current
|
||||
}
|
||||
|
||||
if let updateRank = updateRank, updateRank.count > rankMaxLength {
|
||||
if let updateRank = updateRank, updateRank.count > rankMaxLength || updateRank.containsEmoji {
|
||||
errorImpl?()
|
||||
return
|
||||
}
|
||||
|
||||
@ -2680,7 +2680,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
|
||||
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()
|
||||
} 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 {
|
||||
strongSelf.presentDeleteMessageOptions(messageIds: messageIds, options: actions.options)
|
||||
}
|
||||
|
||||
@ -274,9 +274,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self?.interfaceInteraction?.presentController(controller, nil)
|
||||
})
|
||||
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 {
|
||||
strongSelf.requestLayout(.animated(duration: 0.1, curve: .easeInOut))
|
||||
strongSelf.requestLayout(animated ? .animated(duration: 0.1, curve: .easeInOut) : .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -222,6 +222,7 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState
|
||||
let panel = ChatTextInputPanelNode(presentationInterfaceState: chatPresentationInterfaceState, presentController: { [weak interfaceInteraction] controller in
|
||||
interfaceInteraction?.presentController(controller, nil)
|
||||
})
|
||||
|
||||
panel.interfaceInteraction = interfaceInteraction
|
||||
panel.context = context
|
||||
return panel
|
||||
|
||||
@ -611,10 +611,13 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
}
|
||||
self.recentListNode.isHidden = filter.contains(.excludeRecent)
|
||||
|
||||
let currentRemotePeers = Atomic<([FoundPeer], [FoundPeer])?>(value: nil)
|
||||
|
||||
let presentationDataPromise = self.presentationDataPromise
|
||||
let foundItems = self.searchQuery.get()
|
||||
|> mapToSignal { query -> Signal<([ChatListSearchEntry], Bool)?, NoError> in
|
||||
guard let query = query, !query.isEmpty else {
|
||||
let _ = currentRemotePeers.swap(nil)
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
@ -651,8 +654,9 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
}
|
||||
|
||||
let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError>
|
||||
let currentRemotePeersValue = currentRemotePeers.with { $0 } ?? ([], [])
|
||||
foundRemotePeers = (
|
||||
.single(([], [], true))
|
||||
.single((currentRemotePeersValue.0, currentRemotePeersValue.1, true))
|
||||
|> then(
|
||||
searchPeers(account: context.account, query: query)
|
||||
|> map { ($0.0, $0.1, false) }
|
||||
@ -717,6 +721,8 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
let isSearching = foundRemotePeers.2 || foundRemoteMessages.1
|
||||
var index = 0
|
||||
|
||||
let _ = currentRemotePeers.swap((foundRemotePeers.0, foundRemotePeers.1))
|
||||
|
||||
let filteredPeer:(Peer, Peer) -> Bool = { peer, accountPeer in
|
||||
guard !filter.contains(.excludeSavedMessages) || peer.id != accountPeer.id else { return false }
|
||||
guard !filter.contains(.excludeSecretChats) || peer.id.namespace != Namespaces.Peer.SecretChat else { return false }
|
||||
|
||||
@ -153,11 +153,11 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
return
|
||||
}
|
||||
|
||||
let isPlaying = self.visibilityStatus && item.controllerInteraction.stickerSettings.loopAnimatedStickers
|
||||
let isPlaying = self.visibilityStatus
|
||||
if self.isPlaying != isPlaying {
|
||||
self.isPlaying = isPlaying
|
||||
self.animationNode.visibility = isPlaying
|
||||
if let item = self.item, isPlaying, !self.didSetUpAnimationNode {
|
||||
if self.isPlaying && !self.didSetUpAnimationNode {
|
||||
self.didSetUpAnimationNode = true
|
||||
var telegramFile: TelegramMediaFile?
|
||||
for media in item.message.media {
|
||||
@ -166,11 +166,19 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
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 self.telegramFile != nil {
|
||||
let _ = item.controllerInteraction.openMessage(item.message, .default)
|
||||
} else if let emoji = self.emojiResource, emoji.name == "heart" {
|
||||
let hapticFeedback: HapticFeedback
|
||||
if let currentHapticFeedback = self.hapticFeedback {
|
||||
hapticFeedback = currentHapticFeedback
|
||||
} else {
|
||||
hapticFeedback = HapticFeedback()
|
||||
self.hapticFeedback = hapticFeedback
|
||||
} else if let emoji = self.emojiResource {
|
||||
if item.context.sharedContext.immediateExperimentalUISettings.playAnimatedEmojiOnce {
|
||||
self.animationNode.playIfNeeded()
|
||||
}
|
||||
hapticFeedback.prepareImpact()
|
||||
hapticFeedback.impact(.heavy)
|
||||
Queue.mainQueue().after(0.2) {
|
||||
hapticFeedback.impact(.medium)
|
||||
Queue.mainQueue().after(0.68) {
|
||||
|
||||
if emoji.name == "heart" {
|
||||
let hapticFeedback: HapticFeedback
|
||||
if let currentHapticFeedback = self.hapticFeedback {
|
||||
hapticFeedback = currentHapticFeedback
|
||||
} else {
|
||||
hapticFeedback = HapticFeedback()
|
||||
self.hapticFeedback = hapticFeedback
|
||||
}
|
||||
hapticFeedback.prepareImpact()
|
||||
hapticFeedback.impact(.heavy)
|
||||
Queue.mainQueue().after(0.2) {
|
||||
hapticFeedback.impact(.medium)
|
||||
Queue.mainQueue().after(0.2) {
|
||||
Queue.mainQueue().after(0.74) {
|
||||
hapticFeedback.impact(.medium)
|
||||
Queue.mainQueue().after(0.68) {
|
||||
Queue.mainQueue().after(0.2) {
|
||||
hapticFeedback.impact(.medium)
|
||||
Queue.mainQueue().after(0.2) {
|
||||
Queue.mainQueue().after(0.74) {
|
||||
hapticFeedback.impact(.medium)
|
||||
Queue.mainQueue().after(0.2) {
|
||||
hapticFeedback.impact(.medium)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import Display
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
@ -44,7 +45,6 @@ private final class ActionSheetItemNode: ASDisplayNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.highlightedBackgroundNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
@ -391,6 +391,15 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
completedEffect = true
|
||||
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.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in })
|
||||
|
||||
|
||||
@ -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 {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
|
||||
@ -223,7 +223,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
var displayAttachmentMenu: () -> Void = { }
|
||||
var sendMessage: () -> Void = { }
|
||||
var paste: (ChatTextInputPanelPasteData) -> Void = { _ in }
|
||||
var updateHeight: () -> Void = { }
|
||||
var updateHeight: (Bool) -> Void = { _ in }
|
||||
|
||||
var updateActivity: () -> Void = { }
|
||||
|
||||
@ -1199,7 +1199,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
}
|
||||
|
||||
self.updateActionButtons(hasText: inputHasText, hideMicButton: hideMicButton, animated: animated)
|
||||
self.updateTextHeight()
|
||||
self.updateTextHeight(animated: animated)
|
||||
}
|
||||
|
||||
private func updateActionButtons(hasText: Bool, hideMicButton: Bool, animated: Bool) {
|
||||
@ -1345,12 +1345,12 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
self.actionButtons.updateAccessibility()
|
||||
}
|
||||
|
||||
private func updateTextHeight() {
|
||||
private func updateTextHeight(animated: Bool) {
|
||||
if let (width, leftInset, rightInset, maxHeight, metrics) = self.validLayout {
|
||||
let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset, maxHeight: maxHeight, metrics: metrics)
|
||||
let panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics)
|
||||
if !self.bounds.size.height.isEqual(to: panelHeight) {
|
||||
self.updateHeight()
|
||||
self.updateHeight(animated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,5 +3,4 @@ import Foundation
|
||||
public struct GlobalExperimentalSettings {
|
||||
public static var isAppStoreBuild: Bool = false
|
||||
public static var enableFeed: Bool = false
|
||||
public static var animatedStickers: Bool = false
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ func stickerFromLegacyDocument(_ documentAttachment: TGDocumentMediaAttachment)
|
||||
|
||||
func legacyComponentsStickers(postbox: Postbox, namespace: Int32) -> SSignal {
|
||||
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]] = [:]
|
||||
|
||||
for entry in view.entries {
|
||||
|
||||
@ -51,8 +51,25 @@ func presentLegacySecureIdAttachmentMenu(context: AccountContext, present: @esca
|
||||
mappedIntent = TGPassportAttachIntentSelfie
|
||||
}
|
||||
|
||||
var uploadStarted = false
|
||||
|
||||
guard let attachMenu = TGPassportAttachMenu.present(with: legacyController.context, parentController: emptyController, menuController: nil, title: "", intent: mappedIntent, uploadAction: { signal, completed in
|
||||
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)
|
||||
|> mapToSignal { resources -> Signal<([TelegramMediaResource], SecureIdRecognizedDocumentData?), NoError> in
|
||||
switch type {
|
||||
@ -70,6 +87,7 @@ func presentLegacySecureIdAttachmentMenu(context: AccountContext, present: @esca
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { resourcesAndData in
|
||||
completion(resourcesAndData.0, resourcesAndData.1)
|
||||
statusDisposable.dispose()
|
||||
completed?()
|
||||
})
|
||||
} else {
|
||||
|
||||
@ -114,7 +114,7 @@ public func phoneLabelController(context: AccountContext, currentLabel: String,
|
||||
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)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user