mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
GIF-related fixes
This commit is contained in:
parent
48dba115e6
commit
0fd80bba89
@ -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";
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
@ -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 = []
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
Binary file not shown.
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user