mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-06 17:00:13 +00:00
Emoji 1.1 WIP
This commit is contained in:
parent
47239681a7
commit
095b9d5058
@ -242,7 +242,7 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
|
|
||||||
for index in 0 ..< items.count {
|
for index in 0 ..< items.count {
|
||||||
let item = items[index]
|
let item = items[index]
|
||||||
let itemId = EmojiPagerContentComponent.View.ItemLayer.Key(groupId: 0, fileId: item.file.fileId, staticEmoji: nil)
|
let itemId = EmojiPagerContentComponent.View.ItemLayer.Key(groupId: 0, itemId: .file(item.file.fileId), staticEmoji: nil)
|
||||||
validIds.insert(itemId)
|
validIds.insert(itemId)
|
||||||
|
|
||||||
let itemDimensions = item.file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
|
let itemDimensions = item.file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
|
||||||
@ -259,10 +259,10 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
itemTransition = .immediate
|
itemTransition = .immediate
|
||||||
|
|
||||||
itemLayer = EmojiPagerContentComponent.View.ItemLayer(
|
itemLayer = EmojiPagerContentComponent.View.ItemLayer(
|
||||||
item: EmojiPagerContentComponent.Item(file: item.file, staticEmoji: nil, subgroupId: nil),
|
item: EmojiPagerContentComponent.Item(animationData: EntityKeyboardAnimationData(file: item.file), itemFile: item.file, staticEmoji: nil, subgroupId: nil),
|
||||||
context: context,
|
context: context,
|
||||||
attemptSynchronousLoad: attemptSynchronousLoads,
|
attemptSynchronousLoad: attemptSynchronousLoads,
|
||||||
file: item.file,
|
animationData: EntityKeyboardAnimationData(file: item.file),
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
cache: animationCache,
|
cache: animationCache,
|
||||||
renderer: animationRenderer,
|
renderer: animationRenderer,
|
||||||
@ -281,7 +281,8 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
} else {
|
} else {
|
||||||
placeholderView = EmojiPagerContentComponent.View.ItemPlaceholderView(
|
placeholderView = EmojiPagerContentComponent.View.ItemPlaceholderView(
|
||||||
context: context,
|
context: context,
|
||||||
file: item.file,
|
dimensions: item.file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0),
|
||||||
|
immediateThumbnailData: item.file.immediateThumbnailData,
|
||||||
shimmerView: strongSelf.shimmerHostView,
|
shimmerView: strongSelf.shimmerHostView,
|
||||||
color: theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.08),
|
color: theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.08),
|
||||||
size: itemNativeFitSize
|
size: itemNativeFitSize
|
||||||
|
|||||||
@ -27,7 +27,7 @@ typedef NS_ENUM(NSUInteger, ImageDCTTableType) {
|
|||||||
- (void)forwardWithPixels:(uint8_t const * _Nonnull)pixels coefficients:(int16_t * _Nonnull)coefficients width:(NSInteger)width height:(NSInteger)height bytesPerRow:(NSInteger)bytesPerRow __attribute__((objc_direct));
|
- (void)forwardWithPixels:(uint8_t const * _Nonnull)pixels coefficients:(int16_t * _Nonnull)coefficients width:(NSInteger)width height:(NSInteger)height bytesPerRow:(NSInteger)bytesPerRow __attribute__((objc_direct));
|
||||||
- (void)inverseWithCoefficients:(int16_t const * _Nonnull)coefficients pixels:(uint8_t * _Nonnull)pixels width:(NSInteger)width height:(NSInteger)height coefficientsPerRow:(NSInteger)coefficientsPerRow bytesPerRow:(NSInteger)bytesPerRow __attribute__((objc_direct));
|
- (void)inverseWithCoefficients:(int16_t const * _Nonnull)coefficients pixels:(uint8_t * _Nonnull)pixels width:(NSInteger)width height:(NSInteger)height coefficientsPerRow:(NSInteger)coefficientsPerRow bytesPerRow:(NSInteger)bytesPerRow __attribute__((objc_direct));
|
||||||
- (void)forward4x4:(int16_t const * _Nonnull)normalizedCoefficients coefficients:(int16_t * _Nonnull)coefficients width:(NSInteger)width height:(NSInteger)height __attribute__((objc_direct));
|
- (void)forward4x4:(int16_t const * _Nonnull)normalizedCoefficients coefficients:(int16_t * _Nonnull)coefficients width:(NSInteger)width height:(NSInteger)height __attribute__((objc_direct));
|
||||||
- (void)inverse4x4:(int16_t const * _Nonnull)coefficients normalizedCoefficients:(int16_t * _Nonnull)normalizedCoefficients width:(NSInteger)width height:(NSInteger)height __attribute__((objc_direct));
|
- (void)inverse4x4Add:(int16_t const * _Nonnull)coefficients normalizedCoefficients:(int16_t * _Nonnull)normalizedCoefficients width:(NSInteger)width height:(NSInteger)height __attribute__((objc_direct));
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@ -727,7 +727,7 @@ static inline void transpose_idct4x4_16_bd8(int16x8_t *const a) {
|
|||||||
idct4x4_16_kernel_bd8(a);
|
idct4x4_16_kernel_bd8(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void vpx_idct4x4_16_add_neon(const int16x8_t &top64, const int16x8_t &bottom64, int16_t *dest, int16_t multiplier) {
|
inline void vpx_idct4x4_16_add_neon(const int16x8_t &top64, const int16x8_t &bottom64, const int16x4_t ¤t0, const int16x4_t ¤t1, const int16x4_t ¤t2, const int16x4_t ¤t3, int16_t multiplier, int16_t *dest, int destRowIncrement) {
|
||||||
int16x8_t a[2];
|
int16x8_t a[2];
|
||||||
|
|
||||||
assert(!((intptr_t)dest % sizeof(uint32_t)));
|
assert(!((intptr_t)dest % sizeof(uint32_t)));
|
||||||
@ -745,11 +745,19 @@ inline void vpx_idct4x4_16_add_neon(const int16x8_t &top64, const int16x8_t &bot
|
|||||||
a[0] = vrshrq_n_s16(a[0], 4);
|
a[0] = vrshrq_n_s16(a[0], 4);
|
||||||
a[1] = vrshrq_n_s16(a[1], 4);
|
a[1] = vrshrq_n_s16(a[1], 4);
|
||||||
|
|
||||||
vst1q_s16(dest, a[0]);
|
a[0] = vaddq_s16(a[0], vcombine_s16(current0, current1));
|
||||||
dest += 2 * 4;
|
a[1] = vaddq_s16(a[1], vcombine_s16(current3, current2));
|
||||||
vst1_s16(dest, vget_high_s16(a[1]));
|
|
||||||
dest += 4;
|
vst1_s16(dest + destRowIncrement * 0, vget_low_s16(a[0]));
|
||||||
vst1_s16(dest, vget_low_s16(a[1]));
|
vst1_s16(dest + destRowIncrement * 1, vget_high_s16(a[0]));
|
||||||
|
vst1_s16(dest + destRowIncrement * 2, vget_high_s16(a[1]));
|
||||||
|
vst1_s16(dest + destRowIncrement * 3, vget_low_s16(a[1]));
|
||||||
|
|
||||||
|
//vst1q_s16(dest, a[0]);
|
||||||
|
//dest += 2 * 4;
|
||||||
|
//vst1_s16(dest, vget_high_s16(a[1]));
|
||||||
|
//dest += 4;
|
||||||
|
//vst1_s16(dest, vget_low_s16(a[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dct4x4QuantDC = 58;
|
static int dct4x4QuantDC = 58;
|
||||||
@ -803,11 +811,14 @@ void performForward4x4Dct(int16_t const *normalizedCoefficients, int16_t *coeffi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void performInverse4x4Dct(int16_t const * coefficients, int16_t *normalizedCoefficients, int width, int height, DctAuxiliaryData *auxiliaryData, IFAST_MULT_TYPE *ifmtbl) {
|
void performInverse4x4DctAdd(int16_t const *coefficients, int16_t *normalizedCoefficients, int width, int height, DctAuxiliaryData *auxiliaryData, IFAST_MULT_TYPE *ifmtbl) {
|
||||||
DCTELEM resultBlock[4 * 4];
|
|
||||||
|
|
||||||
for (int y = 0; y < height; y += 4) {
|
for (int y = 0; y < height; y += 4) {
|
||||||
for (int x = 0; x < width; x += 4) {
|
for (int x = 0; x < width; x += 4) {
|
||||||
|
int16x4_t current0 = vld1_s16(&normalizedCoefficients[(y + 0) * width + x]);
|
||||||
|
int16x4_t current1 = vld1_s16(&normalizedCoefficients[(y + 1) * width + x]);
|
||||||
|
int16x4_t current2 = vld1_s16(&normalizedCoefficients[(y + 2) * width + x]);
|
||||||
|
int16x4_t current3 = vld1_s16(&normalizedCoefficients[(y + 3) * width + x]);
|
||||||
|
|
||||||
uint32x2_t sa = vld1_u32((uint32_t *)&coefficients[(y + 0) * width + x]);
|
uint32x2_t sa = vld1_u32((uint32_t *)&coefficients[(y + 0) * width + x]);
|
||||||
uint32x2_t sb = vld1_u32((uint32_t *)&coefficients[(y + 1) * width + x]);
|
uint32x2_t sb = vld1_u32((uint32_t *)&coefficients[(y + 1) * width + x]);
|
||||||
uint32x2_t sc = vld1_u32((uint32_t *)&coefficients[(y + 2) * width + x]);
|
uint32x2_t sc = vld1_u32((uint32_t *)&coefficients[(y + 2) * width + x]);
|
||||||
@ -829,34 +840,7 @@ void performInverse4x4Dct(int16_t const * coefficients, int16_t *normalizedCoeff
|
|||||||
int16x8_t top64 = vreinterpretq_s16_u16(qtop16);
|
int16x8_t top64 = vreinterpretq_s16_u16(qtop16);
|
||||||
int16x8_t bottom64 = vreinterpretq_s16_u16(qbottom16);
|
int16x8_t bottom64 = vreinterpretq_s16_u16(qbottom16);
|
||||||
|
|
||||||
/*DCTELEM coefficientBlock[4 * 4];
|
vpx_idct4x4_16_add_neon(top64, bottom64, current0, current1, current2, current3, dct4x4QuantAC, normalizedCoefficients + y * width + x, width);
|
||||||
|
|
||||||
for (int blockY = 0; blockY < 4; blockY++) {
|
|
||||||
for (int blockX = 0; blockX < 4; blockX++) {
|
|
||||||
coefficientBlock[zigZag4x4Inv[blockY * 4 + blockX]] = coefficients[(y + blockY) * width + (x + blockX)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
top64 = vreinterpretq_s16_u64(vld1q_u64((uint64_t *)&coefficientBlock[0]));
|
|
||||||
bottom64 = vreinterpretq_s16_u64(vld1q_u64((uint64_t *)&coefficientBlock[8]));*/
|
|
||||||
|
|
||||||
vpx_idct4x4_16_add_neon(top64, bottom64, resultBlock, dct4x4QuantAC);
|
|
||||||
|
|
||||||
uint32x2_t a = vld1_u32((uint32_t *)&resultBlock[4 * 0]);
|
|
||||||
uint32x2_t b = vld1_u32((uint32_t *)&resultBlock[4 * 1]);
|
|
||||||
uint32x2_t c = vld1_u32((uint32_t *)&resultBlock[4 * 2]);
|
|
||||||
uint32x2_t d = vld1_u32((uint32_t *)&resultBlock[4 * 3]);
|
|
||||||
|
|
||||||
vst1_u32((uint32_t *)&normalizedCoefficients[(y + 0) * width + x], a);
|
|
||||||
vst1_u32((uint32_t *)&normalizedCoefficients[(y + 1) * width + x], b);
|
|
||||||
vst1_u32((uint32_t *)&normalizedCoefficients[(y + 2) * width + x], c);
|
|
||||||
vst1_u32((uint32_t *)&normalizedCoefficients[(y + 3) * width + x], d);
|
|
||||||
|
|
||||||
/*for (int blockY = 0; blockY < 4; blockY++) {
|
|
||||||
for (int blockX = 0; blockX < 4; blockX++) {
|
|
||||||
normalizedCoefficients[(y + blockY) * width + (x + blockX)] = resultBlock[blockY * 4 + blockX];
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -932,8 +916,8 @@ void DCT::forward4x4(int16_t const *normalizedCoefficients, int16_t *coefficient
|
|||||||
performForward4x4Dct(normalizedCoefficients, coefficients, width, height, (DCTELEM *)_internal->forwardDctData.data());
|
performForward4x4Dct(normalizedCoefficients, coefficients, width, height, (DCTELEM *)_internal->forwardDctData.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCT::inverse4x4(int16_t const *coefficients, int16_t *normalizedCoefficients, int width, int height) {
|
void DCT::inverse4x4Add(int16_t const *coefficients, int16_t *normalizedCoefficients, int width, int height) {
|
||||||
performInverse4x4Dct(coefficients, normalizedCoefficients, width, height, _internal->auxiliaryData, (IFAST_MULT_TYPE *)_internal->inverseDctData.data());
|
performInverse4x4DctAdd(coefficients, normalizedCoefficients, width, height, _internal->auxiliaryData, (IFAST_MULT_TYPE *)_internal->inverseDctData.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ public:
|
|||||||
void forward(uint8_t const *pixels, int16_t *coefficients, int width, int height, int bytesPerRow);
|
void forward(uint8_t const *pixels, int16_t *coefficients, int width, int height, int bytesPerRow);
|
||||||
void inverse(int16_t const *coefficients, uint8_t *pixels, int width, int height, int coefficientsPerRow, int bytesPerRow);
|
void inverse(int16_t const *coefficients, uint8_t *pixels, int width, int height, int coefficientsPerRow, int bytesPerRow);
|
||||||
void forward4x4(int16_t const *normalizedCoefficients, int16_t *coefficients, int width, int height);
|
void forward4x4(int16_t const *normalizedCoefficients, int16_t *coefficients, int width, int height);
|
||||||
void inverse4x4(int16_t const *coefficients, int16_t *normalizedCoefficients, int width, int height);
|
void inverse4x4Add(int16_t const *coefficients, int16_t *normalizedCoefficients, int width, int height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DCTInternal *_internal;
|
DCTInternal *_internal;
|
||||||
|
|||||||
@ -82,8 +82,8 @@
|
|||||||
_dct->forward4x4(normalizedCoefficients, coefficients, (int)width, (int)height);
|
_dct->forward4x4(normalizedCoefficients, coefficients, (int)width, (int)height);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)inverse4x4:(int16_t const * _Nonnull)coefficients normalizedCoefficients:(int16_t * _Nonnull)normalizedCoefficients width:(NSInteger)width height:(NSInteger)height {
|
- (void)inverse4x4Add:(int16_t const * _Nonnull)coefficients normalizedCoefficients:(int16_t * _Nonnull)normalizedCoefficients width:(NSInteger)width height:(NSInteger)height {
|
||||||
_dct->inverse4x4(coefficients, normalizedCoefficients, (int)width, (int)height);
|
_dct->inverse4x4Add(coefficients, normalizedCoefficients, (int)width, (int)height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -118,14 +118,17 @@ void scaleImagePlane(uint8_t *outPlane, int outWidth, int outHeight, int outByte
|
|||||||
}
|
}
|
||||||
|
|
||||||
void convertUInt8toInt16(uint8_t const *source, int16_t *dest, int length) {
|
void convertUInt8toInt16(uint8_t const *source, int16_t *dest, int length) {
|
||||||
for (int i = 0; i < length; i += 8) {
|
for (int i = 0; i < length; i += 8 * 4) {
|
||||||
uint8x8_t lhs8 = vld1_u8(&source[i]);
|
#pragma unroll
|
||||||
int16x8_t lhs = vreinterpretq_s16_u16(vmovl_u8(lhs8));
|
for (int j = 0; j < 4; j++) {
|
||||||
|
uint8x8_t lhs8 = vld1_u8(&source[i + j * 8]);
|
||||||
vst1q_s16(&dest[i], lhs);
|
int16x8_t lhs = vreinterpretq_s16_u16(vmovl_u8(lhs8));
|
||||||
|
|
||||||
|
vst1q_s16(&dest[i + j * 8], lhs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (length % 8 != 0) {
|
if (length % (8 * 4) != 0) {
|
||||||
for (int i = length - (length % 8); i < length; i++) {
|
for (int i = length - (length % (8 * 4)); i < length; i++) {
|
||||||
dest[i] = (int16_t)source[i];
|
dest[i] = (int16_t)source[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,14 +170,17 @@ void subtractArraysInt16(int16_t const *a, int16_t const *b, int16_t *dest, int
|
|||||||
}
|
}
|
||||||
|
|
||||||
void addArraysInt16(int16_t const *a, int16_t const *b, int16_t *dest, int length) {
|
void addArraysInt16(int16_t const *a, int16_t const *b, int16_t *dest, int length) {
|
||||||
for (int i = 0; i < length; i += 8) {
|
for (int i = 0; i < length; i += 8 * 4) {
|
||||||
int16x8_t lhs = vld1q_s16((int16_t *)&a[i]);
|
#pragma unroll
|
||||||
int16x8_t rhs = vld1q_s16((int16_t *)&b[i]);
|
for (int j = 0; j < 4; j++) {
|
||||||
int16x8_t result = vaddq_s16(lhs, rhs);
|
int16x8_t lhs = vld1q_s16((int16_t *)&a[i + j * 8]);
|
||||||
vst1q_s16((int16_t *)&dest[i], result);
|
int16x8_t rhs = vld1q_s16((int16_t *)&b[i + j * 8]);
|
||||||
|
int16x8_t result = vaddq_s16(lhs, rhs);
|
||||||
|
vst1q_s16((int16_t *)&dest[i + j * 8], result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (length % 8 != 0) {
|
if (length % (8 * 4) != 0) {
|
||||||
for (int i = length - (length % 8); i < length; i++) {
|
for (int i = length - (length % (8 * 4)); i < length; i++) {
|
||||||
dest[i] = a[i] - b[i];
|
dest[i] = a[i] - b[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -473,8 +473,8 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
|
|||||||
differenceCoefficients.dct4x4(dctData: dctData, target: dctCoefficients)
|
differenceCoefficients.dct4x4(dctData: dctData, target: dctCoefficients)
|
||||||
|
|
||||||
//previous + delta = current
|
//previous + delta = current
|
||||||
dctCoefficients.idct4x4(dctData: dctData, target: differenceCoefficients)
|
dctCoefficients.idct4x4Add(dctData: dctData, target: previousFrameCoefficients)
|
||||||
previousFrameCoefficients.add(other: differenceCoefficients)
|
//previousFrameCoefficients.add(other: differenceCoefficients)
|
||||||
} else {
|
} else {
|
||||||
isKeyframe = true
|
isKeyframe = true
|
||||||
|
|
||||||
@ -746,30 +746,23 @@ private final class AnimationCacheItemAccessor {
|
|||||||
self.currentCoefficients = currentCoefficients
|
self.currentCoefficients = currentCoefficients
|
||||||
}
|
}
|
||||||
|
|
||||||
let deltaCoefficients: DctCoefficientsYUVA420
|
/*let deltaCoefficients: DctCoefficientsYUVA420
|
||||||
if let current = self.deltaCoefficients {
|
if let current = self.deltaCoefficients {
|
||||||
deltaCoefficients = current
|
deltaCoefficients = current
|
||||||
} else {
|
} else {
|
||||||
deltaCoefficients = DctCoefficientsYUVA420(width: yuvaSurface.yPlane.width, height: yuvaSurface.yPlane.height)
|
deltaCoefficients = DctCoefficientsYUVA420(width: yuvaSurface.yPlane.width, height: yuvaSurface.yPlane.height)
|
||||||
self.deltaCoefficients = deltaCoefficients
|
self.deltaCoefficients = deltaCoefficients
|
||||||
}
|
}*/
|
||||||
|
|
||||||
switch frameType {
|
switch frameType {
|
||||||
case 1:
|
case 1:
|
||||||
dctCoefficients.idct8x8(dctData: self.currentDctData, target: yuvaSurface)
|
dctCoefficients.idct8x8(dctData: self.currentDctData, target: yuvaSurface)
|
||||||
yuvaSurface.toCoefficients(target: currentCoefficients)
|
yuvaSurface.toCoefficients(target: currentCoefficients)
|
||||||
default:
|
default:
|
||||||
dctCoefficients.idct4x4(dctData: self.currentDctData, target: deltaCoefficients)
|
dctCoefficients.idct4x4Add(dctData: self.currentDctData, target: currentCoefficients)
|
||||||
currentCoefficients.add(other: deltaCoefficients)
|
//currentCoefficients.add(other: deltaCoefficients)
|
||||||
|
|
||||||
if !"".isEmpty {
|
currentCoefficients.toYUVA420(target: yuvaSurface)
|
||||||
let deltaFloatCoefficients = FloatCoefficientsYUVA420(width: yuvaSurface.yPlane.width, height: yuvaSurface.yPlane.height)
|
|
||||||
deltaCoefficients.toFloatCoefficients(target: deltaFloatCoefficients)
|
|
||||||
deltaFloatCoefficients.add(constant: 128.0)
|
|
||||||
deltaFloatCoefficients.toYUVA420(target: yuvaSurface)
|
|
||||||
} else {
|
|
||||||
currentCoefficients.toYUVA420(target: yuvaSurface)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.currentFrame = CurrentFrame(index: index, duration: self.durationMapping[index], yuva: yuvaSurface)
|
self.currentFrame = CurrentFrame(index: index, duration: self.durationMapping[index], yuva: yuvaSurface)
|
||||||
|
|||||||
@ -663,7 +663,7 @@ extension DctCoefficientsYUVA420 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func idct4x4(dctData: DctData, target: DctCoefficientsYUVA420) {
|
func idct4x4Add(dctData: DctData, target: DctCoefficientsYUVA420) {
|
||||||
precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height)
|
precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height)
|
||||||
|
|
||||||
for i in 0 ..< 4 {
|
for i in 0 ..< 4 {
|
||||||
@ -694,7 +694,7 @@ extension DctCoefficientsYUVA420 {
|
|||||||
|
|
||||||
//memcpy(coefficients, sourceCoefficients, sourceBytes.count)
|
//memcpy(coefficients, sourceCoefficients, sourceBytes.count)
|
||||||
|
|
||||||
dctData.deltaDct.inverse4x4(sourceCoefficients, normalizedCoefficients: coefficients, width: sourcePlane.width, height: sourcePlane.height)
|
dctData.deltaDct.inverse4x4Add(sourceCoefficients, normalizedCoefficients: coefficients, width: sourcePlane.width, height: sourcePlane.height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,29 +16,48 @@ import MultiAnimationRenderer
|
|||||||
import ShimmerEffect
|
import ShimmerEffect
|
||||||
import TextFormat
|
import TextFormat
|
||||||
|
|
||||||
public func animationCacheFetchFile(context: AccountContext, file: TelegramMediaFile, keyframeOnly: Bool) -> (AnimationCacheFetchOptions) -> Disposable {
|
public enum AnimationCacheAnimationType {
|
||||||
|
case still
|
||||||
|
case lottie
|
||||||
|
case video
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension AnimationCacheAnimationType {
|
||||||
|
init(file: TelegramMediaFile) {
|
||||||
|
if file.isVideoSticker || file.isVideoEmoji {
|
||||||
|
self = .video
|
||||||
|
} else if file.isAnimatedSticker {
|
||||||
|
self = .lottie
|
||||||
|
} else {
|
||||||
|
self = .still
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func animationCacheFetchFile(context: AccountContext, resource: MediaResourceReference, type: AnimationCacheAnimationType, keyframeOnly: Bool) -> (AnimationCacheFetchOptions) -> Disposable {
|
||||||
return { options in
|
return { options in
|
||||||
let source = AnimatedStickerResourceSource(account: context.account, resource: file.resource, fitzModifier: nil, isVideo: false)
|
let source = AnimatedStickerResourceSource(account: context.account, resource: resource.resource, fitzModifier: nil, isVideo: false)
|
||||||
|
|
||||||
let dataDisposable = source.directDataPath(attemptSynchronously: false).start(next: { result in
|
let dataDisposable = source.directDataPath(attemptSynchronously: false).start(next: { result in
|
||||||
guard let result = result else {
|
guard let result = result else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if file.isVideoEmoji || file.isVideoSticker {
|
switch type {
|
||||||
|
case .video:
|
||||||
cacheVideoAnimation(path: result, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, firstFrameOnly: options.firstFrameOnly)
|
cacheVideoAnimation(path: result, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, firstFrameOnly: options.firstFrameOnly)
|
||||||
} else if file.isAnimatedSticker {
|
case .lottie:
|
||||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: result)) else {
|
guard let data = try? Data(contentsOf: URL(fileURLWithPath: result)) else {
|
||||||
options.writer.finish()
|
options.writer.finish()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cacheLottieAnimation(data: data, width: Int(options.size.width), height: Int(options.size.height), keyframeOnly: keyframeOnly, writer: options.writer, firstFrameOnly: options.firstFrameOnly)
|
cacheLottieAnimation(data: data, width: Int(options.size.width), height: Int(options.size.height), keyframeOnly: keyframeOnly, writer: options.writer, firstFrameOnly: options.firstFrameOnly)
|
||||||
} else {
|
case .still:
|
||||||
cacheStillSticker(path: result, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer)
|
cacheStillSticker(path: result, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let fetchDisposable = freeMediaFileResourceInteractiveFetched(account: context.account, fileReference: stickerPackFileReference(file), resource: file.resource).start()
|
let fetchDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: resource).start()
|
||||||
|
|
||||||
return ActionDisposable {
|
return ActionDisposable {
|
||||||
dataDisposable.dispose()
|
dataDisposable.dispose()
|
||||||
@ -165,7 +184,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
|
|||||||
} else {
|
} else {
|
||||||
let pointSize = self.pointSize
|
let pointSize = self.pointSize
|
||||||
let placeholderColor = self.placeholderColor
|
let placeholderColor = self.placeholderColor
|
||||||
self.loadDisposable = self.renderer.loadFirstFrame(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: animationCacheFetchFile(context: self.context, file: file, keyframeOnly: true), completion: { [weak self] result, isFinal in
|
self.loadDisposable = self.renderer.loadFirstFrame(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: animationCacheFetchFile(context: self.context, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: true), completion: { [weak self] result, isFinal in
|
||||||
if !result {
|
if !result {
|
||||||
if !isFinal {
|
if !isFinal {
|
||||||
return
|
return
|
||||||
@ -204,7 +223,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
|
|||||||
if file.isAnimatedSticker || file.isVideoEmoji {
|
if file.isAnimatedSticker || file.isVideoEmoji {
|
||||||
let keyframeOnly = self.pixelSize.width >= 120.0
|
let keyframeOnly = self.pixelSize.width >= 120.0
|
||||||
|
|
||||||
self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: animationCacheFetchFile(context: context, file: file, keyframeOnly: keyframeOnly))
|
self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: animationCacheFetchFile(context: context, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: keyframeOnly))
|
||||||
} else {
|
} else {
|
||||||
self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: { options in
|
self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: { options in
|
||||||
let dataDisposable = context.account.postbox.mediaBox.resourceData(file.resource).start(next: { result in
|
let dataDisposable = context.account.postbox.mediaBox.resourceData(file.resource).start(next: { result in
|
||||||
|
|||||||
@ -30,6 +30,77 @@ private let premiumBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bund
|
|||||||
private let featuredBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeAdd"), color: .white)
|
private let featuredBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeAdd"), color: .white)
|
||||||
private let lockedBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeLock"), color: .white)
|
private let lockedBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeLock"), color: .white)
|
||||||
|
|
||||||
|
public final class EntityKeyboardAnimationData: Equatable {
|
||||||
|
public enum Id: Hashable {
|
||||||
|
case file(MediaId)
|
||||||
|
case stickerPackThumbnail(ItemCollectionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ItemType {
|
||||||
|
case still
|
||||||
|
case lottie
|
||||||
|
case video
|
||||||
|
|
||||||
|
var animationCacheAnimationType: AnimationCacheAnimationType {
|
||||||
|
switch self {
|
||||||
|
case .still:
|
||||||
|
return .still
|
||||||
|
case .lottie:
|
||||||
|
return .lottie
|
||||||
|
case .video:
|
||||||
|
return .video
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public let id: Id
|
||||||
|
public let type: ItemType
|
||||||
|
public let resource: MediaResourceReference
|
||||||
|
public let dimensions: CGSize
|
||||||
|
public let immediateThumbnailData: Data?
|
||||||
|
|
||||||
|
public init(id: Id, type: ItemType, resource: MediaResourceReference, dimensions: CGSize, immediateThumbnailData: Data?) {
|
||||||
|
self.id = id
|
||||||
|
self.type = type
|
||||||
|
self.resource = resource
|
||||||
|
self.dimensions = dimensions
|
||||||
|
self.immediateThumbnailData = immediateThumbnailData
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init(file: TelegramMediaFile) {
|
||||||
|
let type: ItemType
|
||||||
|
if file.isVideoSticker || file.isVideoEmoji {
|
||||||
|
type = .video
|
||||||
|
} else if file.isAnimatedSticker {
|
||||||
|
type = .lottie
|
||||||
|
} else {
|
||||||
|
type = .still
|
||||||
|
}
|
||||||
|
self.init(id: .file(file.fileId), type: type, resource: .standalone(resource: file.resource), dimensions: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), immediateThumbnailData: file.immediateThumbnailData)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: EntityKeyboardAnimationData, rhs: EntityKeyboardAnimationData) -> Bool {
|
||||||
|
if lhs === rhs {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if lhs.resource.resource.id != rhs.resource.resource.id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.dimensions != rhs.dimensions {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.type != rhs.type {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.immediateThumbnailData != rhs.immediateThumbnailData {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class PassthroughLayer: CALayer {
|
private final class PassthroughLayer: CALayer {
|
||||||
var mirrorLayer: CALayer?
|
var mirrorLayer: CALayer?
|
||||||
|
|
||||||
@ -1039,7 +1110,7 @@ private final class GroupEmbeddedView: UIScrollView, UIScrollViewDelegate, Pager
|
|||||||
if let itemRange = itemLayout.visibleItems(for: self.bounds) {
|
if let itemRange = itemLayout.visibleItems(for: self.bounds) {
|
||||||
for index in itemRange.lowerBound ..< itemRange.upperBound {
|
for index in itemRange.lowerBound ..< itemRange.upperBound {
|
||||||
let item = items[index]
|
let item = items[index]
|
||||||
let itemId = EmojiPagerContentComponent.View.ItemLayer.Key(groupId: AnyHashable(0), fileId: item.file?.fileId, staticEmoji: item.staticEmoji)
|
let itemId = EmojiPagerContentComponent.View.ItemLayer.Key(groupId: AnyHashable(0), itemId: item.animationData?.id, staticEmoji: item.staticEmoji)
|
||||||
validIds.insert(itemId)
|
validIds.insert(itemId)
|
||||||
|
|
||||||
let itemLayer: EmojiPagerContentComponent.View.ItemLayer
|
let itemLayer: EmojiPagerContentComponent.View.ItemLayer
|
||||||
@ -1050,7 +1121,7 @@ private final class GroupEmbeddedView: UIScrollView, UIScrollViewDelegate, Pager
|
|||||||
item: item,
|
item: item,
|
||||||
context: context,
|
context: context,
|
||||||
attemptSynchronousLoad: attemptSynchronousLoad,
|
attemptSynchronousLoad: attemptSynchronousLoad,
|
||||||
file: item.file,
|
animationData: item.animationData,
|
||||||
staticEmoji: item.staticEmoji,
|
staticEmoji: item.staticEmoji,
|
||||||
cache: cache,
|
cache: cache,
|
||||||
renderer: renderer,
|
renderer: renderer,
|
||||||
@ -1316,16 +1387,19 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class Item: Equatable {
|
public final class Item: Equatable {
|
||||||
public let file: TelegramMediaFile?
|
public let animationData: EntityKeyboardAnimationData?
|
||||||
|
public let itemFile: TelegramMediaFile?
|
||||||
public let staticEmoji: String?
|
public let staticEmoji: String?
|
||||||
public let subgroupId: Int32?
|
public let subgroupId: Int32?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
file: TelegramMediaFile?,
|
animationData: EntityKeyboardAnimationData?,
|
||||||
|
itemFile: TelegramMediaFile?,
|
||||||
staticEmoji: String?,
|
staticEmoji: String?,
|
||||||
subgroupId: Int32?
|
subgroupId: Int32?
|
||||||
) {
|
) {
|
||||||
self.file = file
|
self.animationData = animationData
|
||||||
|
self.itemFile = itemFile
|
||||||
self.staticEmoji = staticEmoji
|
self.staticEmoji = staticEmoji
|
||||||
self.subgroupId = subgroupId
|
self.subgroupId = subgroupId
|
||||||
}
|
}
|
||||||
@ -1334,7 +1408,10 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
if lhs === rhs {
|
if lhs === rhs {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if lhs.file?.fileId != rhs.file?.fileId {
|
if lhs.animationData?.resource.resource.id != rhs.animationData?.resource.resource.id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.itemFile?.fileId != rhs.itemFile?.fileId {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.staticEmoji != rhs.staticEmoji {
|
if lhs.staticEmoji != rhs.staticEmoji {
|
||||||
@ -1360,7 +1437,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
public let hasClear: Bool
|
public let hasClear: Bool
|
||||||
public let isExpandable: Bool
|
public let isExpandable: Bool
|
||||||
public let displayPremiumBadges: Bool
|
public let displayPremiumBadges: Bool
|
||||||
public let headerItem: EntityKeyboardGroupHeaderItem?
|
public let headerItem: EntityKeyboardAnimationData?
|
||||||
public let items: [Item]
|
public let items: [Item]
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
@ -1375,7 +1452,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
hasClear: Bool,
|
hasClear: Bool,
|
||||||
isExpandable: Bool,
|
isExpandable: Bool,
|
||||||
displayPremiumBadges: Bool,
|
displayPremiumBadges: Bool,
|
||||||
headerItem: EntityKeyboardGroupHeaderItem?,
|
headerItem: EntityKeyboardAnimationData?,
|
||||||
items: [Item]
|
items: [Item]
|
||||||
) {
|
) {
|
||||||
self.supergroupId = supergroupId
|
self.supergroupId = supergroupId
|
||||||
@ -1739,7 +1816,8 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
file: TelegramMediaFile,
|
dimensions: CGSize?,
|
||||||
|
immediateThumbnailData: Data?,
|
||||||
shimmerView: PortalSourceView?,
|
shimmerView: PortalSourceView?,
|
||||||
color: UIColor,
|
color: UIColor,
|
||||||
size: CGSize
|
size: CGSize
|
||||||
@ -1760,7 +1838,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
|
|
||||||
let useDirectContent = self.placeholderView == nil
|
let useDirectContent = self.placeholderView == nil
|
||||||
Queue.concurrentDefaultQueue().async { [weak self] in
|
Queue.concurrentDefaultQueue().async { [weak self] in
|
||||||
if let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: size, scale: min(2.0, UIScreenScale), imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: useDirectContent ? color : .black) {
|
if let image = generateStickerPlaceholderImage(data: immediateThumbnailData, size: size, scale: min(2.0, UIScreenScale), imageSize: dimensions ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: useDirectContent ? color : .black) {
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -1791,16 +1869,16 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
public final class ItemLayer: MultiAnimationRenderTarget {
|
public final class ItemLayer: MultiAnimationRenderTarget {
|
||||||
public struct Key: Hashable {
|
public struct Key: Hashable {
|
||||||
var groupId: AnyHashable
|
var groupId: AnyHashable
|
||||||
var fileId: MediaId?
|
var itemId: EntityKeyboardAnimationData.Id?
|
||||||
var staticEmoji: String?
|
var staticEmoji: String?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
groupId: AnyHashable,
|
groupId: AnyHashable,
|
||||||
fileId: MediaId?,
|
itemId: EntityKeyboardAnimationData.Id?,
|
||||||
staticEmoji: String?
|
staticEmoji: String?
|
||||||
) {
|
) {
|
||||||
self.groupId = groupId
|
self.groupId = groupId
|
||||||
self.fileId = fileId
|
self.itemId = itemId
|
||||||
self.staticEmoji = staticEmoji
|
self.staticEmoji = staticEmoji
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1813,7 +1891,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
|
|
||||||
let item: Item
|
let item: Item
|
||||||
|
|
||||||
private let file: TelegramMediaFile?
|
private let animationData: EntityKeyboardAnimationData?
|
||||||
private let staticEmoji: String?
|
private let staticEmoji: String?
|
||||||
private let placeholderColor: UIColor
|
private let placeholderColor: UIColor
|
||||||
private let size: CGSize
|
private let size: CGSize
|
||||||
@ -1839,7 +1917,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
item: Item,
|
item: Item,
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
attemptSynchronousLoad: Bool,
|
attemptSynchronousLoad: Bool,
|
||||||
file: TelegramMediaFile?,
|
animationData: EntityKeyboardAnimationData?,
|
||||||
staticEmoji: String?,
|
staticEmoji: String?,
|
||||||
cache: AnimationCache,
|
cache: AnimationCache,
|
||||||
renderer: MultiAnimationRenderer,
|
renderer: MultiAnimationRenderer,
|
||||||
@ -1849,7 +1927,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
onUpdateDisplayPlaceholder: @escaping (Bool, Double) -> Void
|
onUpdateDisplayPlaceholder: @escaping (Bool, Double) -> Void
|
||||||
) {
|
) {
|
||||||
self.item = item
|
self.item = item
|
||||||
self.file = file
|
self.animationData = animationData
|
||||||
self.staticEmoji = staticEmoji
|
self.staticEmoji = staticEmoji
|
||||||
self.placeholderColor = placeholderColor
|
self.placeholderColor = placeholderColor
|
||||||
self.onUpdateDisplayPlaceholder = onUpdateDisplayPlaceholder
|
self.onUpdateDisplayPlaceholder = onUpdateDisplayPlaceholder
|
||||||
@ -1860,20 +1938,20 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
if let file = file {
|
if let animationData = animationData {
|
||||||
let loadAnimation: () -> Void = { [weak self] in
|
let loadAnimation: () -> Void = { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
strongSelf.disposable = renderer.add(target: strongSelf, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: pixelSize.width >= 120.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
if attemptSynchronousLoad {
|
if attemptSynchronousLoad {
|
||||||
if !renderer.loadFirstFrameSynchronously(target: self, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize) {
|
if !renderer.loadFirstFrameSynchronously(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize) {
|
||||||
self.updateDisplayPlaceholder(displayPlaceholder: true)
|
self.updateDisplayPlaceholder(displayPlaceholder: true)
|
||||||
|
|
||||||
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
|
self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: true), completion: { [weak self] success, isFinal in
|
||||||
if !isFinal {
|
if !isFinal {
|
||||||
if !success {
|
if !success {
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
@ -1905,7 +1983,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
loadAnimation()
|
loadAnimation()
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: true), completion: { [weak self] success, isFinal in
|
||||||
if !isFinal {
|
if !isFinal {
|
||||||
if !success {
|
if !success {
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
@ -1961,7 +2039,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
|
|
||||||
self.item = layer.item
|
self.item = layer.item
|
||||||
|
|
||||||
self.file = layer.file
|
self.animationData = layer.animationData
|
||||||
self.staticEmoji = layer.staticEmoji
|
self.staticEmoji = layer.staticEmoji
|
||||||
self.placeholderColor = layer.placeholderColor
|
self.placeholderColor = layer.placeholderColor
|
||||||
self.size = layer.size
|
self.size = layer.size
|
||||||
@ -2255,7 +2333,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
guard let strongSelf = self, let component = strongSelf.component else {
|
guard let strongSelf = self, let component = strongSelf.component else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
guard let item = strongSelf.item(atPoint: point), let itemLayer = strongSelf.visibleItemLayers[item.1], let file = item.0.file else {
|
guard let item = strongSelf.item(atPoint: point), let itemLayer = strongSelf.visibleItemLayers[item.1], let file = item.0.itemFile else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if itemLayer.displayPlaceholder {
|
if itemLayer.displayPlaceholder {
|
||||||
@ -3369,15 +3447,11 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemId = ItemLayer.Key(groupId: itemGroup.groupId, fileId: item.file?.fileId, staticEmoji: item.staticEmoji)
|
let itemId = ItemLayer.Key(groupId: itemGroup.groupId, itemId: item.animationData?.id, staticEmoji: item.staticEmoji)
|
||||||
validIds.insert(itemId)
|
validIds.insert(itemId)
|
||||||
|
|
||||||
let itemDimensions: CGSize
|
let itemDimensions: CGSize = item.animationData?.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
if let file = item.file {
|
|
||||||
itemDimensions = file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
|
|
||||||
} else {
|
|
||||||
itemDimensions = CGSize(width: 512.0, height: 512.0)
|
|
||||||
}
|
|
||||||
let itemNativeFitSize = itemDimensions.aspectFitted(CGSize(width: itemLayout.nativeItemSize, height: itemLayout.nativeItemSize))
|
let itemNativeFitSize = itemDimensions.aspectFitted(CGSize(width: itemLayout.nativeItemSize, height: itemLayout.nativeItemSize))
|
||||||
let itemVisibleFitSize = itemDimensions.aspectFitted(CGSize(width: itemLayout.visibleItemSize, height: itemLayout.visibleItemSize))
|
let itemVisibleFitSize = itemDimensions.aspectFitted(CGSize(width: itemLayout.visibleItemSize, height: itemLayout.visibleItemSize))
|
||||||
let itemPlaybackSize = itemDimensions.aspectFitted(CGSize(width: itemLayout.playbackItemSize, height: itemLayout.playbackItemSize))
|
let itemPlaybackSize = itemDimensions.aspectFitted(CGSize(width: itemLayout.playbackItemSize, height: itemLayout.playbackItemSize))
|
||||||
@ -3398,7 +3472,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
item: item,
|
item: item,
|
||||||
context: component.context,
|
context: component.context,
|
||||||
attemptSynchronousLoad: attemptSynchronousLoads,
|
attemptSynchronousLoad: attemptSynchronousLoads,
|
||||||
file: item.file,
|
animationData: item.animationData,
|
||||||
staticEmoji: item.staticEmoji,
|
staticEmoji: item.staticEmoji,
|
||||||
cache: component.animationCache,
|
cache: component.animationCache,
|
||||||
renderer: component.animationRenderer,
|
renderer: component.animationRenderer,
|
||||||
@ -3409,7 +3483,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if displayPlaceholder, let file = item.file {
|
if displayPlaceholder, let animationData = item.animationData {
|
||||||
if let itemLayer = strongSelf.visibleItemLayers[itemId] {
|
if let itemLayer = strongSelf.visibleItemLayers[itemId] {
|
||||||
let placeholderView: ItemPlaceholderView
|
let placeholderView: ItemPlaceholderView
|
||||||
if let current = strongSelf.visibleItemPlaceholderViews[itemId] {
|
if let current = strongSelf.visibleItemPlaceholderViews[itemId] {
|
||||||
@ -3417,7 +3491,8 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
placeholderView = ItemPlaceholderView(
|
placeholderView = ItemPlaceholderView(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
file: file,
|
dimensions: animationData.dimensions,
|
||||||
|
immediateThumbnailData: animationData.immediateThumbnailData,
|
||||||
shimmerView: strongSelf.shimmerHostView,
|
shimmerView: strongSelf.shimmerHostView,
|
||||||
color: placeholderColor,
|
color: placeholderColor,
|
||||||
size: itemNativeFitSize
|
size: itemNativeFitSize
|
||||||
@ -3482,7 +3557,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
itemTransition.setPosition(layer: itemLayer, position: itemPosition)
|
itemTransition.setPosition(layer: itemLayer, position: itemPosition)
|
||||||
|
|
||||||
var badge: ItemLayer.Badge?
|
var badge: ItemLayer.Badge?
|
||||||
if itemGroup.displayPremiumBadges, let file = item.file, file.isPremiumSticker {
|
if itemGroup.displayPremiumBadges, let file = item.itemFile, file.isPremiumSticker {
|
||||||
badge = .premium
|
badge = .premium
|
||||||
}
|
}
|
||||||
itemLayer.update(transition: transition, size: itemFrame.size, badge: badge, blurredBadgeColor: UIColor(white: 0.0, alpha: 0.1), blurredBadgeBackgroundColor: keyboardChildEnvironment.theme.list.plainBackgroundColor)
|
itemLayer.update(transition: transition, size: itemFrame.size, badge: badge, blurredBadgeColor: UIColor(white: 0.0, alpha: 0.1), blurredBadgeBackgroundColor: keyboardChildEnvironment.theme.list.plainBackgroundColor)
|
||||||
@ -3798,10 +3873,10 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
for itemIndex in 0 ..< itemGroup.items.count {
|
for itemIndex in 0 ..< itemGroup.items.count {
|
||||||
let item = itemGroup.items[itemIndex]
|
let item = itemGroup.items[itemIndex]
|
||||||
let itemKey: ItemLayer.Key
|
let itemKey: ItemLayer.Key
|
||||||
if let file = item.file {
|
if let animationData = item.animationData {
|
||||||
itemKey = ItemLayer.Key(groupId: itemGroup.groupId, fileId: file.fileId, staticEmoji: nil)
|
itemKey = ItemLayer.Key(groupId: itemGroup.groupId, itemId: animationData.id, staticEmoji: nil)
|
||||||
} else if let staticEmoji = item.staticEmoji {
|
} else if let staticEmoji = item.staticEmoji {
|
||||||
itemKey = ItemLayer.Key(groupId: itemGroup.groupId, fileId: nil, staticEmoji: staticEmoji)
|
itemKey = ItemLayer.Key(groupId: itemGroup.groupId, itemId: nil, staticEmoji: staticEmoji)
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -3971,10 +4046,10 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
for j in 0 ..< component.itemGroups[i].items.count {
|
for j in 0 ..< component.itemGroups[i].items.count {
|
||||||
let itemKey: ItemLayer.Key
|
let itemKey: ItemLayer.Key
|
||||||
if let file = component.itemGroups[i].items[j].file {
|
if let animationData = component.itemGroups[i].items[j].animationData {
|
||||||
itemKey = ItemLayer.Key(groupId: component.itemGroups[i].groupId, fileId: file.fileId, staticEmoji: nil)
|
itemKey = ItemLayer.Key(groupId: component.itemGroups[i].groupId, itemId: animationData.id, staticEmoji: nil)
|
||||||
} else if let staticEmoji = component.itemGroups[i].items[j].staticEmoji {
|
} else if let staticEmoji = component.itemGroups[i].items[j].staticEmoji {
|
||||||
itemKey = ItemLayer.Key(groupId: component.itemGroups[i].groupId, fileId: nil, staticEmoji: staticEmoji)
|
itemKey = ItemLayer.Key(groupId: component.itemGroups[i].groupId, itemId: nil, staticEmoji: staticEmoji)
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -4013,10 +4088,10 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
for itemIndex in 0 ..< itemGroup.items.count {
|
for itemIndex in 0 ..< itemGroup.items.count {
|
||||||
let item = itemGroup.items[itemIndex]
|
let item = itemGroup.items[itemIndex]
|
||||||
let itemKey: ItemLayer.Key
|
let itemKey: ItemLayer.Key
|
||||||
if let file = item.file {
|
if let animationData = item.animationData {
|
||||||
itemKey = ItemLayer.Key(groupId: itemGroup.groupId, fileId: file.fileId, staticEmoji: nil)
|
itemKey = ItemLayer.Key(groupId: itemGroup.groupId, itemId: animationData.id, staticEmoji: nil)
|
||||||
} else if let staticEmoji = item.staticEmoji {
|
} else if let staticEmoji = item.staticEmoji {
|
||||||
itemKey = ItemLayer.Key(groupId: itemGroup.groupId, fileId: nil, staticEmoji: staticEmoji)
|
itemKey = ItemLayer.Key(groupId: itemGroup.groupId, itemId: nil, staticEmoji: staticEmoji)
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@ -271,7 +271,7 @@ public final class EntityKeyboardComponent: Component {
|
|||||||
isReorderable: false,
|
isReorderable: false,
|
||||||
content: AnyComponent(EntityKeyboardAnimationTopPanelComponent(
|
content: AnyComponent(EntityKeyboardAnimationTopPanelComponent(
|
||||||
context: component.emojiContent.context,
|
context: component.emojiContent.context,
|
||||||
item: .file(file: emoji.file),
|
item: EntityKeyboardAnimationData(file: emoji.file),
|
||||||
isFeatured: false,
|
isFeatured: false,
|
||||||
isPremiumLocked: false,
|
isPremiumLocked: false,
|
||||||
animationCache: component.emojiContent.animationCache,
|
animationCache: component.emojiContent.animationCache,
|
||||||
@ -359,13 +359,13 @@ public final class EntityKeyboardComponent: Component {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !itemGroup.items.isEmpty {
|
if !itemGroup.items.isEmpty {
|
||||||
if let file = itemGroup.items[0].file {
|
if let animationData = itemGroup.items[0].animationData {
|
||||||
topStickerItems.append(EntityKeyboardTopPanelComponent.Item(
|
topStickerItems.append(EntityKeyboardTopPanelComponent.Item(
|
||||||
id: itemGroup.supergroupId,
|
id: itemGroup.supergroupId,
|
||||||
isReorderable: !itemGroup.isFeatured,
|
isReorderable: !itemGroup.isFeatured,
|
||||||
content: AnyComponent(EntityKeyboardAnimationTopPanelComponent(
|
content: AnyComponent(EntityKeyboardAnimationTopPanelComponent(
|
||||||
context: stickerContent.context,
|
context: stickerContent.context,
|
||||||
item: itemGroup.headerItem ?? .file(file: file),
|
item: itemGroup.headerItem ?? animationData,
|
||||||
isFeatured: itemGroup.isFeatured,
|
isFeatured: itemGroup.isFeatured,
|
||||||
isPremiumLocked: itemGroup.isPremiumLocked,
|
isPremiumLocked: itemGroup.isPremiumLocked,
|
||||||
animationCache: stickerContent.animationCache,
|
animationCache: stickerContent.animationCache,
|
||||||
@ -462,13 +462,13 @@ public final class EntityKeyboardComponent: Component {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let file = itemGroup.items[0].file {
|
if let animationData = itemGroup.items[0].animationData {
|
||||||
topEmojiItems.append(EntityKeyboardTopPanelComponent.Item(
|
topEmojiItems.append(EntityKeyboardTopPanelComponent.Item(
|
||||||
id: itemGroup.supergroupId,
|
id: itemGroup.supergroupId,
|
||||||
isReorderable: !itemGroup.isFeatured,
|
isReorderable: !itemGroup.isFeatured,
|
||||||
content: AnyComponent(EntityKeyboardAnimationTopPanelComponent(
|
content: AnyComponent(EntityKeyboardAnimationTopPanelComponent(
|
||||||
context: component.emojiContent.context,
|
context: component.emojiContent.context,
|
||||||
item: itemGroup.headerItem ?? .file(file: file),
|
item: itemGroup.headerItem ?? animationData,
|
||||||
isFeatured: itemGroup.isFeatured,
|
isFeatured: itemGroup.isFeatured,
|
||||||
isPremiumLocked: itemGroup.isPremiumLocked,
|
isPremiumLocked: itemGroup.isPremiumLocked,
|
||||||
animationCache: component.emojiContent.animationCache,
|
animationCache: component.emojiContent.animationCache,
|
||||||
|
|||||||
@ -14,65 +14,11 @@ import MultilineTextComponent
|
|||||||
import LottieAnimationComponent
|
import LottieAnimationComponent
|
||||||
import MurMurHash32
|
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 {
|
final class EntityKeyboardAnimationTopPanelComponent: Component {
|
||||||
typealias EnvironmentType = EntityKeyboardTopPanelItemEnvironment
|
typealias EnvironmentType = EntityKeyboardTopPanelItemEnvironment
|
||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let item: EntityKeyboardGroupHeaderItem
|
let item: EntityKeyboardAnimationData
|
||||||
let isFeatured: Bool
|
let isFeatured: Bool
|
||||||
let isPremiumLocked: Bool
|
let isPremiumLocked: Bool
|
||||||
let animationCache: AnimationCache
|
let animationCache: AnimationCache
|
||||||
@ -83,7 +29,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
|||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
item: EntityKeyboardGroupHeaderItem,
|
item: EntityKeyboardAnimationData,
|
||||||
isFeatured: Bool,
|
isFeatured: Bool,
|
||||||
isPremiumLocked: Bool,
|
isPremiumLocked: Bool,
|
||||||
animationCache: AnimationCache,
|
animationCache: AnimationCache,
|
||||||
@ -159,27 +105,20 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
|||||||
|
|
||||||
let itemEnvironment = environment[EntityKeyboardTopPanelItemEnvironment.self].value
|
let itemEnvironment = environment[EntityKeyboardTopPanelItemEnvironment.self].value
|
||||||
|
|
||||||
let dimensions: CGSize
|
let dimensions: CGSize = component.item.dimensions
|
||||||
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))
|
let displaySize = dimensions.aspectFitted(CGSize(width: 44.0, height: 44.0))
|
||||||
|
|
||||||
if self.itemLayer == nil {
|
if self.itemLayer == nil {
|
||||||
let file = fileFromItem(component.item)
|
|
||||||
|
|
||||||
let itemLayer = EmojiPagerContentComponent.View.ItemLayer(
|
let itemLayer = EmojiPagerContentComponent.View.ItemLayer(
|
||||||
item: EmojiPagerContentComponent.Item(
|
item: EmojiPagerContentComponent.Item(
|
||||||
file: file,
|
animationData: component.item,
|
||||||
|
itemFile: nil,
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
subgroupId: nil
|
subgroupId: nil
|
||||||
),
|
),
|
||||||
context: component.context,
|
context: component.context,
|
||||||
attemptSynchronousLoad: false,
|
attemptSynchronousLoad: false,
|
||||||
file: file,
|
animationData: component.item,
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
cache: component.animationCache,
|
cache: component.animationCache,
|
||||||
renderer: component.animationRenderer,
|
renderer: component.animationRenderer,
|
||||||
@ -267,7 +206,8 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
|||||||
if self.placeholderView == nil, let component = self.component {
|
if self.placeholderView == nil, let component = self.component {
|
||||||
let placeholderView = EmojiPagerContentComponent.View.ItemPlaceholderView(
|
let placeholderView = EmojiPagerContentComponent.View.ItemPlaceholderView(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
file: fileFromItem(component.item),
|
dimensions: component.item.dimensions,
|
||||||
|
immediateThumbnailData: component.item.immediateThumbnailData,
|
||||||
shimmerView: nil,
|
shimmerView: nil,
|
||||||
color: component.theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.08),
|
color: component.theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.08),
|
||||||
size: CGSize(width: 28.0, height: 28.0)
|
size: CGSize(width: 28.0, height: 28.0)
|
||||||
|
|||||||
@ -115,7 +115,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
var isPremiumLocked: Bool
|
var isPremiumLocked: Bool
|
||||||
var isFeatured: Bool
|
var isFeatured: Bool
|
||||||
var isExpandable: Bool
|
var isExpandable: Bool
|
||||||
var headerItem: EntityKeyboardGroupHeaderItem?
|
var headerItem: EntityKeyboardAnimationData?
|
||||||
var items: [EmojiPagerContentComponent.Item]
|
var items: [EmojiPagerContentComponent.Item]
|
||||||
}
|
}
|
||||||
var itemGroups: [ItemGroup] = []
|
var itemGroups: [ItemGroup] = []
|
||||||
@ -146,13 +146,15 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
switch item.content {
|
switch item.content {
|
||||||
case let .file(file):
|
case let .file(file):
|
||||||
resultItem = EmojiPagerContentComponent.Item(
|
resultItem = EmojiPagerContentComponent.Item(
|
||||||
file: file,
|
animationData: EntityKeyboardAnimationData(file: file),
|
||||||
|
itemFile: file,
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
subgroupId: nil
|
subgroupId: nil
|
||||||
)
|
)
|
||||||
case let .text(text):
|
case let .text(text):
|
||||||
resultItem = EmojiPagerContentComponent.Item(
|
resultItem = EmojiPagerContentComponent.Item(
|
||||||
file: nil,
|
animationData: nil,
|
||||||
|
itemFile: nil,
|
||||||
staticEmoji: text,
|
staticEmoji: text,
|
||||||
subgroupId: nil
|
subgroupId: nil
|
||||||
)
|
)
|
||||||
@ -172,7 +174,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
let groupId: AnyHashable = "static"
|
let groupId: AnyHashable = "static"
|
||||||
for emojiString in list {
|
for emojiString in list {
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
file: nil,
|
animationData: nil,
|
||||||
|
itemFile: nil,
|
||||||
staticEmoji: emojiString,
|
staticEmoji: emojiString,
|
||||||
subgroupId: subgroupId.rawValue
|
subgroupId: subgroupId.rawValue
|
||||||
)
|
)
|
||||||
@ -197,7 +200,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
file: item.file,
|
animationData: EntityKeyboardAnimationData(file: item.file),
|
||||||
|
itemFile: item.file,
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
subgroupId: nil
|
subgroupId: nil
|
||||||
)
|
)
|
||||||
@ -214,13 +218,13 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
itemGroupIndexById[groupId] = itemGroups.count
|
itemGroupIndexById[groupId] = itemGroups.count
|
||||||
|
|
||||||
var title = ""
|
var title = ""
|
||||||
var headerItem: EntityKeyboardGroupHeaderItem?
|
var headerItem: EntityKeyboardAnimationData?
|
||||||
inner: for (id, info, _) in view.collectionInfos {
|
inner: for (id, info, _) in view.collectionInfos {
|
||||||
if id == entry.index.collectionId, let info = info as? StickerPackCollectionInfo {
|
if id == entry.index.collectionId, let info = info as? StickerPackCollectionInfo {
|
||||||
title = info.title
|
title = info.title
|
||||||
|
|
||||||
if let thumbnail = info.thumbnail {
|
if let thumbnail = info.thumbnail {
|
||||||
let type: EntityKeyboardGroupHeaderItem.ThumbnailType
|
let type: EntityKeyboardAnimationData.ItemType
|
||||||
if item.file.isAnimatedSticker {
|
if item.file.isAnimatedSticker {
|
||||||
type = .lottie
|
type = .lottie
|
||||||
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
||||||
@ -229,7 +233,13 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
type = .still
|
type = .still
|
||||||
}
|
}
|
||||||
|
|
||||||
headerItem = .packThumbnail(resource: MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource), immediateThumbnailData: info.immediateThumbnailData, dimensions: thumbnail.dimensions.cgSize, type: type)
|
headerItem = EntityKeyboardAnimationData(
|
||||||
|
id: .stickerPackThumbnail(info.id),
|
||||||
|
type: type,
|
||||||
|
resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource),
|
||||||
|
dimensions: thumbnail.dimensions.cgSize,
|
||||||
|
immediateThumbnailData: info.immediateThumbnailData
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
break inner
|
break inner
|
||||||
@ -247,7 +257,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
|
|
||||||
for item in featuredEmojiPack.topItems {
|
for item in featuredEmojiPack.topItems {
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
file: item.file,
|
animationData: EntityKeyboardAnimationData(file: item.file),
|
||||||
|
itemFile: item.file,
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
subgroupId: nil
|
subgroupId: nil
|
||||||
)
|
)
|
||||||
@ -263,10 +274,10 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
} else {
|
} else {
|
||||||
itemGroupIndexById[groupId] = itemGroups.count
|
itemGroupIndexById[groupId] = itemGroups.count
|
||||||
|
|
||||||
var headerItem: EntityKeyboardGroupHeaderItem?
|
var headerItem: EntityKeyboardAnimationData?
|
||||||
|
|
||||||
if let thumbnail = featuredEmojiPack.info.thumbnail {
|
if let thumbnail = featuredEmojiPack.info.thumbnail {
|
||||||
let type: EntityKeyboardGroupHeaderItem.ThumbnailType
|
let info = featuredEmojiPack.info
|
||||||
|
let type: EntityKeyboardAnimationData.ItemType
|
||||||
if item.file.isAnimatedSticker {
|
if item.file.isAnimatedSticker {
|
||||||
type = .lottie
|
type = .lottie
|
||||||
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
||||||
@ -275,7 +286,13 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
type = .still
|
type = .still
|
||||||
}
|
}
|
||||||
|
|
||||||
headerItem = .packThumbnail(resource: MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: featuredEmojiPack.info.id.id, accessHash: featuredEmojiPack.info.accessHash), resource: thumbnail.resource), immediateThumbnailData: featuredEmojiPack.info.immediateThumbnailData, dimensions: thumbnail.dimensions.cgSize, type: type)
|
headerItem = EntityKeyboardAnimationData(
|
||||||
|
id: .stickerPackThumbnail(info.id),
|
||||||
|
type: type,
|
||||||
|
resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource),
|
||||||
|
dimensions: thumbnail.dimensions.cgSize,
|
||||||
|
immediateThumbnailData: info.immediateThumbnailData
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
itemGroups.append(ItemGroup(supergroupId: supergroupId, id: groupId, title: featuredEmojiPack.info.title, subtitle: nil, isPremiumLocked: isPremiumLocked, isFeatured: true, isExpandable: true, headerItem: headerItem, items: [resultItem]))
|
itemGroups.append(ItemGroup(supergroupId: supergroupId, id: groupId, title: featuredEmojiPack.info.title, subtitle: nil, isPremiumLocked: isPremiumLocked, isFeatured: true, isExpandable: true, headerItem: headerItem, items: [resultItem]))
|
||||||
@ -366,7 +383,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
var isPremiumLocked: Bool
|
var isPremiumLocked: Bool
|
||||||
var isFeatured: Bool
|
var isFeatured: Bool
|
||||||
var displayPremiumBadges: Bool
|
var displayPremiumBadges: Bool
|
||||||
var headerItem: EntityKeyboardGroupHeaderItem?
|
var headerItem: EntityKeyboardAnimationData?
|
||||||
var items: [EmojiPagerContentComponent.Item]
|
var items: [EmojiPagerContentComponent.Item]
|
||||||
}
|
}
|
||||||
var itemGroups: [ItemGroup] = []
|
var itemGroups: [ItemGroup] = []
|
||||||
@ -405,7 +422,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
file: item.file,
|
animationData: EntityKeyboardAnimationData(file: item.file),
|
||||||
|
itemFile: item.file,
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
subgroupId: nil
|
subgroupId: nil
|
||||||
)
|
)
|
||||||
@ -439,7 +457,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
file: item.file,
|
animationData: EntityKeyboardAnimationData(file: item.file),
|
||||||
|
itemFile: item.file,
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
subgroupId: nil
|
subgroupId: nil
|
||||||
)
|
)
|
||||||
@ -464,7 +483,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
file: item.media,
|
animationData: EntityKeyboardAnimationData(file: item.media),
|
||||||
|
itemFile: item.media,
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
subgroupId: nil
|
subgroupId: nil
|
||||||
)
|
)
|
||||||
@ -512,7 +532,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
processedIds.insert(item.file.fileId)
|
processedIds.insert(item.file.fileId)
|
||||||
|
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
file: item.file,
|
animationData: EntityKeyboardAnimationData(file: item.file),
|
||||||
|
itemFile: item.file,
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
subgroupId: nil
|
subgroupId: nil
|
||||||
)
|
)
|
||||||
@ -532,7 +553,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
file: item.file,
|
animationData: EntityKeyboardAnimationData(file: item.file),
|
||||||
|
itemFile: item.file,
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
subgroupId: nil
|
subgroupId: nil
|
||||||
)
|
)
|
||||||
@ -543,13 +565,13 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
itemGroupIndexById[groupId] = itemGroups.count
|
itemGroupIndexById[groupId] = itemGroups.count
|
||||||
|
|
||||||
var title = ""
|
var title = ""
|
||||||
var headerItem: EntityKeyboardGroupHeaderItem?
|
var headerItem: EntityKeyboardAnimationData?
|
||||||
inner: for (id, info, _) in view.collectionInfos {
|
inner: for (id, info, _) in view.collectionInfos {
|
||||||
if id == groupId, let info = info as? StickerPackCollectionInfo {
|
if id == groupId, let info = info as? StickerPackCollectionInfo {
|
||||||
title = info.title
|
title = info.title
|
||||||
|
|
||||||
if let thumbnail = info.thumbnail {
|
if let thumbnail = info.thumbnail {
|
||||||
let type: EntityKeyboardGroupHeaderItem.ThumbnailType
|
let type: EntityKeyboardAnimationData.ItemType
|
||||||
if item.file.isAnimatedSticker {
|
if item.file.isAnimatedSticker {
|
||||||
type = .lottie
|
type = .lottie
|
||||||
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
||||||
@ -558,7 +580,13 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
type = .still
|
type = .still
|
||||||
}
|
}
|
||||||
|
|
||||||
headerItem = .packThumbnail(resource: MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource), immediateThumbnailData: info.immediateThumbnailData, dimensions: thumbnail.dimensions.cgSize, type: type)
|
headerItem = EntityKeyboardAnimationData(
|
||||||
|
id: .stickerPackThumbnail(info.id),
|
||||||
|
type: type,
|
||||||
|
resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource),
|
||||||
|
dimensions: thumbnail.dimensions.cgSize,
|
||||||
|
immediateThumbnailData: info.immediateThumbnailData
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
break inner
|
break inner
|
||||||
@ -575,7 +603,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
|
|
||||||
for item in featuredStickerPack.topItems {
|
for item in featuredStickerPack.topItems {
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
file: item.file,
|
animationData: EntityKeyboardAnimationData(file: item.file),
|
||||||
|
itemFile: item.file,
|
||||||
staticEmoji: nil,
|
staticEmoji: nil,
|
||||||
subgroupId: nil
|
subgroupId: nil
|
||||||
)
|
)
|
||||||
@ -592,10 +621,11 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
itemGroupIndexById[groupId] = itemGroups.count
|
itemGroupIndexById[groupId] = itemGroups.count
|
||||||
|
|
||||||
let subtitle: String = strings.StickerPack_StickerCount(Int32(featuredStickerPack.info.count))
|
let subtitle: String = strings.StickerPack_StickerCount(Int32(featuredStickerPack.info.count))
|
||||||
var headerItem: EntityKeyboardGroupHeaderItem?
|
var headerItem: EntityKeyboardAnimationData?
|
||||||
|
|
||||||
if let thumbnail = featuredStickerPack.info.thumbnail {
|
if let thumbnail = featuredStickerPack.info.thumbnail {
|
||||||
let type: EntityKeyboardGroupHeaderItem.ThumbnailType
|
let info = featuredStickerPack.info
|
||||||
|
let type: EntityKeyboardAnimationData.ItemType
|
||||||
if item.file.isAnimatedSticker {
|
if item.file.isAnimatedSticker {
|
||||||
type = .lottie
|
type = .lottie
|
||||||
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
} else if item.file.isVideoEmoji || item.file.isVideoSticker {
|
||||||
@ -604,7 +634,13 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
type = .still
|
type = .still
|
||||||
}
|
}
|
||||||
|
|
||||||
headerItem = .packThumbnail(resource: MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: featuredStickerPack.info.id.id, accessHash: featuredStickerPack.info.accessHash), resource: thumbnail.resource), immediateThumbnailData: featuredStickerPack.info.immediateThumbnailData, dimensions: thumbnail.dimensions.cgSize, type: type)
|
headerItem = EntityKeyboardAnimationData(
|
||||||
|
id: .stickerPackThumbnail(info.id),
|
||||||
|
type: type,
|
||||||
|
resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource),
|
||||||
|
dimensions: thumbnail.dimensions.cgSize,
|
||||||
|
immediateThumbnailData: info.immediateThumbnailData
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: featuredStickerPack.info.title, subtitle: subtitle, actionButtonTitle: strings.Stickers_Install, isPremiumLocked: isPremiumLocked, isFeatured: true, displayPremiumBadges: false, headerItem: headerItem, items: [resultItem]))
|
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: featuredStickerPack.info.title, subtitle: subtitle, actionButtonTitle: strings.Stickers_Install, isPremiumLocked: isPremiumLocked, isFeatured: true, displayPremiumBadges: false, headerItem: headerItem, items: [resultItem]))
|
||||||
@ -1055,7 +1091,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let file = item.file {
|
if let file = item.itemFile {
|
||||||
var text = "."
|
var text = "."
|
||||||
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
|
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
|
||||||
loop: for attribute in file.attributes {
|
loop: for attribute in file.attributes {
|
||||||
@ -1215,7 +1251,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
guard let controllerInteraction = controllerInteraction, let interfaceInteraction = interfaceInteraction else {
|
guard let controllerInteraction = controllerInteraction, let interfaceInteraction = interfaceInteraction else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let file = item.file else {
|
guard let file = item.itemFile else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2041,7 +2077,7 @@ final class EntityInputView: UIView, AttachmentTextInputPanelInputView, UIInputV
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let file = item.file {
|
if let file = item.itemFile {
|
||||||
var text = "."
|
var text = "."
|
||||||
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
|
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
|
||||||
loop: for attribute in file.attributes {
|
loop: for attribute in file.attributes {
|
||||||
|
|||||||
@ -114,6 +114,15 @@ func textInputStateContextQueryRangeAndType(_ inputState: ChatTextInputState) ->
|
|||||||
if inputText.attribute(ChatTextInputAttributes.customEmoji, at: 0, effectiveRange: nil) == nil {
|
if inputText.attribute(ChatTextInputAttributes.customEmoji, at: 0, effectiveRange: nil) == nil {
|
||||||
return [(NSRange(location: 0, length: inputString.length - (string.count - trimmedString.count)), [.emoji], nil)]
|
return [(NSRange(location: 0, length: inputString.length - (string.count - trimmedString.count)), [.emoji], nil)]
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let activeString = inputText.attributedSubstring(from: NSRange(location: 0, length: inputState.selectionRange.upperBound))
|
||||||
|
if let lastCharacter = activeString.string.last, String(lastCharacter).isSingleEmoji {
|
||||||
|
let matchLength = (String(lastCharacter) as NSString).length
|
||||||
|
|
||||||
|
if activeString.attribute(ChatTextInputAttributes.customEmoji, at: activeString.length - matchLength, effectiveRange: nil) == nil {
|
||||||
|
return [(NSRange(location: inputState.selectionRange.upperBound - matchLength, length: matchLength), [.emojiSearch], nil)]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var possibleTypes = PossibleContextQueryTypes([.command, .mention, .hashtag, .emojiSearch])
|
var possibleTypes = PossibleContextQueryTypes([.command, .mention, .hashtag, .emojiSearch])
|
||||||
|
|||||||
@ -328,32 +328,16 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|
|||||||
|
|
||||||
return signal |> then(contextBot)
|
return signal |> then(contextBot)
|
||||||
case let .emojiSearch(query, languageCode, range):
|
case let .emojiSearch(query, languageCode, range):
|
||||||
var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query, completeMatch: query.count < 2)
|
if query.isSingleEmoji {
|
||||||
if !languageCode.lowercased().hasPrefix("en") {
|
let hasPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||||
signal = signal
|
|> map { peer -> Bool in
|
||||||
|> mapToSignal { keywords in
|
guard case let .user(user) = peer else {
|
||||||
return .single(keywords)
|
return false
|
||||||
|> then(
|
}
|
||||||
context.engine.stickers.searchEmojiKeywords(inputLanguageCode: "en-US", query: query, completeMatch: query.count < 3)
|
return user.isPremium
|
||||||
|> map { englishKeywords in
|
|
||||||
return keywords + englishKeywords
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
|> distinctUntilChanged
|
||||||
|
|
||||||
let hasPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
|
||||||
|> map { peer -> Bool in
|
|
||||||
guard case let .user(user) = peer else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return user.isPremium
|
|
||||||
}
|
|
||||||
|> distinctUntilChanged
|
|
||||||
|
|
||||||
return signal
|
|
||||||
|> castError(ChatContextQueryError.self)
|
|
||||||
|> mapToSignal { keywords -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> in
|
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000),
|
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000),
|
||||||
hasPremium
|
hasPremium
|
||||||
@ -361,13 +345,6 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|
|||||||
|> map { view, hasPremium -> [(String, TelegramMediaFile?, String)] in
|
|> map { view, hasPremium -> [(String, TelegramMediaFile?, String)] in
|
||||||
var result: [(String, TelegramMediaFile?, String)] = []
|
var result: [(String, TelegramMediaFile?, String)] = []
|
||||||
|
|
||||||
var allEmoticons: [String: String] = [:]
|
|
||||||
for keyword in keywords {
|
|
||||||
for emoticon in keyword.emoticons {
|
|
||||||
allEmoticons[emoticon] = keyword.keyword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for entry in view.entries {
|
for entry in view.entries {
|
||||||
guard let item = entry.item as? StickerPackItem else {
|
guard let item = entry.item as? StickerPackItem else {
|
||||||
continue
|
continue
|
||||||
@ -375,9 +352,9 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|
|||||||
for attribute in item.file.attributes {
|
for attribute in item.file.attributes {
|
||||||
switch attribute {
|
switch attribute {
|
||||||
case let .CustomEmoji(_, alt, _):
|
case let .CustomEmoji(_, alt, _):
|
||||||
if !alt.isEmpty, let keyword = allEmoticons[alt] {
|
if alt == query {
|
||||||
if !item.file.isPremiumEmoji || hasPremium {
|
if !item.file.isPremiumEmoji || hasPremium {
|
||||||
result.append((alt, item.file, keyword))
|
result.append((alt, item.file, alt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -385,18 +362,83 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for keyword in keywords {
|
|
||||||
for emoticon in keyword.emoticons {
|
|
||||||
result.append((emoticon, nil, keyword.keyword))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|> map { result -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
|> map { result -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
||||||
return { _ in return .emojis(result, range) }
|
return { _ in return .emojis(result, range) }
|
||||||
}
|
}
|
||||||
|> castError(ChatContextQueryError.self)
|
|> castError(ChatContextQueryError.self)
|
||||||
|
} else {
|
||||||
|
var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query, completeMatch: query.count < 2)
|
||||||
|
if !languageCode.lowercased().hasPrefix("en") {
|
||||||
|
signal = signal
|
||||||
|
|> mapToSignal { keywords in
|
||||||
|
return .single(keywords)
|
||||||
|
|> then(
|
||||||
|
context.engine.stickers.searchEmojiKeywords(inputLanguageCode: "en-US", query: query, completeMatch: query.count < 3)
|
||||||
|
|> map { englishKeywords in
|
||||||
|
return keywords + englishKeywords
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||||
|
|> map { peer -> Bool in
|
||||||
|
guard case let .user(user) = peer else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return user.isPremium
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
|
||||||
|
return signal
|
||||||
|
|> castError(ChatContextQueryError.self)
|
||||||
|
|> mapToSignal { keywords -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> in
|
||||||
|
return combineLatest(
|
||||||
|
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000),
|
||||||
|
hasPremium
|
||||||
|
)
|
||||||
|
|> map { view, hasPremium -> [(String, TelegramMediaFile?, String)] in
|
||||||
|
var result: [(String, TelegramMediaFile?, String)] = []
|
||||||
|
|
||||||
|
var allEmoticons: [String: String] = [:]
|
||||||
|
for keyword in keywords {
|
||||||
|
for emoticon in keyword.emoticons {
|
||||||
|
allEmoticons[emoticon] = keyword.keyword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in view.entries {
|
||||||
|
guard let item = entry.item as? StickerPackItem else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for attribute in item.file.attributes {
|
||||||
|
switch attribute {
|
||||||
|
case let .CustomEmoji(_, alt, _):
|
||||||
|
if !alt.isEmpty, let keyword = allEmoticons[alt] {
|
||||||
|
if !item.file.isPremiumEmoji || hasPremium {
|
||||||
|
result.append((alt, item.file, keyword))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for keyword in keywords {
|
||||||
|
for emoticon in keyword.emoticons {
|
||||||
|
result.append((emoticon, nil, keyword.keyword))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|> map { result -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
||||||
|
return { _ in return .emojis(result, range) }
|
||||||
|
}
|
||||||
|
|> castError(ChatContextQueryError.self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -461,7 +461,7 @@ final class CustomEmojiContainerView: UIView {
|
|||||||
let itemSize: CGFloat = floor(24.0 * fontSize / 17.0)
|
let itemSize: CGFloat = floor(24.0 * fontSize / 17.0)
|
||||||
let size = CGSize(width: itemSize, height: itemSize)
|
let size = CGSize(width: itemSize, height: itemSize)
|
||||||
|
|
||||||
view.frame = CGRect(origin: CGPoint(x: floor(rect.midX - size.width / 2.0), y: floor(rect.midY - size.height / 2.0)), size: size)
|
view.frame = CGRect(origin: CGPoint(x: floor(rect.midX - size.width / 2.0), y: floor(rect.midY - size.height / 2.0) + 1.0), size: size)
|
||||||
|
|
||||||
validKeys.insert(key)
|
validKeys.insert(key)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,8 @@ private enum EmojisChatInputContextPanelEntryStableId: Hashable, Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func backgroundCenterImage(_ theme: PresentationTheme) -> UIImage? {
|
private func backgroundCenterImage(_ theme: PresentationTheme) -> UIImage? {
|
||||||
return generateImage(CGSize(width: 30.0, height: 55.0), rotatedContext: { size, context in
|
return generateImage(CGSize(width: 8.0, height: 16.0), rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
/*context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
context.setStrokeColor(theme.list.itemPlainSeparatorColor.cgColor)
|
context.setStrokeColor(theme.list.itemPlainSeparatorColor.cgColor)
|
||||||
context.setFillColor(theme.list.plainBackgroundColor.cgColor)
|
context.setFillColor(theme.list.plainBackgroundColor.cgColor)
|
||||||
let lineWidth = UIScreenPixel
|
let lineWidth = UIScreenPixel
|
||||||
@ -37,8 +37,17 @@ private func backgroundCenterImage(_ theme: PresentationTheme) -> UIImage? {
|
|||||||
context.translateBy(x: -460.5, y: -lineWidth / 2.0 - 364.0 + 27.0)
|
context.translateBy(x: -460.5, y: -lineWidth / 2.0 - 364.0 + 27.0)
|
||||||
context.move(to: CGPoint(x: 0.0, y: lineWidth / 2.0))
|
context.move(to: CGPoint(x: 0.0, y: lineWidth / 2.0))
|
||||||
context.addLine(to: CGPoint(x: size.width, y: lineWidth / 2.0))
|
context.addLine(to: CGPoint(x: size.width, y: lineWidth / 2.0))
|
||||||
context.strokePath()
|
context.strokePath()*/
|
||||||
})
|
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.setStrokeColor(theme.list.itemPlainSeparatorColor.cgColor)
|
||||||
|
context.setFillColor(theme.list.plainBackgroundColor.cgColor)
|
||||||
|
let lineWidth = UIScreenPixel
|
||||||
|
context.setLineWidth(lineWidth)
|
||||||
|
|
||||||
|
context.fill(CGRect(origin: CGPoint(), size: CGSize(width: size.height, height: size.height)))
|
||||||
|
context.stroke(CGRect(origin: CGPoint(x: -lineWidth / 2.0, y: lineWidth / 2.0), size: CGSize(width: size.height + lineWidth, height: size.height - lineWidth)))
|
||||||
|
})?.stretchableImage(withLeftCapWidth: 8, topCapHeight: 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func backgroundLeftImage(_ theme: PresentationTheme) -> UIImage? {
|
private func backgroundLeftImage(_ theme: PresentationTheme) -> UIImage? {
|
||||||
@ -192,8 +201,10 @@ final class EmojisChatInputContextPanelNode: ChatInputContextPanelNode {
|
|||||||
inner: for (range, type, _) in textInputStateContextQueryRangeAndType(textInputState) {
|
inner: for (range, type, _) in textInputStateContextQueryRangeAndType(textInputState) {
|
||||||
if type == [.emojiSearch] {
|
if type == [.emojiSearch] {
|
||||||
var range = range
|
var range = range
|
||||||
range.location -= 1
|
if textInputState.inputText.attributedSubstring(from: range).string.hasPrefix(":") {
|
||||||
range.length += 1
|
range.location -= 1
|
||||||
|
range.length += 1
|
||||||
|
}
|
||||||
hashtagQueryRange = range
|
hashtagQueryRange = range
|
||||||
break inner
|
break inner
|
||||||
}
|
}
|
||||||
@ -267,7 +278,7 @@ final class EmojisChatInputContextPanelNode: ChatInputContextPanelNode {
|
|||||||
self.presentationInterfaceState = interfaceState
|
self.presentationInterfaceState = interfaceState
|
||||||
|
|
||||||
let sideInsets: CGFloat = 10.0 + leftInset
|
let sideInsets: CGFloat = 10.0 + leftInset
|
||||||
let contentWidth = min(size.width - sideInsets - sideInsets, max(24.0, CGFloat(self.currentEntries?.count ?? 0) * 45.0))
|
let contentWidth = min(size.width - sideInsets - sideInsets, max(24.0, CGFloat(self.currentEntries?.count ?? 0) * 45.0 + 5.0))
|
||||||
|
|
||||||
var contentLeftInset: CGFloat = 40.0
|
var contentLeftInset: CGFloat = 40.0
|
||||||
var leftOffset: CGFloat = 0.0
|
var leftOffset: CGFloat = 0.0
|
||||||
@ -279,7 +290,7 @@ final class EmojisChatInputContextPanelNode: ChatInputContextPanelNode {
|
|||||||
|
|
||||||
let backgroundFrame = CGRect(origin: CGPoint(x: sideInsets + leftOffset, y: size.height - 55.0 + 4.0), size: CGSize(width: contentWidth, height: 55.0))
|
let backgroundFrame = CGRect(origin: CGPoint(x: sideInsets + leftOffset, y: size.height - 55.0 + 4.0), size: CGSize(width: contentWidth, height: 55.0))
|
||||||
let backgroundLeftFrame = CGRect(origin: backgroundFrame.origin, size: CGSize(width: contentLeftInset, height: backgroundFrame.size.height - 10.0 + UIScreenPixel))
|
let backgroundLeftFrame = CGRect(origin: backgroundFrame.origin, size: CGSize(width: contentLeftInset, height: backgroundFrame.size.height - 10.0 + UIScreenPixel))
|
||||||
let backgroundCenterFrame = CGRect(origin: CGPoint(x: backgroundLeftFrame.maxX, y: backgroundFrame.minY), size: CGSize(width: 30.0, height: 55.0))
|
let backgroundCenterFrame = CGRect(origin: CGPoint(x: backgroundLeftFrame.maxX, y: backgroundFrame.minY), size: CGSize(width: 30.0, height: backgroundFrame.size.height - 10.0 + UIScreenPixel))
|
||||||
let backgroundRightFrame = CGRect(origin: CGPoint(x: backgroundCenterFrame.maxX, y: backgroundFrame.minY), size: CGSize(width: max(0.0, backgroundFrame.minX + backgroundFrame.size.width - backgroundCenterFrame.maxX), height: backgroundFrame.size.height - 10.0 + UIScreenPixel))
|
let backgroundRightFrame = CGRect(origin: CGPoint(x: backgroundCenterFrame.maxX, y: backgroundFrame.minY), size: CGSize(width: max(0.0, backgroundFrame.minX + backgroundFrame.size.width - backgroundCenterFrame.maxX), height: backgroundFrame.size.height - 10.0 + UIScreenPixel))
|
||||||
transition.updateFrame(node: self.backgroundLeftNode, frame: backgroundLeftFrame)
|
transition.updateFrame(node: self.backgroundLeftNode, frame: backgroundLeftFrame)
|
||||||
transition.updateFrame(node: self.backgroundNode, frame: backgroundCenterFrame)
|
transition.updateFrame(node: self.backgroundNode, frame: backgroundCenterFrame)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user