mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Merge branch 'master' into gradient-messages
This commit is contained in:
commit
ab15d242ad
@ -326,14 +326,12 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
|
||||
|> introduceError(IntentHandlingError.self)
|
||||
|> mapToSignal { account -> Signal<[INMessage], IntentHandlingError> in
|
||||
account.shouldBeServiceTaskMaster.set(.single(.now))
|
||||
let completedUpdating: Signal<Bool, IntentHandlingError> = (.single(true) |> then(account.stateManager.isUpdating))
|
||||
|> introduceError(IntentHandlingError.self)
|
||||
|> filter { !$0 }
|
||||
|> take(1)
|
||||
|> timeout(3.0, queue: Queue.mainQueue(), alternate: .fail(.generic))
|
||||
account.resetStateManagement()
|
||||
|
||||
return completedUpdating
|
||||
|> mapToSignal { value -> Signal<[INMessage], IntentHandlingError> in
|
||||
return account.stateManager.pollStateUpdateCompletion()
|
||||
|> introduceError(IntentHandlingError.self)
|
||||
|> take(1)
|
||||
|> mapToSignal { _ -> Signal<[INMessage], IntentHandlingError> in
|
||||
return unreadMessages(account: account)
|
||||
|> introduceError(IntentHandlingError.self)
|
||||
}
|
||||
|
||||
@ -79,11 +79,14 @@
|
||||
buildConfiguration = "DebugHockeyapp"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableAddressSanitizer = "YES"
|
||||
enableASanStackUseAfterReturn = "YES"
|
||||
enableUBSanitizer = "YES"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
stopOnEveryUBSanitizerIssue = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
queueDebuggingEnabled = "No">
|
||||
|
||||
@ -15,7 +15,7 @@ public let deviceColorSpace: CGColorSpace = {
|
||||
|
||||
let deviceScale = UIScreen.main.scale
|
||||
|
||||
public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (CGSize, UnsafeMutablePointer<UInt8>) -> Void) -> UIImage? {
|
||||
public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (CGSize, UnsafeMutablePointer<UInt8>, Int) -> Void) -> UIImage? {
|
||||
let scaledSize = CGSize(width: size.width * scale, height: size.height * scale)
|
||||
let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
|
||||
let length = bytesPerRow * Int(scaledSize.height)
|
||||
@ -27,7 +27,7 @@ public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (
|
||||
return nil
|
||||
}
|
||||
|
||||
pixelGenerator(scaledSize, bytes)
|
||||
pixelGenerator(scaledSize, bytes, bytesPerRow)
|
||||
|
||||
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@ final class MessageHistoryMetadataTable: Table {
|
||||
|
||||
private func chatListGroupInitializedKey(_ key: InitializedChatListKey) -> ValueBoxKey {
|
||||
self.sharedChatListGroupHistoryInitializedKey.setInt32(0, value: key.groupId.rawValue)
|
||||
self.sharedChatListGroupHistoryInitializedKey.setInt8(8, value: MetadataPrefix.ChatListGroupInitialized.rawValue)
|
||||
self.sharedChatListGroupHistoryInitializedKey.setInt8(4, value: MetadataPrefix.ChatListGroupInitialized.rawValue)
|
||||
return self.sharedChatListGroupHistoryInitializedKey
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
@property (nonatomic, readonly) CGSize dimensions;
|
||||
|
||||
- (instancetype _Nullable)initWithData:(NSData * _Nonnull)data cacheKey:(NSString * _Nonnull)cacheKey;
|
||||
- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height;
|
||||
- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height bytesPerRow:(int32_t)bytesPerRow;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -38,8 +38,8 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height {
|
||||
rlottie::Surface surface((uint32_t *)buffer, width, height, width * 4);
|
||||
- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height bytesPerRow:(int32_t) bytesPerRow{
|
||||
rlottie::Surface surface((uint32_t *)buffer, width, height, bytesPerRow);
|
||||
_animation->renderSync(index, surface);
|
||||
}
|
||||
|
||||
|
||||
@ -82,13 +82,15 @@ private final class AnimatedStickerFrame {
|
||||
let type: AnimationRendererFrameType
|
||||
let width: Int
|
||||
let height: Int
|
||||
let bytesPerRow: Int
|
||||
let isLastFrame: Bool
|
||||
|
||||
init(data: Data, type: AnimationRendererFrameType, width: Int, height: Int, isLastFrame: Bool) {
|
||||
init(data: Data, type: AnimationRendererFrameType, width: Int, height: Int, bytesPerRow: Int, isLastFrame: Bool) {
|
||||
self.data = data
|
||||
self.type = type
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.bytesPerRow = bytesPerRow
|
||||
self.isLastFrame = isLastFrame
|
||||
}
|
||||
}
|
||||
@ -105,6 +107,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
||||
private let data: Data
|
||||
private var scratchBuffer: Data
|
||||
let width: Int
|
||||
let bytesPerRow: Int
|
||||
let height: Int
|
||||
let frameRate: Int
|
||||
private let initialOffset: Int
|
||||
@ -120,6 +123,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
||||
var offset = 0
|
||||
var width = 0
|
||||
var height = 0
|
||||
var bytesPerRow = 0
|
||||
var frameRate = 0
|
||||
|
||||
if !self.data.withUnsafeBytes({ (bytes: UnsafePointer<UInt8>) -> Bool in
|
||||
@ -129,19 +133,23 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
||||
offset += 4
|
||||
var widthValue: Int32 = 0
|
||||
var heightValue: Int32 = 0
|
||||
var bytesPerRowValue: Int32 = 0
|
||||
memcpy(&widthValue, bytes.advanced(by: offset), 4)
|
||||
offset += 4
|
||||
memcpy(&heightValue, bytes.advanced(by: offset), 4)
|
||||
offset += 4
|
||||
memcpy(&bytesPerRowValue, bytes.advanced(by: offset), 4)
|
||||
offset += 4
|
||||
width = Int(widthValue)
|
||||
height = Int(heightValue)
|
||||
bytesPerRow = Int(bytesPerRowValue)
|
||||
|
||||
return true
|
||||
}) {
|
||||
return nil
|
||||
}
|
||||
|
||||
assert(width % 16 == 0)
|
||||
self.bytesPerRow = bytesPerRow
|
||||
|
||||
self.width = width
|
||||
self.height = height
|
||||
@ -150,8 +158,8 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
||||
self.initialOffset = offset
|
||||
self.offset = offset
|
||||
|
||||
self.decodeBuffer = Data(count: width * 4 * height)
|
||||
self.frameBuffer = Data(count: width * 4 * height)
|
||||
self.decodeBuffer = Data(count: self.bytesPerRow * height)
|
||||
self.frameBuffer = Data(count: self.bytesPerRow * height)
|
||||
let frameBufferLength = self.frameBuffer.count
|
||||
self.frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
|
||||
memset(bytes, 0, frameBufferLength)
|
||||
@ -203,7 +211,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
||||
}
|
||||
}
|
||||
|
||||
return AnimatedStickerFrame(data: frameData!, type: .yuva, width: self.width, height: self.height, isLastFrame: isLastFrame)
|
||||
return AnimatedStickerFrame(data: frameData!, type: .yuva, width: self.width, height: self.height, bytesPerRow: self.bytesPerRow, isLastFrame: isLastFrame)
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,6 +220,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
||||
private let data: Data
|
||||
private let width: Int
|
||||
private let height: Int
|
||||
private let bytesPerRow: Int
|
||||
private let frameCount: Int
|
||||
let frameRate: Int
|
||||
private var currentFrame: Int
|
||||
@ -222,6 +231,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
||||
self.data = data
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.bytesPerRow = (4 * Int(width) + 15) & (~15)
|
||||
self.currentFrame = 0
|
||||
guard let rawData = TGGUnzipData(data, 8 * 1024 * 1024) else {
|
||||
return nil
|
||||
@ -232,7 +242,6 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
||||
self.animation = animation
|
||||
self.frameCount = Int(animation.frameCount)
|
||||
self.frameRate = Int(animation.frameRate)
|
||||
assert(width % 16 == 0)
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -242,12 +251,12 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
||||
func takeFrame() -> AnimatedStickerFrame {
|
||||
let frameIndex = self.currentFrame % self.frameCount
|
||||
self.currentFrame += 1
|
||||
var frameData = Data(count: self.width * self.height * 4)
|
||||
var frameData = Data(count: self.bytesPerRow * self.height)
|
||||
frameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
|
||||
memset(bytes, 0, self.width * self.height * 4)
|
||||
self.animation.renderFrame(with: Int32(frameIndex), into: bytes, width: Int32(self.width), height: Int32(self.height))
|
||||
memset(bytes, 0, self.bytesPerRow * self.height)
|
||||
self.animation.renderFrame(with: Int32(frameIndex), into: bytes, width: Int32(self.width), height: Int32(self.height), bytesPerRow: Int32(self.bytesPerRow))
|
||||
}
|
||||
return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height, isLastFrame: frameIndex == self.frameCount)
|
||||
return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height, bytesPerRow: self.bytesPerRow, isLastFrame: frameIndex == self.frameCount)
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,6 +363,9 @@ final class AnimatedStickerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func setup(account: Account, resource: MediaResource, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode = .loop, mode: AnimatedStickerMode) {
|
||||
if width < 2 || height < 2 {
|
||||
return
|
||||
}
|
||||
self.playbackMode = playbackMode
|
||||
switch mode {
|
||||
case .direct:
|
||||
@ -430,7 +442,7 @@ final class AnimatedStickerNode: ASDisplayNode {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, data: frame.data, type: frame.type, completion: {
|
||||
strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, completion: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -83,10 +83,12 @@ func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, cacheKey: St
|
||||
}
|
||||
|
||||
let context = DrawingContext(size: size, scale: 1.0, clear: true)
|
||||
player.renderFrame(with: 0, into: context.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height))
|
||||
player.renderFrame(with: 0, into: context.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height), bytesPerRow: Int32(context.bytesPerRow))
|
||||
|
||||
let yuvaLength = Int(size.width) * Int(size.height) * 2 + Int(size.width) * Int(size.height) / 2
|
||||
assert(yuvaLength % 8 == 0)
|
||||
let yuvaPixelsPerAlphaRow = (Int(size.width) + 1) & (~1)
|
||||
assert(yuvaPixelsPerAlphaRow % 2 == 0)
|
||||
|
||||
let yuvaLength = Int(size.width) * Int(size.height) * 2 + yuvaPixelsPerAlphaRow * Int(size.height) / 2
|
||||
var yuvaFrameData = malloc(yuvaLength)!
|
||||
memset(yuvaFrameData, 0, yuvaLength)
|
||||
|
||||
@ -94,8 +96,8 @@ func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, cacheKey: St
|
||||
free(yuvaFrameData)
|
||||
}
|
||||
|
||||
encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height))
|
||||
decodeYUVAToRGBA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height))
|
||||
encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
|
||||
decodeYUVAToRGBA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
|
||||
|
||||
if let colorImage = context.generateImage() {
|
||||
let colorData = NSMutableData()
|
||||
@ -189,23 +191,29 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize,
|
||||
|
||||
let scale = size.width / 512.0
|
||||
|
||||
let bytesPerRow = (4 * Int(size.width) + 15) & (~15)
|
||||
|
||||
var currentFrame: Int32 = 0
|
||||
|
||||
var fps: Int32 = player.frameRate
|
||||
let _ = fileContext.write(&fps, count: 4)
|
||||
var widthValue: Int32 = Int32(size.width)
|
||||
var heightValue: Int32 = Int32(size.height)
|
||||
var bytesPerRowValue: Int32 = Int32(bytesPerRow)
|
||||
let _ = fileContext.write(&widthValue, count: 4)
|
||||
let _ = fileContext.write(&heightValue, count: 4)
|
||||
let _ = fileContext.write(&bytesPerRowValue, count: 4)
|
||||
|
||||
let frameLength = Int(size.width) * Int(size.height) * 4
|
||||
let frameLength = bytesPerRow * Int(size.height)
|
||||
assert(frameLength % 16 == 0)
|
||||
|
||||
let currentFrameData = malloc(frameLength)!
|
||||
memset(currentFrameData, 0, frameLength)
|
||||
|
||||
let yuvaLength = Int(size.width) * Int(size.height) * 2 + Int(size.width) * Int(size.height) / 2
|
||||
assert(yuvaLength % 8 == 0)
|
||||
let yuvaPixelsPerAlphaRow = (Int(size.width) + 1) & (~1)
|
||||
assert(yuvaPixelsPerAlphaRow % 2 == 0)
|
||||
|
||||
let yuvaLength = Int(size.width) * Int(size.height) * 2 + yuvaPixelsPerAlphaRow * Int(size.height) / 2
|
||||
var yuvaFrameData = malloc(yuvaLength)!
|
||||
memset(yuvaFrameData, 0, yuvaLength)
|
||||
|
||||
@ -234,12 +242,12 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize,
|
||||
|
||||
let drawStartTime = CACurrentMediaTime()
|
||||
memset(currentFrameData, 0, frameLength)
|
||||
player.renderFrame(with: Int32(currentFrame), into: currentFrameData.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height))
|
||||
player.renderFrame(with: Int32(currentFrame), into: currentFrameData.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height), bytesPerRow: Int32(bytesPerRow))
|
||||
drawingTime += CACurrentMediaTime() - drawStartTime
|
||||
|
||||
let appendStartTime = CACurrentMediaTime()
|
||||
|
||||
encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), currentFrameData.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height))
|
||||
encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), currentFrameData.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(bytesPerRow))
|
||||
|
||||
appendingTime += CACurrentMediaTime() - appendStartTime
|
||||
|
||||
@ -250,6 +258,9 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize,
|
||||
lhs.pointee = rhs.pointee ^ lhs.pointee
|
||||
lhs = lhs.advanced(by: 1)
|
||||
rhs = rhs.advanced(by: 1)
|
||||
}
|
||||
for i in (yuvaLength / 8) * 8 ..< yuvaLength {
|
||||
|
||||
}
|
||||
deltaTime += CACurrentMediaTime() - deltaStartTime
|
||||
|
||||
|
||||
@ -8,5 +8,5 @@ enum AnimationRendererFrameType {
|
||||
}
|
||||
|
||||
protocol AnimationRenderer {
|
||||
func render(queue: Queue, width: Int, height: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void)
|
||||
func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void)
|
||||
}
|
||||
|
||||
@ -271,7 +271,7 @@ final class CachedAnimatedStickerRepresentation: CachedMediaResourceRepresentati
|
||||
let height: Int32
|
||||
|
||||
var uniqueId: String {
|
||||
return "animated-sticker-\(self.width)x\(self.height)-v5"
|
||||
return "animated-sticker-\(self.width)x\(self.height)-v6"
|
||||
}
|
||||
|
||||
init(width: Int32, height: Int32) {
|
||||
|
||||
@ -240,6 +240,8 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
|
||||
|
||||
private weak var slowmodeTooltipController: ChatSlowmodeHintController?
|
||||
|
||||
private weak var sendMessageActionsController: ChatSendMessageActionSheetController?
|
||||
|
||||
private var screenCaptureEventsDisposable: Disposable?
|
||||
private let chatAdditionalDataDisposable = MetaDisposable()
|
||||
|
||||
@ -3655,6 +3657,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
|
||||
strongSelf.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .all)
|
||||
}
|
||||
})
|
||||
strongSelf.sendMessageActionsController = controller
|
||||
strongSelf.presentInGlobalOverlay(controller, with: nil)
|
||||
}
|
||||
}, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get()))
|
||||
@ -4026,6 +4029,8 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
|
||||
controller.dismissWithCommitAction()
|
||||
}
|
||||
})
|
||||
|
||||
self.sendMessageActionsController?.dismiss()
|
||||
}
|
||||
|
||||
private func saveInterfaceState(includeScrollState: Bool = true) {
|
||||
|
||||
@ -37,9 +37,11 @@ class ChatHistoryNavigationButtonNode: ASControlNode {
|
||||
}
|
||||
|
||||
private var theme: PresentationTheme
|
||||
private let type: ChatHistoryNavigationButtonType
|
||||
|
||||
init(theme: PresentationTheme, type: ChatHistoryNavigationButtonType) {
|
||||
self.theme = theme
|
||||
self.type = type
|
||||
|
||||
self.imageNode = ASImageNode()
|
||||
self.imageNode.displayWithoutProcessing = true
|
||||
@ -77,7 +79,12 @@ class ChatHistoryNavigationButtonNode: ASControlNode {
|
||||
if self.theme !== theme {
|
||||
self.theme = theme
|
||||
|
||||
switch self.type {
|
||||
case .down:
|
||||
self.imageNode.image = PresentationResourcesChat.chatHistoryNavigationButtonImage(theme)
|
||||
case .mentions:
|
||||
self.imageNode.image = PresentationResourcesChat.chatHistoryMentionsButtonImage(theme)
|
||||
}
|
||||
self.badgeBackgroundNode.image = PresentationResourcesChat.chatHistoryNavigationButtonBadgeImage(theme)
|
||||
|
||||
if let string = self.badgeTextNode.attributedText?.string {
|
||||
|
||||
@ -329,6 +329,7 @@ final class ChatMediaInputNodeInteraction {
|
||||
let dismissPeerSpecificSettings: () -> Void
|
||||
let clearRecentlyUsedStickers: () -> Void
|
||||
|
||||
var stickerSettings: ChatInterfaceStickerSettings?
|
||||
var highlightedStickerItemCollectionId: ItemCollectionId?
|
||||
var highlightedItemCollectionId: ItemCollectionId?
|
||||
var previewedStickerPackItem: StickerPreviewPeekItem?
|
||||
@ -651,6 +652,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.inputNodeInteraction.stickerSettings = self.controllerInteraction.stickerSettings
|
||||
|
||||
let previousEntries = Atomic<([ChatMediaInputPanelEntry], [ChatMediaInputGridEntry])>(value: ([], []))
|
||||
|
||||
|
||||
@ -224,7 +224,8 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
||||
}
|
||||
self.addSubnode(animationNode)
|
||||
}
|
||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.account.postbox, file: item.stickerItem.file, small: false, size: CGSize(width: 160.0, height: 160.0)))
|
||||
let dimensions = item.stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.account.postbox, file: item.stickerItem.file, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))))
|
||||
self.updateVisibility()
|
||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: item.stickerItem.file.resource).start())
|
||||
} else {
|
||||
@ -293,7 +294,9 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
||||
self.animationNode?.visibility = isPlaying
|
||||
if let item = self.item, isPlaying, !self.didSetUpAnimationNode {
|
||||
self.didSetUpAnimationNode = true
|
||||
self.animationNode?.setup(account: item.account, resource: item.stickerItem.file.resource, width: 160, height: 160, mode: .cached)
|
||||
let dimensions = item.stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||
self.animationNode?.setup(account: item.account, resource: item.stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,7 +110,8 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
|
||||
private var visibilityStatus: Bool = false {
|
||||
didSet {
|
||||
if self.visibilityStatus != oldValue {
|
||||
self.animatedStickerNode?.visibility = self.visibilityStatus
|
||||
let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false
|
||||
self.animatedStickerNode?.visibility = self.visibilityStatus && loopAnimatedStickers
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -175,11 +176,17 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
|
||||
let imageSize = representation.dimensions.aspectFitted(boundingImageSize)
|
||||
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
|
||||
imageApply()
|
||||
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, representation: representation))
|
||||
|
||||
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: representation.resource))
|
||||
self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
|
||||
case let .animated(resource):
|
||||
let imageSize = boundingImageSize
|
||||
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
|
||||
imageApply()
|
||||
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: resource, animated: true))
|
||||
self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
|
||||
|
||||
let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false
|
||||
self.imageNode.isHidden = loopAnimatedStickers
|
||||
|
||||
let animatedStickerNode: AnimatedStickerNode
|
||||
if let current = self.animatedStickerNode {
|
||||
@ -190,8 +197,8 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
|
||||
animatedStickerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||
self.addSubnode(animatedStickerNode)
|
||||
animatedStickerNode.setup(account: account, resource: resource, width: 80, height: 80, mode: .cached)
|
||||
animatedStickerNode.visibility = self.visibilityStatus
|
||||
}
|
||||
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
|
||||
if let animatedStickerNode = self.animatedStickerNode {
|
||||
animatedStickerNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
|
||||
}
|
||||
|
||||
@ -127,7 +127,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
if let telegramFile = media as? TelegramMediaFile {
|
||||
if self.telegramFile?.id != telegramFile.id {
|
||||
self.telegramFile = telegramFile
|
||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: CGSize(width: 384.0, height: 384.0), thumbnail: false))
|
||||
let dimensions = telegramFile.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0)), thumbnail: false))
|
||||
self.updateVisibility()
|
||||
self.disposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .message(message: MessageReference(item.message), media: telegramFile)).start())
|
||||
}
|
||||
@ -172,7 +173,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
if !item.controllerInteraction.stickerSettings.loopAnimatedStickers {
|
||||
playbackMode = .once
|
||||
}
|
||||
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, playbackMode: playbackMode, mode: .cached)
|
||||
let dimensions = telegramFile.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
let fittedSize = dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0))
|
||||
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: .cached)
|
||||
} else if let emojiResource = self.emojiResource {
|
||||
var playbackMode: AnimatedStickerPlaybackMode = .loop
|
||||
if item.context.sharedContext.immediateExperimentalUISettings.playAnimatedEmojiOnce {
|
||||
|
||||
@ -458,8 +458,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
}
|
||||
} else {
|
||||
if file.isAnimatedSticker {
|
||||
let dimensions = file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
updateImageSignal = { synchronousLoad in
|
||||
return chatMessageAnimatedSticker(postbox: context.account.postbox, file: file, small: false, size: CGSize(width: 400.0, height: 400.0))
|
||||
return chatMessageAnimatedSticker(postbox: context.account.postbox, file: file, small: false, size: dimensions.aspectFitted(CGSize(width: 400.0, height: 400.0)))
|
||||
}
|
||||
} else if file.isSticker {
|
||||
updateImageSignal = { synchronousLoad in
|
||||
@ -685,7 +686,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
strongSelf.imageNode.isHidden = true
|
||||
}
|
||||
strongSelf.animatedStickerNode = animatedStickerNode
|
||||
animatedStickerNode.setup(account: context.account, resource: updatedAnimatedStickerFile.resource, width: 384, height: 384, mode: .cached)
|
||||
let dimensions = updatedAnimatedStickerFile.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0))
|
||||
animatedStickerNode.setup(account: context.account, resource: updatedAnimatedStickerFile.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
strongSelf.insertSubnode(animatedStickerNode, aboveSubnode: strongSelf.imageNode)
|
||||
animatedStickerNode.visibility = strongSelf.visibility
|
||||
}
|
||||
|
||||
@ -217,7 +217,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
updateImageSignal = chatMessagePhotoThumbnail(account: context.account, photoReference: imageReference)
|
||||
} else if let fileReference = updatedMediaReference.concrete(TelegramMediaFile.self) {
|
||||
if fileReference.media.isAnimatedSticker {
|
||||
updateImageSignal = chatMessageAnimatedSticker(postbox: context.account.postbox, file: fileReference.media, small: false, size: CGSize(width: 160.0, height: 160.0))
|
||||
let dimensions = fileReference.media.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
updateImageSignal = chatMessageAnimatedSticker(postbox: context.account.postbox, file: fileReference.media, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)))
|
||||
updatedFetchMediaSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: fileReference.resourceReference(fileReference.media.resource))
|
||||
} else if fileReference.media.isVideo {
|
||||
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
||||
|
||||
@ -55,12 +55,12 @@ final class ChatSendMessageActionSheetController: ViewController {
|
||||
}
|
||||
|
||||
override func loadDisplayNode() {
|
||||
var accessoryPanelNode: AccessoryPanelNode?
|
||||
if let panel = accessoryPanelForChatPresentationIntefaceState(self.interfaceState, context: self.context, currentPanel: nil, interfaceInteraction: nil), panel is ReplyAccessoryPanelNode || panel is ForwardAccessoryPanelNode {
|
||||
accessoryPanelNode = panel
|
||||
var forwardedCount = 0
|
||||
if let forwardMessageIds = self.interfaceState.interfaceState.forwardMessageIds {
|
||||
forwardedCount = forwardMessageIds.count
|
||||
}
|
||||
|
||||
self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, sendButtonFrame: self.sendButtonFrame, textInputNode: self.textInputNode, accessoryPanelNode: accessoryPanelNode, send: { [weak self] in
|
||||
self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, sendButtonFrame: self.sendButtonFrame, textInputNode: self.textInputNode, forwardedCount: forwardedCount, send: { [weak self] in
|
||||
self?.controllerInteraction?.sendCurrentMessage(false)
|
||||
self?.dismiss(cancel: false)
|
||||
}, sendSilently: { [weak self] in
|
||||
|
||||
@ -106,6 +106,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
private let textFieldFrame: CGRect
|
||||
private let textInputNode: EditableTextNode
|
||||
private let accessoryPanelNode: AccessoryPanelNode?
|
||||
private let forwardedCount: Int?
|
||||
|
||||
private let send: (() -> Void)?
|
||||
private let cancel: (() -> Void)?
|
||||
@ -128,13 +129,15 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
init(context: AccountContext, sendButtonFrame: CGRect, textInputNode: EditableTextNode, accessoryPanelNode: AccessoryPanelNode?, send: (() -> Void)?, sendSilently: (() -> Void)?, cancel: (() -> Void)?) {
|
||||
init(context: AccountContext, sendButtonFrame: CGRect, textInputNode: EditableTextNode, forwardedCount: Int?, send: (() -> Void)?, sendSilently: (() -> Void)?, cancel: (() -> Void)?) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.sendButtonFrame = sendButtonFrame
|
||||
self.textFieldFrame = textInputNode.convert(textInputNode.bounds, to: nil)
|
||||
self.textInputNode = textInputNode
|
||||
self.accessoryPanelNode = accessoryPanelNode
|
||||
self.accessoryPanelNode = nil
|
||||
self.forwardedCount = forwardedCount
|
||||
|
||||
self.send = send
|
||||
self.cancel = cancel
|
||||
|
||||
@ -200,12 +203,18 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
self.sendButtonNode.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(self.presentationData.theme), for: [])
|
||||
self.sendButtonNode.addTarget(self, action: #selector(sendButtonPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.fromMessageTextNode.attributedText = textInputNode.attributedText
|
||||
if let attributedText = textInputNode.attributedText, !attributedText.string.isEmpty {
|
||||
self.fromMessageTextNode.attributedText = attributedText
|
||||
|
||||
if let toAttributedText = textInputNode.attributedText?.mutableCopy() as? NSMutableAttributedString {
|
||||
if let toAttributedText = self.fromMessageTextNode.attributedText?.mutableCopy() as? NSMutableAttributedString {
|
||||
toAttributedText.addAttribute(NSAttributedStringKey.foregroundColor, value: self.presentationData.theme.chat.message.outgoing.primaryTextColor, range: NSMakeRange(0, (toAttributedText.string as NSString).length))
|
||||
self.toMessageTextNode.attributedText = toAttributedText
|
||||
}
|
||||
} else {
|
||||
self.fromMessageTextNode.attributedText = NSAttributedString(string: self.presentationData.strings.Conversation_InputTextPlaceholder, attributes: [NSAttributedStringKey.foregroundColor: self.presentationData.theme.chat.inputPanel.inputPlaceholderColor, NSAttributedStringKey.font: Font.regular(self.presentationData.fontSize.baseDisplaySize)])
|
||||
|
||||
self.toMessageTextNode.attributedText = NSAttributedString(string: self.presentationData.strings.ForwardedMessages(Int32(forwardedCount ?? 0)), attributes: [NSAttributedStringKey.foregroundColor: self.presentationData.theme.chat.message.outgoing.primaryTextColor, NSAttributedStringKey.font: Font.regular(self.presentationData.fontSize.baseDisplaySize)])
|
||||
}
|
||||
self.messageBackgroundNode.contentMode = .scaleToFill
|
||||
|
||||
let graphics = PresentationResourcesChat.principalGraphics(context: self.context, theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper)
|
||||
@ -224,7 +233,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
self.messageClipNode.addSubnode(self.toMessageTextNode)
|
||||
|
||||
if let accessoryPanelNode = self.accessoryPanelNode {
|
||||
self.messageClipNode.addSubnode(accessoryPanelNode)
|
||||
self.addSubnode(accessoryPanelNode)
|
||||
}
|
||||
|
||||
self.contentNodes.forEach(self.contentContainerNode.addSubnode)
|
||||
@ -334,7 +343,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
self.sendButtonNode.layer.animateScale(from: 0.75, to: 1.0, duration: 0.2, timingFunction: kCAMediaTimingFunctionLinear)
|
||||
self.sendButtonNode.layer.animatePosition(from: self.sendButtonFrame.center, to: self.sendButtonNode.position, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
|
||||
let initialWidth = self.textFieldFrame.width + 32.0
|
||||
var initialWidth = self.textFieldFrame.width + 32.0
|
||||
if self.textInputNode.textView.attributedText.string.isEmpty {
|
||||
initialWidth = ceil(layout.size.width - self.textFieldFrame.origin.x - self.sendButtonFrame.width - layout.safeInsets.left - layout.safeInsets.right + 21.0)
|
||||
}
|
||||
|
||||
let fromFrame = CGRect(origin: CGPoint(), size: CGSize(width: initialWidth, height: self.textFieldFrame.height + 2.0))
|
||||
let delta = (fromFrame.height - self.messageClipNode.bounds.height) / 2.0
|
||||
|
||||
@ -392,14 +405,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
intermediateCompletion()
|
||||
})
|
||||
|
||||
Queue.mainQueue().after(0.7) {
|
||||
completedAlpha = true
|
||||
completedButton = true
|
||||
completedBubble = true
|
||||
completedEffect = true
|
||||
intermediateCompletion()
|
||||
}
|
||||
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in })
|
||||
|
||||
@ -432,7 +437,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
self.sendButtonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, timingFunction: kCAMediaTimingFunctionLinear, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
let initialWidth = self.textFieldFrame.width + 32.0
|
||||
var initialWidth = self.textFieldFrame.width + 32.0
|
||||
if self.textInputNode.textView.attributedText.string.isEmpty {
|
||||
initialWidth = ceil(layout.size.width - self.textFieldFrame.origin.x - self.sendButtonFrame.width - layout.safeInsets.left - layout.safeInsets.right + 21.0)
|
||||
}
|
||||
|
||||
let toFrame = CGRect(origin: CGPoint(), size: CGSize(width: initialWidth, height: self.textFieldFrame.height + 1.0))
|
||||
let delta = (toFrame.height - self.messageClipNode.bounds.height) / 2.0
|
||||
|
||||
@ -455,6 +464,8 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
let textOffset = self.textInputNode.textView.contentSize.height - self.textInputNode.textView.contentOffset.y - self.textInputNode.textView.frame.height
|
||||
self.fromMessageTextNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
|
||||
self.toMessageTextNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
|
||||
} else {
|
||||
completedBubble = true
|
||||
}
|
||||
|
||||
self.contentContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 160.0, y: 0.0), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
|
||||
@ -462,6 +473,12 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if let layout = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
|
||||
@ -486,10 +503,13 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
let insets = layout.insets(options: [.statusBar, .input])
|
||||
let inputHeight = layout.inputHeight ?? 0.0
|
||||
|
||||
let contentOffset = self.scrollNode.view.contentOffset.y
|
||||
|
||||
var contentOrigin = CGPoint(x: layout.size.width - sideInset - contentSize.width - layout.safeInsets.right, y: layout.size.height - 6.0 - insets.bottom - contentSize.height)
|
||||
if inputHeight > 0.0 {
|
||||
contentOrigin.y += 60.0
|
||||
}
|
||||
contentOrigin.y = min(contentOrigin.y + contentOffset, layout.size.height - 6.0 - layout.intrinsicInsets.bottom - contentSize.height)
|
||||
|
||||
transition.updateFrame(node: self.contentContainerNode, frame: CGRect(origin: contentOrigin, size: contentSize))
|
||||
var nextY: CGFloat = 0.0
|
||||
@ -504,6 +524,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
if inputHeight.isZero {
|
||||
sendButtonFrame.origin.y -= 60.0
|
||||
}
|
||||
sendButtonFrame.origin.y = min(sendButtonFrame.origin.y + contentOffset, layout.size.height - layout.intrinsicInsets.bottom - initialSendButtonFrame.height)
|
||||
transition.updateFrame(node: self.sendButtonNode, frame: sendButtonFrame)
|
||||
|
||||
var messageFrame = self.textFieldFrame
|
||||
@ -514,8 +535,12 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
messageFrame.origin.y += 60.0
|
||||
}
|
||||
|
||||
if self.textInputNode.textView.numberOfLines == 1 {
|
||||
let textWidth = min(self.textInputNode.textView.sizeThatFits(layout.size).width + 36.0, messageFrame.width)
|
||||
if self.textInputNode.textView.attributedText.string.isEmpty {
|
||||
messageFrame.size.width = ceil(layout.size.width - messageFrame.origin.x - sendButtonFrame.width - layout.safeInsets.left - layout.safeInsets.right + 8.0)
|
||||
}
|
||||
|
||||
if self.textInputNode.textView.numberOfLines == 1 || self.textInputNode.textView.attributedText.string.isEmpty {
|
||||
let textWidth = min(self.toMessageTextNode.textView.sizeThatFits(layout.size).width + 36.0, messageFrame.width)
|
||||
messageFrame.origin.x += messageFrame.width - textWidth
|
||||
messageFrame.size.width = textWidth
|
||||
}
|
||||
@ -542,6 +567,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
textFrame.size.height = self.textInputNode.textView.contentSize.height
|
||||
self.fromMessageTextNode.frame = textFrame
|
||||
self.toMessageTextNode.frame = textFrame
|
||||
|
||||
if let accessoryPanelNode = self.accessoryPanelNode {
|
||||
let size = accessoryPanelNode.calculateSizeThatFits(CGSize(width: messageFrame.width, height: 45.0))
|
||||
accessoryPanelNode.frame = CGRect(origin: CGPoint(x: 0.0, y: self.textFieldFrame.minY - size.height - 7.0), size: size)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
|
||||
@ -380,7 +380,9 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
animationNode.started = { [weak self] in
|
||||
self?.imageNode.alpha = 0.0
|
||||
}
|
||||
animationNode.setup(account: item.account, resource: animatedStickerFile.resource, width: 160, height: 160, mode: .cached)
|
||||
let dimensions = animatedStickerFile.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||
animationNode.setup(account: item.account, resource: animatedStickerFile.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -106,7 +106,9 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
||||
animationNode.started = { [weak self] in
|
||||
self?.imageNode.alpha = 0.0
|
||||
}
|
||||
animationNode.setup(account: account, resource: item.file.resource, width: 160, height: 160, mode: .cached)
|
||||
let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||
animationNode.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
|
||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
|
||||
} else {
|
||||
|
||||
@ -371,10 +371,15 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
if fileUpdated {
|
||||
imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: stillImageSize, boundingSize: stillImageSize, intrinsicInsets: UIEdgeInsets()))
|
||||
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: item.account.postbox, representation: representation)
|
||||
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: item.account.postbox, resource: representation.resource)
|
||||
}
|
||||
case .animated:
|
||||
case let .animated(resource):
|
||||
imageSize = imageBoundingSize
|
||||
|
||||
if fileUpdated {
|
||||
imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageBoundingSize, boundingSize: imageBoundingSize, intrinsicInsets: UIEdgeInsets()))
|
||||
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: item.account.postbox, resource: resource, animated: true)
|
||||
}
|
||||
}
|
||||
if fileUpdated, let resourceReference = resourceReference {
|
||||
updatedFetchSignal = fetchedMediaResource(mediaBox: item.account.postbox.mediaBox, reference: resourceReference)
|
||||
@ -552,6 +557,8 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
||||
case .still:
|
||||
transition.updateFrame(node: strongSelf.imageNode, frame: imageFrame)
|
||||
case let .animated(resource):
|
||||
transition.updateFrame(node: strongSelf.imageNode, frame: imageFrame)
|
||||
|
||||
let animationNode: AnimatedStickerNode
|
||||
if let current = strongSelf.animationNode {
|
||||
animationNode = current
|
||||
@ -562,6 +569,8 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
||||
animationNode.setup(account: item.account, resource: resource, width: 80, height: 80, mode: .cached)
|
||||
}
|
||||
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
|
||||
animationNode.isHidden = !item.playAnimatedStickers
|
||||
strongSelf.imageNode.isHidden = item.playAnimatedStickers
|
||||
if let animationNode = strongSelf.animationNode {
|
||||
transition.updateFrame(node: animationNode, frame: imageFrame)
|
||||
}
|
||||
|
||||
@ -113,7 +113,9 @@ final class TrendingTopItemNode: ASDisplayNode {
|
||||
animationNode.started = { [weak self] in
|
||||
self?.imageNode.alpha = 0.0
|
||||
}
|
||||
animationNode.setup(account: account, resource: item.file.resource, width: 160, height: 160, mode: .cached)
|
||||
let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||
animationNode.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
self.loadDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
|
||||
} else {
|
||||
self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads)
|
||||
|
||||
@ -279,8 +279,10 @@ public final class NotificationViewControllerImpl {
|
||||
}
|
||||
view?.addSubnode(animatedStickerNode)
|
||||
}
|
||||
strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: CGSize(width: 512.0, height: 512.0)))
|
||||
animatedStickerNode.setup(account: accountAndImage.0, resource: fileReference.media.resource, width: 512, height: 512, mode: .direct)
|
||||
let dimensions = fileReference.media.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 512.0, height: 512.0))
|
||||
strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: fittedDimensions))
|
||||
animatedStickerNode.setup(account: accountAndImage.0, resource: fileReference.media.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct)
|
||||
animatedStickerNode.visibility = true
|
||||
|
||||
accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true))
|
||||
|
||||
@ -6,13 +6,16 @@ import SwiftSignalKit
|
||||
import TelegramUIPrivateModule
|
||||
|
||||
final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
|
||||
func render(queue: Queue, width: Int, height: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) {
|
||||
func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) {
|
||||
queue.async { [weak self] in
|
||||
let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData in
|
||||
let calculatedBytesPerRow = (4 * Int(width) + 15) & (~15)
|
||||
assert(bytesPerRow == calculatedBytesPerRow)
|
||||
|
||||
let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData, bytesPerRow in
|
||||
switch type {
|
||||
case .yuva:
|
||||
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||
decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height))
|
||||
decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height), Int32(bytesPerRow))
|
||||
}
|
||||
case .argb:
|
||||
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||
|
||||
@ -165,7 +165,8 @@ final class StickerPackPreviewController: ViewController {
|
||||
let signal = Signal<Bool, NoError> { subscriber in
|
||||
let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: FileMediaReference.standalone(media: item.file).resourceReference(item.file.resource)).start()
|
||||
let data = account.postbox.mediaBox.resourceData(item.file.resource).start()
|
||||
let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, file: item.file, small: false, size: CGSize(width: 160.0, height: 160.0), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in
|
||||
let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, file: item.file, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in
|
||||
let hasContent = next._0 != nil || next._1 != nil
|
||||
subscriber.putNext(hasContent)
|
||||
if hasContent {
|
||||
|
||||
@ -93,7 +93,8 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != stickerItem {
|
||||
if let dimensions = stickerItem.file.dimensions {
|
||||
if stickerItem.file.isAnimatedSticker {
|
||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: account.postbox, file: stickerItem.file, small: false, size: CGSize(width: 160.0, height: 160.0)))
|
||||
let dimensions = stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: account.postbox, file: stickerItem.file, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))))
|
||||
|
||||
if self.animationNode == nil {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
@ -103,7 +104,8 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
self?.imageNode.isHidden = true
|
||||
}
|
||||
}
|
||||
self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: 160, height: 160, mode: .cached)
|
||||
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||
self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
self.animationNode?.visibility = self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true
|
||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
|
||||
} else {
|
||||
|
||||
@ -159,7 +159,9 @@ final class StickerPaneSearchStickerItemNode: GridItemNode {
|
||||
self.animationNode = animationNode
|
||||
self.addSubnode(animationNode)
|
||||
}
|
||||
self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: 160, height: 160, mode: .cached)
|
||||
let dimensions = stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||
self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
self.animationNode?.visibility = self.isVisibleInGrid
|
||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
|
||||
} else {
|
||||
|
||||
@ -86,7 +86,10 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
|
||||
let animationNode = AnimatedStickerNode()
|
||||
self.animationNode = animationNode
|
||||
|
||||
self.animationNode?.setup(account: account, resource: item.file.resource, width: 400, height: 400, mode: .direct)
|
||||
let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 400.0, height: 400.0))
|
||||
|
||||
self.animationNode?.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct)
|
||||
self.animationNode?.visibility = true
|
||||
self.animationNode?.addSubnode(self.textNode)
|
||||
} else {
|
||||
|
||||
@ -184,9 +184,15 @@ private func chatMessageStickerThumbnailData(postbox: Postbox, file: TelegramMed
|
||||
}
|
||||
}
|
||||
|
||||
private func chatMessageStickerPackThumbnailData(postbox: Postbox, representation: TelegramMediaImageRepresentation, synchronousLoad: Bool) -> Signal<Data?, NoError> {
|
||||
let resource = representation.resource
|
||||
let maybeFetched = postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedStickerAJpegRepresentation(size: CGSize(width: 160.0, height: 160.0)), complete: false, fetch: false, attemptSynchronously: synchronousLoad)
|
||||
private func chatMessageStickerPackThumbnailData(postbox: Postbox, resource: MediaResource, animated: Bool, synchronousLoad: Bool) -> Signal<Data?, NoError> {
|
||||
let maybeFetched: Signal<MediaResourceData, NoError>
|
||||
let representation: CachedMediaResourceRepresentation
|
||||
if animated {
|
||||
representation = CachedAnimatedStickerFirstFrameRepresentation(width: 160, height: 160)
|
||||
} else {
|
||||
representation = CachedStickerAJpegRepresentation(size: CGSize(width: 160.0, height: 160.0))
|
||||
}
|
||||
maybeFetched = postbox.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: false, fetch: false, attemptSynchronously: synchronousLoad)
|
||||
|
||||
return maybeFetched
|
||||
|> take(1)
|
||||
@ -195,7 +201,7 @@ private func chatMessageStickerPackThumbnailData(postbox: Postbox, representatio
|
||||
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
|
||||
return .single(loadedData)
|
||||
} else {
|
||||
let fullSizeData = postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedStickerAJpegRepresentation(size: CGSize(width: 160.0, height: 160.0)), complete: false)
|
||||
let fullSizeData = postbox.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: false)
|
||||
|> map { next in
|
||||
return ((next.size == 0 || !next.complete) ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedIfSafe), next.complete)
|
||||
}
|
||||
@ -325,8 +331,8 @@ public func chatMessageSticker(account: Account, file: TelegramMediaFile, small:
|
||||
return chatMessageSticker(postbox: account.postbox, file: file, small: small, fetched: fetched, onlyFullSize: onlyFullSize, thumbnail: thumbnail, synchronousLoad: synchronousLoad)
|
||||
}
|
||||
|
||||
public func chatMessageStickerPackThumbnail(postbox: Postbox, representation: TelegramMediaImageRepresentation, synchronousLoad: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
let signal = chatMessageStickerPackThumbnailData(postbox: postbox, representation: representation, synchronousLoad: synchronousLoad)
|
||||
public func chatMessageStickerPackThumbnail(postbox: Postbox, resource: MediaResource, animated: Bool = false, synchronousLoad: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
let signal = chatMessageStickerPackThumbnailData(postbox: postbox, resource: resource, animated: animated, synchronousLoad: synchronousLoad)
|
||||
|
||||
return signal
|
||||
|> map { fullSizeData in
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height);
|
||||
void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height);
|
||||
void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height, int bytesPerRow);
|
||||
void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height, int bytesPerRow);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#import "YUV.h"
|
||||
#import <Accelerate/Accelerate.h>
|
||||
|
||||
void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height) {
|
||||
void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height, int bytesPerRow) {
|
||||
static vImage_ARGBToYpCbCr info;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
@ -15,7 +15,7 @@ void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height)
|
||||
src.data = (void *)argb;
|
||||
src.width = width;
|
||||
src.height = height;
|
||||
src.rowBytes = width * 4;
|
||||
src.rowBytes = bytesPerRow;
|
||||
|
||||
uint8_t permuteMap[4] = {3, 2, 1, 0};
|
||||
error = vImagePermuteChannels_ARGB8888(&src, &src, permuteMap, kvImageDoNotTile);
|
||||
@ -48,7 +48,7 @@ void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height)
|
||||
}
|
||||
}
|
||||
|
||||
void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height) {
|
||||
void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height, int bytesPerRow) {
|
||||
static vImage_YpCbCrToARGB info;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
@ -74,7 +74,7 @@ void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height)
|
||||
dest.data = (void *)argb;
|
||||
dest.width = width;
|
||||
dest.height = height;
|
||||
dest.rowBytes = width * 4;
|
||||
dest.rowBytes = bytesPerRow;
|
||||
|
||||
error = vImageConvert_420Yp8_CbCr8ToARGB8888(&srcYp, &srcCbCr, &dest, &info, NULL, 0xff, kvImageDoNotTile);
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
||||
public var knockoutWallpaper: Bool
|
||||
|
||||
public static var defaultSettings: ExperimentalUISettings {
|
||||
return ExperimentalUISettings(keepChatNavigationStack: false, skipReadHistory: false, crashOnLongQueries: false, chatListPhotos: false, playAnimatedEmojiOnce: false, knockoutWallpaper: false)
|
||||
return ExperimentalUISettings(keepChatNavigationStack: false, skipReadHistory: false, crashOnLongQueries: false, chatListPhotos: false, playAnimatedEmojiOnce: true, knockoutWallpaper: false)
|
||||
}
|
||||
|
||||
public init(keepChatNavigationStack: Bool, skipReadHistory: Bool, crashOnLongQueries: Bool, chatListPhotos: Bool, playAnimatedEmojiOnce: Bool, knockoutWallpaper: Bool) {
|
||||
@ -28,7 +28,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
||||
self.skipReadHistory = decoder.decodeInt32ForKey("skipReadHistory", orElse: 0) != 0
|
||||
self.crashOnLongQueries = decoder.decodeInt32ForKey("crashOnLongQueries", orElse: 0) != 0
|
||||
self.chatListPhotos = decoder.decodeInt32ForKey("chatListPhotos", orElse: 0) != 0
|
||||
self.playAnimatedEmojiOnce = decoder.decodeInt32ForKey("playAnimatedEmojiOnce", orElse: 0) != 0
|
||||
self.playAnimatedEmojiOnce = decoder.decodeInt32ForKey("playAnimatedEmojiOnce", orElse: 1) != 0
|
||||
self.knockoutWallpaper = decoder.decodeInt32ForKey("knockoutWallpaper", orElse: 0) != 0
|
||||
}
|
||||
|
||||
|
||||
@ -284,15 +284,15 @@ public struct PresentationThemeAccentColor: PostboxCoding, Equatable {
|
||||
}
|
||||
|
||||
public var color: UIColor {
|
||||
let color: UIColor
|
||||
if self.value < 0.5 {
|
||||
color = self.baseColor.color.interpolateTo(self.baseColor.edgeColors.0, fraction: 0.5 - self.value)!
|
||||
} else if self.value > 0.5 {
|
||||
color = self.baseColor.color.interpolateTo(self.baseColor.edgeColors.1, fraction: self.value - 0.5)!
|
||||
} else {
|
||||
color = self.baseColor.color
|
||||
}
|
||||
return color
|
||||
// let color: UIColor
|
||||
// if self.value < 0.5 {
|
||||
// color = self.baseColor.color.interpolateTo(self.baseColor.edgeColors.0, fraction: 0.5 - self.value)!
|
||||
// } else if self.value > 0.5 {
|
||||
// color = self.baseColor.color.interpolateTo(self.baseColor.edgeColors.1, fraction: self.value - 0.5)!
|
||||
// } else {
|
||||
// color = self.baseColor.color
|
||||
// }
|
||||
return self.baseColor.color
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user