mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge commit 'de6f737bc106c29f9c0478d9456dd0d0bcee2d33'
This commit is contained in:
commit
acf5f3cd9f
@ -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) }]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,11 @@
|
||||
@property (nonatomic, copy) void (^timerUpdated)(NSNumber *timeout);
|
||||
@property (nonatomic, copy) void (^captionIsAboveUpdated)(bool captionIsAbove);
|
||||
|
||||
@property (nonatomic, readonly) bool editing;
|
||||
|
||||
- (void)createInputPanelIfNeeded;
|
||||
- (void)beginEditing;
|
||||
- (void)finishEditing;
|
||||
- (void)enableDismissal;
|
||||
|
||||
- (void)onAnimateOut;
|
||||
|
@ -175,7 +175,15 @@
|
||||
[strongSelf.window endEditing:true];
|
||||
strongSelf->_portraitToolbarView.doneButton.userInteractionEnabled = false;
|
||||
strongSelf->_landscapeToolbarView.doneButton.userInteractionEnabled = false;
|
||||
strongSelf->_donePressed(strongSelf->_currentItem);
|
||||
|
||||
if (strongSelf->_captionMixin.editing) {
|
||||
[strongSelf->_captionMixin finishEditing];
|
||||
TGDispatchAfter(0.1, dispatch_get_main_queue(), ^{
|
||||
strongSelf->_donePressed(strongSelf->_currentItem);
|
||||
});
|
||||
} else {
|
||||
strongSelf->_donePressed(strongSelf->_currentItem);
|
||||
}
|
||||
|
||||
[strongSelf->_captionMixin onAnimateOut];
|
||||
};
|
||||
@ -1428,7 +1436,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]])
|
||||
|
||||
{
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
@ -177,19 +181,21 @@
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)finishEditing {
|
||||
if ([self.inputPanel dismissInput]) {
|
||||
_editing = false;
|
||||
|
||||
if (self.finishedWithCaption != nil)
|
||||
self.finishedWithCaption([_inputPanel caption]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleDismissTap:(UITapGestureRecognizer *)gestureRecognizer
|
||||
{
|
||||
if (gestureRecognizer.state != UIGestureRecognizerStateRecognized)
|
||||
return;
|
||||
|
||||
if ([self.inputPanel dismissInput]) {
|
||||
_editing = false;
|
||||
|
||||
[_dismissView removeFromSuperview];
|
||||
|
||||
if (self.finishedWithCaption != nil)
|
||||
self.finishedWithCaption([_inputPanel caption]);
|
||||
}
|
||||
[self finishEditing];
|
||||
}
|
||||
|
||||
#pragma mark - Input Panel Delegate
|
||||
@ -228,6 +234,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;
|
||||
|
||||
|
@ -155,7 +155,7 @@ public func legacyStoryMediaEditor(context: AccountContext, item: TGMediaEditabl
|
||||
})
|
||||
}
|
||||
|
||||
public func legacyMediaEditor(context: AccountContext, peer: Peer, threadTitle: String?, media: AnyMediaReference, mode: LegacyMediaEditorMode, initialCaption: NSAttributedString, snapshots: [UIView], transitionCompletion: (() -> Void)?, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
public func legacyMediaEditor(context: AccountContext, peer: Peer, threadTitle: String?, media: AnyMediaReference, mode: LegacyMediaEditorMode, initialCaption: NSAttributedString, snapshots: [UIView], transitionCompletion: (() -> Void)?, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32, Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
let _ = (fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: media)
|
||||
|> deliverOnMainQueue).start(next: { (value, isImage) in
|
||||
guard case let .data(data) = value, data.complete else {
|
||||
@ -215,7 +215,8 @@ public func legacyMediaEditor(context: AccountContext, peer: Peer, threadTitle:
|
||||
let signals = TGCameraController.resultSignals(for: nil, editingContext: editingContext, currentItem: selectableResult, storeAssets: false, saveEditedPhotos: false, descriptionGenerator: { _1, _2, _3 in
|
||||
nativeGenerator(_1, _2, _3, nil)
|
||||
})
|
||||
sendMessagesWithSignals(signals, false, 0)
|
||||
let isCaptionAbove = editingContext?.isCaptionAbove() ?? false
|
||||
sendMessagesWithSignals(signals, false, 0, isCaptionAbove)
|
||||
}, dismissed: { [weak legacyController] in
|
||||
legacyController?.dismiss()
|
||||
})
|
||||
|
@ -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),
|
||||
|
@ -1628,9 +1628,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
|
||||
|
@ -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())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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) }]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -923,7 +923,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: {
|
||||
@ -5276,7 +5276,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
transitionCompletion()
|
||||
}, getCaptionPanelView: {
|
||||
return nil
|
||||
}, sendMessagesWithSignals: { [weak self] signals, _, _ in
|
||||
}, sendMessagesWithSignals: { [weak self] signals, _, _, _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(context: strongSelf.context, account: strongSelf.context.account, signals: signals!)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] messages in
|
||||
|
@ -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) }]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -1462,9 +1462,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
transitionCompletion()
|
||||
}, getCaptionPanelView: { [weak self] in
|
||||
return self?.getCaptionPanelView(isFile: false)
|
||||
}, sendMessagesWithSignals: { [weak self] signals, _, _ in
|
||||
}, sendMessagesWithSignals: { [weak self] signals, _, _, isCaptionAbove in
|
||||
if let strongSelf = self {
|
||||
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: false)
|
||||
var parameters: ChatSendMessageActionSheetController.SendParameters?
|
||||
if isCaptionAbove {
|
||||
parameters = ChatSendMessageActionSheetController.SendParameters(effect: nil, textIsAboveMedia: true)
|
||||
}
|
||||
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: false, parameters: parameters)
|
||||
}
|
||||
}, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
@ -3801,7 +3805,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText
|
||||
legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, mode: .draw, initialCaption: inputText, snapshots: [], transitionCompletion: nil, getCaptionPanelView: { [weak self] in
|
||||
return self?.getCaptionPanelView(isFile: true)
|
||||
}, sendMessagesWithSignals: { [weak self] signals, _, _ in
|
||||
}, sendMessagesWithSignals: { [weak self] signals, _, _, _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.interfaceInteraction?.setupEditMessage(messageId, { _ in })
|
||||
strongSelf.editMessageMediaWithLegacySignals(signals!)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"app": "11.4",
|
||||
"app": "11.5",
|
||||
"xcode": "16.0",
|
||||
"bazel": "7.3.1:981f82a470bad1349322b6f51c9c6ffa0aa291dab1014fac411543c12e661dff",
|
||||
"macos": "15.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user