GIF-related fixes

This commit is contained in:
Ali 2020-05-24 23:51:23 +04:00
parent 48dba115e6
commit 0fd80bba89
30 changed files with 4043 additions and 3853 deletions

View File

@ -5508,3 +5508,6 @@ Any member of this group will be able to see messages in the channel.";
"OwnershipTransfer.Transfer" = "Transfer";
"TwoStepAuth.Disable" = "Disable";
"Chat.Gifs.TrendingSectionHeader" = "TRENDING GIFS";
"Chat.Gifs.SavedSectionHeader" = "MY GIFS";

View File

@ -82,15 +82,19 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
let isSavedMessages = peerId == context.account.peerId
let chatPeer = transaction.getPeer(peerId)
var peer: Peer?
var maybePeer: Peer?
if let chatPeer = chatPeer {
if let chatPeer = chatPeer as? TelegramSecretChat {
peer = transaction.getPeer(chatPeer.regularPeerId)
maybePeer = transaction.getPeer(chatPeer.regularPeerId)
} else {
peer = chatPeer
maybePeer = chatPeer
}
}
guard let peer = maybePeer else {
return []
}
if !isSavedMessages, let peer = peer as? TelegramUser, !peer.flags.contains(.isSupport) && peer.botInfo == nil && !peer.isDeleted {
if !transaction.isPeerContact(peerId: peer.id) {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_AddToContacts, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) }, action: { _, f in
@ -109,10 +113,29 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
}
}
var isMuted = false
if let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings {
if case .muted = notificationSettings.muteState {
isMuted = true
}
}
var isUnread = false
if let readState = transaction.getCombinedPeerReadState(peerId), readState.isUnread {
isUnread = true
}
let isContact = transaction.isPeerContact(peerId: peerId)
if case .chatList = source {
var hasFolders = false
updateChatListFiltersInteractively(transaction: transaction, { filters in
for filter in filters {
let predicate = chatListFilterPredicate(filter: filter.data)
if predicate.includes(peer: peer, groupId: .root, isRemovedFromTotalUnreadCount: isMuted, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: false) {
continue
}
var data = filter.data
if data.addIncludePeer(peerId: peerId) {
hasFolders = true
@ -128,6 +151,11 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
var updatedItems: [ContextMenuItem] = []
updateChatListFiltersInteractively(transaction: transaction, { filters in
for filter in filters {
let predicate = chatListFilterPredicate(filter: filter.data)
if predicate.includes(peer: peer, groupId: .root, isRemovedFromTotalUnreadCount: isMuted, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: false) {
continue
}
var data = filter.data
if !data.addIncludePeer(peerId: peerId) {
continue
@ -168,11 +196,9 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
return filters
})).start()
if let peer = peer {
chatListController?.present(UndoOverlayController(presentationData: presentationData, content: .chatAddedToFolder(chatTitle: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), folderTitle: filter.title), elevatedLayout: false, animateInAsReplacement: true, action: { _ in
return false
}), in: .current)
}
chatListController?.present(UndoOverlayController(presentationData: presentationData, content: .chatAddedToFolder(chatTitle: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), folderTitle: filter.title), elevatedLayout: false, animateInAsReplacement: true, action: { _ in
return false
}), in: .current)
})
})))
}
@ -195,7 +221,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
})))
}
if let readState = transaction.getCombinedPeerReadState(peerId), readState.isUnread {
if isUnread {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAsRead, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsRead"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = togglePeerUnreadMarkInteractively(postbox: context.account.postbox, viewTracker: context.account.viewTracker, peerId: peerId).start()
f(.default)
@ -248,18 +274,20 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
let isPinned = getPinnedItemIds(transaction: transaction, location: location).contains(.peer(peerId))
items.append(.action(ContextMenuActionItem(text: isPinned ? strings.ChatList_Context_Unpin : strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin" : "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = (toggleItemPinned(postbox: context.account.postbox, location: location, itemId: .peer(peerId))
|> deliverOnMainQueue).start(next: { result in
switch result {
case .done:
break
case .limitExceeded:
break
}
f(.default)
})
})))
if isPinned || filter == nil || peerId.namespace != Namespaces.Peer.SecretChat {
items.append(.action(ContextMenuActionItem(text: isPinned ? strings.ChatList_Context_Unpin : strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin" : "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = (toggleItemPinned(postbox: context.account.postbox, location: location, itemId: .peer(peerId))
|> deliverOnMainQueue).start(next: { result in
switch result {
case .done:
break
case .limitExceeded:
break
}
f(.default)
})
})))
}
}
if !isSavedMessages, let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings {

View File

@ -1219,27 +1219,37 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
strongSelf.processedFeaturedFilters = true
if hasFeatured {
if let _ = strongSelf.validLayout, let parentController = strongSelf.parent as? TabBarController, let sourceFrame = parentController.frameForControllerTab(controller: strongSelf) {
let absoluteFrame = sourceFrame
let text: String
if hasFilters {
text = strongSelf.presentationData.strings.ChatList_TabIconFoldersTooltipNonEmptyFolders
let _ = markChatListFeaturedFiltersAsSeen(postbox: strongSelf.context.account.postbox).start()
} else {
text = strongSelf.presentationData.strings.ChatList_TabIconFoldersTooltipEmptyFolders
}
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 8.0), size: CGSize())
parentController.present(TooltipScreen(text: text, icon: .chatListPress, location: .point(location), shouldDismissOnTouch: { point in
guard let strongSelf = self, let parentController = strongSelf.parent as? TabBarController else {
if let _ = strongSelf.validLayout, let _ = strongSelf.parent as? TabBarController {
let _ = (ApplicationSpecificNotice.incrementChatFolderTips(accountManager: strongSelf.context.sharedContext.accountManager)
|> deliverOnMainQueue).start(next: { count in
guard let strongSelf = self, let _ = strongSelf.validLayout, let parentController = strongSelf.parent as? TabBarController, let sourceFrame = parentController.frameForControllerTab(controller: strongSelf) else {
return
}
if count >= 2 {
return
}
let absoluteFrame = sourceFrame
let text: String
if hasFilters {
text = strongSelf.presentationData.strings.ChatList_TabIconFoldersTooltipNonEmptyFolders
let _ = markChatListFeaturedFiltersAsSeen(postbox: strongSelf.context.account.postbox).start()
} else {
text = strongSelf.presentationData.strings.ChatList_TabIconFoldersTooltipEmptyFolders
}
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 8.0), size: CGSize())
parentController.present(TooltipScreen(text: text, icon: .chatListPress, location: .point(location), shouldDismissOnTouch: { point in
guard let strongSelf = self, let parentController = strongSelf.parent as? TabBarController else {
return .dismiss(consume: false)
}
if parentController.isPointInsideContentArea(point: point) {
return .ignore
}
return .dismiss(consume: false)
}
if parentController.isPointInsideContentArea(point: point) {
return .ignore
}
return .dismiss(consume: false)
}), in: .current)
}), in: .current)
})
}
}
}))

View File

@ -272,7 +272,7 @@ private func groupReferenceRevealOptions(strings: PresentationStrings, theme: Pr
return options
}
private func leftRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isUnread: Bool, isEditing: Bool, isPinned: Bool, isSavedMessages: Bool, groupId: PeerGroupId, filterData: ChatListItemFilterData?) -> [ItemListRevealOption] {
private func leftRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isUnread: Bool, isEditing: Bool, isPinned: Bool, isSavedMessages: Bool, groupId: PeerGroupId, peer: Peer, filterData: ChatListItemFilterData?) -> [ItemListRevealOption] {
if case .group = groupId {
return []
}
@ -286,7 +286,9 @@ private func leftRevealOptions(strings: PresentationStrings, theme: Presentation
if isPinned {
options.append(ItemListRevealOption(key: RevealOptionKey.unpin.rawValue, title: strings.DialogList_Unpin, icon: unpinIcon, color: theme.list.itemDisclosureActions.constructive.fillColor, textColor: theme.list.itemDisclosureActions.constructive.foregroundColor))
} else {
options.append(ItemListRevealOption(key: RevealOptionKey.pin.rawValue, title: strings.DialogList_Pin, icon: pinIcon, color: theme.list.itemDisclosureActions.constructive.fillColor, textColor: theme.list.itemDisclosureActions.constructive.foregroundColor))
if filterData == nil || peer.id.namespace != Namespaces.Peer.SecretChat {
options.append(ItemListRevealOption(key: RevealOptionKey.pin.rawValue, title: strings.DialogList_Pin, icon: pinIcon, color: theme.list.itemDisclosureActions.constructive.fillColor, textColor: theme.list.itemDisclosureActions.constructive.foregroundColor))
}
}
}
return options
@ -1222,7 +1224,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else if promoInfo == nil {
peerRevealOptions = revealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isPinned: isPinned, isMuted: item.context.account.peerId != item.index.messageIndex.id.peerId ? (currentMutedIconImage != nil) : nil, groupId: item.peerGroupId, peerId: renderedPeer.peerId, accountPeerId: item.context.account.peerId, canDelete: true, isEditing: item.editing, filterData: item.filterData)
if case let .chat(itemPeer) = contentPeer {
peerLeftRevealOptions = leftRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isUnread: unreadCount.unread, isEditing: item.editing, isPinned: isPinned, isSavedMessages: itemPeer.peerId == item.context.account.peerId, groupId: item.peerGroupId, filterData: item.filterData)
peerLeftRevealOptions = leftRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isUnread: unreadCount.unread, isEditing: item.editing, isPinned: isPinned, isSavedMessages: itemPeer.peerId == item.context.account.peerId, groupId: item.peerGroupId, peer: itemPeer.peers[itemPeer.peerId]!, filterData: item.filterData)
} else {
peerLeftRevealOptions = []
}

View File

@ -535,8 +535,10 @@ public class ContactsController: ViewController {
return
}
if let peer = peer {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController)
DispatchQueue.main.async {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController)
}
}
} else {
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil))

View File

@ -1057,11 +1057,31 @@ open class NavigationController: UINavigationController, ContainableController,
self.setViewControllers(controllers, animated: animated)
}
public func replaceTopController(_ controller: ViewController, animated: Bool, ready: ValuePromise<Bool>? = nil) {
ready?.set(true)
public func replaceTopController(_ controller: ViewController, animated: Bool, ready: Promise<Bool>? = nil) {
var controllers = self.viewControllers
controllers.removeLast()
controllers.append(controller)
if let rootContainer = self.rootContainer {
var controllerToReplace: ViewController?
switch rootContainer {
case let .flat(container):
controllerToReplace = container.controllers.last
case let .split(container):
controllerToReplace = container.detailControllers.last
}
if let controllerToReplace = controllerToReplace, let index = controllers.firstIndex(where: { $0 === controllerToReplace }) {
controllers[index] = controller
ready?.set(controller.ready.get())
} else {
ready?.set(controller.ready.get())
controllers.removeLast()
controllers.append(controller)
}
} else {
ready?.set(controller.ready.get())
controllers.removeLast()
controllers.append(controller)
}
self.setViewControllers(controllers, animated: animated)
}

View File

@ -370,7 +370,7 @@ public class GalleryController: ViewController, StandalonePresentableController
private let _hiddenMedia = Promise<(MessageId, Media)?>(nil)
private let replaceRootController: (ViewController, ValuePromise<Bool>?) -> Void
private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
private let baseNavigationController: NavigationController?
private var hiddenMediaManagerIndex: Int?
@ -381,7 +381,7 @@ public class GalleryController: ViewController, StandalonePresentableController
private let updateVisibleDisposable = MetaDisposable()
public init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, ValuePromise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) {
public init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) {
self.context = context
self.source = source
self.invertItemOrder = invertItemOrder

View File

@ -7,9 +7,9 @@ import SwiftSignalKit
public final class GalleryControllerInteraction {
public let presentController: (ViewController, ViewControllerPresentationArguments?) -> Void
public let dismissController: () -> Void
public let replaceRootController: (ViewController, ValuePromise<Bool>?) -> Void
public let replaceRootController: (ViewController, Promise<Bool>?) -> Void
public init(presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, dismissController: @escaping () -> Void, replaceRootController: @escaping (ViewController, ValuePromise<Bool>?) -> Void) {
public init(presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, dismissController: @escaping () -> Void, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void) {
self.presentController = presentController
self.dismissController = dismissController
self.replaceRootController = replaceRootController

View File

@ -178,14 +178,14 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable
return self._hiddenMedia.get()
}
private let replaceRootController: (ViewController, ValuePromise<Bool>?) -> Void
private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
private let baseNavigationController: NavigationController?
var openUrl: ((InstantPageUrlItem) -> Void)?
private var innerOpenUrl: (InstantPageUrlItem) -> Void
private var openUrlOptions: (InstantPageUrlItem) -> Void
public init(context: AccountContext, webPage: TelegramMediaWebpage, message: Message? = nil, entries: [InstantPageGalleryEntry], centralIndex: Int, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, replaceRootController: @escaping (ViewController, ValuePromise<Bool>?) -> Void, baseNavigationController: NavigationController?) {
public init(context: AccountContext, webPage: TelegramMediaWebpage, message: Message? = nil, entries: [InstantPageGalleryEntry], centralIndex: Int, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?) {
self.context = context
self.webPage = webPage
self.message = message

View File

@ -123,6 +123,10 @@ public final class SoftwareVideoSource {
}
self.videoStream = videoStream
if let videoStream = self.videoStream {
avFormatContext.seekFrame(forStreamIndex: Int32(videoStream.index), pts: 0, positionOnKeyframe: true)
}
}
deinit {

View File

@ -76,11 +76,11 @@ class SecureIdDocumentGalleryController: ViewController, StandalonePresentableCo
return self._hiddenMedia.get()
}
private let replaceRootController: (ViewController, ValuePromise<Bool>?) -> Void
private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
var deleteResource: ((TelegramMediaResource) -> Void)?
init(context: AccountContext, secureIdContext: SecureIdAccessContext, entries: [SecureIdDocumentGalleryEntry], centralIndex: Int, replaceRootController: @escaping (ViewController, ValuePromise<Bool>?) -> Void) {
init(context: AccountContext, secureIdContext: SecureIdAccessContext, entries: [SecureIdDocumentGalleryEntry], centralIndex: Int, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void) {
self.context = context
self.secureIdContext = secureIdContext
self.replaceRootController = replaceRootController

View File

@ -172,9 +172,9 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
return self._hiddenMedia.get()
}
private let replaceRootController: (ViewController, ValuePromise<Bool>?) -> Void
private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
public init(context: AccountContext, peer: Peer, sourceHasRoundCorners: Bool = true, remoteEntries: Promise<[AvatarGalleryEntry]>? = nil, centralEntryIndex: Int? = nil, replaceRootController: @escaping (ViewController, ValuePromise<Bool>?) -> Void, synchronousLoad: Bool = false) {
public init(context: AccountContext, peer: Peer, sourceHasRoundCorners: Bool = true, remoteEntries: Promise<[AvatarGalleryEntry]>? = nil, centralEntryIndex: Int? = nil, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, synchronousLoad: Bool = false) {
self.context = context
self.peer = peer
self.sourceHasRoundCorners = sourceHasRoundCorners

View File

@ -24,6 +24,13 @@ func addSynchronizeSavedGifsOperation(transaction: Transaction, operation: Synch
transaction.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeSavedGifsOperation(content: .sync))
}
public func isGifSaved(transaction: Transaction, mediaId: MediaId) -> Bool {
if transaction.getOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentGifs, itemId: RecentMediaItemId(mediaId).rawValue) != nil {
return true
}
return false
}
public func addSavedGif(postbox: Postbox, fileReference: FileMediaReference) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Void in
if let resource = fileReference.media.resource as? CloudDocumentMediaResource {

View File

@ -135,6 +135,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
case chatTextSelectionTip = 16
case themeChangeTip = 17
case callsTabTip = 18
case chatFolderTips = 19
var key: ValueBoxKey {
let v = ValueBoxKey(length: 4)
@ -198,6 +199,10 @@ private struct ApplicationSpecificNoticeKeys {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.archiveChatTips.key)
}
static func chatFolderTips() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatFolderTips.key)
}
static func profileCallTips() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.profileCallTips.key)
}
@ -397,6 +402,21 @@ public struct ApplicationSpecificNotice {
}
}
public static func incrementChatFolderTips(accountManager: AccountManager, count: Int = 1) -> Signal<Int, NoError> {
return accountManager.transaction { transaction -> Int in
var currentValue: Int32 = 0
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatFolderTips()) as? ApplicationSpecificCounterNotice {
currentValue = value.value
}
let previousValue = currentValue
currentValue += Int32(count)
transaction.setNotice(ApplicationSpecificNoticeKeys.chatFolderTips(), ApplicationSpecificCounterNotice(value: currentValue))
return Int(previousValue)
}
}
public static func setArchiveIntroDismissed(transaction: AccountManagerModifier, value: Bool) {
transaction.setNotice(ApplicationSpecificNoticeKeys.archiveIntroDismissed(), ApplicationSpecificVariantNotice(value: value))
}

View File

@ -121,7 +121,7 @@ final class AudioWaveformNode: ASDisplayNode {
let diff: CGFloat
let samplePosition = CGFloat(i) / CGFloat(numSamples)
if let position = parameters.progress, abs(position - samplePosition) < 0.01 {
diff = sampleWidth * 0.5
diff = sampleWidth * 1.5
} else {
diff = sampleWidth * 1.5
}

View File

@ -4834,7 +4834,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
self.raiseToListen?.enabled = self.canReadHistoryValue
self.tempVoicePlaylistEnded = { [weak self] in
if let strongSelf = self, let raiseToListen = strongSelf.raiseToListen {
guard let strongSelf = self else {
return
}
if !canSendMessagesToChat(strongSelf.presentationInterfaceState) {
return
}
if let raiseToListen = strongSelf.raiseToListen {
strongSelf.voicePlaylistDidEndTimestamp = CACurrentMediaTime()
raiseToListen.activateBasedOnProximity(delay: 0.0)
}
@ -7651,9 +7658,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self {
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) })
let ready = ValuePromise<Bool>()
let ready = Promise<Bool>()
strongSelf.controllerNavigationDisposable.set((ready.get() |> take(1) |> deliverOnMainQueue).start(next: { _ in
strongSelf.controllerNavigationDisposable.set((ready.get()
|> SwiftSignalKit.filter { $0 }
|> take(1)
|> deliverOnMainQueue).start(next: { _ in
if let strongController = controller {
strongController.dismiss()
}
@ -7782,9 +7792,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self {
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) })
let ready = ValuePromise<Bool>()
let ready = Promise<Bool>()
strongSelf.controllerNavigationDisposable.set((ready.get() |> take(1) |> deliverOnMainQueue).start(next: { _ in
strongSelf.controllerNavigationDisposable.set((ready.get() |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { _ in
if let strongController = controller {
strongController.dismiss()
}

View File

@ -32,7 +32,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
private let paneDidScroll: (ChatMediaInputPane, ChatMediaInputPaneScrollState, ContainedViewLayoutTransition) -> Void
private let fixPaneScroll: (ChatMediaInputPane, ChatMediaInputPaneScrollState) -> Void
private let openGifContextMenu: (FileMediaReference, ASDisplayNode, CGRect, ContextGesture) -> Void
private let openGifContextMenu: (FileMediaReference, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void
private let searchPlaceholderNode: PaneSearchBarPlaceholderNode
var visibleSearchPlaceholderNode: PaneSearchBarPlaceholderNode? {
@ -56,7 +56,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
private var didScrollPreviousState: ChatMediaInputPaneScrollState?
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, paneDidScroll: @escaping (ChatMediaInputPane, ChatMediaInputPaneScrollState, ContainedViewLayoutTransition) -> Void, fixPaneScroll: @escaping (ChatMediaInputPane, ChatMediaInputPaneScrollState) -> Void, openGifContextMenu: @escaping (FileMediaReference, ASDisplayNode, CGRect, ContextGesture) -> Void) {
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, paneDidScroll: @escaping (ChatMediaInputPane, ChatMediaInputPaneScrollState, ContainedViewLayoutTransition) -> Void, fixPaneScroll: @escaping (ChatMediaInputPane, ChatMediaInputPaneScrollState) -> Void, openGifContextMenu: @escaping (FileMediaReference, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void) {
self.account = account
self.theme = theme
self.strings = strings
@ -116,7 +116,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
self.updateMultiplexedNodeLayout(changedIsExpanded: changedIsExpanded, transition: transition)
}
func fileAt(point: CGPoint) -> (FileMediaReference, CGRect)? {
func fileAt(point: CGPoint) -> (FileMediaReference, CGRect, Bool)? {
if let multiplexedNode = self.multiplexedNode {
return multiplexedNode.fileAt(point: point.offsetBy(dx: -multiplexedNode.frame.minX, dy: -multiplexedNode.frame.minY))
} else {
@ -240,8 +240,8 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
let _ = self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect)
}
multiplexedNode.fileContextMenu = { [weak self] fileReference, sourceNode, sourceRect, gesture in
self?.openGifContextMenu(fileReference, sourceNode, sourceRect, gesture)
multiplexedNode.fileContextMenu = { [weak self] fileReference, sourceNode, sourceRect, gesture, isSaved in
self?.openGifContextMenu(fileReference, sourceNode, sourceRect, gesture, isSaved)
}
multiplexedNode.didScroll = { [weak self] offset, height in
@ -268,7 +268,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
}
if let multiplexedNode = strongSelf.multiplexedNode {
fixListScrolling(multiplexedNode)
//fixListScrolling(multiplexedNode)
}
}

View File

@ -476,7 +476,7 @@ final class ChatMediaInputNode: ChatInputNode {
var paneDidScrollImpl: ((ChatMediaInputPane, ChatMediaInputPaneScrollState, ContainedViewLayoutTransition) -> Void)?
var fixPaneScrollImpl: ((ChatMediaInputPane, ChatMediaInputPaneScrollState) -> Void)?
var openGifContextMenuImpl: ((FileMediaReference, ASDisplayNode, CGRect, ContextGesture) -> Void)?
var openGifContextMenuImpl: ((FileMediaReference, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void)?
self.stickerPane = ChatMediaInputStickerPane(theme: theme, strings: strings, paneDidScroll: { pane, state, transition in
paneDidScrollImpl?(pane, state, transition)
@ -487,8 +487,8 @@ final class ChatMediaInputNode: ChatInputNode {
paneDidScrollImpl?(pane, state, transition)
}, fixPaneScroll: { pane, state in
fixPaneScrollImpl?(pane, state)
}, openGifContextMenu: { fileReference, sourceNode, sourceRect, gesture in
openGifContextMenuImpl?(fileReference, sourceNode, sourceRect, gesture)
}, openGifContextMenu: { fileReference, sourceNode, sourceRect, gesture, isSaved in
openGifContextMenuImpl?(fileReference, sourceNode, sourceRect, gesture, isSaved)
})
var getItemIsPreviewedImpl: ((StickerPackItem) -> Bool)?
@ -562,6 +562,9 @@ final class ChatMediaInputNode: ChatInputNode {
self?.searchContainerNode?.deactivate()
self?.inputNodeInteraction.toggleSearch(false, nil, "")
})
searchContainerNode?.openGifContextMenu = { fileReference, sourceNode, sourceRect, gesture, isSaved in
self?.openGifContextMenu(fileReference: fileReference, sourceNode: sourceNode, sourceRect: sourceRect, gesture: gesture, isSaved: isSaved)
}
strongSelf.searchContainerNode = searchContainerNode
if !query.isEmpty {
DispatchQueue.main.async {
@ -907,7 +910,31 @@ final class ChatMediaInputNode: ChatInputNode {
self?.fixPaneScroll(pane: pane, state: state)
}
openGifContextMenuImpl = { [weak self] fileReference, sourceNode, sourceRect, gesture in
openGifContextMenuImpl = { [weak self] fileReference, sourceNode, sourceRect, gesture, isSaved in
self?.openGifContextMenu(fileReference: fileReference, sourceNode: sourceNode, sourceRect: sourceRect, gesture: gesture, isSaved: isSaved)
}
}
deinit {
self.disposable.dispose()
self.searchContainerNodeLoadedDisposable.dispose()
}
private func openGifContextMenu(fileReference: FileMediaReference, sourceNode: ASDisplayNode, sourceRect: CGRect, gesture: ContextGesture, isSaved: Bool) {
let canSaveGif: Bool
if fileReference.media.fileId.namespace == Namespaces.Media.CloudFile {
canSaveGif = true
} else {
canSaveGif = false
}
let _ = (self.context.account.postbox.transaction { transaction -> Bool in
if !canSaveGif {
return false
}
return isGifSaved(transaction: transaction, mediaId: fileReference.media.fileId)
}
|> deliverOnMainQueue).start(next: { [weak self] isGifSaved in
guard let strongSelf = self else {
return
}
@ -919,29 +946,35 @@ final class ChatMediaInputNode: ChatInputNode {
gallery.setHintWillBePresentedInPreviewingContext(true)
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: strings.MediaPicker_Send, icon: { _ in nil }, action: { _, f in
items.append(.action(ContextMenuActionItem(text: strongSelf.strings.MediaPicker_Send, icon: { _ in nil }, action: { _, f in
f(.default)
self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect)
})))
items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { _ in nil }, action: { _, f in
f(.dismissWithoutContent)
guard let strongSelf = self else {
return
}
let _ = removeSavedGif(postbox: strongSelf.context.account.postbox, mediaId: fileReference.media.fileId).start()
let _ = self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect)
})))
if isSaved || isGifSaved {
items.append(.action(ContextMenuActionItem(text: strongSelf.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { _ in nil }, action: { _, f in
f(.dismissWithoutContent)
guard let strongSelf = self else {
return
}
let _ = removeSavedGif(postbox: strongSelf.context.account.postbox, mediaId: fileReference.media.fileId).start()
})))
} else if canSaveGif && !isGifSaved {
items.append(.action(ContextMenuActionItem(text: strongSelf.strings.Preview_SaveGif, icon: { _ in nil }, action: { _, f in
f(.dismissWithoutContent)
guard let strongSelf = self else {
return
}
let _ = addSavedGif(postbox: strongSelf.context.account.postbox, fileReference: fileReference).start()
})))
}
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: sourceNode, sourceRect: sourceRect)), items: .single(items), reactionItems: [], gesture: gesture)
strongSelf.controllerInteraction.presentGlobalOverlayController(contextController, nil)
}
}
deinit {
self.disposable.dispose()
self.searchContainerNodeLoadedDisposable.dispose()
})
}
private func updateThemeAndStrings(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings) {
@ -1067,7 +1100,7 @@ final class ChatMediaInputNode: ChatInputNode {
for pane in panes {
if pane.supernode != nil, pane.frame.contains(point) {
if let pane = pane as? ChatMediaInputGifPane {
if let (file, _) = pane.fileAt(point: point.offsetBy(dx: -pane.frame.minX, dy: -pane.frame.minY)) {
if let (_, _, _) = pane.fileAt(point: point.offsetBy(dx: -pane.frame.minX, dy: -pane.frame.minY)) {
return nil
/*return .single((strongSelf, ChatContextResultPeekContent(account: strongSelf.context.account, contextResult: .internalReference(queryId: 0, id: "", type: "gif", title: nil, description: nil, image: nil, file: file.media, message: .auto(caption: "", entities: nil, replyMarkup: nil)), menu: [
PeekControllerMenuItem(title: strongSelf.strings.ShareMenu_Send, color: .accent, font: .bold, action: { node, rect in

View File

@ -156,6 +156,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
var deactivateSearchBar: (() -> Void)?
var updateActivity: ((Bool) -> Void)?
var requestUpdateQuery: ((String) -> Void)?
var openGifContextMenu: ((FileMediaReference, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void)?
private var hasInitialText = false
@ -329,6 +330,10 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
let _ = self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect)
}
multiplexedNode.fileContextMenu = { [weak self] fileReference, sourceNode, sourceRect, gesture, isSaved in
self?.openGifContextMenu?(fileReference, sourceNode, sourceRect, gesture, isSaved)
}
multiplexedNode.didScroll = { [weak self] offset, height in
guard let strongSelf = self, let multiplexedNode = strongSelf.multiplexedNode else {
return

View File

@ -114,8 +114,7 @@ private final class TrendingHeaderNode: ASDisplayNode {
let height: CGFloat = 72.0
let leftInset: CGFloat = 10.0
//TODO:localize
self.titleNode.attributedText = NSAttributedString(string: "TRENDING GIFS", font: Font.medium(12.0), textColor: theme.chat.inputMediaPanel.stickersSectionTextColor)
self.titleNode.attributedText = NSAttributedString(string: strings.Chat_Gifs_TrendingSectionHeader, font: Font.medium(12.0), textColor: theme.chat.inputMediaPanel.stickersSectionTextColor)
let titleSize = self.titleNode.updateLayout(CGSize(width: width - leftInset * 2.0 - sideInset * 2.0, height: 100.0))
self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: titleSize)
@ -123,7 +122,7 @@ private final class TrendingHeaderNode: ASDisplayNode {
return reactionNode.updateLayout(CGSize(width: 100.0, height: 100.0))
}
let reactionSpacing: CGFloat = 4.0
let reactionSpacing: CGFloat = 8.0
var reactionsOffset: CGFloat = leftInset - 2.0
for i in 0 ..< self.reactionNodes.count {
@ -195,7 +194,7 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
private let timebase: CMTimebase
var fileSelected: ((FileMediaReference, ASDisplayNode, CGRect) -> Void)?
var fileContextMenu: ((FileMediaReference, ASDisplayNode, CGRect, ContextGesture) -> Void)?
var fileContextMenu: ((FileMediaReference, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void)?
var enableVideoNodes = false
init(account: Account, theme: PresentationTheme, strings: PresentationStrings) {
@ -213,9 +212,8 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
self.contextContainerNode = ContextControllerSourceNode()
self.scrollNode = ASScrollNode()
//TODO:localization
self.savedTitleNode = ImmediateTextNode()
self.savedTitleNode.attributedText = NSAttributedString(string: "MY GIFS", font: Font.medium(12.0), textColor: theme.chat.inputMediaPanel.stickersSectionTextColor)
self.savedTitleNode.attributedText = NSAttributedString(string: strings.Chat_Gifs_SavedSectionHeader, font: Font.medium(12.0), textColor: theme.chat.inputMediaPanel.stickersSectionTextColor)
self.trendingHeaderNode = TrendingHeaderNode()
@ -294,8 +292,8 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
guard let strongSelf = self, let gestureLocation = gestureLocation else {
return
}
if let (file, rect) = strongSelf.fileAt(point: gestureLocation) {
strongSelf.fileContextMenu?(file, strongSelf, rect.offsetBy(dx: 0.0, dy: -strongSelf.scrollNode.bounds.minY), gesture)
if let (file, rect, isSaved) = strongSelf.fileAt(point: gestureLocation) {
strongSelf.fileContextMenu?(file, strongSelf, rect.offsetBy(dx: 0.0, dy: -strongSelf.scrollNode.bounds.minY), gesture, isSaved)
} else {
gesture.cancel()
}
@ -587,7 +585,7 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
let itemWidth = floor(width * drawableSize.width / 100.0) - 1
var itemSize = CGSize(width: itemWidth, height: preferredRowSize)
if itemsToRow[index] != nil {
if itemsToRow[index] != nil && currentRowHorizontalOffset + itemSize.width >= drawableSize.width - 10.0 {
itemSize.width = max(itemSize.width, drawableSize.width - currentRowHorizontalOffset)
}
displayItems.append(VisibleVideoItem(fileReference: files[index], frame: CGRect(origin: CGPoint(x: currentRowHorizontalOffset, y: verticalOffset), size: itemSize), isTrending: isTrending))
@ -742,7 +740,7 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
@objc func tapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
if case .ended = recognizer.state {
let point = recognizer.location(in: self.view)
if let (file, rect) = self.fileAt(point: point) {
if let (file, rect, _) = self.fileAt(point: point) {
self.fileSelected?(file, self, rect)
}
}
@ -757,15 +755,22 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
return nil
}
func fileAt(point: CGPoint) -> (FileMediaReference, CGRect)? {
func fileAt(point: CGPoint) -> (FileMediaReference, CGRect, Bool)? {
let offsetPoint = point.offsetBy(dx: 0.0, dy: self.scrollNode.bounds.minY)
return self.offsetFileAt(point: offsetPoint)
}
private func offsetFileAt(point: CGPoint) -> (FileMediaReference, CGRect)? {
private func offsetFileAt(point: CGPoint) -> (FileMediaReference, CGRect, Bool)? {
for item in self.displayItems {
if item.frame.contains(point) {
return (item.fileReference, item.frame)
let isSaved: Bool
switch item.id {
case .saved:
isSaved = true
case .trending:
isSaved = false
}
return (item.fileReference, item.frame, isSaved)
}
}
return nil

View File

@ -39,6 +39,8 @@ final class PaneSearchContainerNode: ASDisplayNode {
private var validLayout: CGSize?
var openGifContextMenu: ((FileMediaReference, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void)?
var ready: Signal<Void, NoError> {
return self.contentNode.ready
}
@ -88,6 +90,9 @@ final class PaneSearchContainerNode: ASDisplayNode {
contentNode.requestUpdateQuery = { [weak self] query in
self?.updateQuery(query)
}
contentNode.openGifContextMenu = { [weak self] file, node, rect, gesture, isSaved in
self?.openGifContextMenu?(file, node, rect, gesture, isSaved)
}
}
}

View File

@ -554,12 +554,12 @@ private enum ItemsLayout {
let frames: [CGRect]
let contentHeight: CGFloat
init(containerWidth: CGFloat, items: [VisualMediaItem]) {
init(containerWidth: CGFloat, items: [VisualMediaItem], bottomInset: CGFloat) {
self.frames = calculateItemFrames(items: items, containerWidth: containerWidth)
if let last = self.frames.last {
self.contentHeight = last.maxY
self.contentHeight = last.maxY + bottomInset
} else {
self.contentHeight = 0.0
self.contentHeight = bottomInset
}
}
@ -876,7 +876,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
case .photoOrVideo:
itemsLayout = .grid(ItemsLayout.Grid(containerWidth: availableWidth, itemCount: self.mediaItems.count, bottomInset: bottomInset))
case .gifs:
itemsLayout = .balanced(ItemsLayout.Balanced(containerWidth: availableWidth, items: self.mediaItems))
itemsLayout = .balanced(ItemsLayout.Balanced(containerWidth: availableWidth, items: self.mediaItems, bottomInset: bottomInset))
}
self.itemsLayout = itemsLayout
}
@ -1152,7 +1152,7 @@ private func calculateItemFrames(items: [VisualMediaItem], containerWidth: CGFlo
let itemWidth = floor(width * containerWidth / 100.0) - 1
var itemSize = CGSize(width: itemWidth, height: preferredRowSize)
if itemsToRow[index] != nil {
if itemsToRow[index] != nil && currentRowHorizontalOffset + itemSize.width >= containerWidth - 10.0 {
itemSize.width = max(itemSize.width, containerWidth - currentRowHorizontalOffset)
}
frames.append(CGRect(origin: CGPoint(x: currentRowHorizontalOffset, y: verticalOffset), size: itemSize))

View File

@ -3692,8 +3692,8 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
if let strongSelf = self {
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
let ready = ValuePromise<Bool>()
strongSelf.activeActionDisposable.set((ready.get() |> take(1) |> deliverOnMainQueue).start(next: { _ in
let ready = Promise<Bool>()
strongSelf.activeActionDisposable.set((ready.get() |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { _ in
if let peerSelectionController = peerSelectionController {
peerSelectionController.dismiss()
}

View File

@ -857,8 +857,8 @@ public class PeerMediaCollectionController: TelegramBaseController {
if let strongSelf = self {
strongSelf.updateInterfaceState(animated: false, { $0.withoutSelectionState() })
let ready = ValuePromise<Bool>()
strongSelf.messageContextDisposable.set((ready.get() |> take(1) |> deliverOnMainQueue).start(next: { _ in
let ready = Promise<Bool>()
strongSelf.messageContextDisposable.set((ready.get() |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { _ in
if let strongController = controller {
strongController.dismiss()
}

View File

@ -8,7 +8,7 @@ import CoreMedia
import UniversalMediaPlayer
private let applyQueue = Queue()
private let workers = ThreadPool(threadCount: 2, threadPriority: 0.09)
private let workers = ThreadPool(threadCount: 3, threadPriority: 0.2)
private var nextWorker = 0
final class SoftwareVideoLayerFrameManager {
@ -141,13 +141,34 @@ final class SoftwareVideoLayerFrameManager {
private func poll() {
if self.frames.count < 3 && !self.polling {
self.polling = true
let minPts = self.minPts
let maxPts = self.maxPts
self.queue.addTask(ThreadPoolTask { [weak self] state in
if state.cancelled.with({ $0 }) {
return
}
if let strongSelf = self {
let frameAndLoop = (strongSelf.source.with { $0 })?.readFrame(maxPts: maxPts)
var frameAndLoop: (MediaTrackFrame?, CGFloat, CGFloat, Bool)?
var hadLoop = false
for _ in 0 ..< 1 {
frameAndLoop = (strongSelf.source.with { $0 })?.readFrame(maxPts: maxPts)
if let frameAndLoop = frameAndLoop {
if frameAndLoop.0 != nil || minPts != nil {
break
} else {
if frameAndLoop.3 {
hadLoop = true
}
//print("skip nil frame loop: \(frameAndLoop.3)")
}
} else {
break
}
}
if let loop = frameAndLoop?.3, loop {
hadLoop = true
}
applyQueue.async {
if let strongSelf = self {
@ -161,10 +182,23 @@ final class SoftwareVideoLayerFrameManager {
strongSelf.minPts = frame.position
}
strongSelf.frames.append(frame)
strongSelf.frames.sort(by: { lhs, rhs in
if CMTimeCompare(lhs.position, rhs.position) < 0 {
return true
} else {
return false
}
})
//print("add frame at \(CMTimeGetSeconds(frame.position))")
let positions = strongSelf.frames.map { CMTimeGetSeconds($0.position) }
//print("frames: \(positions)")
} else {
//print("not adding frames")
}
if let loop = frameAndLoop?.3, loop {
if hadLoop {
strongSelf.maxPts = strongSelf.minPts
strongSelf.minPts = nil
//print("loop at \(strongSelf.minPts)")
}
strongSelf.poll()
}

View File

@ -449,12 +449,12 @@ public final class WalletStrings: Equatable {
public var Wallet_SecureStorageReset_Title: String { return self._s[219]! }
public var Wallet_Receive_CommentHeader: String { return self._s[220]! }
public var Wallet_Info_ReceiveGrams: String { return self._s[221]! }
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
}
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)

View File

@ -97,10 +97,10 @@ class WebSearchGalleryController: ViewController {
return self._hiddenMedia.get()
}
private let replaceRootController: (ViewController, ValuePromise<Bool>?) -> Void
private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
private let baseNavigationController: NavigationController?
init(context: AccountContext, peer: Peer?, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext, entries: [WebSearchGalleryEntry], centralIndex: Int, replaceRootController: @escaping (ViewController, ValuePromise<Bool>?) -> Void, baseNavigationController: NavigationController?, sendCurrent: @escaping (ChatContextResult) -> Void) {
init(context: AccountContext, peer: Peer?, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext, entries: [WebSearchGalleryEntry], centralIndex: Int, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, sendCurrent: @escaping (ChatContextResult) -> Void) {
self.context = context
self.replaceRootController = replaceRootController
self.baseNavigationController = baseNavigationController