UI improvements

This commit is contained in:
Ali 2023-01-31 12:42:37 +01:00
parent 38935ca811
commit 4b2902ac71
21 changed files with 267 additions and 59 deletions

View File

@ -8847,3 +8847,7 @@ Sorry for the inconvenience.";
"Chat.PlaceholderTextNotAllowed" = "Text not allowed"; "Chat.PlaceholderTextNotAllowed" = "Text not allowed";
"CallList.DeleteAll" = "Delete All"; "CallList.DeleteAll" = "Delete All";
"DataUsage.TopSectionAll" = "All";
"DataUsage.TopSectionMobile" = "Mobile";
"DataUsage.TopSectionWifi" = "Wifi";

View File

@ -76,4 +76,6 @@
+ (NSData *)_manuallyEncryptedMessage:(NSData *)preparedData messageId:(int64_t)messageId authKey:(MTDatacenterAuthKey *)authKey; + (NSData *)_manuallyEncryptedMessage:(NSData *)preparedData messageId:(int64_t)messageId authKey:(MTDatacenterAuthKey *)authKey;
- (void)simulateDisconnection;
@end @end

View File

@ -62,4 +62,6 @@
- (void)updateSchemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes; - (void)updateSchemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes;
- (void)simulateDisconnection;
@end @end

View File

@ -228,7 +228,7 @@ static int32_t fixedTimeDifferenceValue = 0;
_isTestingEnvironment = isTestingEnvironment; _isTestingEnvironment = isTestingEnvironment;
_useTempAuthKeys = useTempAuthKeys; _useTempAuthKeys = useTempAuthKeys;
#if DEBUG #if DEBUG
_tempKeyExpiration = 10 * 60; _tempKeyExpiration = 30;
#else #else
_tempKeyExpiration = 24 * 60 * 60; _tempKeyExpiration = 24 * 60 * 60;
#endif #endif

View File

@ -247,6 +247,12 @@ typedef enum {
} }
case MTDatacenterAuthStageReqDH: case MTDatacenterAuthStageReqDH:
{ {
#if DEBUG
if (arc4random_uniform(100) < 50) {
[mtProto simulateDisconnection];
}
#endif
MTBuffer *reqDhBuffer = [[MTBuffer alloc] init]; MTBuffer *reqDhBuffer = [[MTBuffer alloc] init];
[reqDhBuffer appendInt32:(int32_t)0xd712e4be]; [reqDhBuffer appendInt32:(int32_t)0xd712e4be];
[reqDhBuffer appendBytes:_nonce.bytes length:_nonce.length]; [reqDhBuffer appendBytes:_nonce.bytes length:_nonce.length];

View File

@ -657,6 +657,15 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
}]; }];
} }
- (void)simulateDisconnection {
[[MTProto managerQueue] dispatchOnQueue:^
{
if (_transport != nil) {
[_transport simulateDisconnection];
}
}];
}
- (bool)canAskForTransactions - (bool)canAskForTransactions
{ {
return (_mtState & (MTProtoStateAwaitingDatacenterScheme | MTProtoStateAwaitingDatacenterAuthorization | MTProtoStateAwaitingDatacenterAuthToken | MTProtoStateAwaitingTimeFixAndSalts | MTProtoStateStopped)) == 0; return (_mtState & (MTProtoStateAwaitingDatacenterScheme | MTProtoStateAwaitingDatacenterAuthorization | MTProtoStateAwaitingDatacenterAuthToken | MTProtoStateAwaitingTimeFixAndSalts | MTProtoStateStopped)) == 0;
@ -770,7 +779,10 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
return; return;
} }
if (_useUnauthorizedMode) { if (_useUnauthorizedMode) {
#if DEBUG
#else
return; return;
#endif
} }
if (hasConnectionProblems) { if (hasConnectionProblems) {

View File

@ -804,4 +804,18 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0;
}]; }];
} }
- (void)simulateDisconnection {
MTTcpTransportContext *transportContext = _transportContext;
[[MTTcpTransport tcpTransportQueue] dispatchOnQueue:^ {
if (transportContext.connection.scheme != nil) {
MTTransportScheme *scheme = transportContext.connection.scheme;
__weak MTTcpTransport *weakSelf = self;
dispatch_async([MTTcpTransport tcpTransportQueue].nativeQueue, ^{
__strong MTTcpTransport *strongSelf = weakSelf;
[strongSelf connectionWatchdogTimeout:scheme];
});
}
}];
}
@end @end

View File

@ -92,4 +92,7 @@
- (void)updateSchemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes { - (void)updateSchemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes {
} }
- (void)simulateDisconnection {
}
@end @end

View File

@ -1475,6 +1475,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: items items: items
)] )]
} }
@ -1528,6 +1529,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: items items: items
)], isFinalResult)) )], isFinalResult))
} }

View File

@ -397,10 +397,14 @@ final class StickerPackEmojisItemNode: GridItemNode {
if let current = strongSelf.visibleItemPlaceholderViews[itemId] { if let current = strongSelf.visibleItemPlaceholderViews[itemId] {
placeholderView = current placeholderView = current
} else { } else {
var placeholderContent: EmojiPagerContentComponent.View.ItemPlaceholderView.Content?
if let immediateThumbnailData = item.file.immediateThumbnailData {
placeholderContent = .thumbnail(immediateThumbnailData)
}
placeholderView = EmojiPagerContentComponent.View.ItemPlaceholderView( placeholderView = EmojiPagerContentComponent.View.ItemPlaceholderView(
context: context, context: context,
dimensions: item.file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), dimensions: item.file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0),
immediateThumbnailData: item.file.immediateThumbnailData, content: placeholderContent,
shimmerView: nil,//strongSelf.shimmerHostView, shimmerView: nil,//strongSelf.shimmerHostView,
color: theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.08), color: theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.08),
size: itemNativeFitSize size: itemNativeFitSize

View File

@ -233,11 +233,11 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
let imageSize = dimensitons.cgSize.aspectFitted(boundingSize) let imageSize = dimensitons.cgSize.aspectFitted(boundingSize)
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))() self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
var imageFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: textSize.height + textSpacing - topOffset), size: imageSize) var imageFrame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0), y: textSize.height + textSpacing - topOffset), size: imageSize)
var centerOffset: CGFloat = 0.0 var centerOffset: CGFloat = 0.0
if self.item.file.isPremiumSticker { if self.item.file.isPremiumSticker {
let originalImageFrame = imageFrame let originalImageFrame = imageFrame
imageFrame.origin.x = size.width - imageFrame.width - 18.0 imageFrame.origin.x = min(imageFrame.minX + imageFrame.width * 0.1, size.width - imageFrame.width - 18.0)
centerOffset = imageFrame.minX - originalImageFrame.minX centerOffset = imageFrame.minX - originalImageFrame.minX
} }
self.imageNode.frame = imageFrame self.imageNode.frame = imageFrame
@ -254,9 +254,9 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
self.textNode.frame = CGRect(origin: CGPoint(x: floor((imageFrame.size.width - textSize.width) / 2.0) - centerOffset, y: -textSize.height - textSpacing), size: textSize) self.textNode.frame = CGRect(origin: CGPoint(x: floor((imageFrame.size.width - textSize.width) / 2.0) - centerOffset, y: -textSize.height - textSpacing), size: textSize)
if self.item.file.isCustomEmoji { if self.item.file.isCustomEmoji {
return CGSize(width: size.width, height: imageFrame.height) return CGSize(width: boundingSize.width, height: imageFrame.height)
} else { } else {
return CGSize(width: size.width, height: imageFrame.height + textSize.height + textSpacing) return CGSize(width: boundingSize.width, height: imageFrame.height + textSize.height + textSpacing)
} }
} else { } else {
return CGSize(width: size.width, height: 10.0) return CGSize(width: size.width, height: 10.0)

View File

@ -476,6 +476,9 @@ func _internal_searchEmoji(account: Account, query: [String], scope: SearchStick
if let currentCached = cached, currentTime > currentCached.timestamp + searchStickersConfiguration.cacheTimeout { if let currentCached = cached, currentTime > currentCached.timestamp + searchStickersConfiguration.cacheTimeout {
cached = nil cached = nil
} }
#if DEBUG
cached = nil
#endif
return (result, cached, isPremium, searchStickersConfiguration) return (result, cached, isPremium, searchStickersConfiguration)
} }

View File

@ -189,32 +189,6 @@ public extension TelegramEngine {
|> map { items, isFinalResult -> (items: [TelegramMediaFile], isFinalResult: Bool) in |> map { items, isFinalResult -> (items: [TelegramMediaFile], isFinalResult: Bool) in
return (items.map(\.file), isFinalResult) return (items.map(\.file), isFinalResult)
} }
/*return self.account.network.request(Api.functions.messages.searchCustomEmoji(emoticon: emojiString.joined(separator: ""), hash: 0))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.EmojiList?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<[TelegramMediaFile], NoError> in
guard let result = result else {
return .single([])
}
switch result {
case let .emojiList(_, documentIds):
return self.resolveInlineStickers(fileIds: documentIds)
|> map { result -> [TelegramMediaFile] in
var files: [TelegramMediaFile] = []
for id in documentIds {
if let file = result[id] {
files.append(file)
}
}
return files
}
default:
return .single([])
}
}*/
} }
} }
} }

View File

@ -435,6 +435,7 @@ final class AvatarEditorScreenComponent: Component {
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: emojiItems items: emojiItems
) )
) )
@ -454,6 +455,7 @@ final class AvatarEditorScreenComponent: Component {
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: stickerItems items: stickerItems
) )
) )
@ -510,6 +512,7 @@ final class AvatarEditorScreenComponent: Component {
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: items items: items
)], isFinalResult)) )], isFinalResult))
} }

View File

@ -1087,6 +1087,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: items items: items
)] )]
} }
@ -1140,6 +1141,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: items items: items
)], isFinalResult)) )], isFinalResult))
} }
@ -1155,12 +1157,31 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
return return
} }
if group.items.isEmpty && !result.isFinalResult { if group.items.isEmpty && !result.isFinalResult {
self.emojiSearchStateValue.isSearching = true //self.emojiSearchStateValue.isSearching = true
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: [
EmojiPagerContentComponent.ItemGroup(
supergroupId: "search",
groupId: "search",
title: nil,
subtitle: nil,
actionButtonTitle: nil,
isFeatured: false,
isPremiumLocked: false,
isEmbedded: false,
hasClear: false,
collapsedLineCount: nil,
displayPremiumBadges: false,
headerItem: nil,
fillWithLoadingPlaceholders: true,
items: []
)
], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
return return
} }
//DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0, execute: {
self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false) self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
version += 1 version += 1
//})
})) }))
} }
}, },
@ -1416,6 +1437,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: items items: items
)], files.isFinalResult)) )], files.isFinalResult))
} }
@ -1430,7 +1452,25 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
return return
} }
if group.items.isEmpty && !result.isFinalResult { if group.items.isEmpty && !result.isFinalResult {
strongSelf.stickerSearchStateValue.isSearching = true //strongSelf.stickerSearchStateValue.isSearching = true
strongSelf.stickerSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: [
EmojiPagerContentComponent.ItemGroup(
supergroupId: "search",
groupId: "search",
title: nil,
subtitle: nil,
actionButtonTitle: nil,
isFeatured: false,
isPremiumLocked: false,
isEmbedded: false,
hasClear: false,
collapsedLineCount: nil,
displayPremiumBadges: false,
headerItem: nil,
fillWithLoadingPlaceholders: true,
items: []
)
], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
return return
} }
strongSelf.stickerSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false) strongSelf.stickerSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
@ -1467,7 +1507,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
if let emojiSearchResult = emojiSearchState.result { if let emojiSearchResult = emojiSearchState.result {
var emptySearchResults: EmojiPagerContentComponent.EmptySearchResults? var emptySearchResults: EmojiPagerContentComponent.EmptySearchResults?
if !emojiSearchResult.groups.contains(where: { !$0.items.isEmpty }) { if !emojiSearchResult.groups.contains(where: { !$0.items.isEmpty || $0.fillWithLoadingPlaceholders }) {
emptySearchResults = EmojiPagerContentComponent.EmptySearchResults( emptySearchResults = EmojiPagerContentComponent.EmptySearchResults(
text: presentationData.strings.EmojiSearch_SearchEmojiEmptyResult, text: presentationData.strings.EmojiSearch_SearchEmojiEmptyResult,
iconFile: nil iconFile: nil
@ -1485,7 +1525,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
if let stickerSearchResult = stickerSearchState.result { if let stickerSearchResult = stickerSearchState.result {
var stickerSearchResults: EmojiPagerContentComponent.EmptySearchResults? var stickerSearchResults: EmojiPagerContentComponent.EmptySearchResults?
if !stickerSearchResult.groups.contains(where: { !$0.items.isEmpty }) { if !stickerSearchResult.groups.contains(where: { !$0.items.isEmpty || $0.fillWithLoadingPlaceholders }) {
stickerSearchResults = EmojiPagerContentComponent.EmptySearchResults( stickerSearchResults = EmojiPagerContentComponent.EmptySearchResults(
text: presentationData.strings.EmojiSearch_SearchStickersEmptyResult, text: presentationData.strings.EmojiSearch_SearchStickersEmptyResult,
iconFile: nil iconFile: nil

View File

@ -568,6 +568,7 @@ public final class EmojiStatusSelectionController: ViewController {
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: items items: items
)] )]
} }
@ -621,6 +622,7 @@ public final class EmojiStatusSelectionController: ViewController {
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: items items: items
)], isFinalResult)) )], isFinalResult))
} }

View File

@ -2472,6 +2472,7 @@ public final class EmojiPagerContentComponent: Component {
public let collapsedLineCount: Int? public let collapsedLineCount: Int?
public let displayPremiumBadges: Bool public let displayPremiumBadges: Bool
public let headerItem: EntityKeyboardAnimationData? public let headerItem: EntityKeyboardAnimationData?
public let fillWithLoadingPlaceholders: Bool
public let items: [Item] public let items: [Item]
public init( public init(
@ -2487,6 +2488,7 @@ public final class EmojiPagerContentComponent: Component {
collapsedLineCount: Int?, collapsedLineCount: Int?,
displayPremiumBadges: Bool, displayPremiumBadges: Bool,
headerItem: EntityKeyboardAnimationData?, headerItem: EntityKeyboardAnimationData?,
fillWithLoadingPlaceholders: Bool,
items: [Item] items: [Item]
) { ) {
self.supergroupId = supergroupId self.supergroupId = supergroupId
@ -2501,6 +2503,7 @@ public final class EmojiPagerContentComponent: Component {
self.collapsedLineCount = collapsedLineCount self.collapsedLineCount = collapsedLineCount
self.displayPremiumBadges = displayPremiumBadges self.displayPremiumBadges = displayPremiumBadges
self.headerItem = headerItem self.headerItem = headerItem
self.fillWithLoadingPlaceholders = fillWithLoadingPlaceholders
self.items = items self.items = items
} }
@ -2544,6 +2547,9 @@ public final class EmojiPagerContentComponent: Component {
if lhs.headerItem != rhs.headerItem { if lhs.headerItem != rhs.headerItem {
return false return false
} }
if lhs.fillWithLoadingPlaceholders != rhs.fillWithLoadingPlaceholders {
return false
}
if lhs.items != rhs.items { if lhs.items != rhs.items {
return false return false
} }
@ -3035,6 +3041,11 @@ public final class EmojiPagerContentComponent: Component {
} }
public final class ItemPlaceholderView: UIView { public final class ItemPlaceholderView: UIView {
public enum Content {
case thumbnail(Data)
case template(UIImage)
}
private let shimmerView: PortalSourceView? private let shimmerView: PortalSourceView?
private var placeholderView: PortalView? private var placeholderView: PortalView?
private let placeholderMaskLayer: SimpleLayer private let placeholderMaskLayer: SimpleLayer
@ -3043,7 +3054,7 @@ public final class EmojiPagerContentComponent: Component {
public init( public init(
context: AccountContext, context: AccountContext,
dimensions: CGSize?, dimensions: CGSize?,
immediateThumbnailData: Data?, content: Content?,
shimmerView: PortalSourceView?, shimmerView: PortalSourceView?,
color: UIColor, color: UIColor,
size: CGSize size: CGSize
@ -3063,19 +3074,31 @@ public final class EmojiPagerContentComponent: Component {
} }
let useDirectContent = self.placeholderView == nil let useDirectContent = self.placeholderView == nil
Queue.concurrentDefaultQueue().async { [weak self] in if let content {
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) { switch content {
Queue.mainQueue().async { case let .thumbnail(immediateThumbnailData):
guard let strongSelf = self else { Queue.concurrentDefaultQueue().async { [weak self] in
return 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 {
guard let strongSelf = self else {
if useDirectContent { return
strongSelf.layer.contents = image.cgImage }
} else {
strongSelf.placeholderMaskLayer.contents = image.cgImage if useDirectContent {
strongSelf.layer.contents = image.cgImage
} else {
strongSelf.placeholderMaskLayer.contents = image.cgImage
}
}
} }
} }
case let .template(templateImage):
if useDirectContent {
self.layer.contents = templateImage.cgImage
self.tintColor = color
} else {
self.placeholderMaskLayer.contents = templateImage.cgImage
}
} }
} }
} }
@ -3590,6 +3613,7 @@ public final class EmojiPagerContentComponent: Component {
private var visibleSearchHeader: EmojiSearchHeaderView? private var visibleSearchHeader: EmojiSearchHeaderView?
private var visibleEmptySearchResultsView: EmptySearchResultsView? private var visibleEmptySearchResultsView: EmptySearchResultsView?
private var visibleItemPlaceholderViews: [ItemLayer.Key: ItemPlaceholderView] = [:] private var visibleItemPlaceholderViews: [ItemLayer.Key: ItemPlaceholderView] = [:]
private var visibleFillPlaceholdersViews: [Int: ItemPlaceholderView] = [:]
private var visibleItemSelectionLayers: [ItemLayer.Key: ItemSelectionLayer] = [:] private var visibleItemSelectionLayers: [ItemLayer.Key: ItemSelectionLayer] = [:]
private var visibleItemLayers: [ItemLayer.Key: ItemLayer] = [:] private var visibleItemLayers: [ItemLayer.Key: ItemLayer] = [:]
private var visibleGroupHeaders: [AnyHashable: GroupHeaderLayer] = [:] private var visibleGroupHeaders: [AnyHashable: GroupHeaderLayer] = [:]
@ -3600,6 +3624,16 @@ public final class EmojiPagerContentComponent: Component {
private var ignoreScrolling: Bool = false private var ignoreScrolling: Bool = false
private var keepTopPanelVisibleUntilScrollingInput: Bool = false private var keepTopPanelVisibleUntilScrollingInput: Bool = false
private struct FillPlaceholderParams: Equatable {
var size: CGSize
init(size: CGSize) {
self.size = size
}
}
private var fillPlaceholder: (params: FillPlaceholderParams, image: UIImage)?
private var component: EmojiPagerContentComponent? private var component: EmojiPagerContentComponent?
private weak var state: EmptyComponentState? private weak var state: EmptyComponentState?
private var pagerEnvironment: PagerComponentChildEnvironment? private var pagerEnvironment: PagerComponentChildEnvironment?
@ -5243,6 +5277,7 @@ public final class EmojiPagerContentComponent: Component {
var validGroupBorderIds = Set<AnyHashable>() var validGroupBorderIds = Set<AnyHashable>()
var validGroupPremiumButtonIds = Set<AnyHashable>() var validGroupPremiumButtonIds = Set<AnyHashable>()
var validGroupExpandActionButtons = Set<AnyHashable>() var validGroupExpandActionButtons = Set<AnyHashable>()
var validFillPlaceholdersIndices = Set<Int>()
let effectiveVisibleBounds = CGRect(origin: self.scrollView.bounds.origin, size: self.effectiveVisibleSize) let effectiveVisibleBounds = CGRect(origin: self.scrollView.bounds.origin, size: self.effectiveVisibleSize)
let topVisibleDetectionBounds = effectiveVisibleBounds.offsetBy(dx: 0.0, dy: pagerEnvironment.containerInsets.top) let topVisibleDetectionBounds = effectiveVisibleBounds.offsetBy(dx: 0.0, dy: pagerEnvironment.containerInsets.top)
@ -5540,7 +5575,6 @@ public final class EmojiPagerContentComponent: Component {
if !itemGroup.isEmbedded, let groupItemRange = groupItems.groupItems { if !itemGroup.isEmbedded, let groupItemRange = groupItems.groupItems {
for index in groupItemRange.lowerBound ..< groupItemRange.upperBound { for index in groupItemRange.lowerBound ..< groupItemRange.upperBound {
let item = itemGroup.items[index] let item = itemGroup.items[index]
if assignTopVisibleSubgroupId { if assignTopVisibleSubgroupId {
if let subgroupId = item.subgroupId { if let subgroupId = item.subgroupId {
@ -5600,10 +5634,14 @@ public final class EmojiPagerContentComponent: Component {
if let current = strongSelf.visibleItemPlaceholderViews[itemId] { if let current = strongSelf.visibleItemPlaceholderViews[itemId] {
placeholderView = current placeholderView = current
} else { } else {
var placeholderContent: ItemPlaceholderView.Content?
if let immediateThumbnailData = animationData.immediateThumbnailData {
placeholderContent = .thumbnail(immediateThumbnailData)
}
placeholderView = ItemPlaceholderView( placeholderView = ItemPlaceholderView(
context: component.context, context: component.context,
dimensions: animationData.dimensions, dimensions: animationData.dimensions,
immediateThumbnailData: animationData.immediateThumbnailData, content: placeholderContent,
shimmerView: strongSelf.shimmerHostView, shimmerView: strongSelf.shimmerHostView,
color: placeholderColor, color: placeholderColor,
size: itemNativeFitSize size: itemNativeFitSize
@ -5756,6 +5794,61 @@ public final class EmojiPagerContentComponent: Component {
itemLayer.isVisibleForAnimations = keyboardChildEnvironment.isContentInFocus itemLayer.isVisibleForAnimations = keyboardChildEnvironment.isContentInFocus
} }
} }
if itemGroup.fillWithLoadingPlaceholders {
let placeholderSizeFactor: CGFloat = 0.9
let placeholderColor = keyboardChildEnvironment.theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.1)
let fillPlaceholderImage: UIImage?
let fillPlaceholderParams = FillPlaceholderParams(size: CGSize(width: floor(itemLayout.nativeItemSize * placeholderSizeFactor), height: floor(itemLayout.nativeItemSize * placeholderSizeFactor)))
if let current = self.fillPlaceholder, current.params == fillPlaceholderParams {
fillPlaceholderImage = current.image
} else {
switch component.itemLayoutType {
case .compact:
fillPlaceholderImage = generateFilledCircleImage(diameter: fillPlaceholderParams.size.width, color: .black)
case .detailed:
fillPlaceholderImage = generateFilledRoundedRectImage(size: fillPlaceholderParams.size, cornerRadius: floor(fillPlaceholderParams.size.width * 0.2), color: .black)
}
if let fillPlaceholderImage {
self.fillPlaceholder = (fillPlaceholderParams, fillPlaceholderImage)
}
}
let fillPlaceholderContent: ItemPlaceholderView.Content? = fillPlaceholderImage.flatMap(ItemPlaceholderView.Content.template)
var placeholderIndex = groupItems.groupItems?.lowerBound ?? 0
while true {
var itemFrame = itemLayout.frame(groupIndex: groupItems.groupIndex, itemIndex: placeholderIndex)
if itemFrame.minY >= effectiveVisibleBounds.maxY {
break
}
let visibleItemSize = CGSize(width: floor(itemFrame.width * placeholderSizeFactor), height: floor(itemFrame.height * placeholderSizeFactor))
itemFrame = CGRect(origin: CGPoint(x: floor(itemFrame.midX - visibleItemSize.width * 0.5), y: floor(itemFrame.midY - visibleItemSize.height * 0.5)), size: visibleItemSize)
validFillPlaceholdersIndices.insert(placeholderIndex)
let placeholderView: ItemPlaceholderView
if let current = self.visibleFillPlaceholdersViews[placeholderIndex] {
placeholderView = current
} else {
placeholderView = ItemPlaceholderView(
context: component.context,
dimensions: nil,
content: fillPlaceholderContent,
shimmerView: self.shimmerHostView,
color: placeholderColor,
size: itemFrame.size
)
self.visibleFillPlaceholdersViews[placeholderIndex] = placeholderView
self.placeholdersContainerView.addSubview(placeholderView)
}
placeholderView.frame = itemFrame
placeholderView.update(size: itemFrame.size)
placeholderIndex += 1
}
self.updateShimmerIfNeeded()
}
} }
var removedPlaceholerViews = false var removedPlaceholerViews = false
@ -5869,6 +5962,26 @@ public final class EmojiPagerContentComponent: Component {
self.visibleItemSelectionLayers.removeValue(forKey: id) self.visibleItemSelectionLayers.removeValue(forKey: id)
} }
var removedFillPlaceholderIndices: [Int] = []
for (index, placeholderView) in self.visibleFillPlaceholdersViews {
if !validFillPlaceholdersIndices.contains(index) {
if !transition.animation.isImmediate {
placeholderView.alpha = 0.0
placeholderView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak placeholderView] _ in
placeholderView?.removeFromSuperview()
})
} else {
placeholderView.removeFromSuperview()
}
removedFillPlaceholderIndices.append(index)
removedPlaceholerViews = true
}
}
for index in removedFillPlaceholderIndices {
self.visibleFillPlaceholdersViews.removeValue(forKey: index)
}
var removedGroupHeaderIds: [AnyHashable] = [] var removedGroupHeaderIds: [AnyHashable] = []
for (id, groupHeaderLayer) in self.visibleGroupHeaders { for (id, groupHeaderLayer) in self.visibleGroupHeaders {
if !validGroupHeaderIds.contains(id) { if !validGroupHeaderIds.contains(id) {
@ -6679,7 +6792,7 @@ public final class EmojiPagerContentComponent: Component {
var animateContentCrossfade = false var animateContentCrossfade = false
if let previousComponent, previousComponent.itemContentUniqueId != component.itemContentUniqueId, itemTransition.animation.isImmediate { if let previousComponent, previousComponent.itemContentUniqueId != component.itemContentUniqueId, itemTransition.animation.isImmediate {
if previousComponent.itemContentUniqueId?.id != component.itemContentUniqueId?.id { if !(previousComponent.contentItemGroups.contains(where: { $0.fillWithLoadingPlaceholders }) && component.contentItemGroups.contains(where: { $0.fillWithLoadingPlaceholders })) && previousComponent.itemContentUniqueId?.id != component.itemContentUniqueId?.id {
animateContentCrossfade = true animateContentCrossfade = true
} }
} }
@ -6705,6 +6818,15 @@ public final class EmojiPagerContentComponent: Component {
snapshotLayer.animateScale(from: 1.0, to: crossfadeMinScale, duration: 0.2, removeOnCompletion: false) snapshotLayer.animateScale(from: 1.0, to: crossfadeMinScale, duration: 0.2, removeOnCompletion: false)
} }
} }
for (_, placeholderView) in self.visibleFillPlaceholdersViews {
if let snapshotLayer = placeholderView.layer.snapshotContentTree() {
placeholderView.layer.superlayer?.insertSublayer(snapshotLayer, above: placeholderView.layer)
snapshotLayer.animateAlpha(from: CGFloat(snapshotLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotLayer] _ in
snapshotLayer?.removeFromSuperlayer()
})
snapshotLayer.animateScale(from: 1.0, to: crossfadeMinScale, duration: 0.2, removeOnCompletion: false)
}
}
for (_, selectionLayer) in self.visibleItemSelectionLayers { for (_, selectionLayer) in self.visibleItemSelectionLayers {
if let snapshotLayer = selectionLayer.snapshotContentTree() { if let snapshotLayer = selectionLayer.snapshotContentTree() {
selectionLayer.superlayer?.insertSublayer(snapshotLayer, above: selectionLayer) selectionLayer.superlayer?.insertSublayer(snapshotLayer, above: selectionLayer)
@ -6758,6 +6880,10 @@ public final class EmojiPagerContentComponent: Component {
placeholderView.layer.animateAlpha(from: 0.0, to: CGFloat(placeholderView.layer.opacity), duration: 0.2) placeholderView.layer.animateAlpha(from: 0.0, to: CGFloat(placeholderView.layer.opacity), duration: 0.2)
placeholderView.layer.animateScale(from: crossfadeMinScale, to: 1.0, duration: 0.2) placeholderView.layer.animateScale(from: crossfadeMinScale, to: 1.0, duration: 0.2)
} }
for (_, placeholderView) in self.visibleFillPlaceholdersViews {
placeholderView.layer.animateAlpha(from: 0.0, to: CGFloat(placeholderView.layer.opacity), duration: 0.2)
placeholderView.layer.animateScale(from: crossfadeMinScale, to: 1.0, duration: 0.2)
}
for (_, selectionLayer) in self.visibleItemSelectionLayers { for (_, selectionLayer) in self.visibleItemSelectionLayers {
selectionLayer.animateAlpha(from: 0.0, to: CGFloat(selectionLayer.opacity), duration: 0.2) selectionLayer.animateAlpha(from: 0.0, to: CGFloat(selectionLayer.opacity), duration: 0.2)
} }
@ -7718,6 +7844,7 @@ public final class EmojiPagerContentComponent: Component {
collapsedLineCount: group.collapsedLineCount, collapsedLineCount: group.collapsedLineCount,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: headerItem, headerItem: headerItem,
fillWithLoadingPlaceholders: false,
items: group.items items: group.items
) )
} }
@ -8244,6 +8371,7 @@ public final class EmojiPagerContentComponent: Component {
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: group.displayPremiumBadges, displayPremiumBadges: group.displayPremiumBadges,
headerItem: group.headerItem, headerItem: group.headerItem,
fillWithLoadingPlaceholders: false,
items: group.items items: group.items
) )
} }

View File

@ -123,6 +123,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
collapsedLineCount: 3, collapsedLineCount: 3,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: groupItems items: groupItems
)) ))
} }
@ -286,6 +287,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: items items: items
)] )]
} }
@ -339,6 +341,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
collapsedLineCount: nil, collapsedLineCount: nil,
displayPremiumBadges: false, displayPremiumBadges: false,
headerItem: nil, headerItem: nil,
fillWithLoadingPlaceholders: false,
items: items items: items
)], isFinalResult)) )], isFinalResult))
} }

View File

@ -215,10 +215,14 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
private func updateDisplayPlaceholder(displayPlaceholder: Bool, duration: Double) { private func updateDisplayPlaceholder(displayPlaceholder: Bool, duration: Double) {
if displayPlaceholder { if displayPlaceholder {
if self.placeholderView == nil, let component = self.component { if self.placeholderView == nil, let component = self.component {
var placeholderContent: EmojiPagerContentComponent.View.ItemPlaceholderView.Content?
if let immediateThumbnailData = component.item.immediateThumbnailData {
placeholderContent = .thumbnail(immediateThumbnailData)
}
let placeholderView = EmojiPagerContentComponent.View.ItemPlaceholderView( let placeholderView = EmojiPagerContentComponent.View.ItemPlaceholderView(
context: component.context, context: component.context,
dimensions: component.item.dimensions, dimensions: component.item.dimensions,
immediateThumbnailData: component.item.immediateThumbnailData, content: placeholderContent,
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)

View File

@ -913,9 +913,9 @@ final class DataUsageScreenComponent: Component {
component: AnyComponent(SegmentControlComponent( component: AnyComponent(SegmentControlComponent(
theme: environment.theme, theme: environment.theme,
items: [ items: [
SegmentControlComponent.Item(id: AnyHashable(SelectedStats.all), title: "All"), SegmentControlComponent.Item(id: AnyHashable(SelectedStats.all), title: environment.strings.DataUsage_TopSectionAll),
SegmentControlComponent.Item(id: AnyHashable(SelectedStats.mobile), title: "Mobile"), SegmentControlComponent.Item(id: AnyHashable(SelectedStats.mobile), title: environment.strings.DataUsage_TopSectionMobile),
SegmentControlComponent.Item(id: AnyHashable(SelectedStats.wifi), title: "WiFi") SegmentControlComponent.Item(id: AnyHashable(SelectedStats.wifi), title: environment.strings.DataUsage_TopSectionWifi)
], ],
selectedId: "total", selectedId: "total",
action: { [weak self] id in action: { [weak self] id in

View File

@ -2479,7 +2479,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
textView.inputView = updatedInputView textView.inputView = updatedInputView
if textView.isFirstResponder { if textView.isFirstResponder {
if self.chatPresentationInterfaceStateRequiresInputFocus(chatPresentationInterfaceState) { if self.chatPresentationInterfaceStateRequiresInputFocus(chatPresentationInterfaceState) {
waitForKeyboardLayout = true if let validLayout = self.validLayout, validLayout.0.inputHeight != nil {
waitForKeyboardLayout = true
}
} }
textView.reloadInputViews() textView.reloadInputViews()
} }