mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
75070e2aa7
commit
b7c18980f6
@ -266,23 +266,19 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func animateIn(from rect: CGRect) {
|
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.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)
|
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)
|
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)
|
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
|
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)
|
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 {
|
} 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.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 {
|
if let topAccessoryNode = self.topAccessoryNode {
|
||||||
@ -322,12 +318,8 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
|||||||
outCompletion()
|
outCompletion()
|
||||||
completion()
|
completion()
|
||||||
})
|
})
|
||||||
|
|
||||||
if let _ = self.controller?.disappeared {
|
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
|
let scale = rect.width / self.contentNode.frame.width
|
||||||
self.containerNode.layer.animateScale(from: 1.0, to: scale, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
self.containerNode.layer.animateScale(from: 1.0, to: scale, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||||
scaleCompleted = true
|
scaleCompleted = true
|
||||||
@ -338,6 +330,7 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
|||||||
scaleCompleted = true
|
scaleCompleted = true
|
||||||
outCompletion()
|
outCompletion()
|
||||||
})
|
})
|
||||||
|
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.actionsStackNode.alpha.isZero {
|
if !self.actionsStackNode.alpha.isZero {
|
||||||
|
@ -47,6 +47,9 @@
|
|||||||
_codecContext->codec_id = AV_CODEC_ID_VP9;
|
_codecContext->codec_id = AV_CODEC_ID_VP9;
|
||||||
_codecContext->codec_type = AVMEDIA_TYPE_VIDEO;
|
_codecContext->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||||
_codecContext->pix_fmt = AV_PIX_FMT_YUVA420P;
|
_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->width = width;
|
||||||
_codecContext->height = height;
|
_codecContext->height = height;
|
||||||
_codecContext->time_base = (AVRational){1, framerate};
|
_codecContext->time_base = (AVRational){1, framerate};
|
||||||
@ -96,6 +99,9 @@
|
|||||||
AVFrame *frameImpl = (AVFrame *)[frame impl];
|
AVFrame *frameImpl = (AVFrame *)[frame impl];
|
||||||
|
|
||||||
frameImpl->pts = self.framePts;
|
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);
|
int sendRet = avcodec_send_frame(_codecContext, frameImpl);
|
||||||
if (sendRet < 0) {
|
if (sendRet < 0) {
|
||||||
|
@ -81,6 +81,8 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
|
|
||||||
private var item: ItemListTextItem?
|
private var item: ItemListTextItem?
|
||||||
|
|
||||||
|
private var chevronImage: UIImage?
|
||||||
|
|
||||||
public var tag: ItemListItemTag? {
|
public var tag: ItemListItemTag? {
|
||||||
return self.item?.tag
|
return self.item?.tag
|
||||||
}
|
}
|
||||||
@ -117,6 +119,8 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
|
|
||||||
public func asyncLayout() -> (_ item: ItemListTextItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
public func asyncLayout() -> (_ item: ItemListTextItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||||
let makeTitleLayout = TextNode.asyncLayout(self.textNode)
|
let makeTitleLayout = TextNode.asyncLayout(self.textNode)
|
||||||
|
let currentChevronImage = self.chevronImage
|
||||||
|
let currentItem = self.item
|
||||||
|
|
||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
let leftInset: CGFloat = 15.0
|
let leftInset: CGFloat = 15.0
|
||||||
@ -127,6 +131,12 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
let largeTitleFont = Font.semibold(floor(item.presentationData.fontSize.itemListBaseFontSize))
|
let largeTitleFont = Font.semibold(floor(item.presentationData.fontSize.itemListBaseFontSize))
|
||||||
let titleBoldFont = Font.semibold(item.presentationData.fontSize.itemListBaseHeaderFontSize)
|
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
|
let attributedText: NSAttributedString
|
||||||
switch item.text {
|
switch item.text {
|
||||||
case let .plain(text):
|
case let .plain(text):
|
||||||
@ -134,9 +144,18 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
case let .large(text):
|
case let .large(text):
|
||||||
attributedText = NSAttributedString(string: text, font: largeTitleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
attributedText = NSAttributedString(string: text, font: largeTitleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||||
case let .markdown(text):
|
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)
|
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()))
|
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
|
return (layout, { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.item = item
|
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.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
|
strongSelf.activateArea.accessibilityLabel = attributedText.string
|
||||||
|
@ -1344,7 +1344,7 @@ private func monetizationEntries(
|
|||||||
|
|
||||||
var entries: [StatsEntry] = []
|
var entries: [StatsEntry] = []
|
||||||
//TODO:localize
|
//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)"))
|
entries.append(.adsImpressionsTitle(presentationData.theme, "AD IMPRESSIONS (BY HOURS)"))
|
||||||
if !stats.topHoursGraph.isEmpty {
|
if !stats.topHoursGraph.isEmpty {
|
||||||
@ -1361,7 +1361,7 @@ private func monetizationEntries(
|
|||||||
|
|
||||||
entries.append(.adsBalanceTitle(presentationData.theme, "AVAILABLE BALANCE"))
|
entries.append(.adsBalanceTitle(presentationData.theme, "AVAILABLE BALANCE"))
|
||||||
entries.append(.adsBalance(presentationData.theme, data, false, diamond, state.monetizationAddress))
|
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"))
|
entries.append(.adsTransactionsTitle(presentationData.theme, "TRANSACTION HISTORY"))
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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 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);
|
void scaleImagePlane(uint8_t *outPlane, int outWidth, int outHeight, int outBytesPerRow, uint8_t const *inPlane, int inWidth, int inHeight, int inBytesPerRow);
|
||||||
|
|
||||||
|
@ -6,12 +6,15 @@
|
|||||||
static uint8_t permuteMap[4] = { 3, 2, 1, 0 };
|
static uint8_t permuteMap[4] = { 3, 2, 1, 0 };
|
||||||
static uint8_t invertedPermuteMap[4] = { 3, 0, 1, 2 };
|
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 info;
|
||||||
|
static vImage_ARGBToYpCbCr restrictedInfo;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
vImage_YpCbCrPixelRange pixelRange = (vImage_YpCbCrPixelRange){ 0, 128, 255, 255, 255, 1, 255, 0 };
|
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, &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;
|
vImage_Error error = kvImageNoError;
|
||||||
@ -46,7 +49,7 @@ void splitRGBAIntoYUVAPlanes(uint8_t const *argb, uint8_t *outY, uint8_t *outU,
|
|||||||
destA.height = height;
|
destA.height = height;
|
||||||
destA.rowBytes = width;
|
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) {
|
if (error != kvImageNoError) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -366,6 +366,7 @@ extension ImageARGB {
|
|||||||
Int32(self.argbPlane.width),
|
Int32(self.argbPlane.width),
|
||||||
Int32(self.argbPlane.height),
|
Int32(self.argbPlane.height),
|
||||||
Int32(self.argbPlane.bytesPerRow),
|
Int32(self.argbPlane.bytesPerRow),
|
||||||
|
false,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ final class MediaEditorComposer {
|
|||||||
if var compositedImage {
|
if var compositedImage {
|
||||||
let scale = self.outputDimensions.width / compositedImage.extent.width
|
let scale = self.outputDimensions.width / compositedImage.extent.width
|
||||||
compositedImage = compositedImage.samplingLinear().transformed(by: CGAffineTransform(scaleX: scale, y: scale))
|
compositedImage = compositedImage.samplingLinear().transformed(by: CGAffineTransform(scaleX: scale, y: scale))
|
||||||
|
|
||||||
self.ciContext?.render(compositedImage, to: pixelBuffer)
|
self.ciContext?.render(compositedImage, to: pixelBuffer)
|
||||||
completion(pixelBuffer)
|
completion(pixelBuffer)
|
||||||
} else {
|
} else {
|
||||||
@ -164,7 +164,6 @@ final class MediaEditorComposer {
|
|||||||
}
|
}
|
||||||
completion(nil)
|
completion(nil)
|
||||||
}
|
}
|
||||||
private var isFirst = true
|
|
||||||
|
|
||||||
private var cachedTexture: MTLTexture?
|
private var cachedTexture: MTLTexture?
|
||||||
func textureForImage(_ image: UIImage) -> MTLTexture? {
|
func textureForImage(_ image: UIImage) -> MTLTexture? {
|
||||||
|
@ -109,6 +109,7 @@ final class MediaEditorVideoFFMpegWriter: MediaEditorVideoExportWriter {
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
bytesPerRow,
|
bytesPerRow,
|
||||||
|
true,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4596,11 +4596,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
init(
|
init(
|
||||||
media: MediaResult?,
|
media: MediaResult?,
|
||||||
mediaAreas: [MediaArea],
|
mediaAreas: [MediaArea] = [],
|
||||||
caption: NSAttributedString,
|
caption: NSAttributedString = NSAttributedString(),
|
||||||
options: MediaEditorResultPrivacy,
|
options: MediaEditorResultPrivacy = MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false),
|
||||||
stickers: [TelegramMediaFile],
|
stickers: [TelegramMediaFile] = [],
|
||||||
randomId: Int64
|
randomId: Int64 = 0
|
||||||
) {
|
) {
|
||||||
self.media = media
|
self.media = media
|
||||||
self.mediaAreas = mediaAreas
|
self.mediaAreas = mediaAreas
|
||||||
@ -5755,24 +5755,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
let values = mediaEditor.values.withUpdatedQualityPreset(.sticker)
|
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
|
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 {
|
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)
|
self.presentStickerPreview(image: resultImage)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private weak var resultController: PeekController?
|
||||||
func presentStickerPreview(image: UIImage) {
|
func presentStickerPreview(image: UIImage) {
|
||||||
guard let mediaEditor = self.node.mediaEditor else {
|
guard let mediaEditor = self.node.mediaEditor else {
|
||||||
return
|
return
|
||||||
@ -5783,7 +5772,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
var isVideo = false
|
var isVideo = false
|
||||||
if mediaEditor.resultIsVideo {
|
if mediaEditor.resultIsVideo {
|
||||||
isVideo = true
|
isVideo = true
|
||||||
self.performSave(toStickerResource: resource)
|
|
||||||
} else {
|
} else {
|
||||||
Queue.concurrentDefaultQueue().async {
|
Queue.concurrentDefaultQueue().async {
|
||||||
if let data = try? WebP.convert(toWebP: image, quality: 97.0) {
|
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 {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if self.videoExport != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f(.default)
|
|
||||||
|
|
||||||
self.completion(MediaEditorScreen.Result(
|
if isVideo {
|
||||||
media: .sticker(file: file),
|
self.uploadSticker(file, action: .send)
|
||||||
mediaAreas: [],
|
} else {
|
||||||
caption: NSAttributedString(),
|
self.resultController?.disappeared = nil
|
||||||
options: MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false),
|
self.completion(MediaEditorScreen.Result(
|
||||||
stickers: [],
|
media: .sticker(file: file),
|
||||||
randomId: 0
|
mediaAreas: [],
|
||||||
), { [weak self] finished in
|
caption: NSAttributedString(),
|
||||||
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
options: MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false),
|
||||||
self?.dismiss()
|
stickers: [],
|
||||||
Queue.mainQueue().justDispatch {
|
randomId: 0
|
||||||
finished()
|
), { [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
|
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)
|
f(.default)
|
||||||
@ -5899,6 +5890,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Queue.mainQueue().justDispatch {
|
||||||
|
self.node.entitiesView.selectEntity(nil)
|
||||||
|
}
|
||||||
|
|
||||||
let peekController = PeekController(
|
let peekController = PeekController(
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
content: StickerPreviewPeekContent(
|
content: StickerPreviewPeekContent(
|
||||||
@ -5934,6 +5929,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
self.node.previewView.alpha = 1.0
|
self.node.previewView.alpha = 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.resultController = peekController
|
||||||
self.present(peekController, in: .window(.root))
|
self.present(peekController, in: .window(.root))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5942,6 +5938,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
case createStickerPack(title: String)
|
case createStickerPack(title: String)
|
||||||
case addToStickerPack(pack: StickerPackReference, title: String)
|
case addToStickerPack(pack: StickerPackReference, title: String)
|
||||||
case upload
|
case upload
|
||||||
|
case send
|
||||||
}
|
}
|
||||||
|
|
||||||
private func presentCreateStickerPack(file: TelegramMediaFile, completion: @escaping () -> Void) {
|
private func presentCreateStickerPack(file: TelegramMediaFile, completion: @escaping () -> Void) {
|
||||||
@ -6014,69 +6011,118 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
let context = self.context
|
let context = self.context
|
||||||
let dimensions = PixelDimensions(width: 512, height: 512)
|
let dimensions = PixelDimensions(width: 512, height: 512)
|
||||||
let mimeType = file.mimeType
|
let mimeType = file.mimeType
|
||||||
|
let isVideo = file.mimeType == "video/webm"
|
||||||
|
|
||||||
self.updateEditProgress(0.0, cancel: { [weak self] in
|
self.updateEditProgress(0.0, cancel: { [weak self] in
|
||||||
self?.stickerUploadDisposable.set(nil)
|
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))
|
let signal = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||||
|> castError(UploadStickerError.self)
|
|> castError(UploadStickerError.self)
|
||||||
|> mapToSignal { peer -> Signal<UploadStickerStatus, UploadStickerError> in
|
|> mapToSignal { peer -> Signal<UploadStickerStatus, UploadStickerError> in
|
||||||
guard let peer else {
|
guard let peer else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return context.engine.stickers.uploadSticker(peer: peer._asPeer(), resource: file.resource, alt: "", dimensions: dimensions, mimeType: mimeType)
|
return resourceSignal
|
||||||
|> mapToSignal { status -> Signal<UploadStickerStatus, UploadStickerError> in
|
|> mapToSignal { result -> Signal<UploadStickerStatus, UploadStickerError> in
|
||||||
switch status {
|
switch result {
|
||||||
case .progress:
|
case .failed:
|
||||||
return .single(status)
|
return .fail(.generic)
|
||||||
case let .complete(resource, _):
|
case let .progress(progress):
|
||||||
let file = stickerFile(resource: resource, size: file.size ?? 0, dimensions: dimensions, isVideo: file.mimeType == "video/webm")
|
return .single(.progress(progress * 0.5))
|
||||||
switch action {
|
case let .complete(resource):
|
||||||
case .addToFavorites:
|
return context.engine.stickers.uploadSticker(peer: peer._asPeer(), resource: resource, alt: "", dimensions: dimensions, mimeType: mimeType)
|
||||||
return context.engine.stickers.toggleStickerSaved(file: file, saved: true)
|
|> mapToSignal { status -> Signal<UploadStickerStatus, UploadStickerError> in
|
||||||
|> `catch` { _ -> Signal<SavedStickerResult, UploadStickerError> in
|
switch status {
|
||||||
return .fail(.generic)
|
case let .progress(progress):
|
||||||
}
|
return .single(.progress(isVideo ? 0.5 + progress * 0.5 : progress))
|
||||||
|> map { _ in
|
case let .complete(resource, _):
|
||||||
return status
|
let file = stickerFile(resource: resource, size: file.size ?? 0, dimensions: dimensions, isVideo: isVideo)
|
||||||
}
|
switch action {
|
||||||
case let .createStickerPack(title):
|
case .send:
|
||||||
let sticker = ImportSticker(
|
return .single(status)
|
||||||
resource: resource,
|
case .addToFavorites:
|
||||||
emojis: ["😀😂"],
|
return context.engine.stickers.toggleStickerSaved(file: file, saved: true)
|
||||||
dimensions: dimensions,
|
|> `catch` { _ -> Signal<SavedStickerResult, UploadStickerError> in
|
||||||
mimeType: mimeType,
|
return .fail(.generic)
|
||||||
keywords: ""
|
}
|
||||||
)
|
|> map { _ in
|
||||||
return context.engine.stickers.createStickerSet(title: title, shortName: "", stickers: [sticker], thumbnail: nil, type: .stickers(content: .image), software: nil)
|
return status
|
||||||
|> `catch` { _ -> Signal<CreateStickerSetStatus, UploadStickerError> in
|
}
|
||||||
return .fail(.generic)
|
case let .createStickerPack(title):
|
||||||
}
|
let sticker = ImportSticker(
|
||||||
|> mapToSignal { innerStatus in
|
resource: resource,
|
||||||
if case .complete = innerStatus {
|
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)
|
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 {
|
switch status {
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
self.updateEditProgress(progress, cancel: { [weak self] in
|
self.updateEditProgress(progress, cancel: { [weak self] in
|
||||||
|
self?.videoExport?.cancel()
|
||||||
|
self?.videoExport = nil
|
||||||
|
self?.exportDisposable.set(nil)
|
||||||
self?.stickerUploadDisposable.set(nil)
|
self?.stickerUploadDisposable.set(nil)
|
||||||
})
|
})
|
||||||
case let .complete(resource, _):
|
case let .complete(resource, _):
|
||||||
let navigationController = self.navigationController as? NavigationController
|
let navigationController = self.navigationController as? NavigationController
|
||||||
|
|
||||||
let result: MediaEditorScreen.Result
|
let result: MediaEditorScreen.Result
|
||||||
if case .upload = action {
|
switch action {
|
||||||
let file = stickerFile(resource: resource, size: resource.size ?? 0, dimensions: dimensions, isVideo: file.mimeType == "video/webm")
|
case .upload, .send:
|
||||||
result = MediaEditorScreen.Result(
|
let file = stickerFile(resource: resource, size: resource.size ?? 0, dimensions: dimensions, isVideo: isVideo)
|
||||||
media: .sticker(file: file),
|
result = MediaEditorScreen.Result(media: .sticker(file: file))
|
||||||
mediaAreas: [],
|
default:
|
||||||
caption: NSAttributedString(),
|
|
||||||
options: MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false),
|
|
||||||
stickers: [],
|
|
||||||
randomId: 0
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
result = MediaEditorScreen.Result()
|
result = MediaEditorScreen.Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.completion(result, { [weak self] finished in
|
self.completion(result, { [weak self] finished in
|
||||||
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
||||||
guard let self else {
|
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()
|
private var exportDisposable = MetaDisposable()
|
||||||
|
|
||||||
fileprivate var isSavingAvailable = false
|
fileprivate var isSavingAvailable = false
|
||||||
@ -6178,7 +6226,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities)
|
mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities)
|
||||||
|
|
||||||
let isSticker = toStickerResource != nil
|
let isSticker = toStickerResource != nil
|
||||||
|
|
||||||
if !isSticker {
|
if !isSticker {
|
||||||
self.previousSavedValues = mediaEditor.values
|
self.previousSavedValues = mediaEditor.values
|
||||||
self.isSavingAvailable = false
|
self.isSavingAvailable = false
|
||||||
@ -6207,8 +6254,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mediaEditor.resultIsVideo {
|
if mediaEditor.resultIsVideo {
|
||||||
mediaEditor.maybePauseVideo()
|
if !isSticker {
|
||||||
self.node.entitiesView.pause()
|
mediaEditor.maybePauseVideo()
|
||||||
|
self.node.entitiesView.pause()
|
||||||
|
}
|
||||||
|
|
||||||
let exportSubject: Signal<MediaEditorVideoExport.Subject, NoError>
|
let exportSubject: Signal<MediaEditorVideoExport.Subject, NoError>
|
||||||
switch subject {
|
switch subject {
|
||||||
@ -6293,8 +6342,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
switch status {
|
switch status {
|
||||||
case .completed:
|
case .completed:
|
||||||
self.videoExport = nil
|
self.videoExport = nil
|
||||||
if let toStickerResource, let data = try? Data(contentsOf: URL(fileURLWithPath: outputPath)) {
|
if let toStickerResource {
|
||||||
self.context.account.postbox.mediaBox.storeResourceData(toStickerResource.id, data: data)
|
if let data = try? Data(contentsOf: URL(fileURLWithPath: outputPath)) {
|
||||||
|
self.context.account.postbox.mediaBox.storeResourceData(toStickerResource.id, data: data, synchronous: true)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
saveToPhotos(outputPath, true)
|
saveToPhotos(outputPath, true)
|
||||||
self.node.presentSaveTooltip()
|
self.node.presentSaveTooltip()
|
||||||
@ -6337,16 +6388,18 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func cancelVideoExport() {
|
fileprivate func cancelVideoExport() {
|
||||||
if let videoExport = self.videoExport {
|
guard let videoExport = self.videoExport else {
|
||||||
self.previousSavedValues = nil
|
return
|
||||||
|
|
||||||
videoExport.cancel()
|
|
||||||
self.videoExport = nil
|
|
||||||
self.exportDisposable.set(nil)
|
|
||||||
|
|
||||||
self.node.mediaEditor?.play()
|
|
||||||
self.node.entitiesView.play()
|
|
||||||
}
|
}
|
||||||
|
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) {
|
public func updateEditProgress(_ progress: Float, cancel: @escaping () -> Void) {
|
||||||
|
@ -80,10 +80,12 @@ private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode {
|
|||||||
let textFont = Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize)
|
let textFont = Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize)
|
||||||
let textColor = presentationData.theme.list.freeTextColor
|
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)
|
return (TelegramTextAttributes.URL, contents)
|
||||||
})).mutableCopy() as! NSMutableAttributedString
|
})).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 {
|
if themeUpdated || self.chevronImage == nil {
|
||||||
self.chevronImage = generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: presentationData.theme.list.itemAccentColor)
|
self.chevronImage = generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: presentationData.theme.list.itemAccentColor)
|
||||||
}
|
}
|
||||||
|
@ -2678,8 +2678,18 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
},
|
},
|
||||||
updateIsEditingBirthdate: { [weak self] value in
|
updateIsEditingBirthdate: { [weak self] value in
|
||||||
if let self {
|
if let self {
|
||||||
if value, let data = self.data?.cachedData as? CachedUserData, data.birthday == nil {
|
if value {
|
||||||
self.state = self.state.withUpdatingBirthDate(TelegramBirthday(day: 1, month: 1, year: nil))
|
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)
|
self.state = self.state.withIsEditingBirthDate(value)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user