mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Various improvements
This commit is contained in:
parent
e695f61bc7
commit
7d11ba5d5e
@ -32,13 +32,15 @@ public final class AvatarVideoNode: ASDisplayNode {
|
|||||||
private let playbackStartDisposable = MetaDisposable()
|
private let playbackStartDisposable = MetaDisposable()
|
||||||
private var videoLoopCount = 0
|
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) {
|
public init(context: AccountContext) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
self.backgroundNode = ASImageNode()
|
self.backgroundNode = ASImageNode()
|
||||||
self.backgroundNode.displaysAsynchronously = false
|
self.backgroundNode.displaysAsynchronously = false
|
||||||
|
self.backgroundNode.isHidden = true
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -60,15 +62,14 @@ public final class AvatarVideoNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var didAppear = false
|
||||||
|
|
||||||
private func setupAnimation() {
|
private func setupAnimation() {
|
||||||
guard let animationFile = self.animationFile else {
|
guard let animationFile = self.animationFile else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemNativeFitSize = CGSize(width: 128.0, height: 128.0)
|
let itemNativeFitSize = self.internalSize.width > 100.0 ? CGSize(width: 256.0, height: 256.0) : 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 animationData = EntityKeyboardAnimationData(file: animationFile)
|
let animationData = EntityKeyboardAnimationData(file: animationFile)
|
||||||
let itemLayer = EmojiPagerContentComponent.View.ItemLayer(
|
let itemLayer = EmojiPagerContentComponent.View.ItemLayer(
|
||||||
@ -89,82 +90,45 @@ public final class AvatarVideoNode: ASDisplayNode {
|
|||||||
blurredBadgeColor: .clear,
|
blurredBadgeColor: .clear,
|
||||||
accentIconColor: .white,
|
accentIconColor: .white,
|
||||||
pointSize: itemNativeFitSize,
|
pointSize: itemNativeFitSize,
|
||||||
onUpdateDisplayPlaceholder: { displayPlaceholder, _ in
|
onUpdateDisplayPlaceholder: { _, _ 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()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
itemLayer.onContentsUpdate = { [weak self] 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.layerTintColor = UIColor.white.cgColor
|
||||||
itemLayer.frame = itemFrame
|
itemLayer.isVisibleForAnimations = self.visibility
|
||||||
itemLayer.isVisibleForAnimations = true
|
|
||||||
self.itemLayer = itemLayer
|
self.itemLayer = itemLayer
|
||||||
self.backgroundNode.layer.addSublayer(itemLayer)
|
self.backgroundNode.layer.addSublayer(itemLayer)
|
||||||
|
|
||||||
|
if let (size, cornerRadius) = self.validLayout {
|
||||||
|
self.updateLayout(size: size, cornerRadius: cornerRadius, transition: .immediate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func update(peer: EnginePeer, photo: TelegramMediaImage, size: CGSize) {
|
public func update(markup: TelegramMediaImage.EmojiMarkup, size: CGSize) {
|
||||||
self.size = size
|
guard markup != self.emojiMarkup else {
|
||||||
if let markup = photo.emojiMarkup {
|
return
|
||||||
if markup != self.emojiMarkup {
|
}
|
||||||
self.emojiMarkup = markup
|
self.emojiMarkup = markup
|
||||||
|
self.internalSize = size
|
||||||
|
|
||||||
let colors = markup.backgroundColors.map { UInt32(bitPattern: $0) }
|
let colors = markup.backgroundColors.map { UInt32(bitPattern: $0) }
|
||||||
let backgroundImage: UIImage
|
|
||||||
if colors.count == 1 {
|
if colors.count == 1 {
|
||||||
backgroundImage = generateSingleColorImage(size: size, color: UIColor(rgb: colors.first!))!
|
backgroundNode.backgroundColor = UIColor(rgb: colors.first!)
|
||||||
|
self.backgroundNode.image = nil
|
||||||
} else if colors.count == 2 {
|
} else if colors.count == 2 {
|
||||||
backgroundImage = generateGradientImage(size: size, colors: colors.map { UIColor(rgb: $0) }, locations: [0.0, 1.0])!
|
self.backgroundNode.image = generateGradientImage(size: size, colors: colors.map { UIColor(rgb: $0) }, locations: [0.0, 1.0])!
|
||||||
} else {
|
} else {
|
||||||
backgroundImage = GradientBackgroundNode.generatePreview(size: size, colors: colors.map { UIColor(rgb: $0) })
|
self.backgroundNode.image = GradientBackgroundNode.generatePreview(size: size, colors: colors.map { UIColor(rgb: $0) })
|
||||||
}
|
}
|
||||||
self.backgroundNode.image = backgroundImage
|
|
||||||
self.backgroundNode.isHidden = true
|
self.backgroundNode.isHidden = true
|
||||||
|
|
||||||
switch markup.content {
|
switch markup.content {
|
||||||
@ -192,6 +156,11 @@ public final class AvatarVideoNode: ASDisplayNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func update(peer: EnginePeer, photo: TelegramMediaImage, size: CGSize) {
|
||||||
|
self.internalSize = size
|
||||||
|
if let markup = photo.emojiMarkup {
|
||||||
|
self.update(markup: markup, size: size)
|
||||||
} else if let video = smallestVideoRepresentation(photo.videoRepresentations), let peerReference = PeerReference(peer._asPeer()) {
|
} else if let video = smallestVideoRepresentation(photo.videoRepresentations), let peerReference = PeerReference(peer._asPeer()) {
|
||||||
self.backgroundNode.image = nil
|
self.backgroundNode.image = nil
|
||||||
|
|
||||||
@ -205,7 +174,9 @@ public final class AvatarVideoNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var visibility = false
|
||||||
public func updateVisibility(_ isVisible: Bool) {
|
public func updateVisibility(_ isVisible: Bool) {
|
||||||
|
self.visibility = isVisible
|
||||||
if isVisible, let videoContent = self.videoContent, self.videoLoopCount != maxVideoLoopCount {
|
if isVisible, let videoContent = self.videoContent, self.videoLoopCount != maxVideoLoopCount {
|
||||||
if self.videoNode == nil {
|
if self.videoNode == nil {
|
||||||
let context = self.context
|
let context = self.context
|
||||||
@ -262,9 +233,11 @@ public final class AvatarVideoNode: ASDisplayNode {
|
|||||||
self.videoNode = nil
|
self.videoNode = nil
|
||||||
videoNode.removeFromSupernode()
|
videoNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
|
self.itemLayer?.isVisibleForAnimations = isVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateLayout(size: CGSize, cornerRadius: CGFloat, transition: ContainedViewLayoutTransition) {
|
public func updateLayout(size: CGSize, cornerRadius: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
self.validLayout = (size, cornerRadius)
|
||||||
self.layer.cornerRadius = cornerRadius
|
self.layer.cornerRadius = cornerRadius
|
||||||
|
|
||||||
self.backgroundNode.frame = CGRect(origin: .zero, size: size)
|
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.frame = CGRect(origin: .zero, size: size)
|
||||||
videoNode.updateLayout(size: size, transition: transition)
|
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() {
|
public func resetPlayback() {
|
||||||
|
|||||||
@ -1710,7 +1710,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let hasUsername = groupType.hasUsername {
|
if let hasUsername = groupType.hasUsername {
|
||||||
if hasUsername != (channel.addressName != nil) {
|
if hasUsername != (!(channel.addressName ?? "").isEmpty) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1738,8 +1738,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let hasUsername = channelType.hasUsername, hasUsername {
|
if let hasUsername = channelType.hasUsername {
|
||||||
if hasUsername != (channel.addressName != nil) {
|
if hasUsername != (!(channel.addressName ?? "").isEmpty) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1802,7 +1802,7 @@ public final class ChatListNode: ListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let hasUsername = groupType.hasUsername {
|
if let hasUsername = groupType.hasUsername {
|
||||||
if hasUsername != (channel.addressName != nil) {
|
if hasUsername != (!(channel.addressName ?? "").isEmpty) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1831,8 +1831,8 @@ public final class ChatListNode: ListView {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let hasUsername = channelType.hasUsername, hasUsername {
|
if let hasUsername = channelType.hasUsername {
|
||||||
if hasUsername != (channel.addressName != nil) {
|
if hasUsername != (!(channel.addressName ?? "").isEmpty) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,6 +65,11 @@ public struct ContextMenuActionItemIconSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ContextMenuActionItemIconPosition {
|
||||||
|
case left
|
||||||
|
case right
|
||||||
|
}
|
||||||
|
|
||||||
public enum ContextMenuActionBadgeColor {
|
public enum ContextMenuActionBadgeColor {
|
||||||
case accent
|
case accent
|
||||||
case inactive
|
case inactive
|
||||||
@ -102,6 +107,7 @@ public final class ContextMenuActionItem {
|
|||||||
public let badge: ContextMenuActionBadge?
|
public let badge: ContextMenuActionBadge?
|
||||||
public let icon: (PresentationTheme) -> UIImage?
|
public let icon: (PresentationTheme) -> UIImage?
|
||||||
public let iconSource: ContextMenuActionItemIconSource?
|
public let iconSource: ContextMenuActionItemIconSource?
|
||||||
|
public let iconPosition: ContextMenuActionItemIconPosition
|
||||||
public let animationName: String?
|
public let animationName: String?
|
||||||
public let textIcon: (PresentationTheme) -> UIImage?
|
public let textIcon: (PresentationTheme) -> UIImage?
|
||||||
public let textLinkAction: () -> Void
|
public let textLinkAction: () -> Void
|
||||||
@ -117,6 +123,7 @@ public final class ContextMenuActionItem {
|
|||||||
badge: ContextMenuActionBadge? = nil,
|
badge: ContextMenuActionBadge? = nil,
|
||||||
icon: @escaping (PresentationTheme) -> UIImage?,
|
icon: @escaping (PresentationTheme) -> UIImage?,
|
||||||
iconSource: ContextMenuActionItemIconSource? = nil,
|
iconSource: ContextMenuActionItemIconSource? = nil,
|
||||||
|
iconPosition: ContextMenuActionItemIconPosition = .right,
|
||||||
animationName: String? = nil,
|
animationName: String? = nil,
|
||||||
textIcon: @escaping (PresentationTheme) -> UIImage? = { _ in return nil },
|
textIcon: @escaping (PresentationTheme) -> UIImage? = { _ in return nil },
|
||||||
textLinkAction: @escaping () -> Void = {},
|
textLinkAction: @escaping () -> Void = {},
|
||||||
@ -132,6 +139,7 @@ public final class ContextMenuActionItem {
|
|||||||
badge: badge,
|
badge: badge,
|
||||||
icon: icon,
|
icon: icon,
|
||||||
iconSource: iconSource,
|
iconSource: iconSource,
|
||||||
|
iconPosition: iconPosition,
|
||||||
animationName: animationName,
|
animationName: animationName,
|
||||||
textIcon: textIcon,
|
textIcon: textIcon,
|
||||||
textLinkAction: textLinkAction,
|
textLinkAction: textLinkAction,
|
||||||
@ -153,6 +161,7 @@ public final class ContextMenuActionItem {
|
|||||||
badge: ContextMenuActionBadge? = nil,
|
badge: ContextMenuActionBadge? = nil,
|
||||||
icon: @escaping (PresentationTheme) -> UIImage?,
|
icon: @escaping (PresentationTheme) -> UIImage?,
|
||||||
iconSource: ContextMenuActionItemIconSource? = nil,
|
iconSource: ContextMenuActionItemIconSource? = nil,
|
||||||
|
iconPosition: ContextMenuActionItemIconPosition = .right,
|
||||||
animationName: String? = nil,
|
animationName: String? = nil,
|
||||||
textIcon: @escaping (PresentationTheme) -> UIImage? = { _ in return nil },
|
textIcon: @escaping (PresentationTheme) -> UIImage? = { _ in return nil },
|
||||||
textLinkAction: @escaping () -> Void = {},
|
textLinkAction: @escaping () -> Void = {},
|
||||||
@ -167,6 +176,7 @@ public final class ContextMenuActionItem {
|
|||||||
self.badge = badge
|
self.badge = badge
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
self.iconSource = iconSource
|
self.iconSource = iconSource
|
||||||
|
self.iconPosition = iconPosition
|
||||||
self.animationName = animationName
|
self.animationName = animationName
|
||||||
self.textIcon = textIcon
|
self.textIcon = textIcon
|
||||||
self.textLinkAction = textLinkAction
|
self.textLinkAction = textLinkAction
|
||||||
|
|||||||
@ -549,8 +549,11 @@ public class Window1 {
|
|||||||
|
|
||||||
var keyboardHeight: CGFloat
|
var keyboardHeight: CGFloat
|
||||||
if keyboardFrame.isEmpty || keyboardFrame.maxY < screenHeight {
|
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)
|
keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY)
|
||||||
|
if inPopover && !keyboardHeight.isZero {
|
||||||
|
keyboardHeight = max(0.0, keyboardHeight - popoverDelta)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
keyboardHeight = 0.0
|
keyboardHeight = 0.0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3014,21 +3014,6 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U
|
|||||||
return TGPaintingData(drawing: drawingData, entitiesData: entitiesData, image: image, stillImage: stillImage, hasAnimation: hasAnimatedEntities, stickers: stickers)
|
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)) {
|
public func animateOut(_ completion: @escaping (() -> Void)) {
|
||||||
self.selectionContainerView.alpha = 0.0
|
self.selectionContainerView.alpha = 0.0
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,8 @@ typedef enum
|
|||||||
@property (nonatomic, readonly) NSTimeInterval trimEndValue;
|
@property (nonatomic, readonly) NSTimeInterval trimEndValue;
|
||||||
@property (nonatomic, readonly) TGMediaVideoConversionPreset preset;
|
@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, readonly) int64_t documentId;
|
||||||
@property (nonatomic, strong, readonly) NSArray *colors;
|
@property (nonatomic, strong, readonly) NSArray *colors;
|
||||||
|
|
||||||
@ -43,6 +45,7 @@ typedef enum
|
|||||||
+ (instancetype)editAdjustmentsWithOriginalSize:(CGSize)originalSize preset:(TGMediaVideoConversionPreset)preset;
|
+ (instancetype)editAdjustmentsWithOriginalSize:(CGSize)originalSize preset:(TGMediaVideoConversionPreset)preset;
|
||||||
+ (instancetype)editAdjustmentsWithPhotoEditorValues:(PGPhotoEditorValues *)values 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 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)editAdjustmentsWithDictionary:(NSDictionary *)dictionary;
|
||||||
|
|
||||||
+ (instancetype)editAdjustmentsWithOriginalSize:(CGSize)originalSize
|
+ (instancetype)editAdjustmentsWithOriginalSize:(CGSize)originalSize
|
||||||
|
|||||||
@ -151,6 +151,29 @@ const NSTimeInterval TGVideoEditMaximumGifDuration = 30.5;
|
|||||||
return adjustments;
|
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
|
- (instancetype)editAdjustmentsWithPreset:(TGMediaVideoConversionPreset)preset maxDuration:(NSTimeInterval)maxDuration
|
||||||
{
|
{
|
||||||
TGVideoEditAdjustments *adjustments = [[[self class] alloc] init];
|
TGVideoEditAdjustments *adjustments = [[[self class] alloc] init];
|
||||||
|
|||||||
@ -25,6 +25,7 @@ swift_library(
|
|||||||
"//submodules/GalleryUI:GalleryUI",
|
"//submodules/GalleryUI:GalleryUI",
|
||||||
"//submodules/MediaPlayer:UniversalMediaPlayer",
|
"//submodules/MediaPlayer:UniversalMediaPlayer",
|
||||||
"//submodules/AccountContext:AccountContext",
|
"//submodules/AccountContext:AccountContext",
|
||||||
|
"//submodules/AvatarVideoNode:AvatarVideoNode",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
150
submodules/PremiumUI/Sources/HelloView.swift
Normal file
150
submodules/PremiumUI/Sources/HelloView.swift
Normal file
@ -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<Int>()
|
||||||
|
private var activePositions = Set<Int>()
|
||||||
|
|
||||||
|
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<Int> {
|
||||||
|
var ids = Set<Int>()
|
||||||
|
for i in 0 ..< phrases.count {
|
||||||
|
ids.insert(i)
|
||||||
|
}
|
||||||
|
for id in self.activePhrases {
|
||||||
|
ids.remove(id)
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
func availablePositionIds() -> Set<Int> {
|
||||||
|
var ids = Set<Int>()
|
||||||
|
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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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) {
|
override init(frame: CGRect) {
|
||||||
self.contentContainerView = UIView()
|
self.contentContainerView = UIView()
|
||||||
self.contentContainerView.clipsToBounds = true
|
self.contentContainerView.clipsToBounds = true
|
||||||
@ -355,21 +370,30 @@ final class PhoneDemoComponent: Component {
|
|||||||
case fasterStars
|
case fasterStars
|
||||||
case badgeStars
|
case badgeStars
|
||||||
case emoji
|
case emoji
|
||||||
|
case hello
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Model {
|
||||||
|
case notch
|
||||||
|
case island
|
||||||
}
|
}
|
||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let position: Position
|
let position: Position
|
||||||
|
let model: Model
|
||||||
let videoFile: TelegramMediaFile?
|
let videoFile: TelegramMediaFile?
|
||||||
let decoration: BackgroundDecoration
|
let decoration: BackgroundDecoration
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
position: PhoneDemoComponent.Position,
|
position: PhoneDemoComponent.Position,
|
||||||
|
model: Model = .notch,
|
||||||
videoFile: TelegramMediaFile?,
|
videoFile: TelegramMediaFile?,
|
||||||
decoration: BackgroundDecoration = .none
|
decoration: BackgroundDecoration = .none
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.position = position
|
self.position = position
|
||||||
|
self.model = model
|
||||||
self.videoFile = videoFile
|
self.videoFile = videoFile
|
||||||
self.decoration = decoration
|
self.decoration = decoration
|
||||||
}
|
}
|
||||||
@ -381,6 +405,9 @@ final class PhoneDemoComponent: Component {
|
|||||||
if lhs.position != rhs.position {
|
if lhs.position != rhs.position {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.model != rhs.model {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.videoFile != rhs.videoFile {
|
if lhs.videoFile != rhs.videoFile {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -452,6 +479,7 @@ final class PhoneDemoComponent: Component {
|
|||||||
self.containerView.frame = CGRect(origin: .zero, size: availableSize)
|
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.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.bounds = CGRect(origin: .zero, size: phoneSize)
|
||||||
|
self.phoneView.model = component.model
|
||||||
|
|
||||||
switch component.decoration {
|
switch component.decoration {
|
||||||
case .none:
|
case .none:
|
||||||
@ -504,6 +532,13 @@ final class PhoneDemoComponent: Component {
|
|||||||
self.decorationView = starsView
|
self.decorationView = starsView
|
||||||
self.decorationContainerView.addSubview(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)
|
self.phoneView.setup(context: component.context, videoFile: component.videoFile, position: component.position)
|
||||||
|
|||||||
@ -909,9 +909,12 @@ private final class DemoSheetContent: CombinedComponent {
|
|||||||
id: PremiumDemoScreen.Subject.translation,
|
id: PremiumDemoScreen.Subject.translation,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
PageComponent(
|
PageComponent(
|
||||||
content: AnyComponent(AppIconsDemoComponent(
|
content: AnyComponent(PhoneDemoComponent(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
appIcons: appIcons
|
position: .top,
|
||||||
|
model: .island,
|
||||||
|
videoFile: configuration.videos["translations"],
|
||||||
|
decoration: .hello
|
||||||
)),
|
)),
|
||||||
title: strings.Premium_Translation,
|
title: strings.Premium_Translation,
|
||||||
text: isStandalone ? strings.Premium_TranslationStandaloneInfo : strings.Premium_TranslationInfo,
|
text: isStandalone ? strings.Premium_TranslationStandaloneInfo : strings.Premium_TranslationInfo,
|
||||||
|
|||||||
@ -250,7 +250,7 @@ public enum PremiumSource: Equatable {
|
|||||||
return "deeplink"
|
return "deeplink"
|
||||||
}
|
}
|
||||||
case .translation:
|
case .translation:
|
||||||
return "translation"
|
return "translations"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,7 +329,7 @@ enum PremiumPerk: CaseIterable {
|
|||||||
case .emojiStatus:
|
case .emojiStatus:
|
||||||
return "emoji_status"
|
return "emoji_status"
|
||||||
case .translation:
|
case .translation:
|
||||||
return "translation"
|
return "translations"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,7 +428,7 @@ enum PremiumPerk: CaseIterable {
|
|||||||
case .emojiStatus:
|
case .emojiStatus:
|
||||||
return "Premium/Perk/Status"
|
return "Premium/Perk/Status"
|
||||||
case .translation:
|
case .translation:
|
||||||
return "Premium/Perk/Status"
|
return "Premium/Perk/Translation"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1492,7 +1492,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
UIColor(rgb: 0x5A6EEE),
|
UIColor(rgb: 0x5A6EEE),
|
||||||
UIColor(rgb: 0x548DFF),
|
UIColor(rgb: 0x548DFF),
|
||||||
UIColor(rgb: 0x54A3FF),
|
UIColor(rgb: 0x54A3FF),
|
||||||
UIColor(rgb: 0x54bdff)
|
UIColor(rgb: 0x54bdff),
|
||||||
|
UIColor(rgb: 0x71c8ff)
|
||||||
]
|
]
|
||||||
|
|
||||||
let accountContext = context.component.context
|
let accountContext = context.component.context
|
||||||
|
|||||||
@ -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 {
|
if let order = controller.order {
|
||||||
var items: [DemoPagerComponent.Item] = order.compactMap { availableItems[$0] }
|
var items: [DemoPagerComponent.Item] = order.compactMap { availableItems[$0] }
|
||||||
let index: Int
|
let index: Int
|
||||||
|
|||||||
@ -70,45 +70,6 @@ final class SwirlStarsView: UIView, PhoneDemoDecorationView {
|
|||||||
animation.fillMode = .forwards
|
animation.fillMode = .forwards
|
||||||
animation.repeatCount = .infinity
|
animation.repeatCount = .infinity
|
||||||
node.addAnimation(animation, forKey: "rotation")
|
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() {
|
func resetAnimation() {
|
||||||
@ -120,85 +81,3 @@ final class SwirlStarsView: UIView, PhoneDemoDecorationView {
|
|||||||
self.sceneView.frame = CGRect(origin: .zero, size: frame.size)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -496,7 +496,9 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if showTranslate || translateChats {
|
||||||
entries.append(.doNotTranslate(text: presentationData.strings.Localization_DoNotTranslate, value: value))
|
entries.append(.doNotTranslate(text: presentationData.strings.Localization_DoNotTranslate, value: value))
|
||||||
|
}
|
||||||
|
|
||||||
if showTranslate {
|
if showTranslate {
|
||||||
entries.append(.translateInfo(text: ignoredLanguages.count > 1 ? presentationData.strings.Localization_DoNotTranslateManyInfo : presentationData.strings.Localization_DoNotTranslateInfo))
|
entries.append(.translateInfo(text: ignoredLanguages.count > 1 ? presentationData.strings.Localization_DoNotTranslateManyInfo : presentationData.strings.Localization_DoNotTranslateInfo))
|
||||||
|
|||||||
@ -199,6 +199,9 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
|||||||
@objc internal func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
@objc internal func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||||
var updated = textField.text ?? ""
|
var updated = textField.text ?? ""
|
||||||
updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string)
|
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 {
|
if updated.count <= 6 && updated.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil {
|
||||||
textField.text = updated.uppercased()
|
textField.text = updated.uppercased()
|
||||||
textField.textColor = self.theme.chat.inputPanel.inputTextColor
|
textField.textColor = self.theme.chat.inputPanel.inputTextColor
|
||||||
|
|||||||
@ -1291,9 +1291,23 @@ final class AvatarEditorScreenComponent: Component {
|
|||||||
entity.position = CGPoint(x: drawingSize.width / 2.0, y: drawingSize.height / 2.0)
|
entity.position = CGPoint(x: drawingSize.width / 2.0, y: drawingSize.height / 2.0)
|
||||||
entity.scale = 3.3
|
entity.scale = 3.3
|
||||||
|
|
||||||
var documentId: Int64 = 0
|
var fileId: Int64 = 0
|
||||||
if case let .file(file) = entity.content, file.isCustomEmoji {
|
var stickerPackId: Int64 = 0
|
||||||
documentId = file.fileId.id
|
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 }
|
let colors: [NSNumber] = state.selectedBackground.colors.map { Int32(bitPattern: $0) as NSNumber }
|
||||||
@ -1337,9 +1351,15 @@ final class AvatarEditorScreenComponent: Component {
|
|||||||
}, opaque: false)!
|
}, opaque: false)!
|
||||||
|
|
||||||
if entity.isAnimated {
|
if entity.isAnimated {
|
||||||
controller.videoCompletion(combinedImage, tempUrl, TGVideoEditAdjustments(photoEditorValues: adjustments, preset: preset, documentId: documentId, colors: colors), { [weak controller] in
|
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()
|
controller?.dismiss()
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
controller.videoCompletion(combinedImage, tempUrl, TGVideoEditAdjustments(photoEditorValues: adjustments, preset: preset, documentId: fileId, colors: colors), { [weak controller] in
|
||||||
|
controller?.dismiss()
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
controller.imageCompletion(combinedImage, { [weak controller] in
|
controller.imageCompletion(combinedImage, { [weak controller] in
|
||||||
controller?.dismiss()
|
controller?.dismiss()
|
||||||
@ -1436,6 +1456,16 @@ public final class AvatarEditorScreen: ViewControllerComponentContainer {
|
|||||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView())
|
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView())
|
||||||
|
|
||||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
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) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
|
|||||||
@ -1140,6 +1140,9 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
|||||||
@objc internal func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
@objc internal func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||||
var updated = textField.text ?? ""
|
var updated = textField.text ?? ""
|
||||||
updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string)
|
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 {
|
if updated.count <= 6 && updated.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil {
|
||||||
textField.text = updated.uppercased()
|
textField.text = updated.uppercased()
|
||||||
textField.textColor = self.theme.chat.inputPanel.inputTextColor
|
textField.textColor = self.theme.chat.inputPanel.inputTextColor
|
||||||
|
|||||||
@ -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 {
|
guard let _ = self.component, let _ = self.pagerEnvironment, let itemLayout = self.itemLayout else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Translation.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/Translation.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "translatepremium.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
125
submodules/TelegramUI/Images.xcassets/Premium/Perk/Translation.imageset/translatepremium.pdf
vendored
Normal file
125
submodules/TelegramUI/Images.xcassets/Premium/Perk/Translation.imageset/translatepremium.pdf
vendored
Normal file
@ -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
|
||||||
@ -1944,9 +1944,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
if let dismissedTranslationPanelNode = dismissedTranslationPanelNode {
|
if let dismissedTranslationPanelNode = dismissedTranslationPanelNode {
|
||||||
var dismissedPanelFrame = dismissedTranslationPanelNode.frame
|
var dismissedPanelFrame = dismissedTranslationPanelNode.frame
|
||||||
dismissedPanelFrame.origin.y = -dismissedPanelFrame.size.height
|
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?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
|
dismissedTranslationPanelNode.animateOut()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let dismissedImportStatusPanelNode = dismissedImportStatusPanelNode {
|
if let dismissedImportStatusPanelNode = dismissedImportStatusPanelNode {
|
||||||
|
|||||||
@ -1249,7 +1249,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
parentMessage: item.message,
|
parentMessage: item.message,
|
||||||
constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude),
|
constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude),
|
||||||
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
||||||
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
|
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer,
|
||||||
|
associatedData: item.associatedData
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2011,7 +2011,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
parentMessage: item.message,
|
parentMessage: item.message,
|
||||||
constrainedSize: CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude),
|
constrainedSize: CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude),
|
||||||
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
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) })
|
replyInfoSizeApply = (sizeAndApply.0, { synchronousLoads in sizeAndApply.1(synchronousLoads) })
|
||||||
|
|
||||||
|
|||||||
@ -469,7 +469,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
parentMessage: item.message,
|
parentMessage: item.message,
|
||||||
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
|
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
|
||||||
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
||||||
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
|
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer,
|
||||||
|
associatedData: item.associatedData
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
} else if let _ = attribute as? InlineBotMessageAttribute {
|
} else if let _ = attribute as? InlineBotMessageAttribute {
|
||||||
|
|||||||
@ -350,7 +350,8 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
parentMessage: item.message,
|
parentMessage: item.message,
|
||||||
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
|
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
|
||||||
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
||||||
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
|
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer,
|
||||||
|
associatedData: item.associatedData
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
let constrainedSize: CGSize
|
let constrainedSize: CGSize
|
||||||
let animationCache: AnimationCache?
|
let animationCache: AnimationCache?
|
||||||
let animationRenderer: MultiAnimationRenderer?
|
let animationRenderer: MultiAnimationRenderer?
|
||||||
|
let associatedData: ChatMessageItemAssociatedData
|
||||||
|
|
||||||
init(
|
init(
|
||||||
presentationData: ChatPresentationData,
|
presentationData: ChatPresentationData,
|
||||||
@ -42,7 +43,8 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
parentMessage: Message,
|
parentMessage: Message,
|
||||||
constrainedSize: CGSize,
|
constrainedSize: CGSize,
|
||||||
animationCache: AnimationCache?,
|
animationCache: AnimationCache?,
|
||||||
animationRenderer: MultiAnimationRenderer?
|
animationRenderer: MultiAnimationRenderer?,
|
||||||
|
associatedData: ChatMessageItemAssociatedData
|
||||||
) {
|
) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
@ -53,6 +55,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
self.constrainedSize = constrainedSize
|
self.constrainedSize = constrainedSize
|
||||||
self.animationCache = animationCache
|
self.animationCache = animationCache
|
||||||
self.animationRenderer = animationRenderer
|
self.animationRenderer = animationRenderer
|
||||||
|
self.associatedData = associatedData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +168,20 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
|
|
||||||
let messageText: NSAttributedString
|
let messageText: NSAttributedString
|
||||||
if isText {
|
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 {
|
if case .Spoiler = entity.type {
|
||||||
return true
|
return true
|
||||||
} else if case .CustomEmoji = entity.type {
|
} else if case .CustomEmoji = entity.type {
|
||||||
@ -175,9 +191,9 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if entities.count > 0 {
|
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 {
|
} else {
|
||||||
messageText = NSAttributedString(string: textString.string, font: textFont, textColor: textColor)
|
messageText = NSAttributedString(string: text, font: textFont, textColor: textColor)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageText = NSAttributedString(string: textString.string, font: textFont, textColor: textColor)
|
messageText = NSAttributedString(string: textString.string, font: textFont, textColor: textColor)
|
||||||
|
|||||||
@ -677,7 +677,8 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
parentMessage: item.message,
|
parentMessage: item.message,
|
||||||
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
|
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
|
||||||
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
||||||
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
|
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer,
|
||||||
|
associatedData: item.associatedData
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,6 +48,8 @@ final class ChatTranslationPanelNode: ASDisplayNode {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.clipsToBounds = true
|
||||||
|
|
||||||
self.addSubnode(self.separatorNode)
|
self.addSubnode(self.separatorNode)
|
||||||
self.addSubnode(self.button)
|
self.addSubnode(self.button)
|
||||||
self.addSubnode(self.moreButton)
|
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 {
|
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> CGFloat {
|
||||||
let previousIsEnabled = self.chatInterfaceState?.translationState?.isEnabled
|
let previousIsEnabled = self.chatInterfaceState?.translationState?.isEnabled
|
||||||
self.chatInterfaceState = interfaceState
|
self.chatInterfaceState = interfaceState
|
||||||
@ -123,13 +129,14 @@ final class ChatTranslationPanelNode: ASDisplayNode {
|
|||||||
let moreButtonSize = self.moreButton.measure(CGSize(width: 100.0, height: panelHeight))
|
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)
|
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 buttonSpacing: CGFloat = 10.0
|
||||||
let buttonTextSize = self.buttonTextNode.updateLayout(CGSize(width: width - contentRightInset - moreButtonSize.width, height: panelHeight))
|
let buttonTextSize = self.buttonTextNode.updateLayout(CGSize(width: width - contentRightInset - moreButtonSize.width, height: panelHeight))
|
||||||
if let icon = self.buttonIconNode.image {
|
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.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.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: icon.size.width + buttonSpacing, y: floorToScreenPixels((buttonSize.height - buttonTextSize.height) / 2.0)), size: buttonTextSize)
|
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)))
|
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel)))
|
||||||
|
|||||||
@ -7092,10 +7092,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
let isSettings = self.isSettings
|
let isSettings = self.isSettings
|
||||||
self.updateAvatarDisposable.set((signal
|
self.updateAvatarDisposable.set((signal
|
||||||
|> mapToSignal { videoResource -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> in
|
|> mapToSignal { videoResource -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> in
|
||||||
let markup: UploadPeerPhotoMarkup? = nil
|
var markup: UploadPeerPhotoMarkup? = nil
|
||||||
if isSettings {
|
if isSettings {
|
||||||
// let fileId = adjustments?.documentId
|
if let fileId = adjustments?.documentId, let backgroundColors = adjustments?.colors as? [Int32], fileId != 0 {
|
||||||
// let backgroundColors = adjustments?.colors as? [Int32]
|
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 {
|
if case .fallback = mode {
|
||||||
return context.engine.accountData.updateFallbackPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in
|
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)
|
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
|
||||||
|
|||||||
@ -73,7 +73,7 @@ private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {
|
|||||||
case visualMediaStoredState = 5
|
case visualMediaStoredState = 5
|
||||||
case cachedImageRecognizedContent = 6
|
case cachedImageRecognizedContent = 6
|
||||||
case pendingInAppPurchaseState = 7
|
case pendingInAppPurchaseState = 7
|
||||||
case translationState = 8
|
case translationState = 9
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ApplicationSpecificItemCacheCollectionId {
|
public struct ApplicationSpecificItemCacheCollectionId {
|
||||||
|
|||||||
@ -114,7 +114,17 @@ public func translateMessageIds(context: AccountContext, messageIds: [EngineMess
|
|||||||
return context.account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
return context.account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||||
var messageIdsToTranslate: [EngineMessage.Id] = []
|
var messageIdsToTranslate: [EngineMessage.Id] = []
|
||||||
for messageId in messageIds {
|
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 {
|
} else {
|
||||||
messageIdsToTranslate.append(messageId)
|
messageIdsToTranslate.append(messageId)
|
||||||
}
|
}
|
||||||
@ -151,7 +161,7 @@ public func chatTranslationState(context: AccountContext, peerId: EnginePeer.Id)
|
|||||||
} else {
|
} else {
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
|> then(
|
|> 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
|
|> filter { messageHistoryView -> Bool in
|
||||||
return messageHistoryView.0.entries.count > 1
|
return messageHistoryView.0.entries.count > 1
|
||||||
}
|
}
|
||||||
@ -168,7 +178,7 @@ public func chatTranslationState(context: AccountContext, peerId: EnginePeer.Id)
|
|||||||
if message.text.count > 10 {
|
if message.text.count > 10 {
|
||||||
let text = String(message.text.prefix(100))
|
let text = String(message.text.prefix(100))
|
||||||
languageRecognizer.processString(text)
|
languageRecognizer.processString(text)
|
||||||
let hypotheses = languageRecognizer.languageHypotheses(withMaximum: 3)
|
let hypotheses = languageRecognizer.languageHypotheses(withMaximum: 4)
|
||||||
languageRecognizer.reset()
|
languageRecognizer.reset()
|
||||||
|
|
||||||
let filteredLanguages = hypotheses.filter { supportedTranslationLanguages.contains($0.key.rawValue) }.sorted(by: { $0.value > $1.value })
|
let filteredLanguages = hypotheses.filter { supportedTranslationLanguages.contains($0.key.rawValue) }.sorted(by: { $0.value > $1.value })
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user