Improve sticker-related components

This commit is contained in:
Ali 2019-12-27 01:32:42 +04:00
parent b476c87130
commit 2e60947b9e
13 changed files with 2438 additions and 2241 deletions

View File

@ -5238,4 +5238,5 @@ Any member of this group will be able to see messages in the channel.";
"StickerPackActionInfo.AddedTitle" = "Stickers Added";
"StickerPackActionInfo.AddedText" = "%@ has been added to your stickers.";
"StickerPackActionInfo.RemovedTitle" = "Stickers Removed";
"StickerPackActionInfo.ArchivedTitle" = "Stickers Archived";
"StickerPackActionInfo.RemovedText" = "%@ is no longer in your stickers.";

View File

@ -13,6 +13,7 @@ import OverlayStatusController
import AccountContext
import StickerPackPreviewUI
import ItemListStickerPackItem
import UndoUI
public enum ArchivedStickerPacksControllerMode {
case stickers
@ -248,6 +249,7 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv
}
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
var navigationControllerImpl: (() -> NavigationController?)?
let actionsDisposable = DisposableSet()
@ -300,24 +302,42 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv
return
}
let _ = (loadedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false)
|> mapToSignal { result -> Signal<Void, NoError> in
|> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in
switch result {
case let .result(info, items, installed):
if installed {
return .complete()
} else {
return addStickerPackInteractively(postbox: context.account.postbox, info: info, items: items)
}
case .fetching:
break
case let .result(info, items, installed):
if installed {
return .complete()
} else {
return addStickerPackInteractively(postbox: context.account.postbox, info: info, items: items)
|> ignoreValues
|> mapToSignal { _ -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in
return .complete()
}
|> then(.single((info, items)))
}
case .fetching:
break
case .none:
break
}
return .complete()
}
|> deliverOnMainQueue).start(completed: {
|> deliverOnMainQueue).start(next: { info, items in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, type: .success), nil)
var animateInAsReplacement = false
if let navigationController = navigationControllerImpl?() {
for controller in navigationController.overlayControllers {
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitActionAndReplacementAnimation()
animateInAsReplacement = true
}
}
}
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in
return true
}), nil)
let applyPacks: Signal<Void, NoError> = stickerPacks.get()
|> filter { $0 != nil }
@ -428,7 +448,9 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv
controller.present(c, in: .window(.root), with: p)
}
}
navigationControllerImpl = { [weak controller] in
return controller?.navigationController as? NavigationController
}
presentStickerPackController = { [weak controller] info in
let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash)
presentControllerImpl?(StickerPackScreen(context: context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: controller?.navigationController as? NavigationController), nil)

View File

@ -530,7 +530,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
}
}
navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(archivedItem.info.title).0, undo: true, info: archivedItem.info, topItem: archivedItem.topItems.first, account: context.account), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_ArchivedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(archivedItem.info.title).0, undo: true, info: archivedItem.info, topItem: archivedItem.topItems.first, account: context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { action in
if case .undo = action {
let _ = addStickerPackInteractively(postbox: context.account.postbox, info: archivedItem.info, items: items, positionInList: positionInList).start()
}
@ -840,11 +840,11 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
}
switch action {
case .add:
navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: context.account), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in
return true
}))
case let .remove(positionInList):
navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).0, undo: true, info: info, topItem: items.first, account: context.account), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).0, undo: true, info: info, topItem: items.first, account: context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { action in
if case .undo = action {
let _ = addStickerPackInteractively(postbox: context.account.postbox, info: info, items: items, positionInList: positionInList).start()
}

View File

@ -266,3 +266,78 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition)
}
}
public func preloadedStickerPackThumbnail(account: Account, info: StickerPackCollectionInfo, items: [ItemCollectionItem]) -> Signal<Bool, NoError> {
if let thumbnail = info.thumbnail {
let signal = Signal<Bool, NoError> { subscriber in
let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)).start()
let dataDisposable: Disposable
if info.flags.contains(.isAnimated) {
dataDisposable = chatMessageAnimationData(postbox: account.postbox, resource: thumbnail.resource, width: 80, height: 80, synchronousLoad: false).start(next: { data in
if data.complete {
subscriber.putNext(true)
subscriber.putCompletion()
} else {
subscriber.putNext(false)
}
})
} else {
dataDisposable = account.postbox.mediaBox.resourceData(thumbnail.resource, option: .incremental(waitUntilFetchStatus: false)).start(next: { data in
if data.complete {
subscriber.putNext(true)
subscriber.putCompletion()
} else {
subscriber.putNext(false)
}
})
}
return ActionDisposable {
fetched.dispose()
dataDisposable.dispose()
}
}
return signal
}
if let item = items.first as? StickerPackItem {
if item.file.isAnimatedSticker {
let signal = Signal<Bool, NoError> { subscriber in
let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: FileMediaReference.standalone(media: item.file).resourceReference(item.file.resource)).start()
let data = account.postbox.mediaBox.resourceData(item.file.resource).start()
let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512)
let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, file: item.file, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in
let hasContent = next._0 != nil || next._1 != nil
subscriber.putNext(hasContent)
if hasContent {
subscriber.putCompletion()
}
})
return ActionDisposable {
fetched.dispose()
data.dispose()
fetchedRepresentation.dispose()
}
}
return signal
} else {
let signal = Signal<Bool, NoError> { subscriber in
let data = account.postbox.mediaBox.resourceData(item.file.resource).start()
let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512)
let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, file: item.file, small: true, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in
let hasContent = next._0 != nil || next._1 != nil
subscriber.putNext(hasContent)
if hasContent {
subscriber.putCompletion()
}
})
return ActionDisposable {
data.dispose()
fetchedRepresentation.dispose()
}
}
return signal
}
}
return .single(true)
}

View File

@ -183,7 +183,7 @@ private let threadPool: ThreadPool = {
}()
@available(iOS 9.0, *)
public func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize, fitzModifier: EmojiFitzModifier? = nil, cacheKey: String) -> Signal<Data, NoError> {
public func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize, fitzModifier: EmojiFitzModifier? = nil, cacheKey: String) -> Signal<CachedMediaResourceRepresentationResult, NoError> {
return Signal({ subscriber in
let cancelled = Atomic<Bool>(value: false)
@ -212,19 +212,36 @@ public func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: C
var currentFrame: Int32 = 0
let writeBuffer = WriteBuffer()
//let writeBuffer = WriteBuffer()
let tempFile = TempBox.shared.tempFile(fileName: "result.asticker")
guard let file = ManagedFile(queue: nil, path: tempFile.path, mode: .readwrite) else {
return
}
func writeData(_ data: UnsafeRawPointer, length: Int) {
file.write(data, count: length)
}
func commitData() {
}
func completeWithCurrentResult() {
subscriber.putNext(.tempFile(tempFile))
subscriber.putCompletion()
}
var numberOfFramesCommitted = 0
var fps: Int32 = player.frameRate
var frameCount: Int32 = player.frameCount
writeBuffer.write(&fps, length: 4)
writeBuffer.write(&frameCount, length: 4)
writeData(&fps, length: 4)
writeData(&frameCount, length: 4)
var widthValue: Int32 = Int32(size.width)
var heightValue: Int32 = Int32(size.height)
var bytesPerRowValue: Int32 = Int32(bytesPerRow)
writeBuffer.write(&widthValue, length: 4)
writeBuffer.write(&heightValue, length: 4)
writeBuffer.write(&bytesPerRowValue, length: 4)
writeData(&widthValue, length: 4)
writeData(&heightValue, length: 4)
writeData(&bytesPerRowValue, length: 4)
let frameLength = bytesPerRow * Int(size.height)
assert(frameLength % 16 == 0)
@ -293,8 +310,8 @@ public func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: C
compressedFrameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
let length = compression_encode_buffer(bytes, compressedFrameDataLength, previousYuvaFrameData.assumingMemoryBound(to: UInt8.self), yuvaLength, scratchData, COMPRESSION_LZFSE)
var frameLengthValue: Int32 = Int32(length)
writeBuffer.write(&frameLengthValue, length: 4)
writeBuffer.write(bytes, length: length)
writeData(&frameLengthValue, length: 4)
writeData(bytes, length: length)
}
let tmp = previousYuvaFrameData
@ -310,20 +327,14 @@ public func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: C
if numberOfFramesCommitted >= 5 {
numberOfFramesCommitted = 0
subscriber.putNext(writeBuffer.makeData())
writeBuffer.reset()
/*#if DEBUG
usleep(500000)
#endif*/
commitData()
}
}
if writeBuffer.length != 0 {
subscriber.putNext(writeBuffer.makeData())
}
commitData()
completeWithCurrentResult()
subscriber.putCompletion()
/*print("animation render time \(CACurrentMediaTime() - startTime)")
print("of which drawing time \(drawingTime)")

View File

@ -222,7 +222,7 @@ public func searchStickers(account: Account, query: String, scope: SearchSticker
}
public struct FoundStickerSets {
public let infos: [(ItemCollectionId, ItemCollectionInfo, ItemCollectionItem?, Bool)]
public var infos: [(ItemCollectionId, ItemCollectionInfo, ItemCollectionItem?, Bool)]
public let entries: [ItemCollectionViewEntry]
public init(infos: [(ItemCollectionId, ItemCollectionInfo, ItemCollectionItem?, Bool)] = [], entries: [ItemCollectionViewEntry] = []) {
self.infos = infos

View File

@ -798,7 +798,7 @@ final class ChatMediaInputNode: ChatInputNode {
var index = 0
for item in trendingPacks {
if !installedPacks.contains(item.info.id) {
gridEntries.append(.trending(TrendingPaneEntry(index: index, info: item.info, theme: theme, strings: strings, topItems: item.topItems, installed: installedPacks.contains(item.info.id), unread: item.unread)))
gridEntries.append(.trending(TrendingPaneEntry(index: index, info: item.info, theme: theme, strings: strings, topItems: item.topItems, installed: installedPacks.contains(item.info.id), unread: item.unread, topSeparator: true)))
index += 1
}
}
@ -1159,14 +1159,14 @@ final class ChatMediaInputNode: ChatInputNode {
if let index = self.paneArrangement.panes.firstIndex(of: pane), index != self.paneArrangement.currentIndex {
let previousGifPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .gifs
let previousTrendingPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .trending
//let previousTrendingPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .trending
self.paneArrangement = self.paneArrangement.withIndexTransition(0.0).withCurrentIndex(index)
let updatedGifPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .gifs
let updatedTrendingPanelIsActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .trending
//let updatedTrendingPanelIsActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .trending
if updatedTrendingPanelIsActive != previousTrendingPanelWasActive {
/*if updatedTrendingPanelIsActive != previousTrendingPanelWasActive {
transition = .immediate
}
}*/
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible) = self.validLayout {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: transition, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible)
@ -1187,7 +1187,7 @@ final class ChatMediaInputNode: ChatInputNode {
case .trending:
self.setHighlightedItemCollectionId(ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.trending.rawValue, id: 0))
}
if updatedTrendingPanelIsActive != previousTrendingPanelWasActive {
/*if updatedTrendingPanelIsActive != previousTrendingPanelWasActive {
self.controllerInteraction.updateInputMode { current in
switch current {
case let .media(mode, _):
@ -1200,7 +1200,7 @@ final class ChatMediaInputNode: ChatInputNode {
return current
}
}
}
}*/
} else {
if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible) = self.validLayout {
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .animated(duration: 0.25, curve: .spring), interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible)

View File

@ -12,6 +12,7 @@ import OverlayStatusController
import AccountContext
import StickerPackPreviewUI
import PresentationDataUtils
import UndoUI
final class TrendingPaneInteraction {
let installPack: (ItemCollectionInfo) -> Void
@ -33,8 +34,9 @@ final class TrendingPaneEntry: Identifiable, Comparable {
let topItems: [StickerPackItem]
let installed: Bool
let unread: Bool
let topSeparator: Bool
init(index: Int, info: StickerPackCollectionInfo, theme: PresentationTheme, strings: PresentationStrings, topItems: [StickerPackItem], installed: Bool, unread: Bool) {
init(index: Int, info: StickerPackCollectionInfo, theme: PresentationTheme, strings: PresentationStrings, topItems: [StickerPackItem], installed: Bool, unread: Bool, topSeparator: Bool) {
self.index = index
self.info = info
self.theme = theme
@ -42,6 +44,7 @@ final class TrendingPaneEntry: Identifiable, Comparable {
self.topItems = topItems
self.installed = installed
self.unread = unread
self.topSeparator = topSeparator
}
var stableId: ItemCollectionId {
@ -70,6 +73,9 @@ final class TrendingPaneEntry: Identifiable, Comparable {
if lhs.unread != rhs.unread {
return false
}
if lhs.topSeparator != rhs.topSeparator {
return false
}
return true
}
@ -79,7 +85,7 @@ final class TrendingPaneEntry: Identifiable, Comparable {
func item(account: Account, interaction: TrendingPaneInteraction, grid: Bool) -> GridItem {
let info = self.info
return StickerPaneSearchGlobalItem(account: account, theme: self.theme, strings: self.strings, info: self.info, topItems: self.topItems, grid: grid, installed: self.installed, unread: self.unread, open: {
return StickerPaneSearchGlobalItem(account: account, theme: self.theme, strings: self.strings, info: self.info, topItems: self.topItems, grid: grid, topSeparator: self.topSeparator, installed: self.installed, unread: self.unread, open: {
interaction.openPack(info)
}, install: {
interaction.installPack(info)
@ -100,8 +106,8 @@ private func preparedTransition(from fromEntries: [TrendingPaneEntry], to toEntr
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices
let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(account: account, interaction: interaction, grid: true), previousIndex: $0.2) }
let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction, grid: true)) }
let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(account: account, interaction: interaction, grid: false), previousIndex: $0.2) }
let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction, grid: false)) }
return TrendingPaneTransition(deletions: deletions, insertions: insertions, updates: updates, initial: initial)
}
@ -111,7 +117,7 @@ func trendingPaneEntries(trendingEntries: [FeaturedStickerPackItem], installedPa
var index = 0
for item in trendingEntries {
if !installedPacks.contains(item.info.id) {
result.append(TrendingPaneEntry(index: index, info: item.info, theme: theme, strings: strings, topItems: item.topItems, installed: installedPacks.contains(item.info.id), unread: item.unread))
result.append(TrendingPaneEntry(index: index, info: item.info, theme: theme, strings: strings, topItems: item.topItems, installed: installedPacks.contains(item.info.id), unread: item.unread, topSeparator: index != 0))
index += 1
}
}
@ -167,26 +173,52 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane {
let interaction = TrendingPaneInteraction(installPack: { [weak self] info in
if let strongSelf = self, let info = info as? StickerPackCollectionInfo {
let account = strongSelf.context.account
let _ = (loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false)
|> mapToSignal { result -> Signal<Void, NoError> in
|> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in
switch result {
case let .result(info, items, installed):
if installed {
case let .result(info, items, installed):
if installed {
return .complete()
} else {
return preloadedStickerPackThumbnail(account: account, info: info, items: items)
|> filter { $0 }
|> ignoreValues
|> then(
addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: items)
|> ignoreValues
)
|> mapToSignal { _ -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in
return .complete()
} else {
return addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: items)
}
case .fetching:
break
case .none:
break
|> then(.single((info, items)))
}
case .fetching:
break
case .none:
break
}
return .complete()
} |> deliverOnMainQueue).start(completed: {
if let strongSelf = self {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.controllerInteraction.presentController(OverlayStatusController(theme: presentationData.theme, type: .success), nil)
}
|> deliverOnMainQueue).start(next: { info, items in
guard let strongSelf = self else {
return
}
var animateInAsReplacement = false
if let navigationController = strongSelf.controllerInteraction.navigationController() {
for controller in navigationController.overlayControllers {
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitActionAndReplacementAnimation()
animateInAsReplacement = true
}
}
}
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.controllerInteraction.navigationController()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: strongSelf.context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in
return true
}))
})
}
}, openPack: { [weak self] info in

View File

@ -855,11 +855,9 @@ private func fetchAnimatedStickerRepresentation(account: Account, resource: Medi
return Signal({ subscriber in
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
if #available(iOS 9.0, *) {
subscriber.putNext(.reset)
return experimentalConvertCompressedLottieToCombinedMp4(data: data, size: CGSize(width: CGFloat(representation.width), height: CGFloat(representation.height)), fitzModifier: representation.fitzModifier, cacheKey: "\(resource.id.uniqueId)-\(representation.uniqueId)").start(next: { data in
subscriber.putNext(.data(data))
return experimentalConvertCompressedLottieToCombinedMp4(data: data, size: CGSize(width: CGFloat(representation.width), height: CGFloat(representation.height)), fitzModifier: representation.fitzModifier, cacheKey: "\(resource.id.uniqueId)-\(representation.uniqueId)").start(next: { value in
subscriber.putNext(value)
}, completed: {
subscriber.putNext(.done)
subscriber.putCompletion()
})
} else {

View File

@ -35,13 +35,13 @@ private enum StickerSearchEntryId: Equatable, Hashable {
private enum StickerSearchEntry: Identifiable, Comparable {
case sticker(index: Int, code: String?, stickerItem: FoundStickerItem, theme: PresentationTheme)
case global(index: Int, info: StickerPackCollectionInfo, topItems: [StickerPackItem], installed: Bool)
case global(index: Int, info: StickerPackCollectionInfo, topItems: [StickerPackItem], installed: Bool, topSeparator: Bool)
var stableId: StickerSearchEntryId {
switch self {
case let .sticker(_, code, stickerItem, _):
return .sticker(code, stickerItem.file.fileId.id)
case let .global(_, info, _, _):
case let .global(_, info, _, _, _):
return .global(info.id)
}
}
@ -66,8 +66,8 @@ private enum StickerSearchEntry: Identifiable, Comparable {
} else {
return false
}
case let .global(index, info, topItems, installed):
if case .global(index, info, topItems, installed) = rhs {
case let .global(index, info, topItems, installed, topSeparator):
if case .global(index, info, topItems, installed, topSeparator) = rhs {
return true
} else {
return false
@ -84,11 +84,11 @@ private enum StickerSearchEntry: Identifiable, Comparable {
default:
return true
}
case let .global(lhsIndex, _, _, _):
case let .global(lhsIndex, _, _, _, _):
switch rhs {
case .sticker:
return false
case let .global(rhsIndex, _, _, _):
case let .global(rhsIndex, _, _, _, _):
return lhsIndex < rhsIndex
}
}
@ -100,8 +100,8 @@ private enum StickerSearchEntry: Identifiable, Comparable {
return StickerPaneSearchStickerItem(account: account, code: code, stickerItem: stickerItem, inputNodeInteraction: inputNodeInteraction, theme: theme, selected: { node, rect in
interaction.sendSticker(.standalone(media: stickerItem.file), node, rect)
})
case let .global(_, info, topItems, installed):
return StickerPaneSearchGlobalItem(account: account, theme: theme, strings: strings, info: info, topItems: topItems, grid: false, installed: installed, unread: false, open: {
case let .global(_, info, topItems, installed, topSeparator):
return StickerPaneSearchGlobalItem(account: account, theme: theme, strings: strings, info: info, topItems: topItems, grid: false, topSeparator: topSeparator, installed: installed, unread: false, open: {
interaction.open(info)
}, install: {
interaction.install(info)
@ -322,17 +322,57 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
let local = searchStickerSets(postbox: context.account.postbox, query: text)
let remote = searchStickerSetsRemotely(network: context.account.network, query: text)
|> delay(0.2, queue: Queue.mainQueue())
let packs = local
let rawPacks = local
|> mapToSignal { result -> Signal<(FoundStickerSets, Bool, FoundStickerSets?), NoError> in
var localResult = result
if let currentRemote = self.currentRemotePacks.with ({ $0 }) {
localResult = localResult.merge(with: currentRemote)
}
return .single((localResult, false, nil))
|> then(remote |> map { remote -> (FoundStickerSets, Bool, FoundStickerSets?) in
return (result.merge(with: remote), true, remote)
})
|> then(
remote
|> map { remote -> (FoundStickerSets, Bool, FoundStickerSets?) in
return (result.merge(with: remote), true, remote)
}
)
}
let installedPackIds = context.account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])])
|> map { view -> Set<ItemCollectionId> in
var installedPacks = Set<ItemCollectionId>()
if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])] as? ItemCollectionInfosView {
if let packsEntries = stickerPacksView.entriesByNamespace[Namespaces.ItemCollection.CloudStickerPacks] {
for entry in packsEntries {
installedPacks.insert(entry.id)
}
}
}
return installedPacks
}
|> distinctUntilChanged
let packs = combineLatest(rawPacks, installedPackIds)
|> map { packs, installedPackIds -> (FoundStickerSets, Bool, FoundStickerSets?) in
var (localPacks, completed, remotePacks) = packs
for i in 0 ..< localPacks.infos.count {
let installed = installedPackIds.contains(localPacks.infos[i].0)
if installed != localPacks.infos[i].3 {
localPacks.infos[i].3 = installed
}
}
if remotePacks != nil {
for i in 0 ..< remotePacks!.infos.count {
let installed = installedPackIds.contains(remotePacks!.infos[i].0)
if installed != remotePacks!.infos[i].3 {
remotePacks!.infos[i].3 = installed
}
}
}
return (localPacks, completed, remotePacks)
}
signal = combineLatest(stickers, packs)
|> map { stickers, packs -> ([(String?, FoundStickerItem)], FoundStickerSets, Bool, FoundStickerSets?)? in
return (stickers, packs.0, packs.1, packs.2)
@ -374,6 +414,7 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
existingStickerIds.insert(id)
}
}
var isFirstGlobal = true
for (collectionId, info, _, installed) in packs.infos {
if let info = info as? StickerPackCollectionInfo {
var topItems: [StickerPackItem] = []
@ -384,7 +425,8 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
}
}
}
entries.append(.global(index: index, info: info, topItems: topItems, installed: installed))
entries.append(.global(index: index, info: info, topItems: topItems, installed: installed, topSeparator: !isFirstGlobal))
isFirstGlobal = false
index += 1
}
}

View File

@ -39,6 +39,7 @@ final class StickerPaneSearchGlobalItem: GridItem {
let info: StickerPackCollectionInfo
let topItems: [StickerPackItem]
let grid: Bool
let topSeparator: Bool
let installed: Bool
let unread: Bool
let open: () -> Void
@ -47,16 +48,17 @@ final class StickerPaneSearchGlobalItem: GridItem {
let section: GridSection? = StickerPaneSearchGlobalSection()
var fillsRowWithHeight: CGFloat? {
return self.grid ? nil : 128.0
return self.grid ? nil : (128.0 + (self.topSeparator ? 12.0 : 0.0))
}
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, info: StickerPackCollectionInfo, topItems: [StickerPackItem], grid: Bool, installed: Bool, unread: Bool, open: @escaping () -> Void, install: @escaping () -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool) {
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, info: StickerPackCollectionInfo, topItems: [StickerPackItem], grid: Bool, topSeparator: Bool, installed: Bool, unread: Bool, open: @escaping () -> Void, install: @escaping () -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool) {
self.account = account
self.theme = theme
self.strings = strings
self.info = info
self.topItems = topItems
self.grid = grid
self.topSeparator = topSeparator
self.installed = installed
self.unread = unread
self.open = open
@ -91,6 +93,7 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
private let installBackgroundNode: ASImageNode
private let installButtonNode: HighlightTrackingButtonNode
private var itemNodes: [TrendingTopItemNode]
private let topSeparatorNode: ASDisplayNode
private var item: StickerPaneSearchGlobalItem?
private var appliedItem: StickerPaneSearchGlobalItem?
@ -134,6 +137,9 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
self.installButtonNode = HighlightTrackingButtonNode()
self.topSeparatorNode = ASDisplayNode()
self.topSeparatorNode.isLayerBacked = true
self.itemNodes = []
super.init()
@ -144,6 +150,7 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
self.addSubnode(self.installBackgroundNode)
self.addSubnode(self.installTextNode)
self.addSubnode(self.installButtonNode)
self.addSubnode(self.topSeparatorNode)
self.installButtonNode.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
@ -193,6 +200,15 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
let params = ListViewItemLayoutParams(width: self.bounds.size.width, leftInset: 0.0, rightInset: 0.0, availableHeight: self.bounds.height)
var topOffset: CGFloat = 12.0
if item.topSeparator {
topOffset += 12.0
}
self.topSeparatorNode.isHidden = !item.topSeparator
self.topSeparatorNode.frame = CGRect(origin: CGPoint(x: 16.0, y: 16.0), size: CGSize(width: params.width - 16.0 * 2.0, height: UIScreenPixel))
self.topSeparatorNode.backgroundColor = item.theme.chat.inputMediaPanel.stickersSectionTextColor.withAlphaComponent(0.3)
let makeInstallLayout = TextNode.asyncLayout(self.installTextNode)
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let makeDescriptionLayout = TextNode.asyncLayout(self.descriptionNode)
@ -208,7 +224,6 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
let leftInset: CGFloat = 14.0
let rightInset: CGFloat = 16.0
let topOffset: CGFloat = 12.0
let (installLayout, installApply) = makeInstallLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.Stickers_Install, font: buttonFont, textColor: item.theme.list.itemCheckColors.foregroundColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))