mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Residual coding
This commit is contained in:
@@ -24,6 +24,7 @@ import StickerPeekUI
|
||||
import UndoUI
|
||||
import AudioToolbox
|
||||
import SolidRoundedButtonComponent
|
||||
import EmojiTextAttachmentView
|
||||
|
||||
private let premiumBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat List/PeerPremiumIcon"), color: .white)
|
||||
private let featuredBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeAdd"), color: .white)
|
||||
@@ -1359,6 +1360,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
public let hasClear: Bool
|
||||
public let isExpandable: Bool
|
||||
public let displayPremiumBadges: Bool
|
||||
public let headerItem: EntityKeyboardGroupHeaderItem?
|
||||
public let items: [Item]
|
||||
|
||||
public init(
|
||||
@@ -1373,6 +1375,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
hasClear: Bool,
|
||||
isExpandable: Bool,
|
||||
displayPremiumBadges: Bool,
|
||||
headerItem: EntityKeyboardGroupHeaderItem?,
|
||||
items: [Item]
|
||||
) {
|
||||
self.supergroupId = supergroupId
|
||||
@@ -1386,6 +1389,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
self.hasClear = hasClear
|
||||
self.isExpandable = isExpandable
|
||||
self.displayPremiumBadges = displayPremiumBadges
|
||||
self.headerItem = headerItem
|
||||
self.items = items
|
||||
}
|
||||
|
||||
@@ -1426,6 +1430,9 @@ public final class EmojiPagerContentComponent: Component {
|
||||
if lhs.displayPremiumBadges != rhs.displayPremiumBadges {
|
||||
return false
|
||||
}
|
||||
if lhs.headerItem != rhs.headerItem {
|
||||
return false
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
@@ -1592,9 +1599,6 @@ public final class EmojiPagerContentComponent: Component {
|
||||
|
||||
self.itemsPerRow = max(minItemsPerRow, Int((itemHorizontalSpace + minSpacing) / (self.nativeItemSize + minSpacing)))
|
||||
|
||||
//self.itemsPerRow * x + minSpacing * (x - 1) = itemHorizontalSpace
|
||||
//self.itemsPerRow * x + minSpacing * (self.itemsPerRow - 1) = itemHorizontalSpace
|
||||
//x = (itemHorizontalSpace - minSpacing * (self.itemsPerRow - 1)) / self.itemsPerRow
|
||||
let proposedItemSize = floor((itemHorizontalSpace - minSpacing * (CGFloat(self.itemsPerRow) - 1.0)) / CGFloat(self.itemsPerRow))
|
||||
|
||||
self.visibleItemSize = proposedItemSize < self.nativeItemSize ? proposedItemSize : self.nativeItemSize
|
||||
@@ -1862,57 +1866,76 @@ public final class EmojiPagerContentComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.disposable = renderer.add(target: strongSelf, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize, fetch: { size, writer in
|
||||
let source = AnimatedStickerResourceSource(account: context.account, resource: file.resource, fitzModifier: nil, isVideo: false)
|
||||
|
||||
let dataDisposable = source.directDataPath(attemptSynchronously: false).start(next: { result in
|
||||
guard let result = result else {
|
||||
return
|
||||
}
|
||||
|
||||
if file.isVideoEmoji || file.isVideoSticker {
|
||||
cacheVideoAnimation(path: result, width: Int(size.width), height: Int(size.height), writer: writer)
|
||||
} else if file.isAnimatedSticker {
|
||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: result)) else {
|
||||
writer.finish()
|
||||
return
|
||||
}
|
||||
cacheLottieAnimation(data: data, width: Int(size.width), height: Int(size.height), writer: writer)
|
||||
} else {
|
||||
cacheStillSticker(path: result, width: Int(size.width), height: Int(size.height), writer: writer)
|
||||
}
|
||||
})
|
||||
|
||||
let fetchDisposable = freeMediaFileResourceInteractiveFetched(account: context.account, fileReference: stickerPackFileReference(file), resource: file.resource).start()
|
||||
|
||||
return ActionDisposable {
|
||||
dataDisposable.dispose()
|
||||
fetchDisposable.dispose()
|
||||
}
|
||||
})
|
||||
strongSelf.disposable = renderer.add(target: strongSelf, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, file: file, keyframeOnly: pixelSize.width >= 120.0))
|
||||
}
|
||||
|
||||
if attemptSynchronousLoad {
|
||||
if !renderer.loadFirstFrameSynchronously(target: self, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize) {
|
||||
self.updateDisplayPlaceholder(displayPlaceholder: true)
|
||||
}
|
||||
|
||||
loadAnimation()
|
||||
} else {
|
||||
let _ = renderer.loadFirstFrame(target: self, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize, completion: { [weak self] success in
|
||||
loadAnimation()
|
||||
|
||||
if !success {
|
||||
guard let strongSelf = self else {
|
||||
self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, file: file, keyframeOnly: true), completion: { [weak self] success, isFinal in
|
||||
if !isFinal {
|
||||
if !success {
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.updateDisplayPlaceholder(displayPlaceholder: true)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.updateDisplayPlaceholder(displayPlaceholder: true)
|
||||
Queue.mainQueue().async {
|
||||
loadAnimation()
|
||||
|
||||
if !success {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.updateDisplayPlaceholder(displayPlaceholder: true)
|
||||
} else {
|
||||
//self?.updateDisplayPlaceholder(displayPlaceholder: false)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
loadAnimation()
|
||||
}
|
||||
} else {
|
||||
self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, file: file, keyframeOnly: true), completion: { [weak self] success, isFinal in
|
||||
if !isFinal {
|
||||
if !success {
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.updateDisplayPlaceholder(displayPlaceholder: true)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Queue.mainQueue().async {
|
||||
loadAnimation()
|
||||
|
||||
if !success {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.updateDisplayPlaceholder(displayPlaceholder: true)
|
||||
} else {
|
||||
//self?.updateDisplayPlaceholder(displayPlaceholder: false)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if let staticEmoji = staticEmoji {
|
||||
let image = generateImage(self.size, opaque: false, scale: min(UIScreenScale, 3.0), rotatedContext: { size, context in
|
||||
let image = generateImage(pointSize, opaque: false, scale: min(UIScreenScale, 3.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
let preScaleFactor: CGFloat = 1.0
|
||||
|
||||
@@ -271,7 +271,7 @@ public final class EntityKeyboardComponent: Component {
|
||||
isReorderable: false,
|
||||
content: AnyComponent(EntityKeyboardAnimationTopPanelComponent(
|
||||
context: component.emojiContent.context,
|
||||
file: emoji.file,
|
||||
item: .file(file: emoji.file),
|
||||
isFeatured: false,
|
||||
isPremiumLocked: false,
|
||||
animationCache: component.emojiContent.animationCache,
|
||||
@@ -365,7 +365,7 @@ public final class EntityKeyboardComponent: Component {
|
||||
isReorderable: !itemGroup.isFeatured,
|
||||
content: AnyComponent(EntityKeyboardAnimationTopPanelComponent(
|
||||
context: stickerContent.context,
|
||||
file: file,
|
||||
item: itemGroup.headerItem ?? .file(file: file),
|
||||
isFeatured: itemGroup.isFeatured,
|
||||
isPremiumLocked: itemGroup.isPremiumLocked,
|
||||
animationCache: stickerContent.animationCache,
|
||||
@@ -468,7 +468,7 @@ public final class EntityKeyboardComponent: Component {
|
||||
isReorderable: !itemGroup.isFeatured,
|
||||
content: AnyComponent(EntityKeyboardAnimationTopPanelComponent(
|
||||
context: component.emojiContent.context,
|
||||
file: file,
|
||||
item: itemGroup.headerItem ?? .file(file: file),
|
||||
isFeatured: itemGroup.isFeatured,
|
||||
isPremiumLocked: itemGroup.isPremiumLocked,
|
||||
animationCache: component.emojiContent.animationCache,
|
||||
|
||||
@@ -12,12 +12,67 @@ import MultiAnimationRenderer
|
||||
import AccountContext
|
||||
import MultilineTextComponent
|
||||
import LottieAnimationComponent
|
||||
import MurMurHash32
|
||||
|
||||
public enum EntityKeyboardGroupHeaderItem: Equatable {
|
||||
public enum ThumbnailType {
|
||||
case still
|
||||
case lottie
|
||||
case video
|
||||
}
|
||||
|
||||
case file(file: TelegramMediaFile)
|
||||
case packThumbnail(resource: MediaResourceReference, immediateThumbnailData: Data?, dimensions: CGSize, type: ThumbnailType)
|
||||
}
|
||||
|
||||
private func fileFromItem(_ item: EntityKeyboardGroupHeaderItem) -> TelegramMediaFile {
|
||||
let file: TelegramMediaFile
|
||||
switch item {
|
||||
case let .file(fileValue):
|
||||
file = fileValue
|
||||
case let .packThumbnail(resource, immediateThumbnailData, _, type):
|
||||
let mimeType: String
|
||||
let attributes: [TelegramMediaFileAttribute]
|
||||
|
||||
switch type {
|
||||
case .still:
|
||||
mimeType = "image/webp"
|
||||
attributes = [.FileName(fileName: "image.webp")]
|
||||
case .lottie:
|
||||
mimeType = "application/x-tgsticker"
|
||||
attributes = [
|
||||
.FileName(fileName: "sticker.tgs"),
|
||||
.Sticker(displayText: "", packReference: nil, maskData: nil)
|
||||
]
|
||||
case .video:
|
||||
mimeType = "video/webm"
|
||||
attributes = [
|
||||
.FileName(fileName: "sticker.webm"),
|
||||
.Sticker(displayText: "", packReference: nil, maskData: nil)
|
||||
]
|
||||
}
|
||||
|
||||
file = TelegramMediaFile(
|
||||
fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: Int64(murMurHashString32(resource.resource.id.stringRepresentation))),
|
||||
partialReference: nil,
|
||||
resource: resource.resource as! TelegramMediaResource,
|
||||
previewRepresentations: [],
|
||||
videoThumbnails: [],
|
||||
immediateThumbnailData: immediateThumbnailData,
|
||||
mimeType: mimeType,
|
||||
size: nil,
|
||||
attributes: attributes
|
||||
)
|
||||
}
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
final class EntityKeyboardAnimationTopPanelComponent: Component {
|
||||
typealias EnvironmentType = EntityKeyboardTopPanelItemEnvironment
|
||||
|
||||
let context: AccountContext
|
||||
let file: TelegramMediaFile
|
||||
let item: EntityKeyboardGroupHeaderItem
|
||||
let isFeatured: Bool
|
||||
let isPremiumLocked: Bool
|
||||
let animationCache: AnimationCache
|
||||
@@ -28,7 +83,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
file: TelegramMediaFile,
|
||||
item: EntityKeyboardGroupHeaderItem,
|
||||
isFeatured: Bool,
|
||||
isPremiumLocked: Bool,
|
||||
animationCache: AnimationCache,
|
||||
@@ -38,7 +93,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
||||
pressed: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.file = file
|
||||
self.item = item
|
||||
self.isFeatured = isFeatured
|
||||
self.isPremiumLocked = isPremiumLocked
|
||||
self.animationCache = animationCache
|
||||
@@ -52,7 +107,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.file.fileId != rhs.file.fileId {
|
||||
if lhs.item != rhs.item {
|
||||
return false
|
||||
}
|
||||
if lhs.isFeatured != rhs.isFeatured {
|
||||
@@ -104,19 +159,27 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
||||
|
||||
let itemEnvironment = environment[EntityKeyboardTopPanelItemEnvironment.self].value
|
||||
|
||||
let dimensions = component.file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
|
||||
let dimensions: CGSize
|
||||
switch component.item {
|
||||
case let .file(file):
|
||||
dimensions = file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
|
||||
case let .packThumbnail(_, _, dimensionsValue, _):
|
||||
dimensions = dimensionsValue
|
||||
}
|
||||
let displaySize = dimensions.aspectFitted(CGSize(width: 44.0, height: 44.0))
|
||||
|
||||
if self.itemLayer == nil {
|
||||
let file = fileFromItem(component.item)
|
||||
|
||||
let itemLayer = EmojiPagerContentComponent.View.ItemLayer(
|
||||
item: EmojiPagerContentComponent.Item(
|
||||
file: component.file,
|
||||
file: file,
|
||||
staticEmoji: nil,
|
||||
subgroupId: nil
|
||||
),
|
||||
context: component.context,
|
||||
attemptSynchronousLoad: false,
|
||||
file: component.file,
|
||||
file: file,
|
||||
staticEmoji: nil,
|
||||
cache: component.animationCache,
|
||||
renderer: component.animationRenderer,
|
||||
@@ -204,7 +267,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
||||
if self.placeholderView == nil, let component = self.component {
|
||||
let placeholderView = EmojiPagerContentComponent.View.ItemPlaceholderView(
|
||||
context: component.context,
|
||||
file: component.file,
|
||||
file: fileFromItem(component.item),
|
||||
shimmerView: nil,
|
||||
color: component.theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.08),
|
||||
size: CGSize(width: 28.0, height: 28.0)
|
||||
|
||||
Reference in New Issue
Block a user