mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
477 lines
21 KiB
Swift
477 lines
21 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import LegacyComponents
|
|
import SwiftSignalKit
|
|
import TelegramCore
|
|
import SSignalKit
|
|
import UIKit
|
|
import Display
|
|
import TelegramPresentationData
|
|
import AccountContext
|
|
import PhotoResources
|
|
import LegacyUI
|
|
import LegacyMediaPickerUI
|
|
import Postbox
|
|
|
|
class LegacyWebSearchItem: NSObject, TGMediaEditableItem, TGMediaSelectableItem {
|
|
var isVideo: Bool {
|
|
return false
|
|
}
|
|
|
|
var uniqueIdentifier: String! {
|
|
return self.result.id
|
|
}
|
|
|
|
let result: ChatContextResult
|
|
private(set) var thumbnailResource: TelegramMediaResource?
|
|
private(set) var imageResource: TelegramMediaResource?
|
|
let dimensions: CGSize
|
|
let thumbnailImage: Signal<UIImage, NoError>
|
|
let originalImage: Signal<UIImage, NoError>
|
|
let progress: Signal<Float, NoError>
|
|
|
|
init(result: ChatContextResult) {
|
|
self.result = result
|
|
self.dimensions = CGSize()
|
|
self.thumbnailImage = .complete()
|
|
self.originalImage = .complete()
|
|
self.progress = .complete()
|
|
}
|
|
|
|
init(result: ChatContextResult, thumbnailResource: TelegramMediaResource?, imageResource: TelegramMediaResource?, dimensions: CGSize, thumbnailImage: Signal<UIImage, NoError>, originalImage: Signal<UIImage, NoError>, progress: Signal<Float, NoError>) {
|
|
self.result = result
|
|
self.thumbnailResource = thumbnailResource
|
|
self.imageResource = imageResource
|
|
self.dimensions = dimensions
|
|
self.thumbnailImage = thumbnailImage
|
|
self.originalImage = originalImage
|
|
self.progress = progress
|
|
}
|
|
|
|
var originalSize: CGSize {
|
|
return self.dimensions
|
|
}
|
|
|
|
func thumbnailImageSignal() -> SSignal! {
|
|
return SSignal(generator: { subscriber -> SDisposable? in
|
|
let disposable = self.thumbnailImage.start(next: { image in
|
|
subscriber.putNext(image)
|
|
subscriber.putCompletion()
|
|
})
|
|
|
|
return SBlockDisposable(block: {
|
|
disposable.dispose()
|
|
})
|
|
})
|
|
}
|
|
|
|
func screenImageAndProgressSignal() -> SSignal {
|
|
return SSignal { subscriber in
|
|
let imageDisposable = self.originalImage.start(next: { image in
|
|
if !image.degraded() {
|
|
subscriber.putNext(1.0)
|
|
}
|
|
subscriber.putNext(image)
|
|
if !image.degraded() {
|
|
subscriber.putCompletion()
|
|
}
|
|
})
|
|
|
|
let progressDisposable = (self.progress
|
|
|> deliverOnMainQueue).start(next: { next in
|
|
subscriber.putNext(next)
|
|
})
|
|
|
|
return SBlockDisposable {
|
|
imageDisposable.dispose()
|
|
progressDisposable.dispose()
|
|
}
|
|
}
|
|
}
|
|
|
|
func screenImageSignal(_ position: TimeInterval) -> SSignal! {
|
|
return self.originalImageSignal(position)
|
|
}
|
|
|
|
func originalImageSignal(_ position: TimeInterval) -> SSignal! {
|
|
return SSignal(generator: { subscriber -> SDisposable? in
|
|
let disposable = self.originalImage.start(next: { image in
|
|
subscriber.putNext(image)
|
|
if !image.degraded() {
|
|
subscriber.putCompletion()
|
|
}
|
|
})
|
|
|
|
return SBlockDisposable(block: {
|
|
disposable.dispose()
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
private class LegacyWebSearchGalleryItem: TGModernGalleryImageItem, TGModernGalleryEditableItem, TGModernGallerySelectableItem {
|
|
var selectionContext: TGMediaSelectionContext!
|
|
var editingContext: TGMediaEditingContext!
|
|
var stickersContext: TGPhotoPaintStickersContext!
|
|
let item: LegacyWebSearchItem
|
|
|
|
init(item: LegacyWebSearchItem) {
|
|
self.item = item
|
|
super.init()
|
|
}
|
|
|
|
func editableMediaItem() -> TGMediaEditableItem! {
|
|
return self.item
|
|
}
|
|
|
|
func selectableMediaItem() -> TGMediaSelectableItem! {
|
|
return self.item
|
|
}
|
|
|
|
func toolbarTabs() -> TGPhotoEditorTab {
|
|
return [.cropTab, .paintTab, .toolsTab]
|
|
}
|
|
|
|
func uniqueId() -> String! {
|
|
return self.item.uniqueIdentifier
|
|
}
|
|
|
|
override func viewClass() -> AnyClass! {
|
|
return LegacyWebSearchGalleryItemView.self
|
|
}
|
|
|
|
override func isEqual(_ object: Any?) -> Bool {
|
|
if let item = object as? LegacyWebSearchGalleryItem {
|
|
return item.item.result.id == self.item.result.id
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
private class LegacyWebSearchGalleryItemView: TGModernGalleryImageItemView, TGModernGalleryEditableItemView {
|
|
private let readyForTransition = SVariable()
|
|
|
|
@objc func setHiddenAsBeingEdited(_ hidden: Bool) {
|
|
self.imageView.isHidden = hidden
|
|
}
|
|
|
|
@objc func singleTap() {
|
|
if let item = item as? LegacyWebSearchGalleryItem, let selectionContext = item.selectionContext {
|
|
selectionContext.toggleItemSelection(item.selectableMediaItem(), success: nil)
|
|
}
|
|
}
|
|
|
|
override func readyForTransitionIn() -> SSignal! {
|
|
return self.readyForTransition.signal().take(1)
|
|
}
|
|
|
|
override func setItem(_ item: TGModernGalleryItem!, synchronously: Bool) {
|
|
if let item = item as? LegacyWebSearchGalleryItem {
|
|
self._setItem(item)
|
|
self.imageSize = TGFitSize(item.editableMediaItem().originalSize!, CGSize(width: 1600, height: 1600))
|
|
|
|
let signal = item.editingContext.imageSignal(for: item.editableMediaItem())?.map(toSignal: { result -> SSignal in
|
|
if let image = result as? UIImage {
|
|
return SSignal.single(image)
|
|
} else if result == nil, let mediaItem = item.editableMediaItem() as? LegacyWebSearchItem {
|
|
return mediaItem.screenImageAndProgressSignal()
|
|
} else {
|
|
return SSignal.complete()
|
|
}
|
|
})
|
|
|
|
self.imageView.setSignal(signal?.deliver(on: SQueue.main()).afterNext({ [weak self] next in
|
|
if let strongSelf = self, let image = next as? UIImage {
|
|
strongSelf.imageSize = image.size
|
|
strongSelf.reset()
|
|
strongSelf.readyForTransition.set(SSignal.single(true))
|
|
}
|
|
}))
|
|
|
|
self.reset()
|
|
} else {
|
|
self.imageView.setSignal(nil)
|
|
super.setItem(item, synchronously: synchronously)
|
|
}
|
|
}
|
|
|
|
override func contentView() -> UIView! {
|
|
return self.imageView
|
|
}
|
|
|
|
override func transitionContentView() -> UIView! {
|
|
return self.contentView()
|
|
}
|
|
|
|
override func transitionViewContentRect() -> CGRect {
|
|
let contentView = self.transitionContentView()!
|
|
return contentView.convert(contentView.bounds, to: self.transitionView())
|
|
}
|
|
}
|
|
|
|
func legacyWebSearchItem(account: Account, result: ChatContextResult) -> LegacyWebSearchItem? {
|
|
var thumbnailDimensions: CGSize?
|
|
var thumbnailResource: TelegramMediaResource?
|
|
var imageResource: TelegramMediaResource?
|
|
var imageDimensions = CGSize()
|
|
var immediateThumbnailData: Data?
|
|
|
|
let thumbnailSignal: Signal<UIImage, NoError>
|
|
let originalSignal: Signal<UIImage, NoError>
|
|
|
|
switch result {
|
|
case let .externalReference(externalReference):
|
|
if let content = externalReference.content {
|
|
imageResource = content.resource
|
|
}
|
|
if let thumbnail = externalReference.thumbnail {
|
|
thumbnailResource = thumbnail.resource
|
|
thumbnailDimensions = thumbnail.dimensions?.cgSize
|
|
}
|
|
if let dimensions = externalReference.content?.dimensions {
|
|
imageDimensions = dimensions.cgSize
|
|
}
|
|
case let .internalReference(internalReference):
|
|
immediateThumbnailData = internalReference.image?.immediateThumbnailData
|
|
if let image = internalReference.image {
|
|
if let imageRepresentation = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 1000, height: 800)) {
|
|
imageDimensions = imageRepresentation.dimensions.cgSize
|
|
imageResource = imageRepresentation.resource
|
|
}
|
|
if let thumbnailRepresentation = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 200, height: 100)) {
|
|
thumbnailDimensions = thumbnailRepresentation.dimensions.cgSize
|
|
thumbnailResource = thumbnailRepresentation.resource
|
|
}
|
|
}
|
|
}
|
|
|
|
if let imageResource = imageResource {
|
|
let progressSignal = account.postbox.mediaBox.resourceStatus(imageResource)
|
|
|> map { status -> Float in
|
|
switch status {
|
|
case .Local:
|
|
return 1.0
|
|
case .Remote, .Paused:
|
|
return 0.027
|
|
case let .Fetching(_, progress):
|
|
return max(progress, 0.1)
|
|
}
|
|
}
|
|
|
|
var representations: [TelegramMediaImageRepresentation] = []
|
|
if let thumbnailResource = thumbnailResource, let thumbnailDimensions = thumbnailDimensions {
|
|
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
|
}
|
|
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
|
let tmpImage = TelegramMediaImage(imageId: EngineMedia.Id(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
|
thumbnailSignal = chatMessagePhotoDatas(postbox: account.postbox, userLocation: .other, photoReference: .standalone(media: tmpImage), autoFetchFullSize: false)
|
|
|> mapToSignal { value -> Signal<UIImage, NoError> in
|
|
let thumbnailData = value._0
|
|
if let data = thumbnailData, let image = UIImage(data: data) {
|
|
return .single(image)
|
|
} else {
|
|
return .complete()
|
|
}
|
|
}
|
|
originalSignal = chatMessagePhotoDatas(postbox: account.postbox, userLocation: .other, photoReference: .standalone(media: tmpImage), autoFetchFullSize: true)
|
|
|> mapToSignal { value -> Signal<UIImage, NoError> in
|
|
let thumbnailData = value._0
|
|
let fullSizeData = value._1
|
|
let fullSizeComplete = value._3
|
|
|
|
if fullSizeComplete, let data = fullSizeData, let image = UIImage(data: data) {
|
|
return .single(image)
|
|
} else if let data = thumbnailData, let image = UIImage(data: data) {
|
|
image.setDegraded(true)
|
|
return .single(image)
|
|
} else {
|
|
return .complete()
|
|
}
|
|
}
|
|
|
|
return LegacyWebSearchItem(result: result, thumbnailResource: thumbnailResource, imageResource: imageResource, dimensions: imageDimensions, thumbnailImage: thumbnailSignal, originalImage: originalSignal, progress: progressSignal)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private func galleryItems(account: Account, results: [ChatContextResult], current: ChatContextResult, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext) -> ([TGModernGalleryItem], TGModernGalleryItem?) {
|
|
var focusItem: TGModernGalleryItem?
|
|
var galleryItems: [TGModernGalleryItem] = []
|
|
for result in results {
|
|
if let item = legacyWebSearchItem(account: account, result: result) {
|
|
let galleryItem = LegacyWebSearchGalleryItem(item: item)
|
|
galleryItem.selectionContext = selectionContext
|
|
galleryItem.editingContext = editingContext
|
|
if result.id == current.id {
|
|
focusItem = galleryItem
|
|
}
|
|
galleryItems.append(galleryItem)
|
|
}
|
|
}
|
|
return (galleryItems, focusItem)
|
|
}
|
|
|
|
func presentLegacyWebSearchGallery(context: AccountContext, peer: EnginePeer?, threadTitle: String?, chatLocation: ChatLocation?, presentationData: PresentationData, results: [ChatContextResult], current: ChatContextResult, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, updateHiddenMedia: @escaping (String?) -> Void, initialLayout: ContainerViewLayout?, transitionHostView: @escaping () -> UIView?, transitionView: @escaping (ChatContextResult) -> UIView?, completed: @escaping (ChatContextResult) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, present: (ViewController, Any?) -> Void) {
|
|
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
|
|
legacyController.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
|
|
|
|
let recipientName: String?
|
|
if let threadTitle {
|
|
recipientName = threadTitle
|
|
} else {
|
|
if peer?.id == context.account.peerId {
|
|
recipientName = presentationData.strings.DialogList_SavedMessages
|
|
} else {
|
|
recipientName = peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
|
}
|
|
}
|
|
|
|
let paintStickersContext = LegacyPaintStickersContext(context: context)
|
|
paintStickersContext.captionPanelView = {
|
|
return getCaptionPanelView()
|
|
}
|
|
|
|
let controller = TGModernGalleryController(context: legacyController.context)!
|
|
controller.asyncTransitionIn = true
|
|
legacyController.bind(controller: controller)
|
|
|
|
let (items, focusItem) = galleryItems(account: context.account, results: results, current: current, selectionContext: selectionContext, editingContext: editingContext)
|
|
|
|
let currentAppConfiguration = context.currentAppConfiguration.with { $0 }
|
|
let model = TGMediaPickerGalleryModel(context: legacyController.context, items: items, focus: focusItem, selectionContext: selectionContext, editingContext: editingContext, hasCaptions: false, allowCaptionEntities: true, hasTimer: false, onlyCrop: false, inhibitDocumentCaptions: false, hasSelectionPanel: false, hasCamera: false, recipientName: recipientName, isScheduledMessages: false, canShowTelescope: currentAppConfiguration.sgWebSettings.global.canShowTelescope, canSendTelescope: currentAppConfiguration.sgWebSettings.user.canSendTelescope, hasCoverButton: false)!
|
|
model.stickersContext = paintStickersContext
|
|
controller.model = model
|
|
model.controller = controller
|
|
model.useGalleryImageAsEditableItemImage = true
|
|
model.storeOriginalImageForItem = { item, image in
|
|
editingContext.setOriginalImage(image, for: item, synchronous: false)
|
|
}
|
|
model.willFinishEditingItem = { item, adjustments, representation, hasChanges in
|
|
if hasChanges {
|
|
editingContext.setAdjustments(adjustments, for: item)
|
|
}
|
|
editingContext.setTemporaryRep(representation, for: item)
|
|
if let selectionContext = selectionContext, adjustments != nil, let item = item as? TGMediaSelectableItem {
|
|
selectionContext.setItem(item, selected: true)
|
|
}
|
|
}
|
|
model.didFinishEditingItem = { item, adjustments, result, thumbnail in
|
|
editingContext.setImage(result, thumbnailImage: thumbnail, for: item, synchronous: true)
|
|
}
|
|
model.saveItemCaption = { item, caption in
|
|
editingContext.setCaption(caption, for: item)
|
|
if let selectionContext = selectionContext, let caption = caption, caption.length > 0, let item = item as? TGMediaSelectableItem {
|
|
selectionContext.setItem(item, selected: true)
|
|
}
|
|
}
|
|
if let selectionContext = selectionContext {
|
|
model.interfaceView.updateSelectionInterface(selectionContext.count(), counterVisible: selectionContext.count() > 0, animated: false)
|
|
}
|
|
model.interfaceView.donePressed = { item in
|
|
if let item = item as? LegacyWebSearchGalleryItem {
|
|
controller.dismissWhenReady(animated: true)
|
|
completed(item.item.result)
|
|
}
|
|
}
|
|
controller.transitionHost = {
|
|
return transitionHostView()
|
|
}
|
|
var transitionedIn = false
|
|
controller.itemFocused = { item in
|
|
if let item = item as? LegacyWebSearchGalleryItem, transitionedIn {
|
|
updateHiddenMedia(item.item.result.id)
|
|
}
|
|
}
|
|
controller.beginTransitionIn = { item, _ in
|
|
if let item = item as? LegacyWebSearchGalleryItem {
|
|
return transitionView(item.item.result)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
controller.startedTransitionIn = {
|
|
transitionedIn = true
|
|
updateHiddenMedia(current.id)
|
|
}
|
|
controller.beginTransitionOut = { item, _ in
|
|
if let item = item as? LegacyWebSearchGalleryItem {
|
|
return transitionView(item.item.result)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
controller.completedTransitionOut = { [weak legacyController] in
|
|
updateHiddenMedia(nil)
|
|
legacyController?.dismiss()
|
|
}
|
|
present(legacyController, nil)
|
|
}
|
|
|
|
public func legacyEnqueueWebSearchMessages(_ selectionState: TGMediaSelectionContext, _ editingState: TGMediaEditingContext, enqueueChatContextResult: (ChatContextResult) -> Void, enqueueMediaMessages: ([Any]) -> Void)
|
|
{
|
|
var results: [ChatContextResult] = []
|
|
for item in selectionState.selectedItems() {
|
|
if let item = item as? LegacyWebSearchItem {
|
|
results.append(item.result)
|
|
}
|
|
}
|
|
|
|
if !results.isEmpty {
|
|
var signals: [Any] = []
|
|
for result in results {
|
|
let editableItem = LegacyWebSearchItem(result: result)
|
|
if let adjustments = editingState.adjustments(for: editableItem) {
|
|
let animated = adjustments.paintingData?.hasAnimation ?? false
|
|
if let imageSignal = editingState.imageSignal(for: editableItem) {
|
|
let signal = imageSignal.map { image -> Any in
|
|
if let image = image as? UIImage {
|
|
var dict: [AnyHashable: Any] = [
|
|
"type": "editedPhoto",
|
|
"image": image
|
|
]
|
|
|
|
if animated {
|
|
dict["isAnimation"] = true
|
|
if let photoEditorValues = adjustments as? PGPhotoEditorValues {
|
|
dict["adjustments"] = TGVideoEditAdjustments(photoEditorValues: photoEditorValues, preset: TGMediaVideoConversionPresetAnimation)
|
|
}
|
|
|
|
let filePath = NSTemporaryDirectory().appending("/gifvideo_\(arc4random()).jpg")
|
|
let data = image.jpegData(compressionQuality: 0.8)
|
|
if let data = data {
|
|
let _ = try? data.write(to: URL(fileURLWithPath: filePath), options: [])
|
|
}
|
|
dict["url"] = NSURL(fileURLWithPath: filePath)
|
|
|
|
if adjustments.cropApplied(forAvatar: false) || adjustments.hasPainting() || adjustments.toolsApplied() {
|
|
var paintingImage: UIImage? = adjustments.paintingData?.stillImage
|
|
if paintingImage == nil {
|
|
paintingImage = adjustments.paintingData?.image
|
|
}
|
|
|
|
let thumbnailImage = TGPhotoEditorVideoExtCrop(image, paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(image.size, CGSize(width: 512.0, height: 512.0)), adjustments.originalSize, true, true, true, false)
|
|
if let thumbnailImage = thumbnailImage {
|
|
dict["previewImage"] = thumbnailImage
|
|
}
|
|
}
|
|
}
|
|
|
|
return legacyAssetPickerItemGenerator()(dict, nil, nil, nil) as Any
|
|
} else {
|
|
return SSignal.complete()
|
|
}
|
|
}
|
|
signals.append(signal as Any)
|
|
}
|
|
} else {
|
|
enqueueChatContextResult(result)
|
|
}
|
|
}
|
|
|
|
if !signals.isEmpty {
|
|
enqueueMediaMessages(signals)
|
|
}
|
|
}
|
|
}
|