Residual coding

This commit is contained in:
Ali
2022-07-29 18:32:51 +02:00
parent 64076541cd
commit f6ad7dc77a
35 changed files with 2784 additions and 498 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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)