mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-01-05 20:52:48 +00:00
Added animated sticker nodes in context suggestion panel & trending
This commit is contained in:
@@ -29,7 +29,7 @@ public enum ChannelOwnershipTransferError {
|
||||
public func checkOwnershipTranfserAvailability(postbox: Postbox, network: Network, accountStateManager: AccountStateManager, memberId: PeerId) -> Signal<Never, ChannelOwnershipTransferError> {
|
||||
return postbox.transaction { transaction -> Peer? in
|
||||
return transaction.getPeer(memberId)
|
||||
}
|
||||
}
|
||||
|> introduceError(ChannelOwnershipTransferError.self)
|
||||
|> mapToSignal { user -> Signal<Never, ChannelOwnershipTransferError> in
|
||||
guard let user = user else {
|
||||
|
||||
@@ -20,7 +20,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
private var shareButtonNode: HighlightableButtonNode?
|
||||
|
||||
var telegramFile: TelegramMediaFile?
|
||||
private let disposable = MetaDisposable()
|
||||
|
||||
private let dateAndStatusNode: ChatMessageDateAndStatusNode
|
||||
private var replyInfoNode: ChatMessageReplyInfoNode?
|
||||
@@ -46,11 +45,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
self.addSubnode(self.animationNode)
|
||||
self.addSubnode(self.dateAndStatusNode)
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable.dispose()
|
||||
}
|
||||
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@@ -309,13 +309,30 @@ public class ContactsController: ViewController {
|
||||
}
|
||||
|
||||
self.contactsNode.openInvite = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(InviteContactsController(context: strongSelf.context), completion: {
|
||||
if let strongSelf = self {
|
||||
let _ = (DeviceAccess.authorizationStatus(subject: .contacts)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch value {
|
||||
case .allowed:
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(InviteContactsController(context: strongSelf.context), completion: {
|
||||
if let strongSelf = self {
|
||||
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
|
||||
}
|
||||
})
|
||||
case .notDetermined:
|
||||
DeviceAccess.authorizeAccess(to: .contacts)
|
||||
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
default:
|
||||
let presentationData = strongSelf.presentationData
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
|
||||
self?.context.sharedContext.applicationBindings.openSettings()
|
||||
})]), in: .window(.root))
|
||||
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
self.contactsNode.contactListNode.openSortMenu = { [weak self] in
|
||||
|
||||
@@ -83,25 +83,9 @@ final class ContactsControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
inviteImpl = { [weak self] in
|
||||
let _ = (DeviceAccess.authorizationStatus(subject: .contacts)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
switch value {
|
||||
case .allowed:
|
||||
strongSelf.openInvite?()
|
||||
case .notDetermined:
|
||||
DeviceAccess.authorizeAccess(to: .contacts)
|
||||
default:
|
||||
let presentationData = strongSelf.presentationData
|
||||
present(textAlertController(context: strongSelf.context, title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
|
||||
self?.context.sharedContext.applicationBindings.openSettings()
|
||||
})]), nil)
|
||||
}
|
||||
})
|
||||
if let strongSelf = self {
|
||||
strongSelf.openInvite?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ class ContactsPeerItem: ListViewItem {
|
||||
self.deletePeer = deletePeer
|
||||
self.header = header
|
||||
self.itemHighlighting = itemHighlighting
|
||||
self.selectable = self.enabled
|
||||
self.selectable = enabled
|
||||
|
||||
if let index = index {
|
||||
var letter: String = "#"
|
||||
@@ -369,33 +369,14 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) {
|
||||
if let item = self.item, case .selectable = item.selection {
|
||||
return
|
||||
}
|
||||
|
||||
super.setHighlighted(highlighted, at: point, animated: animated)
|
||||
|
||||
self.isHighlighted = highlighted
|
||||
|
||||
self.updateIsHighlighted(transition: (animated && !highlighted) ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
|
||||
|
||||
// if highlighted && self.selectionNode == nil {
|
||||
// self.highlightedBackgroundNode.alpha = 1.0
|
||||
// if self.highlightedBackgroundNode.supernode == nil {
|
||||
// self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: self.separatorNode)
|
||||
// }
|
||||
// } else {
|
||||
// if self.highlightedBackgroundNode.supernode != nil {
|
||||
// if animated {
|
||||
// self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in
|
||||
// if let strongSelf = self {
|
||||
// if completed {
|
||||
// strongSelf.highlightedBackgroundNode.removeFromSupernode()
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// self.highlightedBackgroundNode.alpha = 0.0
|
||||
// } else {
|
||||
// self.highlightedBackgroundNode.removeFromSupernode()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ final class HorizontalStickerGridItem: GridItem {
|
||||
final class HorizontalStickerGridItemNode: GridItemNode {
|
||||
private var currentState: (Account, HorizontalStickerGridItem, CGSize)?
|
||||
private let imageNode: TransformImageNode
|
||||
private var animationNode: AnimatedStickerNode?
|
||||
|
||||
private let stickerFetchedDisposable = MetaDisposable()
|
||||
|
||||
@@ -48,6 +49,18 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
||||
|
||||
private var currentIsPreviewing: Bool = false
|
||||
|
||||
override var isVisibleInGrid: Bool {
|
||||
didSet {
|
||||
if oldValue != self.isVisibleInGrid {
|
||||
if self.isVisibleInGrid {
|
||||
self.animationNode?.visibility = true
|
||||
} else {
|
||||
self.animationNode?.visibility = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stickerItem: StickerPackItem? {
|
||||
if let (_, item, _) = self.currentState {
|
||||
return StickerPackItem(index: ItemCollectionItemIndex(index: 0, id: 0), file: item.file, indexKeys: [])
|
||||
@@ -66,7 +79,7 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
||||
}
|
||||
|
||||
deinit {
|
||||
stickerFetchedDisposable.dispose()
|
||||
self.stickerFetchedDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@@ -78,8 +91,33 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
||||
func setup(account: Account, item: HorizontalStickerGridItem) {
|
||||
if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1.file.id != item.file.id {
|
||||
if let dimensions = item.file.dimensions {
|
||||
self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true))
|
||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: chatMessageStickerResource(file: item.file, small: true)).start())
|
||||
if item.file.isAnimatedSticker {
|
||||
self.stickerFetchedDisposable.set(nil)
|
||||
|
||||
let animationNode: AnimatedStickerNode
|
||||
if let currentAnimationNode = self.animationNode {
|
||||
animationNode = currentAnimationNode
|
||||
} else {
|
||||
animationNode = AnimatedStickerNode()
|
||||
animationNode.transform = self.imageNode.transform
|
||||
animationNode.visibility = self.isVisibleInGrid
|
||||
self.addSubnode(animationNode)
|
||||
self.animationNode = animationNode
|
||||
}
|
||||
animationNode.started = { [weak self] in
|
||||
self?.imageNode.alpha = 0.0
|
||||
}
|
||||
animationNode.setup(account: account, fileReference: stickerPackFileReference(item.file), width: 140, height: 140)
|
||||
} else {
|
||||
self.imageNode.alpha = 1.0
|
||||
self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true))
|
||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: chatMessageStickerResource(file: item.file, small: true)).start())
|
||||
|
||||
if let currentAnimationNode = self.animationNode {
|
||||
self.animationNode = nil
|
||||
currentAnimationNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
self.currentState = (account, item, dimensions)
|
||||
self.setNeedsLayout()
|
||||
@@ -101,6 +139,12 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
||||
let imageFrame = CGRect(origin: CGPoint(x: floor((bounds.size.width - imageSize.width) / 2.0), y: (bounds.size.height - imageSize.height) / 2.0), size: CGSize(width: imageSize.width, height: imageSize.height))
|
||||
self.imageNode.bounds = CGRect(origin: CGPoint(), size: CGSize(width: imageSize.width, height: imageSize.height))
|
||||
self.imageNode.position = CGPoint(x: imageFrame.midX, y: imageFrame.midY)
|
||||
|
||||
if let animationNode = self.animationNode {
|
||||
animationNode.bounds = self.imageNode.bounds
|
||||
animationNode.position = self.imageNode.position
|
||||
animationNode.updateLayout(size: self.imageNode.bounds.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ struct InviteContactsGroupSelectionState: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
private func inviteContactsEntries(accountPeer: Peer?, sortedContacts: [(DeviceContactStableId, DeviceContactBasicData, Int32)], selectionState: InviteContactsGroupSelectionState, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, interaction: InviteContactsInteraction) -> [InviteContactsEntry] {
|
||||
private func inviteContactsEntries(accountPeer: Peer?, sortedContacts: [(DeviceContactStableId, DeviceContactBasicData, Int32)]?, selectionState: InviteContactsGroupSelectionState, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, interaction: InviteContactsInteraction) -> [InviteContactsEntry] {
|
||||
var entries: [InviteContactsEntry] = []
|
||||
|
||||
entries.append(.option(0, ContactListAdditionalOption(title: strings.Contacts_ShareTelegram, icon: .generic(UIImage(bundleImageName: "Contact List/InviteActionIcon")!), action: {
|
||||
@@ -212,35 +212,39 @@ private func inviteContactsEntries(accountPeer: Peer?, sortedContacts: [(DeviceC
|
||||
}), theme, strings))
|
||||
|
||||
var index = 0
|
||||
for (id, contact, count) in sortedContacts {
|
||||
entries.append(.peer(index, id, contact, count, .selectable(selected: selectionState.selectedContactIndices[id] != nil), theme, strings, nameSortOrder, nameDisplayOrder))
|
||||
index += 1
|
||||
if let sortedContacts = sortedContacts {
|
||||
for (id, contact, count) in sortedContacts {
|
||||
entries.append(.peer(index, id, contact, count, .selectable(selected: selectionState.selectedContactIndices[id] != nil), theme, strings, nameSortOrder, nameDisplayOrder))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
private func preparedInviteContactsTransition(account: Account, from fromEntries: [InviteContactsEntry], to toEntries: [InviteContactsEntry], sortedContats: [(DeviceContactStableId, DeviceContactBasicData, Int32)], interaction: InviteContactsInteraction, firstTime: Bool, animated: Bool) -> InviteContactsTransition {
|
||||
private func preparedInviteContactsTransition(account: Account, from fromEntries: [InviteContactsEntry], to toEntries: [InviteContactsEntry], sortedContacts: [(DeviceContactStableId, DeviceContactBasicData, Int32)]?, interaction: InviteContactsInteraction, firstTime: Bool, isLoading: Bool, animated: Bool) -> InviteContactsTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) }
|
||||
|
||||
return InviteContactsTransition(deletions: deletions, insertions: insertions, updates: updates, sortedContats: sortedContats, firstTime: firstTime, animated: animated)
|
||||
return InviteContactsTransition(deletions: deletions, insertions: insertions, updates: updates, sortedContacts: sortedContacts, firstTime: firstTime, isLoading: isLoading, animated: animated)
|
||||
}
|
||||
|
||||
private struct InviteContactsTransition {
|
||||
let deletions: [ListViewDeleteItem]
|
||||
let insertions: [ListViewInsertItem]
|
||||
let updates: [ListViewUpdateItem]
|
||||
let sortedContats: [(DeviceContactStableId, DeviceContactBasicData, Int32)]
|
||||
let sortedContacts: [(DeviceContactStableId, DeviceContactBasicData, Int32)]?
|
||||
let firstTime: Bool
|
||||
let isLoading: Bool
|
||||
let animated: Bool
|
||||
}
|
||||
|
||||
final class InviteContactsControllerNode: ASDisplayNode {
|
||||
let listNode: ListView
|
||||
private var activityIndicator: ActivityIndicator?
|
||||
|
||||
private let context: AccountContext
|
||||
private var searchDisplayController: SearchDisplayController?
|
||||
@@ -365,8 +369,8 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
let currentSortedContacts = self.currentSortedContacts
|
||||
let sortedContacts: Signal<[(DeviceContactStableId, DeviceContactBasicData, Int32)], NoError> = combineLatest(existingNumbers, (context.sharedContext.contactDataManager?.basicData() ?? .single([:])) |> take(1))
|
||||
|> mapToSignal { existingNumbersAndPeerIds, contacts -> Signal<[(DeviceContactStableId, DeviceContactBasicData, Int32)], NoError> in
|
||||
let sortedContacts: Signal<[(DeviceContactStableId, DeviceContactBasicData, Int32)]?, NoError> = combineLatest(existingNumbers, (context.sharedContext.contactDataManager?.basicData() ?? .single([:])) |> take(1))
|
||||
|> mapToSignal { existingNumbersAndPeerIds, contacts -> Signal<[(DeviceContactStableId, DeviceContactBasicData, Int32)]?, NoError> in
|
||||
var mappedContacts: [(String, [DeviceContactNormalizedPhoneNumber])] = []
|
||||
for (id, basicData) in contacts {
|
||||
mappedContacts.append((id: id, basicData.phoneNumbers.map({ phoneNumber in
|
||||
@@ -374,7 +378,7 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
})))
|
||||
}
|
||||
return deviceContactsImportedByCount(postbox: context.account.postbox, contacts: mappedContacts)
|
||||
|> map { counts -> [(DeviceContactStableId, DeviceContactBasicData, Int32)] in
|
||||
|> map { counts -> [(DeviceContactStableId, DeviceContactBasicData, Int32)]? in
|
||||
var result: [(DeviceContactStableId, DeviceContactBasicData, Int32)] = []
|
||||
var contactValues: [DeviceContactStableId: DeviceContactBasicData] = [:]
|
||||
for (id, basicData) in contacts {
|
||||
@@ -410,10 +414,13 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|> beforeNext { sortedContacts in
|
||||
let _ = currentSortedContacts.swap(sortedContacts)
|
||||
if let sortedContacts = sortedContacts {
|
||||
let _ = currentSortedContacts.swap(sortedContacts)
|
||||
}
|
||||
}
|
||||
|
||||
let processingQueue = Queue()
|
||||
transition = (combineLatest(sortedContacts, selectionStateSignal, themeAndStringsPromise.get())
|
||||
transition = (combineLatest(.single(nil) |> then(sortedContacts), selectionStateSignal, themeAndStringsPromise.get())
|
||||
|> mapToQueue { sortedContacts, selectionState, themeAndStrings -> Signal<InviteContactsTransition, NoError> in
|
||||
let signal = deferred { () -> Signal<InviteContactsTransition, NoError> in
|
||||
let entries = inviteContactsEntries(accountPeer: nil, sortedContacts: sortedContacts, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, nameSortOrder: themeAndStrings.2, nameDisplayOrder: themeAndStrings.3, interaction: interaction)
|
||||
@@ -424,16 +431,18 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
} else {
|
||||
animated = false
|
||||
}
|
||||
return .single(preparedInviteContactsTransition(account: context.account, from: previous ?? [], to: entries, sortedContats: sortedContacts, interaction: interaction, firstTime: previous == nil, animated: animated))
|
||||
return .single(preparedInviteContactsTransition(account: context.account, from: previous ?? [], to: entries, sortedContacts: sortedContacts, interaction: interaction, firstTime: previous == nil, isLoading: false, animated: animated))
|
||||
}
|
||||
return signal
|
||||
|> runOn(processingQueue)
|
||||
|
||||
if OSAtomicCompareAndSwap32(1, 0, &firstTime) {
|
||||
return signal
|
||||
|> runOn(Queue.mainQueue())
|
||||
} else {
|
||||
return signal
|
||||
|> runOn(processingQueue)
|
||||
}
|
||||
// if OSAtomicCompareAndSwap32(1, 0, &firstTime) {
|
||||
// return signal
|
||||
// |> runOn(Queue.mainQueue())
|
||||
// } else {
|
||||
// return signal
|
||||
// |> runOn(processingQueue)
|
||||
// }
|
||||
})
|
||||
|> deliverOnMainQueue
|
||||
|
||||
@@ -441,6 +450,8 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
self?.enqueueTransition(transition)
|
||||
})
|
||||
|
||||
self.enqueueTransition(InviteContactsTransition(deletions: [], insertions: [], updates: [], sortedContacts: [], firstTime: true, isLoading: true, animated: false))
|
||||
|
||||
shareImpl = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
var result: [(DeviceContactBasicData, Int32)] = []
|
||||
@@ -524,6 +535,11 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
|
||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
|
||||
if let activityIndicator = self.activityIndicator {
|
||||
let indicatorSize = activityIndicator.measure(CGSize(width: 100.0, height: 100.0))
|
||||
transition.updateFrame(node: activityIndicator, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - indicatorSize.width) / 2.0), y: updateSizeAndInsets.insets.top + 50.0 + floor((layout.size.height - updateSizeAndInsets.insets.top - updateSizeAndInsets.insets.bottom - indicatorSize.height - 50.0) / 2.0)), size: indicatorSize))
|
||||
}
|
||||
|
||||
if !hadValidLayout {
|
||||
self.dequeueTransitions()
|
||||
}
|
||||
@@ -587,6 +603,15 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateOpaqueState: nil, completion: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.readyValue = true
|
||||
|
||||
if transition.isLoading, strongSelf.activityIndicator == nil {
|
||||
let activityIndicator = ActivityIndicator(type: .custom(strongSelf.presentationData.theme.list.itemAccentColor, 22.0, 1.0, false))
|
||||
strongSelf.activityIndicator = activityIndicator
|
||||
strongSelf.insertSubnode(activityIndicator, aboveSubnode: strongSelf.listNode)
|
||||
} else if !transition.isLoading, let activityIndicator = strongSelf.activityIndicator {
|
||||
strongSelf.activityIndicator = nil
|
||||
activityIndicator.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -605,4 +630,3 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
self.selectionState = self.selectionState.withReplacedSelectedContactIds(allSelected ? [] : ids)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,12 +66,63 @@ private let titleFont = Font.bold(16.0)
|
||||
private let statusFont = Font.regular(15.0)
|
||||
private let buttonFont = Font.medium(13.0)
|
||||
|
||||
private final class TrendingTopItemNode: TransformImageNode {
|
||||
var file: TelegramMediaFile? = nil
|
||||
let loadDisposable = MetaDisposable()
|
||||
private final class TrendingTopItemNode: ASDisplayNode {
|
||||
private let imageNode: TransformImageNode
|
||||
private var animationNode: AnimatedStickerNode?
|
||||
public private(set) var file: TelegramMediaFile? = nil
|
||||
private var itemSize: CGSize?
|
||||
private let loadDisposable = MetaDisposable()
|
||||
|
||||
var currentIsPreviewing = false
|
||||
|
||||
var visibility: Bool = false {
|
||||
didSet {
|
||||
if oldValue != self.visibility {
|
||||
self.animationNode?.visibility = self.visibility
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override init() {
|
||||
self.imageNode = TransformImageNode()
|
||||
self.imageNode.contentAnimations = [.subsequentUpdates]
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.imageNode)
|
||||
}
|
||||
|
||||
func setup(account: Account, item: StickerPackItem, itemSize: CGSize, synchronousLoads: Bool) {
|
||||
self.file = item.file
|
||||
self.itemSize = itemSize
|
||||
|
||||
if item.file.isAnimatedSticker {
|
||||
self.loadDisposable.set(nil)
|
||||
|
||||
let animationNode: AnimatedStickerNode
|
||||
if let currentAnimationNode = self.animationNode {
|
||||
animationNode = currentAnimationNode
|
||||
} else {
|
||||
animationNode = AnimatedStickerNode()
|
||||
animationNode.transform = self.imageNode.transform
|
||||
animationNode.visibility = self.visibility
|
||||
self.addSubnode(animationNode)
|
||||
self.animationNode = animationNode
|
||||
}
|
||||
animationNode.started = { [weak self] in
|
||||
self?.imageNode.alpha = 0.0
|
||||
}
|
||||
animationNode.setup(account: account, fileReference: stickerPackFileReference(item.file), width: 140, height: 140)
|
||||
} else {
|
||||
self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads)
|
||||
self.loadDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: chatMessageStickerResource(file: item.file, small: true)).start())
|
||||
|
||||
if let currentAnimationNode = self.animationNode {
|
||||
self.animationNode = nil
|
||||
currentAnimationNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updatePreviewing(animated: Bool, isPreviewing: Bool) {
|
||||
if self.currentIsPreviewing != isPreviewing {
|
||||
self.currentIsPreviewing = isPreviewing
|
||||
@@ -88,6 +139,18 @@ private final class TrendingTopItemNode: TransformImageNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
if let dimensions = self.file?.dimensions, let itemSize = self.itemSize {
|
||||
let imageSize = dimensions.aspectFitted(itemSize)
|
||||
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
|
||||
}
|
||||
|
||||
self.imageNode.frame = self.bounds
|
||||
self.animationNode?.updateLayout(size: self.bounds.size)
|
||||
}
|
||||
}
|
||||
|
||||
class MediaInputPaneTrendingItemNode: ListViewItemNode {
|
||||
@@ -109,6 +172,10 @@ class MediaInputPaneTrendingItemNode: ListViewItemNode {
|
||||
let isVisible = self.visibility != .none
|
||||
|
||||
if isVisible != wasVisible {
|
||||
for node in self.itemNodes {
|
||||
node.visibility = isVisible
|
||||
}
|
||||
|
||||
if isVisible {
|
||||
if let item = self.item, item.unread {
|
||||
self.readDisposable.set((
|
||||
@@ -278,6 +345,8 @@ class MediaInputPaneTrendingItemNode: ListViewItemNode {
|
||||
var offset = sideInset
|
||||
let itemSpacing = (max(0, availableWidth - 5.0 * itemSide - sideInset * 2.0)) / 4.0
|
||||
|
||||
let isVisible = strongSelf.visibility != .none
|
||||
|
||||
for i in 0 ..< topItems.count {
|
||||
let file = topItems[i].file
|
||||
let node: TrendingTopItemNode
|
||||
@@ -285,18 +354,15 @@ class MediaInputPaneTrendingItemNode: ListViewItemNode {
|
||||
node = strongSelf.itemNodes[i]
|
||||
} else {
|
||||
node = TrendingTopItemNode()
|
||||
node.contentAnimations = [.subsequentUpdates]
|
||||
node.visibility = isVisible
|
||||
strongSelf.itemNodes.append(node)
|
||||
strongSelf.addSubnode(node)
|
||||
}
|
||||
if file.fileId != node.file?.fileId {
|
||||
node.file = file
|
||||
node.setSignal(chatMessageSticker(account: item.account, file: file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads)
|
||||
node.loadDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(file), resource: chatMessageStickerResource(file: file, small: true)).start())
|
||||
node.setup(account: item.account, item: topItems[i], itemSize: itemSize, synchronousLoads: synchronousLoads)
|
||||
}
|
||||
if let dimensions = file.dimensions {
|
||||
let imageSize = dimensions.aspectFitted(itemSize)
|
||||
node.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
|
||||
node.frame = CGRect(origin: CGPoint(x: offset, y: 48.0), size: imageSize)
|
||||
offset += itemSize.width + itemSpacing
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user