Various fixes

This commit is contained in:
Ilya Laktyushin 2023-07-25 22:37:45 +02:00
parent f4fc72b762
commit ab6b0140cd
17 changed files with 379 additions and 77 deletions

View File

@ -9728,3 +9728,6 @@ Sorry for the inconvenience.";
"Story.Editor.TooltipPremiumCaptionEntities" = "Subscribe to [Telegram Premium]() to add links and formatting in captions to your stories.";
"Story.Context.TooltipPremiumSaveStories" = "Subscribe to [Telegram Premium]() to save other people's unprotected stories to your Gallery.";
"Story.Privacy.GroupTooLarge" = "Group Too Large";
"Story.Privacy.GroupParticipantsLimit" = "You can select groups that are up to 200 members.";

View File

@ -651,7 +651,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
if let selectionView = entityView.makeSelectionView() {
selectionView.tapped = { [weak self, weak entityView] in
if let self, let entityView = entityView {
if let self, let entityView {
let entityViews = self.subviews.filter { $0 is DrawingEntityView }
self.requestedMenuForEntityView(entityView, entityViews.last === entityView)
}

View File

@ -2978,8 +2978,6 @@ public final class DrawingToolsInteraction {
private var isActive = false
private var validLayout: ContainerViewLayout?
private let startTimestamp = CACurrentMediaTime()
public init(
context: AccountContext,
drawingView: DrawingView,

View File

@ -58,13 +58,9 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
self.textView.delegate = self
self.addSubview(self.textView)
self.emojiViewProvider = { [weak self] emoji in
guard let strongSelf = self else {
return UIView()
}
self.emojiViewProvider = { emoji in
let pointSize: CGFloat = 128.0
return EmojiTextAttachmentView(context: context, userLocation: .other, emoji: emoji, file: emoji.file, cache: strongSelf.context.animationCache, renderer: strongSelf.context.animationRenderer, placeholderColor: UIColor.white.withAlphaComponent(0.12), pointSize: CGSize(width: pointSize, height: pointSize))
return EmojiTextAttachmentView(context: context, userLocation: .other, emoji: emoji, file: emoji.file, cache: context.animationCache, renderer: context.animationRenderer, placeholderColor: UIColor.white.withAlphaComponent(0.12), pointSize: CGSize(width: pointSize, height: pointSize))
}
self.textView.onPaste = { [weak self] in
@ -285,9 +281,10 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
}
self._isEditing = false
self.textView.resignFirstResponder()
self.textView.inputView = nil
self.textView.inputAccessoryView = nil
self.textView.reloadInputViews()
self.textView.resignFirstResponder()
self.textView.isEditable = false
self.textView.isSelectable = false
@ -656,8 +653,8 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
self.updateEditingPosition(animated: animated)
}
self.textView.onLayoutUpdate = {
self.updateEntities()
self.textView.onLayoutUpdate = { [weak self] in
self?.updateEntities()
}
super.update(animated: animated)
@ -1023,15 +1020,20 @@ private class DrawingTextLayoutManager: NSLayoutManager {
private func prepare() {
self.path = nil
self.rectArray.removeAll()
self.enumerateLineFragments(forGlyphRange: NSRange(location: 0, length: ((self.textStorage?.string ?? "") as NSString).length)) { rect, usedRect, textContainer, glyphRange, _ in
var ignoreRange = false
let charecterRange = self.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
let substring = ((self.textStorage?.string ?? "") as NSString).substring(with: charecterRange)
let characterRange = self.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
let substring = ((self.textStorage?.string ?? "") as NSString).substring(with: characterRange)
if substring.trimmingCharacters(in: .newlines).isEmpty {
ignoreRange = true
}
var usedRect = usedRect
if substring.hasSuffix(" ") {
usedRect.size.width -= floorToScreenPixels(usedRect.height * 0.145)
}
if !ignoreRange {
let newRect = CGRect(origin: CGPoint(x: usedRect.minX - self.frameWidthInset, y: usedRect.minY), size: CGSize(width: usedRect.width + self.frameWidthInset * 2.0, height: usedRect.height))
self.rectArray.append(newRect)

View File

@ -579,12 +579,13 @@ final class TextSettingsComponent: CombinedComponent {
)
}
let presentFontPicker = component.presentFontPicker
let font = font.update(
component: TextFontComponent(
selectedValue: component.font,
tag: component.fontTag,
tapped: {
component.presentFontPicker()
presentFontPicker()
}
),
availableSize: CGSize(width: fontAvailableWidth, height: 30.0),

View File

@ -53,10 +53,10 @@ enum MessageContentToUpload {
}
func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, message: Message) -> MessageContentToUpload {
return messageContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, passFetchProgress: false, peerId: message.id.peerId, messageId: message.id, attributes: message.attributes, text: message.text, media: message.media)
return messageContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, passFetchProgress: false, forceNoBigParts: false, peerId: message.id.peerId, messageId: message.id, attributes: message.attributes, text: message.text, media: message.media)
}
func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, peerId: PeerId, messageId: MessageId?, attributes: [MessageAttribute], text: String, media: [Media]) -> MessageContentToUpload {
func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, forceNoBigParts: Bool, peerId: PeerId, messageId: MessageId?, attributes: [MessageAttribute], text: String, media: [Media]) -> MessageContentToUpload {
var contextResult: OutgoingChatContextResultMessageAttribute?
var autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?
var autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?
@ -96,14 +96,14 @@ func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Po
return .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaStory(userId: inputUser, id: media.storyId.id), ""), reuploadInfo: nil, cacheReferenceKey: nil))
}
|> castError(PendingMessageUploadError.self), .text)
} else if let media = media.first, let mediaResult = mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, passFetchProgress: passFetchProgress, peerId: peerId, media: media, text: text, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute, messageId: messageId, attributes: attributes) {
} else if let media = media.first, let mediaResult = mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, passFetchProgress: passFetchProgress, forceNoBigParts: forceNoBigParts, peerId: peerId, media: media, text: text, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute, messageId: messageId, attributes: attributes) {
return .signal(mediaResult, .media)
} else {
return .signal(.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .text(text), reuploadInfo: nil, cacheReferenceKey: nil))), .text)
}
}
func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, peerId: PeerId, media: Media, text: String, autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?, autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute]) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? {
func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, forceNoBigParts: Bool, peerId: PeerId, media: Media, text: String, autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?, autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute]) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? {
if let image = media as? TelegramMediaImage, let largest = largestImageRepresentation(image.representations) {
if peerId.namespace == Namespaces.Peer.SecretChat, let resource = largest.resource as? SecretFileMediaResource {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(.inputEncryptedFile(id: resource.fileId, accessHash: resource.accessHash), resource.decryptedSize, resource.key), reuploadInfo: nil, cacheReferenceKey: nil)))
@ -123,7 +123,7 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post
}
}
}
return uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, forceReupload: true, isGrouped: isGrouped, passFetchProgress: false, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)
return uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, forceReupload: true, isGrouped: isGrouped, passFetchProgress: false, forceNoBigParts: false, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)
} else {
if forceReupload {
let mediaReference: AnyMediaReference
@ -157,7 +157,7 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), ttlSeconds: nil, query: emojiSearchQuery), text), reuploadInfo: nil, cacheReferenceKey: nil)))
}
} else {
return uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, forceReupload: forceReupload, isGrouped: isGrouped, passFetchProgress: passFetchProgress, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)
return uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, forceReupload: forceReupload, isGrouped: isGrouped, passFetchProgress: passFetchProgress, forceNoBigParts: forceNoBigParts, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)
}
} else if let contact = media as? TelegramMediaContact {
let input = Api.InputMedia.inputMediaContact(phoneNumber: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName, vcard: contact.vCardData ?? "")
@ -619,8 +619,8 @@ private enum UploadedMediaFileAndThumbnail {
case done(TelegramMediaFile, UploadedMediaThumbnailResult)
}
private func uploadedThumbnail(network: Network, postbox: Postbox, resourceReference: MediaResourceReference) -> Signal<Api.InputFile?, PendingMessageUploadError> {
return multipartUpload(network: network, postbox: postbox, source: .resource(resourceReference), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image, userContentType: .image), hintFileSize: nil, hintFileIsLarge: false, forceNoBigParts: false)
private func uploadedThumbnail(network: Network, postbox: Postbox, resourceReference: MediaResourceReference, forceNoBigParts: Bool = false) -> Signal<Api.InputFile?, PendingMessageUploadError> {
return multipartUpload(network: network, postbox: postbox, source: .resource(resourceReference), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image, userContentType: .image), hintFileSize: nil, hintFileIsLarge: false, forceNoBigParts: forceNoBigParts)
|> mapError { _ -> PendingMessageUploadError in return .generic }
|> mapToSignal { result -> Signal<Api.InputFile?, PendingMessageUploadError> in
switch result {
@ -656,7 +656,7 @@ public func statsCategoryForFileWithAttributes(_ attributes: [TelegramMediaFileA
return .file
}
private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, peerId: PeerId, messageId: MessageId?, text: String, attributes: [MessageAttribute], file: TelegramMediaFile) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> {
private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, forceNoBigParts: Bool, peerId: PeerId, messageId: MessageId?, text: String, attributes: [MessageAttribute], file: TelegramMediaFile) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> {
return maybePredownloadedFileResource(postbox: postbox, auxiliaryMethods: auxiliaryMethods, peerId: peerId, resource: file.resource, forceRefresh: forceReupload)
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
var referenceKey: CachedSentMediaReferenceKey?
@ -781,7 +781,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
fileReference = .standalone(media: media)
}
return uploadedThumbnail(network: network, postbox: postbox, resourceReference: fileReference.resourceReference(smallestThumbnail.resource))
return uploadedThumbnail(network: network, postbox: postbox, resourceReference: fileReference.resourceReference(smallestThumbnail.resource), forceNoBigParts: forceNoBigParts)
|> mapError { _ -> PendingMessageUploadError in return .generic }
|> map { result in
if let result = result {

View File

@ -59,7 +59,7 @@ private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox,
case let .update(media):
let generateUploadSignal: (Bool) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? = { forceReupload in
let augmentedMedia = augmentMediaWithReference(media)
return mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: stateManager.auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: mediaReferenceRevalidationContext, forceReupload: forceReupload, isGrouped: false, passFetchProgress: false, peerId: messageId.peerId, media: augmentedMedia, text: "", autoremoveMessageAttribute: nil, autoclearMessageAttribute: nil, messageId: nil, attributes: [])
return mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: stateManager.auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: mediaReferenceRevalidationContext, forceReupload: forceReupload, isGrouped: false, passFetchProgress: false, forceNoBigParts: false, peerId: messageId.peerId, media: augmentedMedia, text: "", autoremoveMessageAttribute: nil, autoclearMessageAttribute: nil, messageId: nil, attributes: [])
}
if let uploadSignal = generateUploadSignal(forceReupload) {
uploadedMedia = .single(.progress(0.027))

View File

@ -688,6 +688,7 @@ private func uploadedStoryContent(postbox: Postbox, network: Network, media: Med
forceReupload: true,
isGrouped: false,
passFetchProgress: passFetchProgress,
forceNoBigParts: true,
peerId: accountPeerId,
messageId: nil,
attributes: attributes,

View File

@ -1651,9 +1651,15 @@ public class CameraScreen: ViewController {
guard let camera = self.camera else {
return
}
let location = gestureRecognizer.location(in: self.mainPreviewView)
let point = self.mainPreviewView.cameraPoint(for: location)
camera.focus(at: point, autoFocus: false)
let location = gestureRecognizer.location(in: gestureRecognizer.view)
if self.cameraState.isDualCameraEnabled && self.additionalPreviewContainerView.frame.contains(location) {
self.toggleCameraPositionAction.invoke(Void())
} else {
let location = gestureRecognizer.location(in: self.mainPreviewView)
let point = self.mainPreviewView.cameraPoint(for: location)
camera.focus(at: point, autoFocus: false)
}
}
@objc private func handleDoubleTap(_ gestureRecognizer: UITapGestureRecognizer) {

View File

@ -42,6 +42,7 @@ final class MediaEditorComposer {
private let values: MediaEditorValues
private let dimensions: CGSize
private let outputDimensions: CGSize
private let textScale: CGFloat
private let ciContext: CIContext?
private var textureCache: CVMetalTextureCache?
@ -53,10 +54,11 @@ final class MediaEditorComposer {
private let drawingImage: CIImage?
private var entities: [MediaEditorComposerEntity]
init(account: Account, values: MediaEditorValues, dimensions: CGSize, outputDimensions: CGSize) {
init(account: Account, values: MediaEditorValues, dimensions: CGSize, outputDimensions: CGSize, textScale: CGFloat) {
self.values = values
self.dimensions = dimensions
self.outputDimensions = outputDimensions
self.textScale = textScale
let colorSpace = CGColorSpaceCreateDeviceRGB()
self.colorSpace = colorSpace
@ -77,7 +79,7 @@ final class MediaEditorComposer {
var entities: [MediaEditorComposerEntity] = []
for entity in values.entities {
entities.append(contentsOf: composerEntitiesForDrawingEntity(account: account, entity: entity.entity, colorSpace: colorSpace))
entities.append(contentsOf: composerEntitiesForDrawingEntity(account: account, textScale: textScale, entity: entity.entity, colorSpace: colorSpace))
}
self.entities = entities
@ -155,7 +157,7 @@ final class MediaEditorComposer {
CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool, &pixelBuffer)
if let pixelBuffer, let context = self.ciContext {
makeEditorImageFrameComposition(context: context, inputImage: image, gradientImage: self.gradientImage, drawingImage: self.drawingImage, dimensions: self.dimensions, values: self.values, entities: self.entities, time: time, completion: { compositedImage in
makeEditorImageFrameComposition(context: context, inputImage: image, gradientImage: self.gradientImage, drawingImage: self.drawingImage, dimensions: self.dimensions, outputDimensions: self.outputDimensions, values: self.values, entities: self.entities, time: time, completion: { compositedImage in
if var compositedImage {
let scale = self.outputDimensions.width / self.dimensions.width
compositedImage = compositedImage.samplingLinear().transformed(by: CGAffineTransform(scaleX: scale, y: scale))
@ -176,11 +178,11 @@ final class MediaEditorComposer {
guard let context = self.ciContext else {
return
}
makeEditorImageFrameComposition(context: context, inputImage: inputImage, gradientImage: self.gradientImage, drawingImage: self.drawingImage, dimensions: self.dimensions, values: self.values, entities: self.entities, time: time, completion: completion)
makeEditorImageFrameComposition(context: context, inputImage: inputImage, gradientImage: self.gradientImage, drawingImage: self.drawingImage, dimensions: self.dimensions, outputDimensions: self.outputDimensions, values: self.values, entities: self.entities, time: time, textScale: self.textScale, completion: completion)
}
}
public func makeEditorImageComposition(context: CIContext, account: Account, inputImage: UIImage, dimensions: CGSize, values: MediaEditorValues, time: CMTime, completion: @escaping (UIImage?) -> Void) {
public func makeEditorImageComposition(context: CIContext, account: Account, inputImage: UIImage, dimensions: CGSize, values: MediaEditorValues, time: CMTime, textScale: CGFloat, completion: @escaping (UIImage?) -> Void) {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let inputImage = CIImage(image: inputImage, options: [.colorSpace: colorSpace])!
let gradientImage: CIImage
@ -197,10 +199,10 @@ public func makeEditorImageComposition(context: CIContext, account: Account, inp
var entities: [MediaEditorComposerEntity] = []
for entity in values.entities {
entities.append(contentsOf: composerEntitiesForDrawingEntity(account: account, entity: entity.entity, colorSpace: colorSpace))
entities.append(contentsOf: composerEntitiesForDrawingEntity(account: account, textScale: textScale, entity: entity.entity, colorSpace: colorSpace))
}
makeEditorImageFrameComposition(context: context, inputImage: inputImage, gradientImage: gradientImage, drawingImage: drawingImage, dimensions: dimensions, values: values, entities: entities, time: time, completion: { ciImage in
makeEditorImageFrameComposition(context: context, inputImage: inputImage, gradientImage: gradientImage, drawingImage: drawingImage, dimensions: dimensions, outputDimensions: dimensions, values: values, entities: entities, time: time, textScale: textScale, completion: { ciImage in
if let ciImage {
if let cgImage = context.createCGImage(ciImage, from: CGRect(origin: .zero, size: ciImage.extent.size)) {
Queue.mainQueue().async {
@ -213,7 +215,7 @@ public func makeEditorImageComposition(context: CIContext, account: Account, inp
})
}
private func makeEditorImageFrameComposition(context: CIContext, inputImage: CIImage, gradientImage: CIImage, drawingImage: CIImage?, dimensions: CGSize, values: MediaEditorValues, entities: [MediaEditorComposerEntity], time: CMTime, completion: @escaping (CIImage?) -> Void) {
private func makeEditorImageFrameComposition(context: CIContext, inputImage: CIImage, gradientImage: CIImage, drawingImage: CIImage?, dimensions: CGSize, outputDimensions: CGSize, values: MediaEditorValues, entities: [MediaEditorComposerEntity], time: CMTime, textScale: CGFloat = 1.0, completion: @escaping (CIImage?) -> Void) {
var resultImage = CIImage(color: .black).cropped(to: CGRect(origin: .zero, size: dimensions)).transformed(by: CGAffineTransform(translationX: -dimensions.width / 2.0, y: -dimensions.height / 2.0))
resultImage = gradientImage.composited(over: resultImage)
@ -267,8 +269,10 @@ private func makeEditorImageFrameComposition(context: CIContext, inputImage: CII
image = image.transformed(by: resetTransform)
var baseScale: CGFloat = 1.0
if let entityBaseScale = entity.baseScale {
baseScale = entityBaseScale
if let scale = entity.baseScale {
baseScale = scale
} else if let _ = entity.baseDrawingSize {
// baseScale = textScale
} else if let baseSize = entity.baseSize {
baseScale = baseSize.width / image.extent.width
}

View File

@ -12,11 +12,11 @@ import TelegramAnimatedStickerNode
import YuvConversion
import StickerResources
private func prerenderTextTransformations(entity: DrawingTextEntity, image: UIImage, colorSpace: CGColorSpace) -> MediaEditorComposerStaticEntity {
private func prerenderTextTransformations(entity: DrawingTextEntity, image: UIImage, textScale: CGFloat, colorSpace: CGColorSpace) -> MediaEditorComposerStaticEntity {
let imageSize = image.size
let angle = -entity.rotation
let scale = entity.scale
let scale = entity.scale * 0.5 * textScale
let rotatedSize = CGSize(
width: abs(imageSize.width * cos(angle)) + abs(imageSize.height * sin(angle)),
@ -43,10 +43,10 @@ private func prerenderTextTransformations(entity: DrawingTextEntity, image: UIIm
}
}, scale: 1.0)!
return MediaEditorComposerStaticEntity(image: CIImage(image: newImage, options: [.colorSpace: colorSpace])!, position: entity.position, scale: 1.0, rotation: 0.0, baseSize: nil, baseScale: 1.0, mirrored: false)
return MediaEditorComposerStaticEntity(image: CIImage(image: newImage, options: [.colorSpace: colorSpace])!, position: entity.position, scale: 1.0, rotation: 0.0, baseSize: nil, baseDrawingSize: CGSize(width: 1080, height: 1920), mirrored: false)
}
func composerEntitiesForDrawingEntity(account: Account, entity: DrawingEntity, colorSpace: CGColorSpace, tintColor: UIColor? = nil) -> [MediaEditorComposerEntity] {
func composerEntitiesForDrawingEntity(account: Account, textScale: CGFloat, entity: DrawingEntity, colorSpace: CGColorSpace, tintColor: UIColor? = nil) -> [MediaEditorComposerEntity] {
if let entity = entity as? DrawingStickerEntity {
let content: MediaEditorComposerStickerEntity.Content
switch entity.content {
@ -62,19 +62,18 @@ func composerEntitiesForDrawingEntity(account: Account, entity: DrawingEntity, c
return [MediaEditorComposerStickerEntity(account: account, content: content, position: entity.position, scale: entity.scale, rotation: entity.rotation, baseSize: entity.baseSize, mirrored: entity.mirrored, colorSpace: colorSpace, tintColor: tintColor, isStatic: entity.isExplicitlyStatic)]
} else if let renderImage = entity.renderImage, let image = CIImage(image: renderImage, options: [.colorSpace: colorSpace]) {
if let entity = entity as? DrawingBubbleEntity {
return [MediaEditorComposerStaticEntity(image: image, position: entity.position, scale: 1.0, rotation: entity.rotation, baseSize: entity.size, baseScale: nil, mirrored: false)]
return [MediaEditorComposerStaticEntity(image: image, position: entity.position, scale: 1.0, rotation: entity.rotation, baseSize: entity.size, mirrored: false)]
} else if let entity = entity as? DrawingSimpleShapeEntity {
return [MediaEditorComposerStaticEntity(image: image, position: entity.position, scale: 1.0, rotation: entity.rotation, baseSize: entity.size, baseScale: nil, mirrored: false)]
return [MediaEditorComposerStaticEntity(image: image, position: entity.position, scale: 1.0, rotation: entity.rotation, baseSize: entity.size, mirrored: false)]
} else if let entity = entity as? DrawingVectorEntity {
return [MediaEditorComposerStaticEntity(image: image, position: CGPoint(x: entity.drawingSize.width * 0.5, y: entity.drawingSize.height * 0.5), scale: 1.0, rotation: 0.0, baseSize: entity.drawingSize, baseScale: nil, mirrored: false)]
return [MediaEditorComposerStaticEntity(image: image, position: CGPoint(x: entity.drawingSize.width * 0.5, y: entity.drawingSize.height * 0.5), scale: 1.0, rotation: 0.0, baseSize: entity.drawingSize, mirrored: false)]
} else if let entity = entity as? DrawingTextEntity {
var entities: [MediaEditorComposerEntity] = []
// entities.append(prerenderTextTransformations(entity: entity, image: renderImage, colorSpace: colorSpace))
entities.append(prerenderTextTransformations(entity: entity, image: renderImage, textScale: textScale, colorSpace: colorSpace))
entities.append(MediaEditorComposerStaticEntity(image: image, position: entity.position, scale: entity.scale, rotation: entity.rotation, baseSize: nil, baseScale: 0.5, mirrored: false))
if let renderSubEntities = entity.renderSubEntities {
for subEntity in renderSubEntities {
entities.append(contentsOf: composerEntitiesForDrawingEntity(account: account, entity: subEntity, colorSpace: colorSpace, tintColor: entity.color.toUIColor()))
entities.append(contentsOf: composerEntitiesForDrawingEntity(account: account, textScale: textScale, entity: subEntity, colorSpace: colorSpace, tintColor: entity.color.toUIColor()))
}
}
return entities
@ -90,15 +89,26 @@ private class MediaEditorComposerStaticEntity: MediaEditorComposerEntity {
let rotation: CGFloat
let baseSize: CGSize?
let baseScale: CGFloat?
let baseDrawingSize: CGSize?
let mirrored: Bool
init(image: CIImage, position: CGPoint, scale: CGFloat, rotation: CGFloat, baseSize: CGSize?, baseScale: CGFloat?, mirrored: Bool) {
init(
image: CIImage,
position: CGPoint,
scale: CGFloat,
rotation: CGFloat,
baseSize: CGSize?,
baseScale: CGFloat? = nil,
baseDrawingSize: CGSize? = nil,
mirrored: Bool
) {
self.image = image
self.position = position
self.scale = scale
self.rotation = rotation
self.baseSize = baseSize
self.baseScale = baseScale
self.baseDrawingSize = baseDrawingSize
self.mirrored = mirrored
}
@ -127,6 +137,7 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity {
let rotation: CGFloat
let baseSize: CGSize?
let baseScale: CGFloat? = nil
let baseDrawingSize: CGSize? = nil
let mirrored: Bool
let colorSpace: CGColorSpace
let tintColor: UIColor?
@ -475,6 +486,7 @@ protocol MediaEditorComposerEntity {
var rotation: CGFloat { get }
var baseSize: CGSize? { get }
var baseScale: CGFloat? { get }
var baseDrawingSize: CGSize? { get }
var mirrored: Bool { get }
func image(for time: CMTime, frameRate: Float, context: CIContext, completion: @escaping (CIImage?) -> Void)

View File

@ -261,6 +261,7 @@ public final class MediaEditorVideoExport {
private let account: Account
private let subject: Subject
private let configuration: Configuration
private let textScale: CGFloat
private let outputPath: String
private var reader: AVAssetReader?
@ -295,11 +296,12 @@ public final class MediaEditorVideoExport {
private let semaphore = DispatchSemaphore(value: 0)
public init(account: Account, subject: Subject, configuration: Configuration, outputPath: String) {
public init(account: Account, subject: Subject, configuration: Configuration, outputPath: String, textScale: CGFloat = 1.0) {
self.account = account
self.subject = subject
self.configuration = configuration
self.outputPath = outputPath
self.textScale = textScale
if FileManager.default.fileExists(atPath: outputPath) {
try? FileManager.default.removeItem(atPath: outputPath)
@ -354,7 +356,7 @@ public final class MediaEditorVideoExport {
guard self.composer == nil else {
return
}
self.composer = MediaEditorComposer(account: self.account, values: self.configuration.values, dimensions: self.configuration.composerDimensions, outputDimensions: self.configuration.dimensions)
self.composer = MediaEditorComposer(account: self.account, values: self.configuration.values, dimensions: self.configuration.composerDimensions, outputDimensions: self.configuration.dimensions, textScale: self.textScale)
}
private func setupWithAsset(_ asset: AVAsset, additionalAsset: AVAsset?) {

View File

@ -3320,7 +3320,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
}
private func openEditCategory(privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, pin: Bool, completion: @escaping (EngineStoryPrivacy) -> Void) {
let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .contacts(privacy.base), initialPeerIds: Set(privacy.additionallyIncludePeers))
let subject: ShareWithPeersScreen.StateContext.Subject
if privacy.base == .nobody {
subject = .chats
} else {
subject = .contacts(privacy.base)
}
let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: subject, initialPeerIds: Set(privacy.additionallyIncludePeers))
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
guard let self else {
return
@ -3561,7 +3567,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
if let resultImage = mediaEditor.resultImage {
mediaEditor.seek(0.0, andPlay: false)
makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: resultImage, dimensions: storyDimensions, values: values, time: .zero, completion: { resultImage in
makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: resultImage, dimensions: storyDimensions, values: values, time: .zero, textScale: 2.0, completion: { resultImage in
guard let resultImage else {
return
}
@ -3844,7 +3850,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
inputImage = UIImage()
}
makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: inputImage, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] coverImage in
makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: inputImage, dimensions: storyDimensions, values: mediaEditor.values, time: firstFrameTime, textScale: 2.0, completion: { [weak self] coverImage in
if let self {
Logger.shared.log("MediaEditor", "Completed with video \(videoResult)")
self.completion(randomId, .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions), caption, self.state.privacy, stickers, { [weak self] finished in
@ -3867,7 +3873,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
if let image = mediaEditor.resultImage {
self.saveDraft(id: randomId)
makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] resultImage in
makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, textScale: 2.0, completion: { [weak self] resultImage in
if let self, let resultImage {
Logger.shared.log("MediaEditor", "Completed with image \(resultImage)")
self.completion(randomId, .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), caption, self.state.privacy, stickers, { [weak self] finished in
@ -3995,7 +4001,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
}
let configuration = recommendedVideoExportConfiguration(values: mediaEditor.values, duration: duration, forceFullHd: true, frameRate: 60.0)
let outputPath = NSTemporaryDirectory() + "\(Int64.random(in: 0 ..< .max)).mp4"
let videoExport = MediaEditorVideoExport(account: self.context.account, subject: exportSubject, configuration: configuration, outputPath: outputPath)
let videoExport = MediaEditorVideoExport(account: self.context.account, subject: exportSubject, configuration: configuration, outputPath: outputPath, textScale: 2.0)
self.videoExport = videoExport
videoExport.start()
@ -4028,7 +4034,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
} else {
if let image = mediaEditor.resultImage {
Queue.concurrentDefaultQueue().async {
makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { resultImage in
makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, textScale: 2.0, completion: { resultImage in
if let data = resultImage?.jpegData(compressionQuality: 0.8) {
let outputPath = NSTemporaryDirectory() + "\(Int64.random(in: 0 ..< .max)).jpg"
try? data.write(to: URL(fileURLWithPath: outputPath))

View File

@ -9,15 +9,21 @@ final class SectionHeaderComponent: Component {
let theme: PresentationTheme
let style: ShareWithPeersScreenComponent.Style
let title: String
let actionTitle: String?
let action: (() -> Void)?
init(
theme: PresentationTheme,
style: ShareWithPeersScreenComponent.Style,
title: String
title: String,
actionTitle: String?,
action: (() -> Void)?
) {
self.theme = theme
self.style = style
self.title = title
self.actionTitle = actionTitle
self.action = action
}
static func ==(lhs: SectionHeaderComponent, rhs: SectionHeaderComponent) -> Bool {
@ -30,12 +36,16 @@ final class SectionHeaderComponent: Component {
if lhs.title != rhs.title {
return false
}
if lhs.actionTitle != rhs.actionTitle {
return false
}
return true
}
final class View: UIView {
private let title = ComponentView<Empty>()
private let backgroundView: BlurredBackgroundView
private let action = ComponentView<Empty>()
private var component: SectionHeaderComponent?
private weak var state: EmptyComponentState?
@ -95,6 +105,32 @@ final class SectionHeaderComponent: Component {
}
}
if let actionTitle = component.actionTitle {
let actionSize = self.action.update(
transition: .immediate,
component: AnyComponent(
Button(content: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: actionTitle, font: Font.regular(13.0), textColor: component.theme.list.itemSecondaryTextColor))
)), action: { [weak self] in
if let self, let component = self.component {
component.action?()
}
})
),
environment: {},
containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
)
if let view = self.action.view {
if view.superview == nil {
self.addSubview(view)
}
let actionFrame = CGRect(origin: CGPoint(x: availableSize.width - leftInset - titleSize.width, y: floor((height - titleSize.height) / 2.0)), size: actionSize)
view.frame = actionFrame
}
} else if self.action.view?.superview != nil {
self.action.view?.removeFromSuperview()
}
let size = CGSize(width: availableSize.width, height: height)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))

View File

@ -307,6 +307,10 @@ final class ShareWithPeersScreenComponent: Component {
private var savedSelectedPeers: [EnginePeer.Id] = []
private var selectedPeers: [EnginePeer.Id] = []
private var selectedGroups: [EnginePeer.Id] = []
private var peersMap: [EnginePeer.Id: EnginePeer] = [:]
private var selectedCategories = Set<CategoryId>()
private var selectedOptions = Set<OptionId>()
@ -557,6 +561,119 @@ final class ShareWithPeersScreenComponent: Component {
controller.present(tooltipScreen, in: .window(.root))
}
private func toggleGroupPeer(_ peer: EnginePeer) {
guard let component = self.component, let environment = self.environment, let controller = self.environment?.controller() else {
return
}
var groupTooLarge = false
if case let .legacyGroup(group) = peer, group.participantCount > 200 {
groupTooLarge = true
} else if let stateValue = self.effectiveStateValue, let count = stateValue.participants[peer.id], count > 200 {
groupTooLarge = true
}
if groupTooLarge {
let alertController = textAlertController(
context: component.context,
forceTheme: defaultDarkColorPresentationTheme,
title: environment.strings.Story_Privacy_GroupTooLarge,
text: environment.strings.Story_Privacy_GroupParticipantsLimit,
actions: [
TextAlertAction(type: .defaultAction, title: "OK", action: {})
],
actionLayout: .vertical
)
controller.present(alertController, in: .window(.root))
return
}
var append = false
if let index = self.selectedGroups.firstIndex(of: peer.id) {
self.selectedGroups.remove(at: index)
} else {
self.selectedGroups.append(peer.id)
append = true
}
let context = component.context
if peer.id.namespace == Namespaces.Peer.CloudGroup {
let _ = (context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Peer.LegacyGroupParticipants(id: peer.id)
)
|> mapToSignal { participants -> Signal<[EnginePeer], NoError> in
if case let .known(participants) = participants {
return context.engine.data.get(
EngineDataMap(participants.map { TelegramEngine.EngineData.Item.Peer.Peer(id: $0.peerId) })
)
|> map { peers in
var result: [EnginePeer] = []
for participant in participants {
if let peer = peers[participant.peerId], let peer, peer.id != context.account.peerId {
result.append(peer)
}
}
return result
}
} else {
let _ = context.engine.peers.fetchAndUpdateCachedPeerData(peerId: peer.id).start()
return .complete()
}
}
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] peers in
guard let self else {
return
}
var peerIds = Set<EnginePeer.Id>()
for peer in peers {
self.peersMap[peer.id] = peer
peerIds.insert(peer.id)
}
if append {
self.selectedPeers.append(contentsOf: peers.map { $0.id })
} else {
self.selectedPeers = self.selectedPeers.filter { !peerIds.contains($0) }
}
let transition = Transition(animation: .curve(duration: 0.35, curve: .spring))
self.state?.updated(transition: transition)
})
} else if peer.id.namespace == Namespaces.Peer.CloudChannel {
let participants: Signal<[EnginePeer], NoError> = Signal { subscriber in
let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peer.id, requestUpdate: true, updated: { list in
var peers: [EnginePeer] = []
for item in list.list {
peers.append(EnginePeer(item.peer))
}
if !peers.isEmpty {
subscriber.putNext(peers)
subscriber.putCompletion()
}
})
return disposable
}
|> take(1)
let _ = (participants
|> deliverOnMainQueue).start(next: { [weak self] peers in
guard let self else {
return
}
var peerIds = Set<EnginePeer.Id>()
for peer in peers {
self.peersMap[peer.id] = peer
peerIds.insert(peer.id)
}
if append {
self.selectedPeers.append(contentsOf: peers.map { $0.id })
} else {
self.selectedPeers = self.selectedPeers.filter { !peerIds.contains($0) }
}
let transition = Transition(animation: .curve(duration: 0.35, curve: .spring))
self.state?.updated(transition: transition)
})
}
}
private func updateScrolling(transition: Transition) {
guard let component = self.component, let environment = self.environment, let itemLayout = self.itemLayout else {
return
@ -665,14 +782,22 @@ final class ShareWithPeersScreenComponent: Component {
component: AnyComponent(SectionHeaderComponent(
theme: environment.theme,
style: itemLayout.style,
title: sectionTitle
title: sectionTitle,
actionTitle: (section.id == 1 && !self.selectedPeers.isEmpty) ? environment.strings.Contacts_DeselectAll : nil,
action: { [weak self] in
if let self {
self.selectedPeers = []
self.selectedGroups = []
let transition = Transition(animation: .curve(duration: 0.35, curve: .spring))
self.state?.updated(transition: transition)
}
}
)),
environment: {},
containerSize: sectionHeaderFrame.size
)
if let sectionHeaderView = sectionHeader.view {
if sectionHeaderView.superview == nil {
sectionHeaderView.isUserInteractionEnabled = false
self.scrollContentClippingView.addSubview(sectionHeaderView)
}
if minSectionHeader == nil {
@ -736,7 +861,8 @@ final class ShareWithPeersScreenComponent: Component {
self.selectedCategories.removeAll()
self.selectedCategories.insert(categoryId)
if self.selectedPeers.isEmpty && categoryId == .selectedContacts {
let closeFriends = self.component?.stateContext.stateValue?.closeFriendsPeers ?? []
if categoryId == .selectedContacts && self.selectedPeers.isEmpty {
component.editCategory(
EngineStoryPrivacy(base: .nobody, additionallyIncludePeers: []),
self.selectedOptions.contains(.screenshot),
@ -744,6 +870,14 @@ final class ShareWithPeersScreenComponent: Component {
)
controller.dismissAllTooltips()
controller.dismiss()
} else if categoryId == .closeFriends && closeFriends.isEmpty {
component.editCategory(
EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []),
self.selectedOptions.contains(.screenshot),
self.selectedOptions.contains(.pin)
)
controller.dismissAllTooltips()
controller.dismiss()
}
}
self.state?.updated(transition: Transition(animation: .curve(duration: 0.35, curve: .spring)))
@ -811,6 +945,20 @@ final class ShareWithPeersScreenComponent: Component {
self.visibleItems[itemId] = visibleItem
}
let subtitle: String?
if case let .legacyGroup(group) = peer {
subtitle = environment.strings.Conversation_StatusMembers(Int32(group.participantCount))
} else if case .channel = peer {
if let count = stateValue.participants[peer.id] {
subtitle = environment.strings.Conversation_StatusMembers(Int32(count))
} else {
subtitle = nil
}
} else {
subtitle = nil
}
let isSelected = self.selectedPeers.contains(peer.id) || self.selectedGroups.contains(peer.id)
let _ = visibleItem.update(
transition: itemTransition,
component: AnyComponent(PeerListItemComponent(
@ -821,19 +969,23 @@ final class ShareWithPeersScreenComponent: Component {
sideInset: itemLayout.sideInset,
title: peer.displayTitle(strings: environment.strings, displayOrder: .firstLast),
peer: peer,
subtitle: nil,
subtitle: subtitle,
subtitleAccessory: .none,
presence: stateValue.presences[peer.id],
selectionState: .editing(isSelected: self.selectedPeers.contains(peer.id), isTinted: false),
selectionState: .editing(isSelected: isSelected, isTinted: false),
hasNext: true,
action: { [weak self] peer in
guard let self else {
return
}
if let index = self.selectedPeers.firstIndex(of: peer.id) {
self.selectedPeers.remove(at: index)
if peer.id.isGroupOrChannel {
self.toggleGroupPeer(peer)
} else {
self.selectedPeers.append(peer.id)
if let index = self.selectedPeers.firstIndex(of: peer.id) {
self.selectedPeers.remove(at: index)
} else {
self.selectedPeers.append(peer.id)
}
}
let transition = Transition(animation: .curve(duration: 0.35, curve: .spring))
@ -1244,9 +1396,20 @@ final class ShareWithPeersScreenComponent: Component {
var tokens: [TokenListTextField.Token] = []
for peerId in self.selectedPeers {
guard let stateValue = self.defaultStateValue, let peer = stateValue.peers.first(where: { $0.id == peerId }) else {
guard let stateValue = self.defaultStateValue else {
continue
}
var peer: EnginePeer?
if let peerValue = self.peersMap[peerId] {
peer = peerValue
} else if let peerValue = stateValue.peers.first(where: { $0.id == peerId }) {
peer = peerValue
}
guard let peer else {
continue
}
tokens.append(TokenListTextField.Token(
id: AnyHashable(peerId),
title: peer.compactDisplayTitle,
@ -1440,7 +1603,7 @@ final class ShareWithPeersScreenComponent: Component {
actionButtonTitle = environment.strings.Story_Privacy_PostStory
}
case .chats:
title = ""
title = environment.strings.Story_Privacy_CategorySelectedContacts
case let .contacts(category):
switch category {
case .closeFriends:
@ -1507,6 +1670,13 @@ final class ShareWithPeersScreenComponent: Component {
transition.setFrame(layer: self.navigationSeparatorLayer, frame: CGRect(origin: CGPoint(x: containerSideInset, y: navigationHeight), size: CGSize(width: containerWidth, height: UIScreenPixel)))
let badge: Int
if case .stories = component.stateContext.subject {
badge = 0
} else {
badge = self.selectedPeers.count
}
let actionButtonSize = self.actionButton.update(
transition: transition,
component: AnyComponent(ButtonComponent(
@ -1519,7 +1689,7 @@ final class ShareWithPeersScreenComponent: Component {
id: actionButtonTitle,
component: AnyComponent(ButtonTextContentComponent(
text: actionButtonTitle,
badge: 0,
badge: badge,
textColor: environment.theme.list.itemCheckColors.foregroundColor,
badgeBackground: environment.theme.list.itemCheckColors.foregroundColor,
badgeForeground: environment.theme.list.itemCheckColors.fillColor
@ -1755,15 +1925,18 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
public final class State {
let peers: [EnginePeer]
let presences: [EnginePeer.Id: EnginePeer.Presence]
let participants: [EnginePeer.Id: Int]
let closeFriendsPeers: [EnginePeer]
fileprivate init(
peers: [EnginePeer],
presences: [EnginePeer.Id: EnginePeer.Presence],
participants: [EnginePeer.Id: Int],
closeFriendsPeers: [EnginePeer]
) {
self.peers = peers
self.presences = presences
self.participants = participants
self.closeFriendsPeers = closeFriendsPeers
}
}
@ -1820,6 +1993,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
let state = State(
peers: peers.compactMap { $0 },
presences: [:],
participants: [:],
closeFriendsPeers: closeFriends
)
self.stateValue = state
@ -1828,16 +2002,36 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
self.readySubject.set(true)
})
case .chats:
self.stateDisposable = (context.engine.messages.chatList(group: .root, count: 200)
|> deliverOnMainQueue).start(next: { [weak self] chatList in
self.stateDisposable = (combineLatest(
context.engine.messages.chatList(group: .root, count: 200) |> take(1),
context.engine.data.get(TelegramEngine.EngineData.Item.Contacts.List(includePresences: true))
)
|> mapToSignal { chatList, contacts -> Signal<(EngineChatList, EngineContactList, [EnginePeer.Id: Optional<Int>]), NoError> in
return context.engine.data.get(
EngineDataMap(chatList.items.map(\.renderedPeer.peerId).map(TelegramEngine.EngineData.Item.Peer.ParticipantCount.init))
)
|> map { participantCountMap -> (EngineChatList, EngineContactList, [EnginePeer.Id: Optional<Int>]) in
return (chatList, contacts, participantCountMap)
}
}
|> deliverOnMainQueue).start(next: { [weak self] chatList, contacts, participantCounts in
guard let self else {
return
}
var participants: [EnginePeer.Id: Int] = [:]
for (key, value) in participantCounts {
if let value {
participants[key] = value
}
}
var existingIds = Set<EnginePeer.Id>()
var selectedPeers: [EnginePeer] = []
for item in chatList.items.reversed() {
if self.initialPeerIds.contains(item.renderedPeer.peerId), let peer = item.renderedPeer.peer {
selectedPeers.append(peer)
existingIds.insert(peer.id)
}
}
@ -1847,12 +2041,39 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
}
var peers: [EnginePeer] = []
peers = chatList.items.filter { !self.initialPeerIds.contains($0.renderedPeer.peerId) && $0.renderedPeer.peerId != context.account.peerId }.reversed().compactMap { $0.renderedPeer.peer }
peers = chatList.items.filter { peer in
if let peer = peer.renderedPeer.peer {
if self.initialPeerIds.contains(peer.id) {
return false
}
if peer.id == context.account.peerId {
return false
}
if peer.isService {
return false
}
if case let .channel(channel) = peer {
if channel.isForum {
return false
}
if case .broadcast = channel.info {
return false
}
}
return true
} else {
return false
}
}.reversed().compactMap { $0.renderedPeer.peer }
for peer in peers {
existingIds.insert(peer.id)
}
peers.insert(contentsOf: selectedPeers, at: 0)
let state = State(
peers: peers,
presences: presences,
participants: participants,
closeFriendsPeers: []
)
self.stateValue = state
@ -1908,6 +2129,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
let state = State(
peers: peers,
presences: contactList.presences,
participants: [:],
closeFriendsPeers: []
)
@ -1946,11 +2168,20 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
} else {
return true
}
} else if case let .channel(channel) = peer {
if channel.isForum {
return false
}
if case .broadcast = channel.info {
return false
}
return true
} else {
return false
return true
}
},
presences: [:],
participants: [:],
closeFriendsPeers: []
)
self.stateValue = state

View File

@ -450,7 +450,7 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate {
let previousContentHeight = self.scrollNode.view.contentSize.height
let contentHeight = currentOffset.y + 29.0 + verticalInset
let nodeHeight = min(contentHeight, 110.0)
let nodeHeight = min(contentHeight, 140.0)
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel)))
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: nodeHeight)))

View File

@ -13054,7 +13054,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if !value || concealed || botApp.flags.contains(.notActivated) {
let context = self.context
let controller = webAppLaunchConfirmationController(context: context, updatedPresentationData: self.updatedPresentationData, peer: botPeer, requestWriteAccess: !botApp.flags.contains(.notActivated) && botApp.flags.contains(.requiresWriteAccess), completion: { allowWrite in
let controller = webAppLaunchConfirmationController(context: context, updatedPresentationData: self.updatedPresentationData, peer: botPeer, requestWriteAccess: botApp.flags.contains(.notActivated) && botApp.flags.contains(.requiresWriteAccess), completion: { allowWrite in
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).start()
openBotApp(allowWrite)
}, showMore: { [weak self] in