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"; "OwnershipTransfer.Transfer" = "Transfer";
"TwoStepAuth.Disable" = "Disable"; "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 isSavedMessages = peerId == context.account.peerId
let chatPeer = transaction.getPeer(peerId) let chatPeer = transaction.getPeer(peerId)
var peer: Peer? var maybePeer: Peer?
if let chatPeer = chatPeer { if let chatPeer = chatPeer {
if let chatPeer = chatPeer as? TelegramSecretChat { if let chatPeer = chatPeer as? TelegramSecretChat {
peer = transaction.getPeer(chatPeer.regularPeerId) maybePeer = transaction.getPeer(chatPeer.regularPeerId)
} else { } 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 !isSavedMessages, let peer = peer as? TelegramUser, !peer.flags.contains(.isSupport) && peer.botInfo == nil && !peer.isDeleted {
if !transaction.isPeerContact(peerId: peer.id) { 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 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 { if case .chatList = source {
var hasFolders = false var hasFolders = false
updateChatListFiltersInteractively(transaction: transaction, { filters in updateChatListFiltersInteractively(transaction: transaction, { filters in
for filter in filters { 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 var data = filter.data
if data.addIncludePeer(peerId: peerId) { if data.addIncludePeer(peerId: peerId) {
hasFolders = true hasFolders = true
@ -128,6 +151,11 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
var updatedItems: [ContextMenuItem] = [] var updatedItems: [ContextMenuItem] = []
updateChatListFiltersInteractively(transaction: transaction, { filters in updateChatListFiltersInteractively(transaction: transaction, { filters in
for filter in filters { 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 var data = filter.data
if !data.addIncludePeer(peerId: peerId) { if !data.addIncludePeer(peerId: peerId) {
continue continue
@ -168,11 +196,9 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
return filters return filters
})).start() })).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
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
return false }), in: .current)
}), 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 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() let _ = togglePeerUnreadMarkInteractively(postbox: context.account.postbox, viewTracker: context.account.viewTracker, peerId: peerId).start()
f(.default) f(.default)
@ -248,18 +274,20 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
let isPinned = getPinnedItemIds(transaction: transaction, location: location).contains(.peer(peerId)) 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 if isPinned || filter == nil || peerId.namespace != Namespaces.Peer.SecretChat {
let _ = (toggleItemPinned(postbox: context.account.postbox, location: location, itemId: .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
|> deliverOnMainQueue).start(next: { result in let _ = (toggleItemPinned(postbox: context.account.postbox, location: location, itemId: .peer(peerId))
switch result { |> deliverOnMainQueue).start(next: { result in
case .done: switch result {
break case .done:
case .limitExceeded: break
break case .limitExceeded:
} break
f(.default) }
}) f(.default)
}))) })
})))
}
} }
if !isSavedMessages, let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings { if !isSavedMessages, let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings {

View File

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

View File

@ -272,7 +272,7 @@ private func groupReferenceRevealOptions(strings: PresentationStrings, theme: Pr
return options 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 { if case .group = groupId {
return [] return []
} }
@ -286,7 +286,9 @@ private func leftRevealOptions(strings: PresentationStrings, theme: Presentation
if isPinned { 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)) 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 { } 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 return options
@ -1222,7 +1224,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else if promoInfo == nil { } 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) 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 { 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 { } else {
peerLeftRevealOptions = [] peerLeftRevealOptions = []
} }

View File

@ -535,8 +535,10 @@ public class ContactsController: ViewController {
return return
} }
if let peer = peer { if let peer = peer {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { DispatchQueue.main.async {
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) 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 { } else {
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil)) (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) self.setViewControllers(controllers, animated: animated)
} }
public func replaceTopController(_ controller: ViewController, animated: Bool, ready: ValuePromise<Bool>? = nil) { public func replaceTopController(_ controller: ViewController, animated: Bool, ready: Promise<Bool>? = nil) {
ready?.set(true)
var controllers = self.viewControllers 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) 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 _hiddenMedia = Promise<(MessageId, Media)?>(nil)
private let replaceRootController: (ViewController, ValuePromise<Bool>?) -> Void private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
private let baseNavigationController: NavigationController? private let baseNavigationController: NavigationController?
private var hiddenMediaManagerIndex: Int? private var hiddenMediaManagerIndex: Int?
@ -381,7 +381,7 @@ public class GalleryController: ViewController, StandalonePresentableController
private let updateVisibleDisposable = MetaDisposable() 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.context = context
self.source = source self.source = source
self.invertItemOrder = invertItemOrder self.invertItemOrder = invertItemOrder

View File

@ -7,9 +7,9 @@ import SwiftSignalKit
public final class GalleryControllerInteraction { public final class GalleryControllerInteraction {
public let presentController: (ViewController, ViewControllerPresentationArguments?) -> Void public let presentController: (ViewController, ViewControllerPresentationArguments?) -> Void
public let dismissController: () -> 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.presentController = presentController
self.dismissController = dismissController self.dismissController = dismissController
self.replaceRootController = replaceRootController self.replaceRootController = replaceRootController

View File

@ -178,14 +178,14 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable
return self._hiddenMedia.get() return self._hiddenMedia.get()
} }
private let replaceRootController: (ViewController, ValuePromise<Bool>?) -> Void private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
private let baseNavigationController: NavigationController? private let baseNavigationController: NavigationController?
var openUrl: ((InstantPageUrlItem) -> Void)? var openUrl: ((InstantPageUrlItem) -> Void)?
private var innerOpenUrl: (InstantPageUrlItem) -> Void private var innerOpenUrl: (InstantPageUrlItem) -> Void
private var openUrlOptions: (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.context = context
self.webPage = webPage self.webPage = webPage
self.message = message self.message = message

View File

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

View File

@ -76,11 +76,11 @@ class SecureIdDocumentGalleryController: ViewController, StandalonePresentableCo
return self._hiddenMedia.get() return self._hiddenMedia.get()
} }
private let replaceRootController: (ViewController, ValuePromise<Bool>?) -> Void private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
var deleteResource: ((TelegramMediaResource) -> 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.context = context
self.secureIdContext = secureIdContext self.secureIdContext = secureIdContext
self.replaceRootController = replaceRootController self.replaceRootController = replaceRootController

View File

@ -172,9 +172,9 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
return self._hiddenMedia.get() 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.context = context
self.peer = peer self.peer = peer
self.sourceHasRoundCorners = sourceHasRoundCorners 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)) 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> { public func addSavedGif(postbox: Postbox, fileReference: FileMediaReference) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Void in return postbox.transaction { transaction -> Void in
if let resource = fileReference.media.resource as? CloudDocumentMediaResource { if let resource = fileReference.media.resource as? CloudDocumentMediaResource {

View File

@ -135,6 +135,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
case chatTextSelectionTip = 16 case chatTextSelectionTip = 16
case themeChangeTip = 17 case themeChangeTip = 17
case callsTabTip = 18 case callsTabTip = 18
case chatFolderTips = 19
var key: ValueBoxKey { var key: ValueBoxKey {
let v = ValueBoxKey(length: 4) let v = ValueBoxKey(length: 4)
@ -198,6 +199,10 @@ private struct ApplicationSpecificNoticeKeys {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.archiveChatTips.key) 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 { static func profileCallTips() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.profileCallTips.key) 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) { public static func setArchiveIntroDismissed(transaction: AccountManagerModifier, value: Bool) {
transaction.setNotice(ApplicationSpecificNoticeKeys.archiveIntroDismissed(), ApplicationSpecificVariantNotice(value: value)) transaction.setNotice(ApplicationSpecificNoticeKeys.archiveIntroDismissed(), ApplicationSpecificVariantNotice(value: value))
} }

View File

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

View File

@ -4834,7 +4834,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
self.raiseToListen?.enabled = self.canReadHistoryValue self.raiseToListen?.enabled = self.canReadHistoryValue
self.tempVoicePlaylistEnded = { [weak self] in 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() strongSelf.voicePlaylistDidEndTimestamp = CACurrentMediaTime()
raiseToListen.activateBasedOnProximity(delay: 0.0) raiseToListen.activateBasedOnProximity(delay: 0.0)
} }
@ -7651,9 +7658,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self { if let strongSelf = self {
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) }) 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 { if let strongController = controller {
strongController.dismiss() strongController.dismiss()
} }
@ -7782,9 +7792,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self { if let strongSelf = self {
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) }) 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 { if let strongController = controller {
strongController.dismiss() strongController.dismiss()
} }

View File

@ -32,7 +32,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
private let paneDidScroll: (ChatMediaInputPane, ChatMediaInputPaneScrollState, ContainedViewLayoutTransition) -> Void private let paneDidScroll: (ChatMediaInputPane, ChatMediaInputPaneScrollState, ContainedViewLayoutTransition) -> Void
private let fixPaneScroll: (ChatMediaInputPane, ChatMediaInputPaneScrollState) -> 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 private let searchPlaceholderNode: PaneSearchBarPlaceholderNode
var visibleSearchPlaceholderNode: PaneSearchBarPlaceholderNode? { var visibleSearchPlaceholderNode: PaneSearchBarPlaceholderNode? {
@ -56,7 +56,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
private var didScrollPreviousState: ChatMediaInputPaneScrollState? 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.account = account
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
@ -116,7 +116,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
self.updateMultiplexedNodeLayout(changedIsExpanded: changedIsExpanded, transition: transition) self.updateMultiplexedNodeLayout(changedIsExpanded: changedIsExpanded, transition: transition)
} }
func fileAt(point: CGPoint) -> (FileMediaReference, CGRect)? { func fileAt(point: CGPoint) -> (FileMediaReference, CGRect, Bool)? {
if let multiplexedNode = self.multiplexedNode { if let multiplexedNode = self.multiplexedNode {
return multiplexedNode.fileAt(point: point.offsetBy(dx: -multiplexedNode.frame.minX, dy: -multiplexedNode.frame.minY)) return multiplexedNode.fileAt(point: point.offsetBy(dx: -multiplexedNode.frame.minX, dy: -multiplexedNode.frame.minY))
} else { } else {
@ -240,8 +240,8 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
let _ = self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect) let _ = self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect)
} }
multiplexedNode.fileContextMenu = { [weak self] fileReference, sourceNode, sourceRect, gesture in multiplexedNode.fileContextMenu = { [weak self] fileReference, sourceNode, sourceRect, gesture, isSaved in
self?.openGifContextMenu(fileReference, sourceNode, sourceRect, gesture) self?.openGifContextMenu(fileReference, sourceNode, sourceRect, gesture, isSaved)
} }
multiplexedNode.didScroll = { [weak self] offset, height in multiplexedNode.didScroll = { [weak self] offset, height in
@ -268,7 +268,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
} }
if let multiplexedNode = strongSelf.multiplexedNode { 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 paneDidScrollImpl: ((ChatMediaInputPane, ChatMediaInputPaneScrollState, ContainedViewLayoutTransition) -> Void)?
var fixPaneScrollImpl: ((ChatMediaInputPane, ChatMediaInputPaneScrollState) -> 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 self.stickerPane = ChatMediaInputStickerPane(theme: theme, strings: strings, paneDidScroll: { pane, state, transition in
paneDidScrollImpl?(pane, state, transition) paneDidScrollImpl?(pane, state, transition)
@ -487,8 +487,8 @@ final class ChatMediaInputNode: ChatInputNode {
paneDidScrollImpl?(pane, state, transition) paneDidScrollImpl?(pane, state, transition)
}, fixPaneScroll: { pane, state in }, fixPaneScroll: { pane, state in
fixPaneScrollImpl?(pane, state) fixPaneScrollImpl?(pane, state)
}, openGifContextMenu: { fileReference, sourceNode, sourceRect, gesture in }, openGifContextMenu: { fileReference, sourceNode, sourceRect, gesture, isSaved in
openGifContextMenuImpl?(fileReference, sourceNode, sourceRect, gesture) openGifContextMenuImpl?(fileReference, sourceNode, sourceRect, gesture, isSaved)
}) })
var getItemIsPreviewedImpl: ((StickerPackItem) -> Bool)? var getItemIsPreviewedImpl: ((StickerPackItem) -> Bool)?
@ -562,6 +562,9 @@ final class ChatMediaInputNode: ChatInputNode {
self?.searchContainerNode?.deactivate() self?.searchContainerNode?.deactivate()
self?.inputNodeInteraction.toggleSearch(false, nil, "") 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 strongSelf.searchContainerNode = searchContainerNode
if !query.isEmpty { if !query.isEmpty {
DispatchQueue.main.async { DispatchQueue.main.async {
@ -907,7 +910,31 @@ final class ChatMediaInputNode: ChatInputNode {
self?.fixPaneScroll(pane: pane, state: state) 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 { guard let strongSelf = self else {
return return
} }
@ -919,29 +946,35 @@ final class ChatMediaInputNode: ChatInputNode {
gallery.setHintWillBePresentedInPreviewingContext(true) gallery.setHintWillBePresentedInPreviewingContext(true)
var items: [ContextMenuItem] = [] 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) f(.default)
self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect) let _ = 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()
}))) })))
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 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) 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) strongSelf.controllerInteraction.presentGlobalOverlayController(contextController, nil)
} })
}
deinit {
self.disposable.dispose()
self.searchContainerNodeLoadedDisposable.dispose()
} }
private func updateThemeAndStrings(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings) { private func updateThemeAndStrings(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings) {
@ -1067,7 +1100,7 @@ final class ChatMediaInputNode: ChatInputNode {
for pane in panes { for pane in panes {
if pane.supernode != nil, pane.frame.contains(point) { if pane.supernode != nil, pane.frame.contains(point) {
if let pane = pane as? ChatMediaInputGifPane { 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 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: [ /*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 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 deactivateSearchBar: (() -> Void)?
var updateActivity: ((Bool) -> Void)? var updateActivity: ((Bool) -> Void)?
var requestUpdateQuery: ((String) -> Void)? var requestUpdateQuery: ((String) -> Void)?
var openGifContextMenu: ((FileMediaReference, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void)?
private var hasInitialText = false private var hasInitialText = false
@ -329,6 +330,10 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
let _ = self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect) 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 multiplexedNode.didScroll = { [weak self] offset, height in
guard let strongSelf = self, let multiplexedNode = strongSelf.multiplexedNode else { guard let strongSelf = self, let multiplexedNode = strongSelf.multiplexedNode else {
return return

View File

@ -114,8 +114,7 @@ private final class TrendingHeaderNode: ASDisplayNode {
let height: CGFloat = 72.0 let height: CGFloat = 72.0
let leftInset: CGFloat = 10.0 let leftInset: CGFloat = 10.0
//TODO:localize self.titleNode.attributedText = NSAttributedString(string: strings.Chat_Gifs_TrendingSectionHeader, font: Font.medium(12.0), textColor: theme.chat.inputMediaPanel.stickersSectionTextColor)
self.titleNode.attributedText = NSAttributedString(string: "TRENDING GIFS", 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)) 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) 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)) 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 var reactionsOffset: CGFloat = leftInset - 2.0
for i in 0 ..< self.reactionNodes.count { for i in 0 ..< self.reactionNodes.count {
@ -195,7 +194,7 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
private let timebase: CMTimebase private let timebase: CMTimebase
var fileSelected: ((FileMediaReference, ASDisplayNode, CGRect) -> Void)? var fileSelected: ((FileMediaReference, ASDisplayNode, CGRect) -> Void)?
var fileContextMenu: ((FileMediaReference, ASDisplayNode, CGRect, ContextGesture) -> Void)? var fileContextMenu: ((FileMediaReference, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void)?
var enableVideoNodes = false var enableVideoNodes = false
init(account: Account, theme: PresentationTheme, strings: PresentationStrings) { init(account: Account, theme: PresentationTheme, strings: PresentationStrings) {
@ -213,9 +212,8 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
self.contextContainerNode = ContextControllerSourceNode() self.contextContainerNode = ContextControllerSourceNode()
self.scrollNode = ASScrollNode() self.scrollNode = ASScrollNode()
//TODO:localization
self.savedTitleNode = ImmediateTextNode() 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() self.trendingHeaderNode = TrendingHeaderNode()
@ -294,8 +292,8 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
guard let strongSelf = self, let gestureLocation = gestureLocation else { guard let strongSelf = self, let gestureLocation = gestureLocation else {
return return
} }
if let (file, rect) = strongSelf.fileAt(point: gestureLocation) { if let (file, rect, isSaved) = strongSelf.fileAt(point: gestureLocation) {
strongSelf.fileContextMenu?(file, strongSelf, rect.offsetBy(dx: 0.0, dy: -strongSelf.scrollNode.bounds.minY), gesture) strongSelf.fileContextMenu?(file, strongSelf, rect.offsetBy(dx: 0.0, dy: -strongSelf.scrollNode.bounds.minY), gesture, isSaved)
} else { } else {
gesture.cancel() gesture.cancel()
} }
@ -587,7 +585,7 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
let itemWidth = floor(width * drawableSize.width / 100.0) - 1 let itemWidth = floor(width * drawableSize.width / 100.0) - 1
var itemSize = CGSize(width: itemWidth, height: preferredRowSize) 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) 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)) 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) { @objc func tapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
if case .ended = recognizer.state { if case .ended = recognizer.state {
let point = recognizer.location(in: self.view) 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) self.fileSelected?(file, self, rect)
} }
} }
@ -757,15 +755,22 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
return nil 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) let offsetPoint = point.offsetBy(dx: 0.0, dy: self.scrollNode.bounds.minY)
return self.offsetFileAt(point: offsetPoint) 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 { for item in self.displayItems {
if item.frame.contains(point) { 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 return nil

View File

@ -39,6 +39,8 @@ final class PaneSearchContainerNode: ASDisplayNode {
private var validLayout: CGSize? private var validLayout: CGSize?
var openGifContextMenu: ((FileMediaReference, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void)?
var ready: Signal<Void, NoError> { var ready: Signal<Void, NoError> {
return self.contentNode.ready return self.contentNode.ready
} }
@ -88,6 +90,9 @@ final class PaneSearchContainerNode: ASDisplayNode {
contentNode.requestUpdateQuery = { [weak self] query in contentNode.requestUpdateQuery = { [weak self] query in
self?.updateQuery(query) 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 frames: [CGRect]
let contentHeight: CGFloat let contentHeight: CGFloat
init(containerWidth: CGFloat, items: [VisualMediaItem]) { init(containerWidth: CGFloat, items: [VisualMediaItem], bottomInset: CGFloat) {
self.frames = calculateItemFrames(items: items, containerWidth: containerWidth) self.frames = calculateItemFrames(items: items, containerWidth: containerWidth)
if let last = self.frames.last { if let last = self.frames.last {
self.contentHeight = last.maxY self.contentHeight = last.maxY + bottomInset
} else { } else {
self.contentHeight = 0.0 self.contentHeight = bottomInset
} }
} }
@ -876,7 +876,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
case .photoOrVideo: case .photoOrVideo:
itemsLayout = .grid(ItemsLayout.Grid(containerWidth: availableWidth, itemCount: self.mediaItems.count, bottomInset: bottomInset)) itemsLayout = .grid(ItemsLayout.Grid(containerWidth: availableWidth, itemCount: self.mediaItems.count, bottomInset: bottomInset))
case .gifs: 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 self.itemsLayout = itemsLayout
} }
@ -1152,7 +1152,7 @@ private func calculateItemFrames(items: [VisualMediaItem], containerWidth: CGFlo
let itemWidth = floor(width * containerWidth / 100.0) - 1 let itemWidth = floor(width * containerWidth / 100.0) - 1
var itemSize = CGSize(width: itemWidth, height: preferredRowSize) 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) itemSize.width = max(itemSize.width, containerWidth - currentRowHorizontalOffset)
} }
frames.append(CGRect(origin: CGPoint(x: currentRowHorizontalOffset, y: verticalOffset), size: itemSize)) 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 { if let strongSelf = self {
strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone) strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
let ready = ValuePromise<Bool>() let ready = Promise<Bool>()
strongSelf.activeActionDisposable.set((ready.get() |> take(1) |> deliverOnMainQueue).start(next: { _ in strongSelf.activeActionDisposable.set((ready.get() |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { _ in
if let peerSelectionController = peerSelectionController { if let peerSelectionController = peerSelectionController {
peerSelectionController.dismiss() peerSelectionController.dismiss()
} }

View File

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

View File

@ -8,7 +8,7 @@ import CoreMedia
import UniversalMediaPlayer import UniversalMediaPlayer
private let applyQueue = Queue() 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 private var nextWorker = 0
final class SoftwareVideoLayerFrameManager { final class SoftwareVideoLayerFrameManager {
@ -141,13 +141,34 @@ final class SoftwareVideoLayerFrameManager {
private func poll() { private func poll() {
if self.frames.count < 3 && !self.polling { if self.frames.count < 3 && !self.polling {
self.polling = true self.polling = true
let minPts = self.minPts
let maxPts = self.maxPts let maxPts = self.maxPts
self.queue.addTask(ThreadPoolTask { [weak self] state in self.queue.addTask(ThreadPoolTask { [weak self] state in
if state.cancelled.with({ $0 }) { if state.cancelled.with({ $0 }) {
return return
} }
if let strongSelf = self { 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 { applyQueue.async {
if let strongSelf = self { if let strongSelf = self {
@ -161,10 +182,23 @@ final class SoftwareVideoLayerFrameManager {
strongSelf.minPts = frame.position strongSelf.minPts = frame.position
} }
strongSelf.frames.append(frame) 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.maxPts = strongSelf.minPts
strongSelf.minPts = nil strongSelf.minPts = nil
//print("loop at \(strongSelf.minPts)")
} }
strongSelf.poll() 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_SecureStorageReset_Title: String { return self._s[219]! }
public var Wallet_Receive_CommentHeader: String { return self._s[220]! } public var Wallet_Receive_CommentHeader: String { return self._s[220]! }
public var Wallet_Info_ReceiveGrams: String { return self._s[221]! } 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 form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator) let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue) 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 form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator) let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue) return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)

View File

@ -97,10 +97,10 @@ class WebSearchGalleryController: ViewController {
return self._hiddenMedia.get() return self._hiddenMedia.get()
} }
private let replaceRootController: (ViewController, ValuePromise<Bool>?) -> Void private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
private let baseNavigationController: NavigationController? 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.context = context
self.replaceRootController = replaceRootController self.replaceRootController = replaceRootController
self.baseNavigationController = baseNavigationController self.baseNavigationController = baseNavigationController