Various fixes

This commit is contained in:
Ilya Laktyushin 2024-03-25 01:09:45 +04:00
parent 75070e2aa7
commit b7c18980f6
12 changed files with 225 additions and 137 deletions

View File

@ -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 {

View File

@ -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) {

View File

@ -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

View File

@ -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"))

View File

@ -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);

View File

@ -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;
}

View File

@ -366,6 +366,7 @@ extension ImageARGB {
Int32(self.argbPlane.width),
Int32(self.argbPlane.height),
Int32(self.argbPlane.bytesPerRow),
false,
false
)
}

View File

@ -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? {

View File

@ -109,6 +109,7 @@ final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter {
width,
height,
bytesPerRow,
true,
true
)

View File

@ -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<PrepareStickerStatus, UploadStickerError>
if isVideo {
self.performSave(toStickerResource: file.resource)
resourceSignal = self.videoExportPromise.get()
|> castError(UploadStickerError.self)
|> filter { $0 != nil }
|> take(1)
|> mapToSignal { videoExport -> Signal<PrepareStickerStatus, UploadStickerError> in
guard let videoExport else {
return .complete()
}
return videoExport.status
|> castError(UploadStickerError.self)
|> mapToSignal { status -> Signal<PrepareStickerStatus, UploadStickerError> 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<UploadStickerStatus, UploadStickerError> 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<UploadStickerStatus, UploadStickerError> 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<SavedStickerResult, UploadStickerError> 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<CreateStickerSetStatus, UploadStickerError> in
return .fail(.generic)
}
|> mapToSignal { innerStatus in
if case .complete = innerStatus {
return resourceSignal
|> mapToSignal { result -> Signal<UploadStickerStatus, UploadStickerError> 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<UploadStickerStatus, UploadStickerError> 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<SavedStickerResult, UploadStickerError> 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<CreateStickerSetStatus, UploadStickerError> 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<Bool, UploadStickerError> 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<Bool, UploadStickerError> 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<MediaEditorVideoExport?>(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<MediaEditorVideoExport.Subject, NoError>
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) {

View File

@ -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)
}

View File

@ -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)