diff --git a/submodules/ContextUI/Sources/PeekControllerNode.swift b/submodules/ContextUI/Sources/PeekControllerNode.swift index 632d501ce1..4a28d0e110 100644 --- a/submodules/ContextUI/Sources/PeekControllerNode.swift +++ b/submodules/ContextUI/Sources/PeekControllerNode.swift @@ -266,23 +266,19 @@ final class PeekControllerNode: ViewControllerTracingNode { } func animateIn(from rect: CGRect) { - if let appeared = self.controller?.appeared { - appeared() - } else { - self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) - } - self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) self.blurView.layer.animateAlpha(from: 0.0, to: self.blurView.alpha, duration: 0.3) let offset = CGPoint(x: rect.midX - self.containerNode.position.x, y: rect.midY - self.containerNode.position.y) self.containerNode.layer.animateSpring(from: NSValue(cgPoint: offset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.4, initialVelocity: 0.0, damping: 110.0, additive: true) - if rect.width > 10.0 { + if let appeared = self.controller?.appeared { + appeared() let scale = rect.width / self.contentNode.frame.width self.containerNode.layer.animateSpring(from: scale as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4, initialVelocity: 0.0, damping: 110.0) } else { self.containerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4, initialVelocity: 0.0, damping: 110.0) + self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) } if let topAccessoryNode = self.topAccessoryNode { @@ -322,12 +318,8 @@ final class PeekControllerNode: ViewControllerTracingNode { outCompletion() completion() }) + if let _ = self.controller?.disappeared { - } else { - self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) - } - - if rect.width > 10.0 { let scale = rect.width / self.contentNode.frame.width self.containerNode.layer.animateScale(from: 1.0, to: scale, duration: 0.25, removeOnCompletion: false, completion: { _ in scaleCompleted = true @@ -338,6 +330,7 @@ final class PeekControllerNode: ViewControllerTracingNode { scaleCompleted = true outCompletion() }) + self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) } if !self.actionsStackNode.alpha.isZero { diff --git a/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m b/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m index 03114fdc99..81ccc78103 100755 --- a/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m +++ b/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m @@ -47,6 +47,9 @@ _codecContext->codec_id = AV_CODEC_ID_VP9; _codecContext->codec_type = AVMEDIA_TYPE_VIDEO; _codecContext->pix_fmt = AV_PIX_FMT_YUVA420P; + _codecContext->color_range = AVCOL_RANGE_MPEG; + _codecContext->color_primaries = AVCOL_PRI_BT709; + _codecContext->colorspace = AVCOL_SPC_BT709; _codecContext->width = width; _codecContext->height = height; _codecContext->time_base = (AVRational){1, framerate}; @@ -96,6 +99,9 @@ AVFrame *frameImpl = (AVFrame *)[frame impl]; frameImpl->pts = self.framePts; + frameImpl->color_range = AVCOL_RANGE_MPEG; + frameImpl->color_primaries = AVCOL_PRI_BT709; + frameImpl->colorspace = AVCOL_SPC_BT709; int sendRet = avcodec_send_frame(_codecContext, frameImpl); if (sendRet < 0) { diff --git a/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift b/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift index c338e765eb..2c30236c89 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift @@ -81,6 +81,8 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode { private var item: ItemListTextItem? + private var chevronImage: UIImage? + public var tag: ItemListItemTag? { return self.item?.tag } @@ -117,6 +119,8 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode { public func asyncLayout() -> (_ item: ItemListTextItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { let makeTitleLayout = TextNode.asyncLayout(self.textNode) + let currentChevronImage = self.chevronImage + let currentItem = self.item return { item, params, neighbors in let leftInset: CGFloat = 15.0 @@ -127,6 +131,12 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode { let largeTitleFont = Font.semibold(floor(item.presentationData.fontSize.itemListBaseFontSize)) let titleBoldFont = Font.semibold(item.presentationData.fontSize.itemListBaseHeaderFontSize) + var themeUpdated = false + var chevronImage = currentChevronImage + if currentItem?.presentationData.theme !== item.presentationData.theme { + themeUpdated = true + } + let attributedText: NSAttributedString switch item.text { case let .plain(text): @@ -134,9 +144,18 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode { case let .large(text): attributedText = NSAttributedString(string: text, font: largeTitleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor) case let .markdown(text): - attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.presentationData.theme.list.freeTextColor), bold: MarkdownAttributeSet(font: titleBoldFont, textColor: item.presentationData.theme.list.freeTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.presentationData.theme.list.itemAccentColor), linkAttribute: { contents in + let mutableAttributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.presentationData.theme.list.freeTextColor), bold: MarkdownAttributeSet(font: titleBoldFont, textColor: item.presentationData.theme.list.freeTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.presentationData.theme.list.itemAccentColor), linkAttribute: { contents in return (TelegramTextAttributes.URL, contents) - })) + })).mutableCopy() as! NSMutableAttributedString + if let _ = text.range(of: ">]"), let range = mutableAttributedText.string.range(of: ">") { + if themeUpdated || currentChevronImage == nil { + chevronImage = generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: item.presentationData.theme.list.itemAccentColor) + } + if let chevronImage { + mutableAttributedText.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: mutableAttributedText.string)) + } + } + attributedText = mutableAttributedText } let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset * 2.0 - params.leftInset - params.rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) @@ -158,6 +177,7 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode { return (layout, { [weak self] in if let strongSelf = self { strongSelf.item = item + strongSelf.chevronImage = chevronImage strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height)) strongSelf.activateArea.accessibilityLabel = attributedText.string diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index fc3ba23e44..3e4b28a8a3 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -1344,7 +1344,7 @@ private func monetizationEntries( var entries: [StatsEntry] = [] //TODO:localize - entries.append(.adsHeader(presentationData.theme, "Telegram shares 50% of the revenue from ads displayed in your channel. [Learn More]()")) + entries.append(.adsHeader(presentationData.theme, "Telegram shares 50% of the revenue from ads displayed in your channel. [Learn More >]()")) entries.append(.adsImpressionsTitle(presentationData.theme, "AD IMPRESSIONS (BY HOURS)")) if !stats.topHoursGraph.isEmpty { @@ -1361,7 +1361,7 @@ private func monetizationEntries( entries.append(.adsBalanceTitle(presentationData.theme, "AVAILABLE BALANCE")) entries.append(.adsBalance(presentationData.theme, data, false, diamond, state.monetizationAddress)) - entries.append(.adsBalanceInfo(presentationData.theme, "We will transfer your balance to the TON wallet address you specify. [Learn More]()")) + entries.append(.adsBalanceInfo(presentationData.theme, "We will transfer your balance to the TON wallet address you specify. [Learn More >]()")) entries.append(.adsTransactionsTitle(presentationData.theme, "TRANSACTION HISTORY")) diff --git a/submodules/TelegramUI/Components/AnimationCache/ImageDCT/PublicHeaders/ImageDCT/YuvConversion.h b/submodules/TelegramUI/Components/AnimationCache/ImageDCT/PublicHeaders/ImageDCT/YuvConversion.h index b3d2e2b03d..7710863f27 100644 --- a/submodules/TelegramUI/Components/AnimationCache/ImageDCT/PublicHeaders/ImageDCT/YuvConversion.h +++ b/submodules/TelegramUI/Components/AnimationCache/ImageDCT/PublicHeaders/ImageDCT/YuvConversion.h @@ -7,7 +7,7 @@ extern "C" { #endif -void splitRGBAIntoYUVAPlanes(uint8_t const *argb, uint8_t *outY, uint8_t *outU, uint8_t *outV, uint8_t *outA, int width, int height, int bytesPerRow, bool keepColorsOrder); +void splitRGBAIntoYUVAPlanes(uint8_t const *argb, uint8_t *outY, uint8_t *outU, uint8_t *outV, uint8_t *outA, int width, int height, int bytesPerRow, bool restrictedRange, bool keepColorsOrder); void combineYUVAPlanesIntoARGB(uint8_t *argb, uint8_t const *inY, uint8_t const *inU, uint8_t const *inV, uint8_t const *inA, int width, int height, int bytesPerRow); void scaleImagePlane(uint8_t *outPlane, int outWidth, int outHeight, int outBytesPerRow, uint8_t const *inPlane, int inWidth, int inHeight, int inBytesPerRow); diff --git a/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/YuvConversion.m b/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/YuvConversion.m index 3adaf579cf..b8558b556c 100644 --- a/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/YuvConversion.m +++ b/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/YuvConversion.m @@ -6,12 +6,15 @@ static uint8_t permuteMap[4] = { 3, 2, 1, 0 }; static uint8_t invertedPermuteMap[4] = { 3, 0, 1, 2 }; -void splitRGBAIntoYUVAPlanes(uint8_t const *argb, uint8_t *outY, uint8_t *outU, uint8_t *outV, uint8_t *outA, int width, int height, int bytesPerRow, bool keepColorsOrder) { +void splitRGBAIntoYUVAPlanes(uint8_t const *argb, uint8_t *outY, uint8_t *outU, uint8_t *outV, uint8_t *outA, int width, int height, int bytesPerRow, bool restrictedRange, bool keepColorsOrder) { static vImage_ARGBToYpCbCr info; + static vImage_ARGBToYpCbCr restrictedInfo; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ vImage_YpCbCrPixelRange pixelRange = (vImage_YpCbCrPixelRange){ 0, 128, 255, 255, 255, 1, 255, 0 }; + vImage_YpCbCrPixelRange restrictedPixelRange = (vImage_YpCbCrPixelRange){ 16, 128, 235, 240, 255, 0, 255, 0 }; vImageConvert_ARGBToYpCbCr_GenerateConversion(kvImage_ARGBToYpCbCrMatrix_ITU_R_709_2, &pixelRange, &info, kvImageARGB8888, kvImage420Yp8_Cb8_Cr8, 0); + vImageConvert_ARGBToYpCbCr_GenerateConversion(kvImage_ARGBToYpCbCrMatrix_ITU_R_709_2, &restrictedPixelRange, &restrictedInfo, kvImageARGB8888, kvImage420Yp8_Cb8_Cr8, 0); }); vImage_Error error = kvImageNoError; @@ -46,7 +49,7 @@ void splitRGBAIntoYUVAPlanes(uint8_t const *argb, uint8_t *outY, uint8_t *outU, destA.height = height; destA.rowBytes = width; - error = vImageConvert_ARGB8888To420Yp8_Cb8_Cr8(&src, &destYp, &destCb, &destCr, &info, keepColorsOrder ? invertedPermuteMap : permuteMap, kvImageDoNotTile); + error = vImageConvert_ARGB8888To420Yp8_Cb8_Cr8(&src, &destYp, &destCb, &destCr, restrictedRange ? &restrictedInfo : &info, keepColorsOrder ? invertedPermuteMap : permuteMap, kvImageDoNotTile); if (error != kvImageNoError) { return; } diff --git a/submodules/TelegramUI/Components/AnimationCache/Sources/ImageData.swift b/submodules/TelegramUI/Components/AnimationCache/Sources/ImageData.swift index 62234c883b..66c853510f 100644 --- a/submodules/TelegramUI/Components/AnimationCache/Sources/ImageData.swift +++ b/submodules/TelegramUI/Components/AnimationCache/Sources/ImageData.swift @@ -366,6 +366,7 @@ extension ImageARGB { Int32(self.argbPlane.width), Int32(self.argbPlane.height), Int32(self.argbPlane.bytesPerRow), + false, false ) } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift index 2a691e1ff7..569fed2ea2 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift @@ -152,7 +152,7 @@ final class MediaEditorComposer { if var compositedImage { let scale = self.outputDimensions.width / compositedImage.extent.width compositedImage = compositedImage.samplingLinear().transformed(by: CGAffineTransform(scaleX: scale, y: scale)) - + self.ciContext?.render(compositedImage, to: pixelBuffer) completion(pixelBuffer) } else { @@ -164,7 +164,6 @@ final class MediaEditorComposer { } completion(nil) } - private var isFirst = true private var cachedTexture: MTLTexture? func textureForImage(_ image: UIImage) -> MTLTexture? { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoFFMpegWriter.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoFFMpegWriter.swift index 0921a4703c..c9b703df92 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoFFMpegWriter.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoFFMpegWriter.swift @@ -109,6 +109,7 @@ final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter { width, height, bytesPerRow, + true, true ) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index faa08988d2..88dbe55ed6 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -4596,11 +4596,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate init( media: MediaResult?, - mediaAreas: [MediaArea], - caption: NSAttributedString, - options: MediaEditorResultPrivacy, - stickers: [TelegramMediaFile], - randomId: Int64 + mediaAreas: [MediaArea] = [], + caption: NSAttributedString = NSAttributedString(), + options: MediaEditorResultPrivacy = MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false), + stickers: [TelegramMediaFile] = [], + randomId: Int64 = 0 ) { self.media = media self.mediaAreas = mediaAreas @@ -5755,24 +5755,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let values = mediaEditor.values.withUpdatedQualityPreset(.sticker) makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: image, dimensions: storyDimensions, outputDimensions: CGSize(width: 512, height: 512), values: values, time: .zero, textScale: 2.0, completion: { [weak self] resultImage in if let self, let resultImage { -// let dimensions = CGSize(width: 512, height: 512) -// let scaledImage = generateImage(dimensions, contextGenerator: { size, context in -// context.clear(CGRect(origin: CGPoint(), size: size)) -// -// context.addPath(CGPath(roundedRect: CGRect(origin: .zero, size: size), cornerWidth: size.width / 8.0, cornerHeight: size.width / 8.0, transform: nil)) -// context.clip() -// -// let scaledSize = resultImage.size.aspectFilled(size) -// context.draw(resultImage.cgImage!, in: CGRect(origin: CGPoint(x: floor((size.width - scaledSize.width) / 2.0), y: floor((size.height - scaledSize.height) / 2.0)), size: scaledSize)) -// }, opaque: false, scale: 1.0)! - self.presentStickerPreview(image: resultImage) } }) } } - + private weak var resultController: PeekController? func presentStickerPreview(image: UIImage) { guard let mediaEditor = self.node.mediaEditor else { return @@ -5783,7 +5772,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate var isVideo = false if mediaEditor.resultIsVideo { isVideo = true - self.performSave(toStickerResource: resource) } else { Queue.concurrentDefaultQueue().async { if let data = try? WebP.convert(toWebP: image, quality: 97.0) { @@ -5804,26 +5792,29 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate guard let self else { return } - if self.videoExport != nil { - return - } - f(.default) - self.completion(MediaEditorScreen.Result( - media: .sticker(file: file), - mediaAreas: [], - caption: NSAttributedString(), - options: MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false), - stickers: [], - randomId: 0 - ), { [weak self] finished in - self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in - self?.dismiss() - Queue.mainQueue().justDispatch { - finished() - } + if isVideo { + self.uploadSticker(file, action: .send) + } else { + self.resultController?.disappeared = nil + self.completion(MediaEditorScreen.Result( + media: .sticker(file: file), + mediaAreas: [], + caption: NSAttributedString(), + options: MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false), + stickers: [], + randomId: 0 + ), { [weak self] finished in + self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in + self?.dismiss() + Queue.mainQueue().justDispatch { + finished() + } + }) }) - }) + } + + f(.default) }))) menuItems.append(.action(ContextMenuActionItem(text: presentationData.strings.Stickers_AddToFavorites, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Fave"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.default) @@ -5899,6 +5890,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } + Queue.mainQueue().justDispatch { + self.node.entitiesView.selectEntity(nil) + } + let peekController = PeekController( presentationData: presentationData, content: StickerPreviewPeekContent( @@ -5934,6 +5929,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.node.previewView.alpha = 1.0 } } + self.resultController = peekController self.present(peekController, in: .window(.root)) } @@ -5942,6 +5938,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate case createStickerPack(title: String) case addToStickerPack(pack: StickerPackReference, title: String) case upload + case send } private func presentCreateStickerPack(file: TelegramMediaFile, completion: @escaping () -> Void) { @@ -6014,69 +6011,118 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let context = self.context let dimensions = PixelDimensions(width: 512, height: 512) let mimeType = file.mimeType + let isVideo = file.mimeType == "video/webm" self.updateEditProgress(0.0, cancel: { [weak self] in self?.stickerUploadDisposable.set(nil) }) + enum PrepareStickerStatus { + case progress(Float) + case complete(TelegramMediaResource) + case failed + } + let resourceSignal: Signal + if isVideo { + self.performSave(toStickerResource: file.resource) + resourceSignal = self.videoExportPromise.get() + |> castError(UploadStickerError.self) + |> filter { $0 != nil } + |> take(1) + |> mapToSignal { videoExport -> Signal in + guard let videoExport else { + return .complete() + } + return videoExport.status + |> castError(UploadStickerError.self) + |> mapToSignal { status -> Signal in + switch status { + case .unknown: + return .single(.progress(0.0)) + case let .progress(progress): + return .single(.progress(progress)) + case .completed: + return .single(.complete(file.resource)) + |> delay(0.05, queue: Queue.mainQueue()) + case .failed: + return .single(.failed) + } + } + } + } else { + resourceSignal = .single(.complete(file.resource)) + } + let signal = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) |> castError(UploadStickerError.self) |> mapToSignal { peer -> Signal in guard let peer else { return .complete() } - return context.engine.stickers.uploadSticker(peer: peer._asPeer(), resource: file.resource, alt: "", dimensions: dimensions, mimeType: mimeType) - |> mapToSignal { status -> Signal in - switch status { - case .progress: - return .single(status) - case let .complete(resource, _): - let file = stickerFile(resource: resource, size: file.size ?? 0, dimensions: dimensions, isVideo: file.mimeType == "video/webm") - switch action { - case .addToFavorites: - return context.engine.stickers.toggleStickerSaved(file: file, saved: true) - |> `catch` { _ -> Signal in - return .fail(.generic) - } - |> map { _ in - return status - } - case let .createStickerPack(title): - let sticker = ImportSticker( - resource: resource, - emojis: ["😀😂"], - dimensions: dimensions, - mimeType: mimeType, - keywords: "" - ) - return context.engine.stickers.createStickerSet(title: title, shortName: "", stickers: [sticker], thumbnail: nil, type: .stickers(content: .image), software: nil) - |> `catch` { _ -> Signal in - return .fail(.generic) - } - |> mapToSignal { innerStatus in - if case .complete = innerStatus { + return resourceSignal + |> mapToSignal { result -> Signal in + switch result { + case .failed: + return .fail(.generic) + case let .progress(progress): + return .single(.progress(progress * 0.5)) + case let .complete(resource): + return context.engine.stickers.uploadSticker(peer: peer._asPeer(), resource: resource, alt: "", dimensions: dimensions, mimeType: mimeType) + |> mapToSignal { status -> Signal in + switch status { + case let .progress(progress): + return .single(.progress(isVideo ? 0.5 + progress * 0.5 : progress)) + case let .complete(resource, _): + let file = stickerFile(resource: resource, size: file.size ?? 0, dimensions: dimensions, isVideo: isVideo) + switch action { + case .send: + return .single(status) + case .addToFavorites: + return context.engine.stickers.toggleStickerSaved(file: file, saved: true) + |> `catch` { _ -> Signal in + return .fail(.generic) + } + |> map { _ in + return status + } + case let .createStickerPack(title): + let sticker = ImportSticker( + resource: resource, + emojis: ["😀😂"], + dimensions: dimensions, + mimeType: mimeType, + keywords: "" + ) + return context.engine.stickers.createStickerSet(title: title, shortName: "", stickers: [sticker], thumbnail: nil, type: .stickers(content: .image), software: nil) + |> `catch` { _ -> Signal in + return .fail(.generic) + } + |> mapToSignal { innerStatus in + if case .complete = innerStatus { + return .single(status) + } else { + return .complete() + } + } + case let .addToStickerPack(pack, _): + let sticker = ImportSticker( + resource: resource, + emojis: ["😀😂"], + dimensions: dimensions, + mimeType: mimeType, + keywords: "" + ) + return context.engine.stickers.addStickerToStickerSet(packReference: pack, sticker: sticker) + |> `catch` { _ -> Signal in + return .fail(.generic) + } + |> map { _ in + return status + } + case .upload: return .single(status) - } else { - return .complete() } } - case let .addToStickerPack(pack, _): - let sticker = ImportSticker( - resource: resource, - emojis: ["😀😂"], - dimensions: dimensions, - mimeType: mimeType, - keywords: "" - ) - return context.engine.stickers.addStickerToStickerSet(packReference: pack, sticker: sticker) - |> `catch` { _ -> Signal in - return .fail(.generic) - } - |> map { _ in - return status - } - case .upload: - return .single(status) } } } @@ -6090,26 +6136,23 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate switch status { case let .progress(progress): self.updateEditProgress(progress, cancel: { [weak self] in + self?.videoExport?.cancel() + self?.videoExport = nil + self?.exportDisposable.set(nil) self?.stickerUploadDisposable.set(nil) }) case let .complete(resource, _): let navigationController = self.navigationController as? NavigationController let result: MediaEditorScreen.Result - if case .upload = action { - let file = stickerFile(resource: resource, size: resource.size ?? 0, dimensions: dimensions, isVideo: file.mimeType == "video/webm") - result = MediaEditorScreen.Result( - media: .sticker(file: file), - mediaAreas: [], - caption: NSAttributedString(), - options: MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false), - stickers: [], - randomId: 0 - ) - } else { + switch action { + case .upload, .send: + let file = stickerFile(resource: resource, size: resource.size ?? 0, dimensions: dimensions, isVideo: isVideo) + result = MediaEditorScreen.Result(media: .sticker(file: file)) + default: result = MediaEditorScreen.Result() } - + self.completion(result, { [weak self] finished in self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in guard let self else { @@ -6147,7 +6190,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate })) } - private var videoExport: MediaEditorVideoExport? + private var videoExport: MediaEditorVideoExport? { + didSet { + self.videoExportPromise.set(.single(self.videoExport)) + } + } + private var videoExportPromise = Promise(nil) private var exportDisposable = MetaDisposable() fileprivate var isSavingAvailable = false @@ -6178,7 +6226,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities) let isSticker = toStickerResource != nil - if !isSticker { self.previousSavedValues = mediaEditor.values self.isSavingAvailable = false @@ -6207,8 +6254,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } if mediaEditor.resultIsVideo { - mediaEditor.maybePauseVideo() - self.node.entitiesView.pause() + if !isSticker { + mediaEditor.maybePauseVideo() + self.node.entitiesView.pause() + } let exportSubject: Signal switch subject { @@ -6293,8 +6342,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate switch status { case .completed: self.videoExport = nil - if let toStickerResource, let data = try? Data(contentsOf: URL(fileURLWithPath: outputPath)) { - self.context.account.postbox.mediaBox.storeResourceData(toStickerResource.id, data: data) + if let toStickerResource { + if let data = try? Data(contentsOf: URL(fileURLWithPath: outputPath)) { + self.context.account.postbox.mediaBox.storeResourceData(toStickerResource.id, data: data, synchronous: true) + } } else { saveToPhotos(outputPath, true) self.node.presentSaveTooltip() @@ -6337,16 +6388,18 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } fileprivate func cancelVideoExport() { - if let videoExport = self.videoExport { - self.previousSavedValues = nil - - videoExport.cancel() - self.videoExport = nil - self.exportDisposable.set(nil) - - self.node.mediaEditor?.play() - self.node.entitiesView.play() + guard let videoExport = self.videoExport else { + return } + videoExport.cancel() + + self.videoExport = nil + self.exportDisposable.set(nil) + + self.previousSavedValues = nil + + self.node.mediaEditor?.play() + self.node.entitiesView.play() } public func updateEditProgress(_ progress: Float, cancel: @escaping () -> Void) { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCommentItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCommentItem.swift index 6f5f7ad96b..59374afed5 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCommentItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCommentItem.swift @@ -80,10 +80,12 @@ private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode { let textFont = Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize) let textColor = presentationData.theme.list.freeTextColor - let attributedText = parseMarkdownIntoAttributedString(item.text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: presentationData.theme.list.itemAccentColor), linkAttribute: { contents in + var text = item.text + text = text.replacingOccurrences(of: " >]", with: "\u{00A0}>]") + let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: presentationData.theme.list.itemAccentColor), linkAttribute: { contents in return (TelegramTextAttributes.URL, contents) })).mutableCopy() as! NSMutableAttributedString - if let range = attributedText.string.range(of: ">") { + if let _ = item.text.range(of: ">]"), let range = attributedText.string.range(of: ">") { if themeUpdated || self.chevronImage == nil { self.chevronImage = generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: presentationData.theme.list.itemAccentColor) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 09b05e1cf4..b7133f907c 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -2678,8 +2678,18 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }, updateIsEditingBirthdate: { [weak self] value in if let self { - if value, let data = self.data?.cachedData as? CachedUserData, data.birthday == nil { - self.state = self.state.withUpdatingBirthDate(TelegramBirthday(day: 1, month: 1, year: nil)) + if value { + if let data = self.data?.cachedData as? CachedUserData { + if data.birthday == nil { + self.state = self.state.withUpdatingBirthDate(TelegramBirthday(day: 1, month: 1, year: nil)) + } else { + self.state = self.state.withUpdatingBirthDate(nil) + } + } + } else { + if self.state.updatingBirthDate != .some(nil) { + self.state = self.state.withUpdatingBirthDate(nil) + } } self.state = self.state.withIsEditingBirthDate(value)