Various improvements

This commit is contained in:
Ilya Laktyushin 2024-11-29 00:28:50 +04:00
parent 6b528aa70f
commit c4013c3df6
22 changed files with 335 additions and 169 deletions

View File

@ -1244,7 +1244,7 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode {
let query = text.trimmingCharacters(in: .whitespacesAndNewlines)
if query.isSingleEmoji {
signals = .single([context.engine.stickers.searchStickers(query: [text.basicEmoji.0])
signals = .single([context.engine.stickers.searchStickers(query: nil, emoticon: [text.basicEmoji.0], inputLanguageCode: "")
|> map { (nil, $0.items) }])
} else if query.count > 1, let languageCode = languageCode, !languageCode.isEmpty && languageCode != "emoji" {
var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query.lowercased(), completeMatch: query.count < 3)
@ -1260,17 +1260,11 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode {
)
}
}
signals = signal
|> map { keywords -> [Signal<(String?, [FoundStickerItem]), NoError>] in
var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = []
let emoticons = keywords.flatMap { $0.emoticons }
for emoji in emoticons {
signals.append(context.engine.stickers.searchStickers(query: [emoji.basicEmoji.0])
|> take(1)
|> map { (emoji, $0.items) })
}
return signals
let emoticon = keywords.flatMap { $0.emoticons }.map { $0.basicEmoji.0 }
return [context.engine.stickers.searchStickers(query: query, emoticon: emoticon, inputLanguageCode: languageCode)
|> map { (nil, $0.items) }]
}
}

View File

@ -1428,7 +1428,7 @@
|| [view isDescendantOfView:_landscapeToolbarView]
|| [view isDescendantOfView:_selectedPhotosView]
|| [view isDescendantOfView:_captionMixin.inputPanelView]
|| [view isDescendantOfView:_captionMixin.dismissView]
|| ([view isDescendantOfView:_captionMixin.dismissView] && _captionMixin.dismissView.alpha > 0.0)
|| [view isKindOfClass:[TGMenuButtonView class]])
{

View File

@ -100,9 +100,9 @@ typedef enum
_currentTimeLabel.text = @"0:00";
_currentTimeLabel.textColor = [UIColor whiteColor];
_currentTimeLabel.layer.shadowOffset = CGSizeMake(0.0, 0.0);
_currentTimeLabel.layer.shadowRadius = 2.0;
_currentTimeLabel.layer.shadowRadius = 4.0;
_currentTimeLabel.layer.shadowColor = [UIColor blackColor].CGColor;
_currentTimeLabel.layer.shadowOpacity = 0.4;
_currentTimeLabel.layer.shadowOpacity = 0.6;
_currentTimeLabel.layer.rasterizationScale = TGScreenScaling();
_currentTimeLabel.layer.shouldRasterize = true;
[self addSubview:_currentTimeLabel];
@ -115,9 +115,9 @@ typedef enum
_inverseTimeLabel.textAlignment = NSTextAlignmentRight;
_inverseTimeLabel.textColor = [UIColor whiteColor];
_inverseTimeLabel.layer.shadowOffset = CGSizeMake(0.0, 0.0);
_inverseTimeLabel.layer.shadowRadius = 2.0;
_inverseTimeLabel.layer.shadowRadius = 4.0;
_inverseTimeLabel.layer.shadowColor = [UIColor blackColor].CGColor;
_inverseTimeLabel.layer.shadowOpacity = 0.4;
_inverseTimeLabel.layer.shadowOpacity = 0.6;
_inverseTimeLabel.layer.rasterizationScale = TGScreenScaling();
_inverseTimeLabel.layer.shouldRasterize = true;
[self addSubview:_inverseTimeLabel];

View File

@ -61,7 +61,6 @@
_inputPanel.sendPressed = ^(NSAttributedString *string) {
__strong TGPhotoCaptionInputMixin *strongSelf = weakSelf;
[TGViewController enableAutorotation];
strongSelf->_dismissView.hidden = true;
strongSelf->_editing = false;
@ -74,9 +73,7 @@
[TGViewController disableAutorotation];
[strongSelf beginEditing];
strongSelf->_dismissView.hidden = false;
if (strongSelf.panelFocused != nil)
strongSelf.panelFocused();
@ -129,12 +126,13 @@
_dismissView = [[UIView alloc] initWithFrame:parentView.bounds];
_dismissView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_dismissView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.4];
_dismissTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDismissTap:)];
_dismissTapRecognizer.enabled = false;
[_dismissView addGestureRecognizer:_dismissTapRecognizer];
//[parentView insertSubview:_dismissView belowSubview:_backgroundView];
[parentView insertSubview:_dismissView belowSubview:_inputPanelView];
}
- (void)setCaption:(NSAttributedString *)caption
@ -168,6 +166,12 @@
[self createDismissViewIfNeeded];
[self createInputPanelIfNeeded];
_dismissView.alpha = 0.0;
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
_dismissView.alpha = 1.0f;
} completion:^(BOOL finished) {
}];
}
- (void)enableDismissal
@ -184,9 +188,7 @@
if ([self.inputPanel dismissInput]) {
_editing = false;
[_dismissView removeFromSuperview];
if (self.finishedWithCaption != nil)
self.finishedWithCaption([_inputPanel caption]);
}
@ -228,6 +230,19 @@
_keyboardHeight = keyboardHeight;
CGFloat fadeAlpha = 1.0;
if (keyboardHeight < FLT_EPSILON) {
fadeAlpha = 0.0;
}
if (ABS(_dismissView.alpha - fadeAlpha) > FLT_EPSILON) {
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
_dismissView.alpha = fadeAlpha;
} completion:^(BOOL finished) {
}];
}
if (!UIInterfaceOrientationIsPortrait([[LegacyComponentsGlobals provider] applicationStatusBarOrientation]) && !TGIsPad())
return;

View File

@ -2028,7 +2028,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
))
return .single(resultGroups)
} else {
let remoteSignal = context.engine.stickers.searchEmoji(emojiString: Array(allEmoticons.keys))
let remoteSignal = context.engine.stickers.searchEmoji(query: query, emoticon: Array(allEmoticons.keys), inputLanguageCode: languageCode)
return combineLatest(
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000) |> take(1),

View File

@ -1608,9 +1608,9 @@ private func monetizationEntries(
if canViewRevenue {
entries.append(.adsTonBalanceTitle(presentationData.theme, isBot ? presentationData.strings.Monetization_Bot_BalanceTitle : presentationData.strings.Monetization_TonBalanceTitle))
entries.append(.adsTonBalance(presentationData.theme, data, isCreator && data.balances.availableBalance > 0, data.balances.withdrawEnabled))
entries.append(.adsTonBalance(presentationData.theme, data, (isCreator || isBot) && data.balances.availableBalance > 0, data.balances.withdrawEnabled))
if isCreator {
if isCreator || isBot {
let withdrawalInfoText: String
if data.balances.availableBalance == 0 {
withdrawalInfoText = presentationData.strings.Monetization_Balance_ZeroInfo

View File

@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
return 193
return 195
}
public func parseMessage(_ data: Data!) -> Any! {

View File

@ -81,13 +81,20 @@ func _internal_randomGreetingSticker(account: Account) -> Signal<FoundStickerIte
}
}
func _internal_searchStickers(account: Account, query: [String], scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
func _internal_searchStickers(account: Account, query: String?, emoticon: [String], inputLanguageCode: String, scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
if scope.isEmpty {
return .single(([], true))
}
var query = query
if query == ["\u{2764}"] {
query = ["\u{2764}\u{FE0F}"]
var emoticon = emoticon
if emoticon == ["\u{2764}"] {
emoticon = ["\u{2764}\u{FE0F}"]
}
let cacheKey: String
if let query, !query.isEmpty {
cacheKey = query
} else {
cacheKey = emoticon.sorted().joined()
}
return account.postbox.transaction { transaction -> ([FoundStickerItem], CachedStickerQueryResult?, Bool, SearchStickersConfiguration) in
@ -98,7 +105,7 @@ func _internal_searchStickers(account: Account, query: [String], scope: SearchSt
for entry in transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudSavedStickers) {
if let item = entry.contents.get(SavedStickerItem.self) {
for representation in item.stringRepresentations {
for queryItem in query {
for queryItem in emoticon {
if representation.hasPrefix(queryItem) {
result.append(FoundStickerItem(file: item.file, stringRepresentations: item.stringRepresentations))
break
@ -125,7 +132,7 @@ func _internal_searchStickers(account: Account, query: [String], scope: SearchSt
currentItems.insert(file.fileId)
for case let .Sticker(displayText, _, _) in file.attributes {
for queryItem in query {
for queryItem in emoticon {
if displayText.hasPrefix(queryItem) {
matchingRecentItemsIds.insert(file.fileId)
break
@ -143,12 +150,9 @@ func _internal_searchStickers(account: Account, query: [String], scope: SearchSt
}
}
var searchQueries: [ItemCollectionSearchQuery] = query.map { queryItem -> ItemCollectionSearchQuery in
let searchQueries: [ItemCollectionSearchQuery] = emoticon.map { queryItem -> ItemCollectionSearchQuery in
return .exact(ValueBoxKey(queryItem))
}
if query == ["\u{2764}"] {
searchQueries = [.any([ValueBoxKey("\u{2764}"), ValueBoxKey("\u{2764}\u{FE0F}")])]
}
var installedItems: [FoundStickerItem] = []
var installedAnimatedItems: [FoundStickerItem] = []
@ -187,7 +191,7 @@ func _internal_searchStickers(account: Account, query: [String], scope: SearchSt
continue
}
if matchingRecentItemsIds.contains(file.fileId) {
result.append(FoundStickerItem(file: file, stringRepresentations: query))
result.append(FoundStickerItem(file: file, stringRepresentations: emoticon))
}
}
@ -196,7 +200,7 @@ func _internal_searchStickers(account: Account, query: [String], scope: SearchSt
continue
}
if matchingRecentItemsIds.contains(file.fileId) {
result.append(FoundStickerItem(file: file, stringRepresentations: query))
result.append(FoundStickerItem(file: file, stringRepresentations: emoticon))
}
}
@ -205,8 +209,7 @@ func _internal_searchStickers(account: Account, query: [String], scope: SearchSt
result.append(contentsOf: installedItems)
}
let combinedQuery = query.joined(separator: "")
var cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(combinedQuery)))?.get(CachedStickerQueryResult.self)
var cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(cacheKey)))?.get(CachedStickerQueryResult.self)
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue
@ -292,14 +295,17 @@ func _internal_searchStickers(account: Account, query: [String], scope: SearchSt
}
}
let remote = account.network.request(Api.functions.messages.getStickers(emoticon: query.joined(separator: ""), hash: cached?.hash ?? 0))
|> `catch` { _ -> Signal<Api.messages.Stickers, NoError> in
return .single(.stickersNotModified)
}
|> mapToSignal { result -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> in
return account.postbox.transaction { transaction -> (items: [FoundStickerItem], isFinalResult: Bool) in
switch result {
case let .stickers(hash, stickers):
let remote: Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError>
if let query, !query.isEmpty {
let flags: Int32 = 0
remote = account.network.request(Api.functions.messages.searchStickers(flags: flags, q: query, emoticon: emoticon.joined(separator: ""), langCode: [inputLanguageCode], offset: 0, limit: 128, hash: cached?.hash ?? 0))
|> `catch` { _ -> Signal<Api.messages.FoundStickers, NoError> in
return .single(.foundStickersNotModified(flags: 0, nextOffset: nil))
}
|> mapToSignal { result -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> in
return account.postbox.transaction { transaction -> (items: [FoundStickerItem], isFinalResult: Bool) in
switch result {
case let .foundStickers(_, _, hash, stickers):
var result: [FoundStickerItem] = []
let currentItemIds = Set<MediaId>(localItems.map { $0.file.fileId })
@ -369,19 +375,113 @@ func _internal_searchStickers(account: Account, query: [String], scope: SearchSt
}
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
if let entry = CodableEntry(CachedStickerQueryResult(items: files, hash: hash, timestamp: currentTime)) {
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(query.joined(separator: ""))), entry: entry)
if hash != 0, let entry = CodableEntry(CachedStickerQueryResult(items: files, hash: hash, timestamp: currentTime)) {
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(cacheKey)), entry: entry)
}
return (result, true)
case .stickersNotModified:
case .foundStickersNotModified:
break
}
return (tempResult, true)
}
}
} else {
remote = account.network.request(Api.functions.messages.getStickers(emoticon: emoticon.joined(separator: ""), hash: cached?.hash ?? 0))
|> `catch` { _ -> Signal<Api.messages.Stickers, NoError> in
return .single(.stickersNotModified)
}
|> mapToSignal { result -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> in
return account.postbox.transaction { transaction -> (items: [FoundStickerItem], isFinalResult: Bool) in
switch result {
case let .stickers(hash, stickers):
var result: [FoundStickerItem] = []
let currentItemIds = Set<MediaId>(localItems.map { $0.file.fileId })
var premiumItems: [FoundStickerItem] = []
var otherItems: [FoundStickerItem] = []
for item in localItems {
if item.file.isPremiumSticker {
premiumItems.append(item)
} else {
otherItems.append(item)
}
}
var foundItems: [FoundStickerItem] = []
var foundAnimatedItems: [FoundStickerItem] = []
var foundPremiumItems: [FoundStickerItem] = []
var files: [TelegramMediaFile] = []
for sticker in stickers {
if let file = telegramMediaFileFromApiDocument(sticker, altDocuments: []), let id = file.id {
files.append(file)
if !currentItemIds.contains(id) {
if file.isPremiumSticker {
foundPremiumItems.append(FoundStickerItem(file: file, stringRepresentations: []))
} else if file.isAnimatedSticker || file.isVideoSticker {
foundAnimatedItems.append(FoundStickerItem(file: file, stringRepresentations: []))
} else {
foundItems.append(FoundStickerItem(file: file, stringRepresentations: []))
}
}
}
}
let allPremiumItems = premiumItems + foundPremiumItems
let allOtherItems = otherItems + foundAnimatedItems + foundItems
if isPremium {
let batchCount = Int(searchStickersConfiguration.normalStickersPerPremiumCount)
if batchCount == 0 {
result.append(contentsOf: allPremiumItems)
result.append(contentsOf: allOtherItems)
} else {
if allPremiumItems.isEmpty {
result.append(contentsOf: allOtherItems)
} else {
var i = 0
for premiumItem in allPremiumItems {
if i < allOtherItems.count {
for j in i ..< min(i + batchCount, allOtherItems.count) {
result.append(allOtherItems[j])
}
i += batchCount
}
result.append(premiumItem)
}
if i < allOtherItems.count {
for j in i ..< allOtherItems.count {
result.append(allOtherItems[j])
}
}
}
}
} else {
result.append(contentsOf: allOtherItems)
result.append(contentsOf: allPremiumItems.prefix(max(0, Int(searchStickersConfiguration.premiumStickersCount))))
}
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
if let entry = CodableEntry(CachedStickerQueryResult(items: files, hash: hash, timestamp: currentTime)) {
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(cacheKey)), entry: entry)
}
return (result, true)
case .stickersNotModified:
break
}
return (tempResult, true)
}
return (tempResult, true)
}
}
return .single((tempResult, false))
|> then(remote)
|> then(
remote
|> delay(0.2, queue: Queue.concurrentDefaultQueue())
)
}
}
@ -770,16 +870,24 @@ func _internal_searchStickers(account: Account, category: EmojiSearchCategories.
}
}
func _internal_searchEmoji(account: Account, query: [String], scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
func _internal_searchEmoji(account: Account, query: String?, emoticon: [String], inputLanguageCode: String, scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
if scope.isEmpty {
return .single(([], true))
}
var query = query
if query == ["\u{2764}"] {
query = ["\u{2764}\u{FE0F}"]
var emoticon = emoticon
if emoticon == ["\u{2764}"] {
emoticon = ["\u{2764}\u{FE0F}"]
}
let combinedQuery = query.sorted().joined(separator: "")
let querySet = Set(query)
let cacheKey: String
if let query, !query.isEmpty {
cacheKey = query
} else {
cacheKey = emoticon.sorted().joined()
}
let querySet = Set(emoticon)
return account.postbox.transaction { transaction -> ([FoundStickerItem], CachedStickerQueryResult?, Bool, SearchStickersConfiguration) in
let isPremium = transaction.getPeer(account.peerId)?.isPremium ?? false
@ -812,7 +920,7 @@ func _internal_searchEmoji(account: Account, query: [String], scope: SearchStick
result.append(contentsOf: installedItems)
}
var cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedEmojiQueryResults, key: CachedStickerQueryResult.cacheKey(combinedQuery)))?.get(CachedStickerQueryResult.self)
var cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedEmojiQueryResults, key: CachedStickerQueryResult.cacheKey(cacheKey)))?.get(CachedStickerQueryResult.self)
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue
@ -839,54 +947,99 @@ func _internal_searchEmoji(account: Account, query: [String], scope: SearchStick
}
}
let remote = account.network.request(Api.functions.messages.searchCustomEmoji(emoticon: query.joined(separator: ""), hash: cached?.hash ?? 0))
|> `catch` { _ -> Signal<Api.EmojiList, NoError> in
return .single(.emojiListNotModified)
}
|> mapToSignal { result -> Signal<(files: [TelegramMediaFile], hash: Int64)?, NoError> in
switch result {
case .emojiListNotModified:
return .single(nil)
case let .emojiList(hash, documentIds):
return TelegramEngine(account: account).stickers.resolveInlineStickers(fileIds: documentIds)
|> map { fileMap -> (files: [TelegramMediaFile], hash: Int64)? in
var files: [TelegramMediaFile] = []
for documentId in documentIds {
if let file = fileMap[documentId] {
files.append(file)
}
}
return (files, hash)
}
let remote: Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError>
if let query, !query.isEmpty {
let flags: Int32 = 1 << 0
remote = account.network.request(Api.functions.messages.searchStickers(flags: flags, q: query, emoticon: emoticon.joined(separator: ""), langCode: [inputLanguageCode], offset: 0, limit: 128, hash: cached?.hash ?? 0))
|> `catch` { _ -> Signal<Api.messages.FoundStickers, NoError> in
return .single(.foundStickersNotModified(flags: 0, nextOffset: nil))
}
}
|> mapToSignal { result -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> in
return account.postbox.transaction { transaction -> (items: [FoundStickerItem], isFinalResult: Bool) in
if let (fileItems, hash) = result {
var result: [FoundStickerItem] = localItems
var currentItemIds = Set<MediaId>(localItems.map { $0.file.fileId })
|> mapToSignal { result -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> in
return account.postbox.transaction { transaction -> (items: [FoundStickerItem], isFinalResult: Bool) in
switch result {
case let .foundStickers(_, _, hash, stickers):
var result: [FoundStickerItem] = localItems
var currentItemIds = Set<MediaId>(localItems.map { $0.file.fileId })
var files: [TelegramMediaFile] = []
for file in fileItems {
files.append(file)
if !currentItemIds.contains(file.fileId) {
currentItemIds.insert(file.fileId)
result.append(FoundStickerItem(file: file, stringRepresentations: []))
var files: [TelegramMediaFile] = []
for sticker in stickers {
guard let file = telegramMediaFileFromApiDocument(sticker, altDocuments: nil) else {
continue
}
files.append(file)
if !currentItemIds.contains(file.fileId) {
currentItemIds.insert(file.fileId)
result.append(FoundStickerItem(file: file, stringRepresentations: []))
}
}
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
if let entry = CodableEntry(CachedStickerQueryResult(items: files, hash: hash, timestamp: currentTime)) {
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedEmojiQueryResults, key: CachedStickerQueryResult.cacheKey(cacheKey)), entry: entry)
}
return (result, true)
case .foundStickersNotModified:
break
}
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
if let entry = CodableEntry(CachedStickerQueryResult(items: files, hash: hash, timestamp: currentTime)) {
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedEmojiQueryResults, key: CachedStickerQueryResult.cacheKey(combinedQuery)), entry: entry)
}
return (result, true)
return (intermediateResult, true)
}
}
} else {
remote = account.network.request(Api.functions.messages.searchCustomEmoji(emoticon: emoticon.joined(separator: ""), hash: cached?.hash ?? 0))
|> `catch` { _ -> Signal<Api.EmojiList, NoError> in
return .single(.emojiListNotModified)
}
|> mapToSignal { result -> Signal<(files: [TelegramMediaFile], hash: Int64)?, NoError> in
switch result {
case .emojiListNotModified:
return .single(nil)
case let .emojiList(hash, documentIds):
return TelegramEngine(account: account).stickers.resolveInlineStickers(fileIds: documentIds)
|> map { fileMap -> (files: [TelegramMediaFile], hash: Int64)? in
var files: [TelegramMediaFile] = []
for documentId in documentIds {
if let file = fileMap[documentId] {
files.append(file)
}
}
return (files, hash)
}
}
}
|> mapToSignal { result -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> in
return account.postbox.transaction { transaction -> (items: [FoundStickerItem], isFinalResult: Bool) in
if let (fileItems, hash) = result {
var result: [FoundStickerItem] = localItems
var currentItemIds = Set<MediaId>(localItems.map { $0.file.fileId })
var files: [TelegramMediaFile] = []
for file in fileItems {
files.append(file)
if !currentItemIds.contains(file.fileId) {
currentItemIds.insert(file.fileId)
result.append(FoundStickerItem(file: file, stringRepresentations: []))
}
}
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
if let entry = CodableEntry(CachedStickerQueryResult(items: files, hash: hash, timestamp: currentTime)) {
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedEmojiQueryResults, key: CachedStickerQueryResult.cacheKey(cacheKey)), entry: entry)
}
return (result, true)
}
return (intermediateResult, true)
}
return (intermediateResult, true)
}
}
return .single((intermediateResult, false))
|> then(remote)
|> then(
remote
|> delay(0.2, queue: Queue.concurrentDefaultQueue())
)
}
}

View File

@ -30,8 +30,8 @@ public extension TelegramEngine {
return _internal_randomGreetingSticker(account: self.account)
}
public func searchStickers(query: [String], scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
return _internal_searchStickers(account: self.account, query: query, scope: scope)
public func searchStickers(query: String?, emoticon: [String], inputLanguageCode: String = "", scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
return _internal_searchStickers(account: self.account, query: query, emoticon: emoticon, inputLanguageCode: inputLanguageCode, scope: scope)
}
public func searchStickers(category: EmojiSearchCategories.Group, scope: SearchStickersScope = [.installed, .remote]) -> Signal<(items: [FoundStickerItem], isFinalResult: Bool), NoError> {
@ -287,15 +287,15 @@ public extension TelegramEngine {
return _internal_resolveInlineStickersLocal(postbox: self.account.postbox, fileIds: fileIds)
}
public func searchEmoji(emojiString: [String]) -> Signal<(items: [TelegramMediaFile], isFinalResult: Bool), NoError> {
return _internal_searchEmoji(account: self.account, query: emojiString)
public func searchEmoji(query: String?, emoticon: [String], inputLanguageCode: String = "") -> Signal<(items: [TelegramMediaFile], isFinalResult: Bool), NoError> {
return _internal_searchEmoji(account: self.account, query: query, emoticon: emoticon, inputLanguageCode: inputLanguageCode)
|> map { items, isFinalResult -> (items: [TelegramMediaFile], isFinalResult: Bool) in
return (items.map(\.file), isFinalResult)
}
}
public func searchEmoji(category: EmojiSearchCategories.Group) -> Signal<(items: [TelegramMediaFile], isFinalResult: Bool), NoError> {
return _internal_searchEmoji(account: self.account, query: category.identifiers)
return _internal_searchEmoji(account: self.account, query: nil, emoticon: category.identifiers, inputLanguageCode: "")
|> map { items, isFinalResult -> (items: [TelegramMediaFile], isFinalResult: Bool) in
return (items.map(\.file), isFinalResult)
}

View File

@ -338,7 +338,7 @@ final class AvatarEditorScreenComponent: Component {
|> mapToSignal { keywords -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in
return combineLatest(
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000) |> take(1),
combineLatest(keywords.map { context.engine.stickers.searchStickers(query: $0.emoticons)
combineLatest(keywords.map { context.engine.stickers.searchStickers(query: query, emoticon: $0.emoticons, inputLanguageCode: languageCode)
|> map { items -> [FoundStickerItem] in
return items.items
}

View File

@ -1020,6 +1020,7 @@ private final class CameraScreenComponent: CombinedComponent {
hasAccess: hasAllRequiredAccess,
hideControls: component.cameraState.collageProgress > 1.0 - .ulpOfOne,
collageProgress: component.cameraState.collageProgress,
collageCount: component.cameraState.isCollageEnabled ? component.cameraState.collageGrid.count : nil,
tintColor: controlsTintColor,
shutterState: shutterState,
lastGalleryAsset: state.lastGalleryAsset,
@ -1683,6 +1684,7 @@ public class CameraScreenImpl: ViewController, CameraScreen {
fileprivate var additionalPreviewView: CameraSimplePreviewView
fileprivate let previewBlurView: BlurView
fileprivate let mainPreviewBlurView: BlurView
private var mainPreviewSnapshotView: UIView?
private var additionalPreviewSnapshotView: UIView?
fileprivate let previewFrameLeftDimView: UIView
@ -1795,6 +1797,9 @@ public class CameraScreenImpl: ViewController, CameraScreen {
self.previewBlurView = BlurView()
self.previewBlurView.isUserInteractionEnabled = false
self.mainPreviewBlurView = BlurView()
self.mainPreviewBlurView.isUserInteractionEnabled = false
var isDualCameraEnabled = Camera.isDualCameraSupported(forRoundVideo: false)
if isDualCameraEnabled {
if let isDualCameraEnabledValue = UserDefaults.standard.object(forKey: "TelegramStoryCameraIsDualEnabled") as? NSNumber {
@ -2122,9 +2127,17 @@ public class CameraScreenImpl: ViewController, CameraScreen {
}
}
if case .position = modeChange {
UIView.transition(with: self.previewContainerView, duration: 0.4, options: [.transitionFlipFromLeft, .curveEaseOut], animations: {
self.previewBlurView.effect = UIBlurEffect(style: .dark)
})
if self.cameraState.isCollageEnabled {
self.mainPreviewBlurView.frame = self.mainPreviewContainerView.bounds
self.mainPreviewContainerView.addSubview(self.mainPreviewBlurView)
UIView.transition(with: self.mainPreviewContainerView, duration: 0.4, options: [.transitionFlipFromLeft, .curveEaseOut], animations: {
self.mainPreviewBlurView.effect = UIBlurEffect(style: .dark)
})
} else {
UIView.transition(with: self.previewContainerView, duration: 0.4, options: [.transitionFlipFromLeft, .curveEaseOut], animations: {
self.previewBlurView.effect = UIBlurEffect(style: .dark)
})
}
} else {
self.previewContainerView.insertSubview(self.previewBlurView, belowSubview: self.additionalPreviewContainerView)
@ -2137,6 +2150,13 @@ public class CameraScreenImpl: ViewController, CameraScreen {
self.previewBlurView.effect = UIBlurEffect(style: .dark)
}
} else {
if self.mainPreviewBlurView.effect != nil {
UIView.animate(withDuration: 0.4, animations: {
self.mainPreviewBlurView.effect = nil
}, completion: { _ in
self.mainPreviewBlurView.removeFromSuperview()
})
}
if self.previewBlurView.effect != nil {
UIView.animate(withDuration: 0.4, animations: {
self.previewBlurView.effect = nil
@ -2971,7 +2991,7 @@ public class CameraScreenImpl: ViewController, CameraScreen {
}
self.updatingCollageProgress = true
self.controller?.updateCameraState({ state in
return state.updatedCollageProgress(collageState.progress)
return state.updatedCollageProgress(collageState.innerProgress)
}, transition: .spring(duration: 0.3))
self.updatingCollageProgress = false
})

View File

@ -36,6 +36,7 @@ private final class ShutterButtonContentComponent: Component {
let shutterState: ShutterButtonState
let blobState: ShutterBlobView.BlobState
let collageProgress: Float
let collageCount: Int?
let highlightedAction: ActionSlot<Bool>
let updateOffsetX: ActionSlot<(CGFloat, ComponentTransition)>
let updateOffsetY: ActionSlot<(CGFloat, ComponentTransition)>
@ -47,6 +48,7 @@ private final class ShutterButtonContentComponent: Component {
shutterState: ShutterButtonState,
blobState: ShutterBlobView.BlobState,
collageProgress: Float,
collageCount: Int?,
highlightedAction: ActionSlot<Bool>,
updateOffsetX: ActionSlot<(CGFloat, ComponentTransition)>,
updateOffsetY: ActionSlot<(CGFloat, ComponentTransition)>
@ -57,6 +59,7 @@ private final class ShutterButtonContentComponent: Component {
self.shutterState = shutterState
self.blobState = blobState
self.collageProgress = collageProgress
self.collageCount = collageCount
self.highlightedAction = highlightedAction
self.updateOffsetX = updateOffsetX
self.updateOffsetY = updateOffsetY
@ -81,6 +84,9 @@ private final class ShutterButtonContentComponent: Component {
if lhs.collageProgress != rhs.collageProgress {
return false
}
if lhs.collageCount != rhs.collageCount {
return false
}
return true
}
@ -314,6 +320,8 @@ private final class ShutterButtonContentComponent: Component {
self.innerLayer.bounds = CGRect(origin: .zero, size: innerSize)
self.innerLayer.position = CGPoint(x: maximumShutterSize.width / 2.0, y: maximumShutterSize.height / 2.0)
let totalProgress = component.collageCount.flatMap { 1.0 / Double($0) } ?? 1.0
self.progressLayer.bounds = CGRect(origin: .zero, size: maximumShutterSize)
self.progressLayer.position = CGPoint(x: maximumShutterSize.width / 2.0, y: maximumShutterSize.height / 2.0)
transition.setShapeLayerPath(layer: self.progressLayer, path: ringPath)
@ -321,10 +329,14 @@ private final class ShutterButtonContentComponent: Component {
self.progressLayer.strokeColor = videoRedColor.cgColor
self.progressLayer.lineWidth = ringWidth + UIScreenPixel
self.progressLayer.lineCap = .round
self.progressLayer.transform = CATransform3DMakeRotation(-.pi / 2.0, 0.0, 0.0, 1.0)
if totalProgress < 1.0 {
self.progressLayer.transform = CATransform3DMakeRotation(-.pi / 2.0 + CGFloat(component.collageProgress) * 2.0 * .pi, 0.0, 0.0, 1.0)
} else {
self.progressLayer.transform = CATransform3DMakeRotation(-.pi / 2.0, 0.0, 0.0, 1.0)
}
let previousValue = self.progressLayer.strokeEnd
self.progressLayer.strokeEnd = CGFloat(recordingProgress ?? 0.0)
self.progressLayer.strokeEnd = CGFloat(recordingProgress ?? 0.0) * totalProgress
self.progressLayer.animateStrokeEnd(from: previousValue, to: self.progressLayer.strokeEnd, duration: 0.33)
return maximumShutterSize
@ -554,6 +566,7 @@ final class CaptureControlsComponent: Component {
let hasAccess: Bool
let hideControls: Bool
let collageProgress: Float
let collageCount: Int?
let tintColor: UIColor
let shutterState: ShutterButtonState
let lastGalleryAsset: PHAsset?
@ -576,6 +589,7 @@ final class CaptureControlsComponent: Component {
hasAccess: Bool,
hideControls: Bool,
collageProgress: Float,
collageCount: Int?,
tintColor: UIColor,
shutterState: ShutterButtonState,
lastGalleryAsset: PHAsset?,
@ -597,6 +611,7 @@ final class CaptureControlsComponent: Component {
self.hasAccess = hasAccess
self.hideControls = hideControls
self.collageProgress = collageProgress
self.collageCount = collageCount
self.tintColor = tintColor
self.shutterState = shutterState
self.lastGalleryAsset = lastGalleryAsset
@ -632,6 +647,9 @@ final class CaptureControlsComponent: Component {
if lhs.collageProgress != rhs.collageProgress {
return false
}
if lhs.collageCount != rhs.collageCount {
return false
}
if lhs.tintColor != rhs.tintColor {
return false
}
@ -1152,6 +1170,7 @@ final class CaptureControlsComponent: Component {
shutterState: component.shutterState,
blobState: blobState,
collageProgress: component.collageProgress,
collageCount: component.collageCount,
highlightedAction: self.shutterHightlightedAction,
updateOffsetX: self.shutterUpdateOffsetX,
updateOffsetY: self.shutterUpdateOffsetY

View File

@ -938,7 +938,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
let remoteSignal: Signal<(items: [TelegramMediaFile], isFinalResult: Bool), NoError>
let remotePacksSignal: Signal<(sets: FoundStickerSets, isFinalResult: Bool), NoError>
if hasPremium {
remoteSignal = context.engine.stickers.searchEmoji(emojiString: Array(allEmoticons.keys))
remoteSignal = context.engine.stickers.searchEmoji(query: query, emoticon: Array(allEmoticons.keys), inputLanguageCode: languageCode)
remotePacksSignal = context.engine.stickers.searchEmojiSets(query: query)
|> mapToSignal { localResult in
return .single((localResult, false))

View File

@ -348,7 +348,7 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
let query = text.trimmingCharacters(in: .whitespacesAndNewlines)
if query.isSingleEmoji {
signals = .single([context.engine.stickers.searchStickers(query: [text.basicEmoji.0])
signals = .single([context.engine.stickers.searchStickers(query: nil, emoticon: [text.basicEmoji.0])
|> map { (nil, $0.items) }])
} else if query.count > 1, let languageCode = languageCode, !languageCode.isEmpty && languageCode != "emoji" {
var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query.lowercased(), completeMatch: query.count < 3)
@ -364,17 +364,11 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
)
}
}
signals = signal
|> map { keywords -> [Signal<(String?, [FoundStickerItem]), NoError>] in
var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = []
let emoticons = keywords.flatMap { $0.emoticons }
for emoji in emoticons {
signals.append(context.engine.stickers.searchStickers(query: [emoji.basicEmoji.0])
// |> take(1)
|> map { (emoji, $0.items) })
}
return signals
let emoticon = keywords.flatMap { $0.emoticons }.map { $0.basicEmoji.0 }
return [context.engine.stickers.searchStickers(query: query, emoticon: emoticon, inputLanguageCode: languageCode)
|> map { (nil, $0.items) }]
}
}

View File

@ -111,7 +111,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, cha
case .installed:
scope = [.installed]
}
return context.engine.stickers.searchStickers(query: [query.basicEmoji.0], scope: scope)
return context.engine.stickers.searchStickers(query: nil, emoticon: [query.basicEmoji.0], scope: scope)
|> map { items -> [FoundStickerItem] in
return items.items
}

View File

@ -1759,17 +1759,19 @@ public final class MessageInputPanelComponent: Component {
}
}
var lightFieldColor = UIColor(white: 1.0, alpha: 0.09)
var fieldBackgroundIsDark = false
if component.useGrayBackground {
fieldBackgroundIsDark = false
} else if component.style == .media {
fieldBackgroundIsDark = true
fieldBackgroundIsDark = false
lightFieldColor = UIColor(white: 0.2, alpha: 0.45)
} else if self.textFieldExternalState.hasText && component.alwaysDarkWhenHasText {
fieldBackgroundIsDark = true
} else if isEditing || component.style == .editor {
fieldBackgroundIsDark = true
}
self.fieldBackgroundView.updateColor(color: fieldBackgroundIsDark ? UIColor(white: 0.0, alpha: 0.5) : UIColor(white: 1.0, alpha: 0.09), transition: transition.containedViewLayoutTransition)
self.fieldBackgroundView.updateColor(color: fieldBackgroundIsDark ? UIColor(white: 0.0, alpha: 0.5) : lightFieldColor, transition: transition.containedViewLayoutTransition)
if let placeholder = self.placeholder.view, let vibrancyPlaceholderView = self.vibrancyPlaceholder.view {
placeholder.isHidden = self.textFieldExternalState.hasText
vibrancyPlaceholderView.isHidden = placeholder.isHidden

View File

@ -687,7 +687,7 @@ final class PeerAllowedReactionsScreenComponent: Component {
))
return .single(resultGroups)
} else {
let remoteSignal = context.engine.stickers.searchEmoji(emojiString: Array(allEmoticons.keys))
let remoteSignal = context.engine.stickers.searchEmoji(query: query, emoticon: Array(allEmoticons.keys), inputLanguageCode: languageCode)
return combineLatest(
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000) |> take(1),

View File

@ -798,7 +798,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
let botsKey = ValueBoxKey(length: 8)
botsKey.setInt64(0, value: 0)
let iconLoaded = Atomic<[EnginePeer.Id: Bool]>(value: [:])
//let iconLoaded = Atomic<[EnginePeer.Id: Bool]>(value: [:])
let bots = context.engine.data.subscribe(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: Namespaces.CachedItemCollection.attachMenuBots, id: botsKey))
|> mapToSignal { entry -> Signal<[AttachMenuBot], NoError> in
let bots: [AttachMenuBots.Bot] = entry?.get(AttachMenuBots.self)?.bots ?? []
@ -811,32 +811,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
if let maybePeer = peersMap[bot.peerId], let peer = maybePeer {
let resultBot = AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes, flags: bot.flags)
if bot.flags.contains(.showInSettings) {
if let peer = PeerReference(peer._asPeer()), let icon = bot.icons[.iOSSettingsStatic] {
let fileReference: FileMediaReference = .attachBot(peer: peer, media: icon)
let signal: Signal<AttachMenuBot?, NoError>
if let _ = iconLoaded.with({ $0 })[peer.id] {
signal = .single(resultBot)
} else {
signal = .single(nil)
|> then(
preloadedBotIcon(account: context.account, fileReference: fileReference)
|> filter { $0 }
|> map { _ -> AttachMenuBot? in
return resultBot
}
|> afterNext { _ in
let _ = iconLoaded.modify { current in
var updated = current
updated[peer.id] = true
return updated
}
}
)
}
result.append(signal)
} else {
result.append(.single(resultBot))
}
result.append(.single(resultBot))
}
}
}

View File

@ -920,7 +920,7 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
}
let _ = freeMediaFileInteractiveFetched(account: context.account, userLocation: .other, fileReference: fileReference).startStandalone()
} else {
iconSignal = .single(UIImage(bundleImageName: "Settings/Menu/Websites")!)
iconSignal = .single(UIImage())
}
let label: PeerInfoScreenDisclosureItem.Label = bot.flags.contains(.notActivated) || bot.flags.contains(.showInSettingsDisclaimer) ? .titleBadge(presentationData.strings.Settings_New, presentationData.theme.list.itemAccentColor) : .none
items[.apps]!.append(PeerInfoScreenDisclosureItem(id: bot.peer.id.id._internalGetInt64Value(), label: label, text: bot.shortName, icon: nil, iconSignal: iconSignal, action: {

View File

@ -370,7 +370,7 @@ final class BusinessIntroSetupScreenComponent: Component {
var signals: Signal<[Signal<(String?, [FoundStickerItem]), NoError>], NoError> = .single([])
if query.isSingleEmoji {
signals = .single([context.engine.stickers.searchStickers(query: [query.basicEmoji.0])
signals = .single([context.engine.stickers.searchStickers(query: nil, emoticon: [query.basicEmoji.0])
|> map { (nil, $0.items) }])
} else if query.count > 1, !languageCode.isEmpty && languageCode != "emoji" {
var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query.lowercased(), completeMatch: query.count < 3)
@ -385,18 +385,12 @@ final class BusinessIntroSetupScreenComponent: Component {
}
)
}
}
}
signals = signal
|> map { keywords -> [Signal<(String?, [FoundStickerItem]), NoError>] in
var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = []
let emoticons = keywords.flatMap { $0.emoticons }
for emoji in emoticons {
signals.append(context.engine.stickers.searchStickers(query: [emoji.basicEmoji.0])
|> take(1)
|> map { (emoji, $0.items) })
}
return signals
let emoticon = keywords.flatMap { $0.emoticons }.map { $0.basicEmoji.0 }
return [context.engine.stickers.searchStickers(query: query, emoticon: emoticon, inputLanguageCode: languageCode)
|> map { (nil, $0.items) }]
}
}

View File

@ -1080,7 +1080,7 @@ public class StickerPickerScreen: ViewController {
}
let remoteSignal: Signal<(items: [TelegramMediaFile], isFinalResult: Bool), NoError>
if hasPremium {
remoteSignal = context.engine.stickers.searchEmoji(emojiString: Array(allEmoticons.keys))
remoteSignal = context.engine.stickers.searchEmoji(query: query, emoticon: Array(allEmoticons.keys), inputLanguageCode: languageCode)
} else {
remoteSignal = .single(([], true))
}

View File

@ -95,7 +95,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
case .installed:
scope = [.installed]
}
return context.engine.stickers.searchStickers(query: [query.basicEmoji.0], scope: scope)
return context.engine.stickers.searchStickers(query: nil, emoticon: [query.basicEmoji.0], scope: scope)
|> map { items -> [FoundStickerItem] in
return items.items
}