diff --git a/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift b/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift index e76786ed9e..9f233ccfa9 100644 --- a/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift +++ b/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift @@ -32,13 +32,15 @@ public final class AvatarVideoNode: ASDisplayNode { private let playbackStartDisposable = MetaDisposable() private var videoLoopCount = 0 - private var size = CGSize(width: 60.0, height: 60.0) + private var validLayout: (CGSize, CGFloat)? + private var internalSize = CGSize(width: 60.0, height: 60.0) public init(context: AccountContext) { self.context = context self.backgroundNode = ASImageNode() self.backgroundNode.displaysAsynchronously = false + self.backgroundNode.isHidden = true super.init() @@ -60,16 +62,15 @@ public final class AvatarVideoNode: ASDisplayNode { } } + private var didAppear = false + private func setupAnimation() { guard let animationFile = self.animationFile else { return } - let itemNativeFitSize = CGSize(width: 128.0, height: 128.0) - - let size = CGSize(width: self.size.width * 0.67, height: self.size.height * 0.67) - let itemFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.size.width - size.width) / 2.0), y: floorToScreenPixels((self.size.height - size.height) / 2.0)), size: size) - + let itemNativeFitSize = self.internalSize.width > 100.0 ? CGSize(width: 256.0, height: 256.0) : CGSize(width: 128.0, height: 128.0) + let animationData = EntityKeyboardAnimationData(file: animationFile) let itemLayer = EmojiPagerContentComponent.View.ItemLayer( item: EmojiPagerContentComponent.Item( @@ -89,109 +90,77 @@ public final class AvatarVideoNode: ASDisplayNode { blurredBadgeColor: .clear, accentIconColor: .white, pointSize: itemNativeFitSize, - onUpdateDisplayPlaceholder: { displayPlaceholder, _ in - if !displayPlaceholder { - print() - } -// guard let strongSelf = self else { -// return -// } -// if displayPlaceholder { -// if let itemLayer = strongSelf.visibleItemLayers[itemId] { -// let placeholderView: EmojiPagerContentComponent.View.ItemPlaceholderView -// if let current = strongSelf.visibleItemPlaceholderViews[itemId] { -// placeholderView = current -// } else { -// placeholderView = EmojiPagerContentComponent.View.ItemPlaceholderView( -// context: context, -// dimensions: item.file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), -// immediateThumbnailData: item.file.immediateThumbnailData, -// shimmerView: nil,//strongSelf.shimmerHostView, -// color: theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.08), -// size: itemNativeFitSize -// ) -// strongSelf.visibleItemPlaceholderViews[itemId] = placeholderView -// strongSelf.view.insertSubview(placeholderView, at: 0) -// } -// placeholderView.frame = itemLayer.frame -// placeholderView.update(size: placeholderView.bounds.size) -// -// strongSelf.updateShimmerIfNeeded() -// } -// } else { -// if let placeholderView = strongSelf.visibleItemPlaceholderViews[itemId] { -// strongSelf.visibleItemPlaceholderViews.removeValue(forKey: itemId) -// -// if duration > 0.0 { -// placeholderView.layer.opacity = 0.0 -// placeholderView.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, completion: { [weak self, weak placeholderView] _ in -// guard let strongSelf = self else { -// return -// } -// placeholderView?.removeFromSuperview() -// strongSelf.updateShimmerIfNeeded() -// }) -// } else { -// placeholderView.removeFromSuperview() -// strongSelf.updateShimmerIfNeeded() -// } -// } -// } + onUpdateDisplayPlaceholder: { _, _ in } ) itemLayer.onContentsUpdate = { [weak self] in - self?.backgroundNode.isHidden = false + if let self { + if !self.didAppear { + self.didAppear = true + Queue.mainQueue().after(0.15) { + self.backgroundNode.isHidden = false + } + } + } } itemLayer.layerTintColor = UIColor.white.cgColor - itemLayer.frame = itemFrame - itemLayer.isVisibleForAnimations = true + itemLayer.isVisibleForAnimations = self.visibility self.itemLayer = itemLayer self.backgroundNode.layer.addSublayer(itemLayer) + + if let (size, cornerRadius) = self.validLayout { + self.updateLayout(size: size, cornerRadius: cornerRadius, transition: .immediate) + } + } + + public func update(markup: TelegramMediaImage.EmojiMarkup, size: CGSize) { + guard markup != self.emojiMarkup else { + return + } + self.emojiMarkup = markup + self.internalSize = size + + let colors = markup.backgroundColors.map { UInt32(bitPattern: $0) } + if colors.count == 1 { + backgroundNode.backgroundColor = UIColor(rgb: colors.first!) + self.backgroundNode.image = nil + } else if colors.count == 2 { + self.backgroundNode.image = generateGradientImage(size: size, colors: colors.map { UIColor(rgb: $0) }, locations: [0.0, 1.0])! + } else { + self.backgroundNode.image = GradientBackgroundNode.generatePreview(size: size, colors: colors.map { UIColor(rgb: $0) }) + } + self.backgroundNode.isHidden = true + + switch markup.content { + case let .emoji(fileId): + self.fileDisposable = (self.context.engine.stickers.resolveInlineStickers(fileIds: [fileId]) + |> deliverOnMainQueue).start(next: { [weak self] files in + if let strongSelf = self, let file = files.values.first { + strongSelf.animationFile = file + strongSelf.setupAnimation() + } + }) + case let .sticker(packReference, fileId): + self.fileDisposable = (self.context.engine.stickers.loadedStickerPack(reference: packReference, forceActualized: false) + |> map { pack -> TelegramMediaFile? in + if case let .result(_, items, _) = pack, let item = items.first(where: { $0.file.fileId.id == fileId }) { + return item.file + } + return nil + } + |> deliverOnMainQueue).start(next: { [weak self] file in + if let strongSelf = self, let file { + strongSelf.animationFile = file + strongSelf.setupAnimation() + } + }) + } } public func update(peer: EnginePeer, photo: TelegramMediaImage, size: CGSize) { - self.size = size + self.internalSize = size if let markup = photo.emojiMarkup { - if markup != self.emojiMarkup { - self.emojiMarkup = markup - - let colors = markup.backgroundColors.map { UInt32(bitPattern: $0) } - let backgroundImage: UIImage - if colors.count == 1 { - backgroundImage = generateSingleColorImage(size: size, color: UIColor(rgb: colors.first!))! - } else if colors.count == 2 { - backgroundImage = generateGradientImage(size: size, colors: colors.map { UIColor(rgb: $0) }, locations: [0.0, 1.0])! - } else { - backgroundImage = GradientBackgroundNode.generatePreview(size: size, colors: colors.map { UIColor(rgb: $0) }) - } - self.backgroundNode.image = backgroundImage - self.backgroundNode.isHidden = true - - switch markup.content { - case let .emoji(fileId): - self.fileDisposable = (self.context.engine.stickers.resolveInlineStickers(fileIds: [fileId]) - |> deliverOnMainQueue).start(next: { [weak self] files in - if let strongSelf = self, let file = files.values.first { - strongSelf.animationFile = file - strongSelf.setupAnimation() - } - }) - case let .sticker(packReference, fileId): - self.fileDisposable = (self.context.engine.stickers.loadedStickerPack(reference: packReference, forceActualized: false) - |> map { pack -> TelegramMediaFile? in - if case let .result(_, items, _) = pack, let item = items.first(where: { $0.file.fileId.id == fileId }) { - return item.file - } - return nil - } - |> deliverOnMainQueue).start(next: { [weak self] file in - if let strongSelf = self, let file { - strongSelf.animationFile = file - strongSelf.setupAnimation() - } - }) - } - } + self.update(markup: markup, size: size) } else if let video = smallestVideoRepresentation(photo.videoRepresentations), let peerReference = PeerReference(peer._asPeer()) { self.backgroundNode.image = nil @@ -205,7 +174,9 @@ public final class AvatarVideoNode: ASDisplayNode { } } + private var visibility = false public func updateVisibility(_ isVisible: Bool) { + self.visibility = isVisible if isVisible, let videoContent = self.videoContent, self.videoLoopCount != maxVideoLoopCount { if self.videoNode == nil { let context = self.context @@ -262,9 +233,11 @@ public final class AvatarVideoNode: ASDisplayNode { self.videoNode = nil videoNode.removeFromSupernode() } + self.itemLayer?.isVisibleForAnimations = isVisible } public func updateLayout(size: CGSize, cornerRadius: CGFloat, transition: ContainedViewLayoutTransition) { + self.validLayout = (size, cornerRadius) self.layer.cornerRadius = cornerRadius self.backgroundNode.frame = CGRect(origin: .zero, size: size) @@ -273,6 +246,12 @@ public final class AvatarVideoNode: ASDisplayNode { videoNode.frame = CGRect(origin: .zero, size: size) videoNode.updateLayout(size: size, transition: transition) } + + if let itemLayer = self.itemLayer { + let itemSize = CGSize(width: size.width * 0.67, height: size.height * 0.67) + let itemFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - itemSize.width) / 2.0), y: floorToScreenPixels((size.height - itemSize.height) / 2.0)), size: itemSize) + itemLayer.frame = itemFrame + } } public func resetPlayback() { diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index ce4fe06b48..4ae0fc051c 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -1710,7 +1710,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } } if let hasUsername = groupType.hasUsername { - if hasUsername != (channel.addressName != nil) { + if hasUsername != (!(channel.addressName ?? "").isEmpty) { return false } } @@ -1738,8 +1738,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { return false } } - if let hasUsername = channelType.hasUsername, hasUsername { - if hasUsername != (channel.addressName != nil) { + if let hasUsername = channelType.hasUsername { + if hasUsername != (!(channel.addressName ?? "").isEmpty) { return false } } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index b896f7b468..ec18cf508e 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -1802,7 +1802,7 @@ public final class ChatListNode: ListView { } } if let hasUsername = groupType.hasUsername { - if hasUsername != (channel.addressName != nil) { + if hasUsername != (!(channel.addressName ?? "").isEmpty) { return false } } @@ -1831,8 +1831,8 @@ public final class ChatListNode: ListView { return false } } - if let hasUsername = channelType.hasUsername, hasUsername { - if hasUsername != (channel.addressName != nil) { + if let hasUsername = channelType.hasUsername { + if hasUsername != (!(channel.addressName ?? "").isEmpty) { return false } } diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index dd197c7e08..a9ca3c6e41 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -65,6 +65,11 @@ public struct ContextMenuActionItemIconSource { } } +public enum ContextMenuActionItemIconPosition { + case left + case right +} + public enum ContextMenuActionBadgeColor { case accent case inactive @@ -102,6 +107,7 @@ public final class ContextMenuActionItem { public let badge: ContextMenuActionBadge? public let icon: (PresentationTheme) -> UIImage? public let iconSource: ContextMenuActionItemIconSource? + public let iconPosition: ContextMenuActionItemIconPosition public let animationName: String? public let textIcon: (PresentationTheme) -> UIImage? public let textLinkAction: () -> Void @@ -117,6 +123,7 @@ public final class ContextMenuActionItem { badge: ContextMenuActionBadge? = nil, icon: @escaping (PresentationTheme) -> UIImage?, iconSource: ContextMenuActionItemIconSource? = nil, + iconPosition: ContextMenuActionItemIconPosition = .right, animationName: String? = nil, textIcon: @escaping (PresentationTheme) -> UIImage? = { _ in return nil }, textLinkAction: @escaping () -> Void = {}, @@ -132,6 +139,7 @@ public final class ContextMenuActionItem { badge: badge, icon: icon, iconSource: iconSource, + iconPosition: iconPosition, animationName: animationName, textIcon: textIcon, textLinkAction: textLinkAction, @@ -153,6 +161,7 @@ public final class ContextMenuActionItem { badge: ContextMenuActionBadge? = nil, icon: @escaping (PresentationTheme) -> UIImage?, iconSource: ContextMenuActionItemIconSource? = nil, + iconPosition: ContextMenuActionItemIconPosition = .right, animationName: String? = nil, textIcon: @escaping (PresentationTheme) -> UIImage? = { _ in return nil }, textLinkAction: @escaping () -> Void = {}, @@ -167,6 +176,7 @@ public final class ContextMenuActionItem { self.badge = badge self.icon = icon self.iconSource = iconSource + self.iconPosition = iconPosition self.animationName = animationName self.textIcon = textIcon self.textLinkAction = textLinkAction diff --git a/submodules/Display/Source/WindowContent.swift b/submodules/Display/Source/WindowContent.swift index 4b26926b33..9981e42339 100644 --- a/submodules/Display/Source/WindowContent.swift +++ b/submodules/Display/Source/WindowContent.swift @@ -549,8 +549,11 @@ public class Window1 { var keyboardHeight: CGFloat if keyboardFrame.isEmpty || keyboardFrame.maxY < screenHeight { - if isTablet && screenHeight - keyboardFrame.maxY < 5.0 { + if inPopover || (isTablet && screenHeight - keyboardFrame.maxY < 5.0) { keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY) + if inPopover && !keyboardHeight.isZero { + keyboardHeight = max(0.0, keyboardHeight - popoverDelta) + } } else { keyboardHeight = 0.0 } diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index eebe5489d5..fe74b08ada 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -3013,22 +3013,7 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U return TGPaintingData(drawing: drawingData, entitiesData: entitiesData, image: image, stillImage: stillImage, hasAnimation: hasAnimatedEntities, stickers: stickers) } - - public func resultImage() -> UIImage! { - let image = generateImage(self.drawingView.imageSize, contextGenerator: { size, context in - let bounds = CGRect(origin: .zero, size: size) - context.clear(bounds) - if let cgImage = self.drawingView.drawingImage?.cgImage { - context.draw(cgImage, in: bounds) - } - context.translateBy(x: size.width / 2.0, y: size.height / 2.0) - context.scaleBy(x: 1.0, y: -1.0) - context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) - self.entitiesView.layer.render(in: context) - }, opaque: false, scale: 1.0) - return image - } - + public func animateOut(_ completion: @escaping (() -> Void)) { self.selectionContainerView.alpha = 0.0 diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h index d2ae68296c..23ec3cb634 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h @@ -27,6 +27,8 @@ typedef enum @property (nonatomic, readonly) NSTimeInterval trimEndValue; @property (nonatomic, readonly) TGMediaVideoConversionPreset preset; +@property (nonatomic, readonly) int64_t stickerPackId; +@property (nonatomic, readonly) int64_t stickerPackAccessHash; @property (nonatomic, readonly) int64_t documentId; @property (nonatomic, strong, readonly) NSArray *colors; @@ -43,6 +45,7 @@ typedef enum + (instancetype)editAdjustmentsWithOriginalSize:(CGSize)originalSize preset:(TGMediaVideoConversionPreset)preset; + (instancetype)editAdjustmentsWithPhotoEditorValues:(PGPhotoEditorValues *)values preset:(TGMediaVideoConversionPreset)preset; + (instancetype)editAdjustmentsWithPhotoEditorValues:(PGPhotoEditorValues *)values preset:(TGMediaVideoConversionPreset)preset documentId:(int64_t)documentId colors:(NSArray *)colors; ++ (instancetype)editAdjustmentsWithPhotoEditorValues:(PGPhotoEditorValues *)values preset:(TGMediaVideoConversionPreset)preset stickerPackId:(int64_t)stickerPackId stickerPackAccessHash:(int64_t)stickerPackAccessHash documentId:(int64_t)documentId colors:(NSArray *)colors; + (instancetype)editAdjustmentsWithDictionary:(NSDictionary *)dictionary; + (instancetype)editAdjustmentsWithOriginalSize:(CGSize)originalSize diff --git a/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m b/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m index db6b510d2e..55fa3dc762 100644 --- a/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m +++ b/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m @@ -151,6 +151,29 @@ const NSTimeInterval TGVideoEditMaximumGifDuration = 30.5; return adjustments; } ++ (instancetype)editAdjustmentsWithPhotoEditorValues:(PGPhotoEditorValues *)values preset:(TGMediaVideoConversionPreset)preset stickerPackId:(int64_t)stickerPackId stickerPackAccessHash:(int64_t)stickerPackAccessHash documentId:(int64_t)documentId colors:(NSArray *)colors { + TGVideoEditAdjustments *adjustments = [[[self class] alloc] init]; + adjustments->_originalSize = values.originalSize; + CGRect cropRect = values.cropRect; + if (CGRectIsEmpty(cropRect)) { + cropRect = CGRectMake(0.0f, 0.0f, values.originalSize.width, values.originalSize.height); + } + adjustments->_cropRect = cropRect; + adjustments->_cropOrientation = values.cropOrientation; + adjustments->_cropRotation = values.cropRotation; + adjustments->_cropLockedAspectRatio = values.cropLockedAspectRatio; + adjustments->_cropMirrored = values.cropMirrored; + adjustments->_paintingData = [values.paintingData dataForAnimation]; + adjustments->_sendAsGif = true; + adjustments->_preset = preset; + adjustments->_stickerPackId = stickerPackId; + adjustments->_stickerPackAccessHash = stickerPackAccessHash; + adjustments->_documentId = documentId; + adjustments->_colors = colors; + + return adjustments; +} + - (instancetype)editAdjustmentsWithPreset:(TGMediaVideoConversionPreset)preset maxDuration:(NSTimeInterval)maxDuration { TGVideoEditAdjustments *adjustments = [[[self class] alloc] init]; diff --git a/submodules/PeerInfoAvatarListNode/BUILD b/submodules/PeerInfoAvatarListNode/BUILD index a6cb6685e6..f07f8b1e65 100644 --- a/submodules/PeerInfoAvatarListNode/BUILD +++ b/submodules/PeerInfoAvatarListNode/BUILD @@ -25,6 +25,7 @@ swift_library( "//submodules/GalleryUI:GalleryUI", "//submodules/MediaPlayer:UniversalMediaPlayer", "//submodules/AccountContext:AccountContext", + "//submodules/AvatarVideoNode:AvatarVideoNode", ], visibility = [ "//visibility:public", diff --git a/submodules/PremiumUI/Sources/HelloView.swift b/submodules/PremiumUI/Sources/HelloView.swift new file mode 100644 index 0000000000..a7fe6eb60e --- /dev/null +++ b/submodules/PremiumUI/Sources/HelloView.swift @@ -0,0 +1,150 @@ +import Foundation +import UIKit +import Display +import SwiftSignalKit + +private let phrases = [ + "Вітаю", + "你好", + "Hello", + "سلام", + "Bonjour", + "Guten tag", + "שלום", + "नमस्ते", + "Ciao", + "こんにちは", + "Hei", + "Olá", + "Привет", + "Zdravo", + "Hola", + "Привіт", + "Salom", + "Halo" +] + +private var activeCount = 13 + +private let referenceWidth: CGFloat = 1180 +private let positions: [CGPoint] = [ + CGPoint(x: 315.0, y: 83.0), + CGPoint(x: 676.0, y: 18.0), + CGPoint(x: 880.0, y: 130.0), + CGPoint(x: 90.0, y: 214.0), + CGPoint(x: 550.0, y: 150.0), + CGPoint(x: 1130.0, y: 220.0), + CGPoint(x: 220.0, y: 440.0), + CGPoint(x: 1080.0, y: 350.0), + CGPoint(x: 85.0, y: 630.0), + CGPoint(x: 1180.0, y: 550.0), + CGPoint(x: 150.0, y: 810.0), + CGPoint(x: 1010.0, y: 770.0), + CGPoint(x: 40.0, y: 1000.0), + CGPoint(x: 1130.0, y: 1000.0) +] + +final class HelloView: UIView, PhoneDemoDecorationView { + private var activePhrases = Set() + private var activePositions = Set() + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setupAnimations() { + guard self.activePhrases.isEmpty else { + return + } + var ids: [Int] = [] + for i in 0 ..< phrases.count { + ids.append(i) + } + ids.shuffle() + + let phraseIds = Array(self.availablePhraseIds()).shuffled() + let positionIds = Array(self.availablePositionIds()).shuffled() + + for i in 0 ..< activeCount { + let delay: Double = Double.random(in: 0.0 ..< 0.8) + Queue.mainQueue().after(delay) { + self.spawnPhrase(phraseIds[i], positionIndex: positionIds[i]) + } + } + } + + func availablePhraseIds() -> Set { + var ids = Set() + for i in 0 ..< phrases.count { + ids.insert(i) + } + for id in self.activePhrases { + ids.remove(id) + } + return ids + } + + func availablePositionIds() -> Set { + var ids = Set() + for i in 0 ..< positions.count { + ids.insert(i) + } + for id in self.activePositions { + ids.remove(id) + } + return ids + } + + func spawnNextPhrase() { + let phraseIds = Array(self.availablePhraseIds()).shuffled() + let positionIds = Array(self.availablePositionIds()).shuffled() + if let phrase = phraseIds.first, let position = positionIds.first { + self.spawnPhrase(phrase, positionIndex: position) + } + } + + func spawnPhrase(_ index: Int, positionIndex: Int) { + let view = UILabel() + view.alpha = 0.0 + view.text = phrases[index] + view.font = Font.with(size: 24.0, design: .round, weight: .semibold, traits: []) + view.textColor = UIColor(rgb: 0xffffff, alpha: CGFloat.random(in: 0.4 ... 0.6)) + view.layer.compositingFilter = "softLightBlendMode" + view.sizeToFit() + view.center = self.positionForIndex(positionIndex) + + self.activePhrases.insert(index) + self.activePositions.insert(positionIndex) + + let duration: Double = Double.random(in: 1.75...2.25) + view.layer.animateKeyframes(values: [0.0, 1.0, 0.0] as [NSNumber], duration: duration, keyPath: "opacity", removeOnCompletion: false, completion: { [weak view] _ in + self.activePhrases.remove(index) + self.activePositions.remove(positionIndex) + view?.removeFromSuperview() + self.spawnNextPhrase() + }) + view.layer.animateScale(from: CGFloat.random(in: 0.4 ..< 0.6), to: CGFloat.random(in: 0.9 ..< 1.2), duration: duration, removeOnCompletion: false) + + self.addSubview(view) + } + + func positionForIndex(_ index: Int) -> CGPoint { + var position = positions[index] + let spread: CGPoint = CGPoint(x: 30.0, y: 5.0) + position.x = (self.frame.width - self.frame.height) / 2.0 + position.x / referenceWidth * self.frame.height + CGFloat.random(in: -spread.x ... spread.x) + position.y = position.y / referenceWidth * self.frame.height + CGFloat.random(in: -spread.y ... spread.y) + return position + } + + func setVisible(_ visible: Bool) { + self.setupAnimations() + } + + func resetAnimation() { + + } +} diff --git a/submodules/PremiumUI/Sources/PhoneDemoComponent.swift b/submodules/PremiumUI/Sources/PhoneDemoComponent.swift index 3e90cd04c7..7e0530f6ab 100644 --- a/submodules/PremiumUI/Sources/PhoneDemoComponent.swift +++ b/submodules/PremiumUI/Sources/PhoneDemoComponent.swift @@ -138,6 +138,21 @@ private final class PhoneView: UIView { } } + var model: PhoneDemoComponent.Model = .notch { + didSet { + if self.model != oldValue { + switch self.model { + case .notch: + self.borderView.image = phoneBorderImage + self.shimmerBorderView.image = phoneBorderMaskImage + case .island: + self.borderView.image = phoneBorderImage14 + self.shimmerBorderView.image = phoneBorderMaskImage14 + } + } + } + } + override init(frame: CGRect) { self.contentContainerView = UIView() self.contentContainerView.clipsToBounds = true @@ -355,21 +370,30 @@ final class PhoneDemoComponent: Component { case fasterStars case badgeStars case emoji + case hello + } + + enum Model { + case notch + case island } let context: AccountContext let position: Position + let model: Model let videoFile: TelegramMediaFile? let decoration: BackgroundDecoration public init( context: AccountContext, position: PhoneDemoComponent.Position, + model: Model = .notch, videoFile: TelegramMediaFile?, decoration: BackgroundDecoration = .none ) { self.context = context self.position = position + self.model = model self.videoFile = videoFile self.decoration = decoration } @@ -381,6 +405,9 @@ final class PhoneDemoComponent: Component { if lhs.position != rhs.position { return false } + if lhs.model != rhs.model { + return false + } if lhs.videoFile != rhs.videoFile { return false } @@ -452,6 +479,7 @@ final class PhoneDemoComponent: Component { self.containerView.frame = CGRect(origin: .zero, size: availableSize) self.decorationContainerView.frame = CGRect(origin: CGPoint(x: -availableSize.width * 0.5, y: 0.0), size: CGSize(width: availableSize.width * 2.0, height: availableSize.height)) self.phoneView.bounds = CGRect(origin: .zero, size: phoneSize) + self.phoneView.model = component.model switch component.decoration { case .none: @@ -504,6 +532,13 @@ final class PhoneDemoComponent: Component { self.decorationView = starsView self.decorationContainerView.addSubview(starsView) } + case .hello: + if let _ = self.decorationView as? HelloView { + } else { + let starsView = HelloView(frame: self.decorationContainerView.bounds) + self.decorationView = starsView + self.decorationContainerView.addSubview(starsView) + } } self.phoneView.setup(context: component.context, videoFile: component.videoFile, position: component.position) diff --git a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift index 92eacefffa..aa824e59ca 100644 --- a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift @@ -909,9 +909,12 @@ private final class DemoSheetContent: CombinedComponent { id: PremiumDemoScreen.Subject.translation, component: AnyComponent( PageComponent( - content: AnyComponent(AppIconsDemoComponent( + content: AnyComponent(PhoneDemoComponent( context: component.context, - appIcons: appIcons + position: .top, + model: .island, + videoFile: configuration.videos["translations"], + decoration: .hello )), title: strings.Premium_Translation, text: isStandalone ? strings.Premium_TranslationStandaloneInfo : strings.Premium_TranslationInfo, diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index e5a6edd5da..b17f5847cf 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -250,7 +250,7 @@ public enum PremiumSource: Equatable { return "deeplink" } case .translation: - return "translation" + return "translations" } } } @@ -329,7 +329,7 @@ enum PremiumPerk: CaseIterable { case .emojiStatus: return "emoji_status" case .translation: - return "translation" + return "translations" } } @@ -428,7 +428,7 @@ enum PremiumPerk: CaseIterable { case .emojiStatus: return "Premium/Perk/Status" case .translation: - return "Premium/Perk/Status" + return "Premium/Perk/Translation" } } } @@ -1492,7 +1492,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { UIColor(rgb: 0x5A6EEE), UIColor(rgb: 0x548DFF), UIColor(rgb: 0x54A3FF), - UIColor(rgb: 0x54bdff) + UIColor(rgb: 0x54bdff), + UIColor(rgb: 0x71c8ff) ] let accountContext = context.component.context diff --git a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift index 676672d5ef..7508c05894 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift @@ -1184,6 +1184,26 @@ public class PremiumLimitsListScreen: ViewController { ) ) + availableItems[.translation] = DemoPagerComponent.Item( + AnyComponentWithIdentity( + id: PremiumDemoScreen.Subject.translation, + component: AnyComponent( + PageComponent( + content: AnyComponent(PhoneDemoComponent( + context: context, + position: .top, + model: .island, + videoFile: configuration.videos["translations"], + decoration: .hello + )), + title: strings.Premium_Translation, + text: isStandalone ? strings.Premium_TranslationStandaloneInfo : strings.Premium_TranslationInfo, + textColor: textColor + ) + ) + ) + ) + if let order = controller.order { var items: [DemoPagerComponent.Item] = order.compactMap { availableItems[$0] } let index: Int diff --git a/submodules/PremiumUI/Sources/SwirlStarsView.swift b/submodules/PremiumUI/Sources/SwirlStarsView.swift index 6b80754777..bf8a510522 100644 --- a/submodules/PremiumUI/Sources/SwirlStarsView.swift +++ b/submodules/PremiumUI/Sources/SwirlStarsView.swift @@ -70,47 +70,8 @@ final class SwirlStarsView: UIView, PhoneDemoDecorationView { animation.fillMode = .forwards animation.repeatCount = .infinity node.addAnimation(animation, forKey: "rotation") - - self.setupMovementAnimation() } - func setupMovementAnimation() { - guard let node = self.sceneView.scene?.rootNode.childNode(withName: "star", recursively: false) else { - return - } - - node.position = SCNVector3(3.5, 0.0, -2.0) - let firstPath = UIBezierPath() - firstPath.move(to: CGPoint(x: 3.5, y: -2.0)) - firstPath.addLine(to: CGPoint(x: -15.5, y: 15.5)) - - let firstAction = SCNAction.moveAlong(path: firstPath, duration: 2.0) - - SCNTransaction.begin() - SCNTransaction.animationDuration = 2.0 - node.runAction(firstAction) - SCNTransaction.completionBlock = { [weak self, weak node] in - Queue.mainQueue().after(2.2, { - node?.position = SCNVector3(0.0, 0.0, -3.0) - let secondPath = UIBezierPath() - secondPath.move(to: CGPoint(x: 0.0, y: -3.0)) - secondPath.addLine(to: CGPoint(x: 15.5, y: 20.0)) - - let secondAction = SCNAction.moveAlong(path: secondPath, duration: 2.0) - SCNTransaction.begin() - SCNTransaction.animationDuration = 2.0 - node?.runAction(secondAction) - SCNTransaction.completionBlock = { [weak self] in - Queue.mainQueue().after(2.2, { - self?.setupMovementAnimation() - }) - } - SCNTransaction.commit() - }) - } - SCNTransaction.commit() - } - func resetAnimation() { } @@ -120,85 +81,3 @@ final class SwirlStarsView: UIView, PhoneDemoDecorationView { self.sceneView.frame = CGRect(origin: .zero, size: frame.size) } } - -extension UIBezierPath { - var elements: [PathElement] { - var pathElements = [PathElement]() - withUnsafeMutablePointer(to: &pathElements) { elementsPointer in - cgPath.apply(info: elementsPointer) { (userInfo, nextElementPointer) in - let nextElement = PathElement(element: nextElementPointer.pointee) - let elementsPointer = userInfo!.assumingMemoryBound(to: [PathElement].self) - elementsPointer.pointee.append(nextElement) - } - } - return pathElements - } -} - -enum PathElement { - case moveToPoint(CGPoint) - case addLineToPoint(CGPoint) - case addQuadCurveToPoint(CGPoint, CGPoint) - case addCurveToPoint(CGPoint, CGPoint, CGPoint) - case closeSubpath - - init(element: CGPathElement) { - switch element.type { - case .moveToPoint: - self = .moveToPoint(element.points[0]) - case .addLineToPoint: - self = .addLineToPoint(element.points[0]) - case .addQuadCurveToPoint: - self = .addQuadCurveToPoint(element.points[0], element.points[1]) - case .addCurveToPoint: - self = .addCurveToPoint(element.points[0], element.points[1], element.points[2]) - case .closeSubpath: - self = .closeSubpath - @unknown default: - self = .closeSubpath - } - } -} - -public extension SCNAction { - class func moveAlong(path: UIBezierPath, duration animationDuration: Double) -> SCNAction { - let points = path.elements - var actions = [SCNAction]() - - for point in points { - switch point { - case .moveToPoint(let a): - let moveAction = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration) - actions.append(moveAction) - break - - case .addCurveToPoint(let a, let b, let c): - let moveAction1 = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration) - let moveAction2 = SCNAction.move(to: SCNVector3(b.x, 0, b.y), duration: animationDuration) - let moveAction3 = SCNAction.move(to: SCNVector3(c.x, 0, c.y), duration: animationDuration) - actions.append(moveAction1) - actions.append(moveAction2) - actions.append(moveAction3) - break - - case .addLineToPoint(let a): - let moveAction = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration) - actions.append(moveAction) - break - - case .addQuadCurveToPoint(let a, let b): - let moveAction1 = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration) - let moveAction2 = SCNAction.move(to: SCNVector3(b.x, 0, b.y), duration: animationDuration) - actions.append(moveAction1) - actions.append(moveAction2) - break - - default: - let moveAction = SCNAction.move(to: SCNVector3(0, 0, 0), duration: animationDuration) - actions.append(moveAction) - break - } - } - return SCNAction.sequence(actions) - } -} diff --git a/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift b/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift index d896646870..facfef46ac 100644 --- a/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift +++ b/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift @@ -496,7 +496,9 @@ final class LocalizationListControllerNode: ViewControllerTracingNode { } } - entries.append(.doNotTranslate(text: presentationData.strings.Localization_DoNotTranslate, value: value)) + if showTranslate || translateChats { + entries.append(.doNotTranslate(text: presentationData.strings.Localization_DoNotTranslate, value: value)) + } if showTranslate { entries.append(.translateInfo(text: ignoredLanguages.count > 1 ? presentationData.strings.Localization_DoNotTranslateManyInfo : presentationData.strings.Localization_DoNotTranslateInfo)) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift index 5584747a03..178ac8ad57 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift @@ -199,6 +199,9 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { @objc internal func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { var updated = textField.text ?? "" updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string) + if updated.hasPrefix("#") { + updated.removeFirst() + } if updated.count <= 6 && updated.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil { textField.text = updated.uppercased() textField.textColor = self.theme.chat.inputPanel.inputTextColor diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift index 4cf8539699..b8ec60c334 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift @@ -1291,9 +1291,23 @@ final class AvatarEditorScreenComponent: Component { entity.position = CGPoint(x: drawingSize.width / 2.0, y: drawingSize.height / 2.0) entity.scale = 3.3 - var documentId: Int64 = 0 - if case let .file(file) = entity.content, file.isCustomEmoji { - documentId = file.fileId.id + var fileId: Int64 = 0 + var stickerPackId: Int64 = 0 + var stickerPackAccessHash: Int64 = 0 + if case let .file(file) = entity.content { + if file.isCustomEmoji { + fileId = file.fileId.id + } else if file.isAnimatedSticker { + for attribute in file.attributes { + if case let .Sticker(_, packReference, _) = attribute, let packReference, case let .id(id, accessHash) = packReference { + fileId = file.fileId.id + stickerPackId = id + stickerPackAccessHash = accessHash + break + } + } + + } } let colors: [NSNumber] = state.selectedBackground.colors.map { Int32(bitPattern: $0) as NSNumber } @@ -1337,9 +1351,15 @@ final class AvatarEditorScreenComponent: Component { }, opaque: false)! if entity.isAnimated { - controller.videoCompletion(combinedImage, tempUrl, TGVideoEditAdjustments(photoEditorValues: adjustments, preset: preset, documentId: documentId, colors: colors), { [weak controller] in - controller?.dismiss() - }) + if stickerPackId != 0 { + controller.videoCompletion(combinedImage, tempUrl, TGVideoEditAdjustments(photoEditorValues: adjustments, preset: preset, stickerPackId: stickerPackId, stickerPackAccessHash: stickerPackAccessHash, documentId: fileId, colors: colors), { [weak controller] in + controller?.dismiss() + }) + } else { + controller.videoCompletion(combinedImage, tempUrl, TGVideoEditAdjustments(photoEditorValues: adjustments, preset: preset, documentId: fileId, colors: colors), { [weak controller] in + controller?.dismiss() + }) + } } else { controller.imageCompletion(combinedImage, { [weak controller] in controller?.dismiss() @@ -1436,6 +1456,16 @@ public final class AvatarEditorScreen: ViewControllerComponentContainer { self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView()) self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) + + self.scrollToTop = { [weak self] in + if let self { + if let view = self.node.hostView.findTaggedView(tag: EmojiPagerContentComponent.Tag(id: AnyHashable("emoji"))) as? EmojiPagerContentComponent.View { + view.scrollToTop() + } else if let view = self.node.hostView.findTaggedView(tag: EmojiPagerContentComponent.Tag(id: AnyHashable("emoji"))) as? EmojiPagerContentComponent.View { + view.scrollToTop() + } + } + } } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/ColorPickerComponent.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/ColorPickerComponent.swift index 5c3b6b53ff..0d61a2f55b 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/ColorPickerComponent.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/ColorPickerComponent.swift @@ -1140,6 +1140,9 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { @objc internal func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { var updated = textField.text ?? "" updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string) + if updated.hasPrefix("#") { + updated.removeFirst() + } if updated.count <= 6 && updated.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil { textField.text = updated.uppercased() textField.textColor = self.theme.chat.inputPanel.inputTextColor diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 2898557a45..aa599ad5f0 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -3878,7 +3878,7 @@ public final class EmojiPagerContentComponent: Component { } } - private func scrollToTop() { + public func scrollToTop() { guard let _ = self.component, let _ = self.pagerEnvironment, let itemLayout = self.itemLayout else { return } @@ -5061,7 +5061,7 @@ public final class EmojiPagerContentComponent: Component { self.visibleSearchHeader?.endEditing(true) } } - + public func scrollViewDidScroll(_ scrollView: UIScrollView) { if self.ignoreScrolling { return diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Perk/Translation.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Translation.imageset/Contents.json new file mode 100644 index 0000000000..2d7643cb2c --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Translation.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "translatepremium.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Perk/Translation.imageset/translatepremium.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Translation.imageset/translatepremium.pdf new file mode 100644 index 0000000000..eb8899318f --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Perk/Translation.imageset/translatepremium.pdf @@ -0,0 +1,125 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 10.920044 10.255630 cm +1.000000 1.000000 1.000000 scn +5.750232 15.581270 m +5.426097 15.905405 4.900570 15.905405 4.576435 15.581270 c +4.252300 15.257134 4.252300 14.731607 4.576435 14.407473 c +6.743102 12.240807 l +7.067237 11.916671 7.592764 11.916671 7.916899 12.240807 c +8.241034 12.564941 8.241034 13.090468 7.916899 13.414603 c +5.750232 15.581270 l +h +12.746666 11.491037 m +10.580000 11.491037 l +4.080000 11.491037 l +1.913333 11.491037 l +1.454937 11.491037 1.083333 11.119434 1.083333 10.661037 c +1.083333 10.202641 1.454937 9.831038 1.913333 9.831038 c +3.267690 9.831038 l +3.397668 6.842636 4.247280 4.446305 5.928415 2.763829 c +4.622104 2.096912 2.938897 1.741038 0.830000 1.741038 c +0.371604 1.741038 0.000000 1.369434 0.000000 0.911038 c +0.000000 0.452641 0.371604 0.081038 0.830000 0.081038 c +3.431095 0.081038 5.621857 0.582098 7.330002 1.650631 c +9.038148 0.582098 11.228905 0.081038 13.830000 0.081038 c +14.288396 0.081038 14.660000 0.452641 14.660000 0.911038 c +14.660000 1.369434 14.288396 1.741038 13.830000 1.741038 c +11.721103 1.741038 10.037899 2.096912 8.731588 2.763829 c +10.412724 4.446305 11.262332 6.842636 11.392309 9.831038 c +12.746666 9.831038 l +13.205063 9.831038 13.576666 10.202641 13.576666 10.661037 c +13.576666 11.119434 13.205063 11.491037 12.746666 11.491037 c +h +7.104398 3.935437 m +5.818785 5.221051 5.057345 7.146626 4.929302 9.831038 c +9.730699 9.831038 l +9.602655 7.146626 8.841215 5.221051 7.555602 3.935437 c +7.482480 3.862315 7.407287 3.790889 7.330002 3.721178 c +7.252717 3.790889 7.177521 3.862315 7.104398 3.935437 c +h +f* +n +Q +q +1.000000 0.000000 -0.000000 1.000000 4.419800 5.891495 cm +1.000000 1.000000 1.000000 scn +6.473423 13.172696 m +6.345813 13.484631 6.042246 13.688431 5.705219 13.688431 c +5.368191 13.688431 5.064625 13.484631 4.937015 13.172696 c +0.062015 1.256029 l +-0.111549 0.831761 0.091686 0.347124 0.515953 0.173560 c +0.940221 -0.000004 1.424859 0.203231 1.598423 0.627499 c +3.160168 4.445098 l +8.250270 4.445098 l +9.812015 0.627499 l +9.985579 0.203231 10.470217 -0.000004 10.894484 0.173560 c +11.318751 0.347124 11.521987 0.831761 11.348423 1.256029 c +6.473423 13.172696 l +h +7.571179 6.105098 m +5.705219 10.666334 l +3.839258 6.105098 l +7.571179 6.105098 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 2291 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000002381 00000 n +0000002404 00000 n +0000002577 00000 n +0000002651 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2710 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 9d127c45ab..ac92e08b49 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1944,9 +1944,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if let dismissedTranslationPanelNode = dismissedTranslationPanelNode { var dismissedPanelFrame = dismissedTranslationPanelNode.frame dismissedPanelFrame.origin.y = -dismissedPanelFrame.size.height - transition.updateFrame(node: dismissedTranslationPanelNode, frame: dismissedPanelFrame, completion: { [weak dismissedTranslationPanelNode] _ in + transition.updateAlpha(node: dismissedTranslationPanelNode, alpha: 0.0, completion: { [weak dismissedTranslationPanelNode] _ in dismissedTranslationPanelNode?.removeFromSupernode() }) + dismissedTranslationPanelNode.animateOut() } if let dismissedImportStatusPanelNode = dismissedImportStatusPanelNode { diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index f402b61cf3..23ec16609e 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1249,7 +1249,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { parentMessage: item.message, constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude), animationCache: item.controllerInteraction.presentationContext.animationCache, - animationRenderer: item.controllerInteraction.presentationContext.animationRenderer + animationRenderer: item.controllerInteraction.presentationContext.animationRenderer, + associatedData: item.associatedData )) } diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 20d4968f6b..0bbe5642f3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -2011,7 +2011,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode parentMessage: item.message, constrainedSize: CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude), animationCache: item.controllerInteraction.presentationContext.animationCache, - animationRenderer: item.controllerInteraction.presentationContext.animationRenderer + animationRenderer: item.controllerInteraction.presentationContext.animationRenderer, + associatedData: item.associatedData )) replyInfoSizeApply = (sizeAndApply.0, { synchronousLoads in sizeAndApply.1(synchronousLoads) }) diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index e46afc445e..ab652a3aab 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -469,7 +469,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD parentMessage: item.message, constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), animationCache: item.controllerInteraction.presentationContext.animationCache, - animationRenderer: item.controllerInteraction.presentationContext.animationRenderer + animationRenderer: item.controllerInteraction.presentationContext.animationRenderer, + associatedData: item.associatedData )) } } else if let _ = attribute as? InlineBotMessageAttribute { diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index 4a97b47fba..48768e4926 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -350,7 +350,8 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { parentMessage: item.message, constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), animationCache: item.controllerInteraction.presentationContext.animationCache, - animationRenderer: item.controllerInteraction.presentationContext.animationRenderer + animationRenderer: item.controllerInteraction.presentationContext.animationRenderer, + associatedData: item.associatedData )) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift index dcf630538e..52ae2d0986 100644 --- a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift @@ -32,6 +32,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { let constrainedSize: CGSize let animationCache: AnimationCache? let animationRenderer: MultiAnimationRenderer? + let associatedData: ChatMessageItemAssociatedData init( presentationData: ChatPresentationData, @@ -42,7 +43,8 @@ class ChatMessageReplyInfoNode: ASDisplayNode { parentMessage: Message, constrainedSize: CGSize, animationCache: AnimationCache?, - animationRenderer: MultiAnimationRenderer? + animationRenderer: MultiAnimationRenderer?, + associatedData: ChatMessageItemAssociatedData ) { self.presentationData = presentationData self.strings = strings @@ -53,6 +55,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { self.constrainedSize = constrainedSize self.animationCache = animationCache self.animationRenderer = animationRenderer + self.associatedData = associatedData } } @@ -165,7 +168,20 @@ class ChatMessageReplyInfoNode: ASDisplayNode { let messageText: NSAttributedString if isText { - let entities = (arguments.message.textEntitiesAttribute?.entities ?? []).filter { entity in + var text = arguments.message.text + var messageEntities = arguments.message.textEntitiesAttribute?.entities ?? [] + + if let translateToLanguage = arguments.associatedData.translateToLanguage, !text.isEmpty { + for attribute in arguments.message.attributes { + if let attribute = attribute as? TranslationMessageAttribute, !attribute.text.isEmpty, attribute.toLang == translateToLanguage { + text = attribute.text + messageEntities = attribute.entities + break + } + } + } + + let entities = messageEntities.filter { entity in if case .Spoiler = entity.type { return true } else if case .CustomEmoji = entity.type { @@ -175,9 +191,9 @@ class ChatMessageReplyInfoNode: ASDisplayNode { } } if entities.count > 0 { - messageText = stringWithAppliedEntities(trimToLineCount(arguments.message.text, lineCount: 1), entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: arguments.message) + messageText = stringWithAppliedEntities(trimToLineCount(text, lineCount: 1), entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: arguments.message) } else { - messageText = NSAttributedString(string: textString.string, font: textFont, textColor: textColor) + messageText = NSAttributedString(string: text, font: textFont, textColor: textColor) } } else { messageText = NSAttributedString(string: textString.string, font: textFont, textColor: textColor) diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index b6b13a9955..7e3b0dd9cd 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -677,7 +677,8 @@ class ChatMessageStickerItemNode: ChatMessageItemView { parentMessage: item.message, constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), animationCache: item.controllerInteraction.presentationContext.animationCache, - animationRenderer: item.controllerInteraction.presentationContext.animationRenderer + animationRenderer: item.controllerInteraction.presentationContext.animationRenderer, + associatedData: item.associatedData )) } diff --git a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift index ee6c2c1c57..12303a1e80 100644 --- a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift @@ -48,6 +48,8 @@ final class ChatTranslationPanelNode: ASDisplayNode { super.init() + self.clipsToBounds = true + self.addSubnode(self.separatorNode) self.addSubnode(self.button) self.addSubnode(self.moreButton) @@ -63,6 +65,10 @@ final class ChatTranslationPanelNode: ASDisplayNode { } } + func animateOut() { + self.layer.animateBounds(from: self.bounds, to: self.bounds.offsetBy(dx: 0.0, dy: self.bounds.size.height), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + } + func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> CGFloat { let previousIsEnabled = self.chatInterfaceState?.translationState?.isEnabled self.chatInterfaceState = interfaceState @@ -123,13 +129,14 @@ final class ChatTranslationPanelNode: ASDisplayNode { let moreButtonSize = self.moreButton.measure(CGSize(width: 100.0, height: panelHeight)) self.moreButton.frame = CGRect(origin: CGPoint(x: width - contentRightInset - moreButtonSize.width, y: floorToScreenPixels((panelHeight - moreButtonSize.height) / 2.0)), size: moreButtonSize) + let buttonPadding: CGFloat = 10.0 let buttonSpacing: CGFloat = 10.0 let buttonTextSize = self.buttonTextNode.updateLayout(CGSize(width: width - contentRightInset - moreButtonSize.width, height: panelHeight)) if let icon = self.buttonIconNode.image { - let buttonSize = CGSize(width: buttonTextSize.width + icon.size.width + buttonSpacing, height: panelHeight) + let buttonSize = CGSize(width: buttonTextSize.width + icon.size.width + buttonSpacing + buttonPadding * 2.0, height: panelHeight) self.button.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - buttonSize.width) / 2.0), y: 0.0), size: buttonSize) - self.buttonIconNode.frame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((buttonSize.height - icon.size.height) / 2.0)), size: icon.size) - self.buttonTextNode.frame = CGRect(origin: CGPoint(x: icon.size.width + buttonSpacing, y: floorToScreenPixels((buttonSize.height - buttonTextSize.height) / 2.0)), size: buttonTextSize) + self.buttonIconNode.frame = CGRect(origin: CGPoint(x: buttonPadding, y: floorToScreenPixels((buttonSize.height - icon.size.height) / 2.0)), size: icon.size) + self.buttonTextNode.frame = CGRect(origin: CGPoint(x: buttonPadding + icon.size.width + buttonSpacing, y: floorToScreenPixels((buttonSize.height - buttonTextSize.height) / 2.0)), size: buttonTextSize) } transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 132b7c0356..c988f455b1 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -7092,10 +7092,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate let isSettings = self.isSettings self.updateAvatarDisposable.set((signal |> mapToSignal { videoResource -> Signal in - let markup: UploadPeerPhotoMarkup? = nil + var markup: UploadPeerPhotoMarkup? = nil if isSettings { -// let fileId = adjustments?.documentId -// let backgroundColors = adjustments?.colors as? [Int32] + if let fileId = adjustments?.documentId, let backgroundColors = adjustments?.colors as? [Int32], fileId != 0 { + if let packId = adjustments?.stickerPackId, let accessHash = adjustments?.stickerPackAccessHash, packId != 0 { + markup = .sticker(packReference: .id(id: packId, accessHash: accessHash), fileId: fileId, backgroundColors: backgroundColors) + } else { + markup = .emoji(fileId: fileId, backgroundColors: backgroundColors) + } + } if case .fallback = mode { return context.engine.accountData.updateFallbackPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations) diff --git a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift index d3cc10954c..eda2f2d1d9 100644 --- a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift +++ b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift @@ -73,7 +73,7 @@ private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 { case visualMediaStoredState = 5 case cachedImageRecognizedContent = 6 case pendingInAppPurchaseState = 7 - case translationState = 8 + case translationState = 9 } public struct ApplicationSpecificItemCacheCollectionId { diff --git a/submodules/TranslateUI/Sources/ChatTranslation.swift b/submodules/TranslateUI/Sources/ChatTranslation.swift index bfd81db8cb..131572cb04 100644 --- a/submodules/TranslateUI/Sources/ChatTranslation.swift +++ b/submodules/TranslateUI/Sources/ChatTranslation.swift @@ -114,7 +114,17 @@ public func translateMessageIds(context: AccountContext, messageIds: [EngineMess return context.account.postbox.transaction { transaction -> Signal in var messageIdsToTranslate: [EngineMessage.Id] = [] for messageId in messageIds { - if let message = transaction.getMessage(messageId), !message.text.isEmpty, let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, translation.toLang == toLang { + if let message = transaction.getMessage(messageId) { + if let replyAttribute = message.attributes.first(where: { $0 is ReplyMessageAttribute }) as? ReplyMessageAttribute, let replyMessage = message.associatedMessages[replyAttribute.messageId] { + if !replyMessage.text.isEmpty, let translation = replyMessage.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, translation.toLang == toLang { + } else { + messageIdsToTranslate.append(replyMessage.id) + } + } + if !message.text.isEmpty, let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, translation.toLang == toLang { + } else { + messageIdsToTranslate.append(messageId) + } } else { messageIdsToTranslate.append(messageId) } @@ -151,7 +161,7 @@ public func chatTranslationState(context: AccountContext, peerId: EnginePeer.Id) } else { return .single(nil) |> then( - context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: 16, fixedCombinedReadStates: nil) + context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: 32, fixedCombinedReadStates: nil) |> filter { messageHistoryView -> Bool in return messageHistoryView.0.entries.count > 1 } @@ -168,7 +178,7 @@ public func chatTranslationState(context: AccountContext, peerId: EnginePeer.Id) if message.text.count > 10 { let text = String(message.text.prefix(100)) languageRecognizer.processString(text) - let hypotheses = languageRecognizer.languageHypotheses(withMaximum: 3) + let hypotheses = languageRecognizer.languageHypotheses(withMaximum: 4) languageRecognizer.reset() let filteredLanguages = hypotheses.filter { supportedTranslationLanguages.contains($0.key.rawValue) }.sorted(by: { $0.value > $1.value })