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
340cc2c385
commit
f02722c954
@ -10867,3 +10867,6 @@ Sorry for the inconvenience.";
|
||||
"Chat.PlayOnceMesasgeClose" = "Close";
|
||||
"Chat.PlayOnceMesasgeCloseAndDelete" = "Close and Delete";
|
||||
"Chat.PlayOnceMesasge.DisableScreenCapture" = "Sorry, you can't play this message while screen recording is active.";
|
||||
|
||||
"Conversation.ContactAddContact" = "ADD";
|
||||
"Conversation.ContactMessage" = "MESSAGE";
|
||||
|
@ -228,6 +228,8 @@ public protocol ManagedAudioRecorder: AnyObject {
|
||||
var recordingState: Signal<AudioRecordingState, NoError> { get }
|
||||
|
||||
func start()
|
||||
func pause()
|
||||
func resume()
|
||||
func stop()
|
||||
func takenRecordedData() -> Signal<RecordedAudioData?, NoError>
|
||||
}
|
||||
|
@ -826,6 +826,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
}, finishMediaRecording: { _ in
|
||||
}, stopMediaRecording: {
|
||||
}, lockMediaRecording: {
|
||||
}, resumeMediaRecording: {
|
||||
}, deleteRecordedMedia: {
|
||||
}, sendRecordedMedia: { _, _ in
|
||||
}, displayRestrictedInfo: { _, _ in
|
||||
|
@ -17,6 +17,7 @@ public enum ChatLoadingMessageSubject {
|
||||
public enum ChatFinishMediaRecordingAction {
|
||||
case dismiss
|
||||
case preview
|
||||
case pause
|
||||
case send(viewOnce: Bool)
|
||||
}
|
||||
|
||||
@ -108,6 +109,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
public let finishMediaRecording: (ChatFinishMediaRecordingAction) -> Void
|
||||
public let stopMediaRecording: () -> Void
|
||||
public let lockMediaRecording: () -> Void
|
||||
public let resumeMediaRecording: () -> Void
|
||||
public let deleteRecordedMedia: () -> Void
|
||||
public let sendRecordedMedia: (Bool, Bool) -> Void
|
||||
public let displayRestrictedInfo: (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void
|
||||
@ -214,6 +216,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void,
|
||||
stopMediaRecording: @escaping () -> Void,
|
||||
lockMediaRecording: @escaping () -> Void,
|
||||
resumeMediaRecording: @escaping () -> Void,
|
||||
deleteRecordedMedia: @escaping () -> Void,
|
||||
sendRecordedMedia: @escaping (Bool, Bool) -> Void,
|
||||
displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void,
|
||||
@ -319,6 +322,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
self.finishMediaRecording = finishMediaRecording
|
||||
self.stopMediaRecording = stopMediaRecording
|
||||
self.lockMediaRecording = lockMediaRecording
|
||||
self.resumeMediaRecording = resumeMediaRecording
|
||||
self.deleteRecordedMedia = deleteRecordedMedia
|
||||
self.sendRecordedMedia = sendRecordedMedia
|
||||
self.displayRestrictedInfo = displayRestrictedInfo
|
||||
@ -431,6 +435,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
}, finishMediaRecording: { _ in
|
||||
}, stopMediaRecording: {
|
||||
}, lockMediaRecording: {
|
||||
}, resumeMediaRecording: {
|
||||
}, deleteRecordedMedia: {
|
||||
}, sendRecordedMedia: { _, _ in
|
||||
}, displayRestrictedInfo: { _, _ in
|
||||
|
@ -8,7 +8,7 @@ import VideoToolbox
|
||||
|
||||
private let queue = Queue()
|
||||
|
||||
func cutoutStickerImage(from image: UIImage) -> Signal<UIImage?, NoError> {
|
||||
public func cutoutStickerImage(from image: UIImage) -> Signal<UIImage?, NoError> {
|
||||
if #available(iOS 17.0, *) {
|
||||
guard let cgImage = image.cgImage else {
|
||||
return .single(nil)
|
||||
|
@ -440,7 +440,6 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
}
|
||||
|> take(1)
|
||||
|
||||
|
||||
let product: Signal<InAppPurchaseManager.Product?, NoError> = products
|
||||
|> map { products in
|
||||
if let product = products.first(where: { $0.id == productIdentifier }) {
|
||||
@ -469,19 +468,12 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
completion = updatePendingInAppPurchaseState(engine: self.engine, productId: productIdentifier, content: nil)
|
||||
|
||||
let receiptData = getReceiptData() ?? Data()
|
||||
|
||||
#if DEBUG
|
||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||
let fileResource = LocalFileMediaResource(fileId: id, size: Int64(receiptData.count), isSecretRelated: false)
|
||||
self.engine.account.postbox.mediaBox.storeResourceData(fileResource.id, data: receiptData)
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: Int64(receiptData.count), attributes: [.FileName(fileName: "Receipt.dat")])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
||||
|
||||
let _ = enqueueMessages(account: self.engine.account, peerId: self.engine.account.peerId, messages: [message]).start()
|
||||
self.debugSaveReceipt(receiptData: receiptData)
|
||||
#endif
|
||||
|
||||
self.disposableSet.set(
|
||||
@ -553,6 +545,17 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func debugSaveReceipt(receiptData: Data) {
|
||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||
let fileResource = LocalFileMediaResource(fileId: id, size: Int64(receiptData.count), isSecretRelated: false)
|
||||
self.engine.account.postbox.mediaBox.storeResourceData(fileResource.id, data: receiptData)
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: Int64(receiptData.count), attributes: [.FileName(fileName: "Receipt.dat")])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
||||
|
||||
let _ = enqueueMessages(account: self.engine.account, peerId: self.engine.account.peerId, messages: [message]).start()
|
||||
}
|
||||
}
|
||||
|
||||
private final class PendingInAppPurchaseState: Codable {
|
||||
|
@ -317,24 +317,31 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
||||
CGContextSetStrokeColorWithColor(context, (self.pallete != nil ? self.pallete.borderColor : UIColorRGB(0xb2b2b2)).CGColor);
|
||||
CGContextSetLineWidth(context, TGScreenPixel);
|
||||
|
||||
CGRect rect1 = CGRectMake(TGScreenPixel / 2.0f, TGScreenPixel / 2.0f, 40.0f - TGScreenPixel, 40.0 - TGScreenPixel);
|
||||
CGContextFillEllipseInRect(context, rect1);
|
||||
CGContextStrokeEllipseInRect(context, rect1);
|
||||
|
||||
CGRect iconRect = CGRectInset(rect1, 12.0f, 12.0f);
|
||||
CGFloat radius = 1.0f;
|
||||
CGContextAddPath(context, [UIBezierPath bezierPathWithRoundedRect:CGRectMake(12.0, 12.0, 5.0, 14.0) cornerRadius:1.0].CGPath);
|
||||
CGContextFillPath(context);
|
||||
|
||||
CGFloat minx = CGRectGetMinX(iconRect), midx = CGRectGetMidX(iconRect), maxx = CGRectGetMaxX(iconRect);
|
||||
CGFloat miny = CGRectGetMinY(iconRect), midy = CGRectGetMidY(iconRect), maxy = CGRectGetMaxY(iconRect);
|
||||
CGContextAddPath(context, [UIBezierPath bezierPathWithRoundedRect:CGRectMake(40.0 - 12.0 - 5.0, 12.0, 5.0, 14.0) cornerRadius:1.0].CGPath);
|
||||
CGContextFillPath(context);
|
||||
|
||||
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
|
||||
|
||||
CGContextMoveToPoint(context, minx, midy);
|
||||
CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
|
||||
CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
|
||||
CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
|
||||
CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
|
||||
CGContextClosePath(context);
|
||||
// CGRect rect1 = CGRectMake(TGScreenPixel / 2.0f, TGScreenPixel / 2.0f, 40.0f - TGScreenPixel, 40.0 - TGScreenPixel);
|
||||
// CGContextFillEllipseInRect(context, rect1);
|
||||
// CGContextStrokeEllipseInRect(context, rect1);
|
||||
//
|
||||
// CGRect iconRect = CGRectInset(rect1, 12.0f, 12.0f);
|
||||
// CGFloat radius = 1.0f;
|
||||
//
|
||||
// CGFloat minx = CGRectGetMinX(iconRect), midx = CGRectGetMidX(iconRect), maxx = CGRectGetMaxX(iconRect);
|
||||
// CGFloat miny = CGRectGetMinY(iconRect), midy = CGRectGetMidY(iconRect), maxy = CGRectGetMaxY(iconRect);
|
||||
//
|
||||
// CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
|
||||
//
|
||||
// CGContextMoveToPoint(context, minx, midy);
|
||||
// CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
|
||||
// CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
|
||||
// CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
|
||||
// CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
|
||||
// CGContextClosePath(context);
|
||||
|
||||
CGContextSetFillColorWithColor(context, (self.pallete != nil ? self.pallete.buttonColor : TGAccentColor()).CGColor);
|
||||
CGContextDrawPath(context, kCGPathFill);
|
||||
|
@ -21,6 +21,8 @@ public enum MessageContentKindKey {
|
||||
case liveLocation
|
||||
case expiredImage
|
||||
case expiredVideo
|
||||
case expiredVoiceMessage
|
||||
case expiredVideoMessage
|
||||
case poll
|
||||
case restricted
|
||||
case dice
|
||||
@ -44,6 +46,8 @@ public enum MessageContentKind: Equatable {
|
||||
case liveLocation
|
||||
case expiredImage
|
||||
case expiredVideo
|
||||
case expiredVoiceMessage
|
||||
case expiredVideoMessage
|
||||
case poll(String)
|
||||
case restricted(String)
|
||||
case dice(String)
|
||||
@ -137,6 +141,18 @@ public enum MessageContentKind: Equatable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .expiredVoiceMessage:
|
||||
if case .expiredVoiceMessage = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .expiredVideoMessage:
|
||||
if case .expiredVideoMessage = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .poll:
|
||||
if case .poll = other {
|
||||
return true
|
||||
@ -206,6 +222,10 @@ public enum MessageContentKind: Equatable {
|
||||
return .expiredImage
|
||||
case .expiredVideo:
|
||||
return .expiredVideo
|
||||
case .expiredVoiceMessage:
|
||||
return .expiredVoiceMessage
|
||||
case .expiredVideoMessage:
|
||||
return .expiredVideoMessage
|
||||
case .poll:
|
||||
return .poll
|
||||
case .restricted:
|
||||
@ -405,6 +425,10 @@ public func stringForMediaKind(_ kind: MessageContentKind, strings: Presentation
|
||||
return (NSAttributedString(string: strings.Message_ImageExpired), true)
|
||||
case .expiredVideo:
|
||||
return (NSAttributedString(string: strings.Message_VideoExpired), true)
|
||||
case .expiredVoiceMessage:
|
||||
return (NSAttributedString(string: strings.Message_VoiceMessageExpired), true)
|
||||
case .expiredVideoMessage:
|
||||
return (NSAttributedString(string: strings.Message_VideoMessageExpired), true)
|
||||
case let .poll(text):
|
||||
return (NSAttributedString(string: "📊 \(text)"), false)
|
||||
case let .restricted(text):
|
||||
|
@ -24,6 +24,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentButtonNode",
|
||||
"//submodules/TelegramUI/Components/ChatControllerInteraction",
|
||||
"//submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -14,13 +14,17 @@ import ChatMessageBubbleContentNode
|
||||
import ChatMessageItemCommon
|
||||
import ChatMessageAttachedContentButtonNode
|
||||
import ChatControllerInteraction
|
||||
import MessageInlineBlockBackgroundView
|
||||
|
||||
private let avatarFont = avatarPlaceholderFont(size: 16.0)
|
||||
|
||||
private let titleFont = Font.medium(14.0)
|
||||
private let titleFont = Font.semibold(14.0)
|
||||
private let textFont = Font.regular(14.0)
|
||||
|
||||
public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
private var backgroundView: MessageInlineBlockBackgroundView?
|
||||
private var actionButtonSeparator: SimpleLayer?
|
||||
|
||||
private let avatarNode: AvatarNode
|
||||
private let dateAndStatusNode: ChatMessageDateAndStatusNode
|
||||
private let titleNode: TextNode
|
||||
@ -29,23 +33,27 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
private var contact: TelegramMediaContact?
|
||||
private var contactInfo : String?
|
||||
|
||||
private let buttonNode: ChatMessageAttachedContentButtonNode
|
||||
private let addButtonNode: ChatMessageAttachedContentButtonNode
|
||||
private let messageButtonNode: ChatMessageAttachedContentButtonNode
|
||||
|
||||
required public init() {
|
||||
self.avatarNode = AvatarNode(font: avatarFont)
|
||||
self.dateAndStatusNode = ChatMessageDateAndStatusNode()
|
||||
self.titleNode = TextNode()
|
||||
self.textNode = TextNode()
|
||||
self.buttonNode = ChatMessageAttachedContentButtonNode()
|
||||
self.addButtonNode = ChatMessageAttachedContentButtonNode()
|
||||
self.messageButtonNode = ChatMessageAttachedContentButtonNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.avatarNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
self.addSubnode(self.addButtonNode)
|
||||
self.addSubnode(self.messageButtonNode)
|
||||
|
||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
self.addButtonNode.addTarget(self, action: #selector(self.addButtonPressed), forControlEvents: .touchUpInside)
|
||||
self.messageButtonNode.addTarget(self, action: #selector(self.messageButtonPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.dateAndStatusNode.reactionSelected = { [weak self] value in
|
||||
guard let strongSelf = self, let item = strongSelf.item else {
|
||||
@ -65,7 +73,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
override public func accessibilityActivate() -> Bool {
|
||||
self.buttonPressed()
|
||||
self.addButtonPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
@ -84,7 +92,8 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let statusLayout = self.dateAndStatusNode.asyncLayout()
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeTextLayout = TextNode.asyncLayout(self.textNode)
|
||||
let makeButtonLayout = ChatMessageAttachedContentButtonNode.asyncLayout(self.buttonNode)
|
||||
let makeMessageButtonLayout = ChatMessageAttachedContentButtonNode.asyncLayout(self.messageButtonNode)
|
||||
let makeAddButtonLayout = ChatMessageAttachedContentButtonNode.asyncLayout(self.addButtonNode)
|
||||
|
||||
let previousContact = self.contact
|
||||
let previousContactInfo = self.contactInfo
|
||||
@ -102,6 +111,39 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
incoming = false
|
||||
}
|
||||
|
||||
|
||||
var contactPeer: Peer?
|
||||
if let peerId = selectedContact?.peerId, let peer = item.message.peers[peerId] {
|
||||
contactPeer = peer
|
||||
}
|
||||
|
||||
let nameColors = contactPeer?.nameColor.flatMap { item.context.peerNameColors.get($0, dark: item.presentationData.theme.theme.overallDarkAppearance) }
|
||||
|
||||
let messageTheme = incoming ? item.presentationData.theme.theme.chat.message.incoming : item.presentationData.theme.theme.chat.message.outgoing
|
||||
let mainColor: UIColor
|
||||
var secondaryColor: UIColor?
|
||||
var tertiaryColor: UIColor?
|
||||
if !incoming {
|
||||
mainColor = messageTheme.accentTextColor
|
||||
if let _ = nameColors?.secondary {
|
||||
secondaryColor = .clear
|
||||
}
|
||||
if let _ = nameColors?.tertiary {
|
||||
tertiaryColor = .clear
|
||||
}
|
||||
} else {
|
||||
var authorNameColor: UIColor?
|
||||
authorNameColor = nameColors?.main
|
||||
secondaryColor = nameColors?.secondary
|
||||
tertiaryColor = nameColors?.tertiary
|
||||
|
||||
if let authorNameColor {
|
||||
mainColor = authorNameColor
|
||||
} else {
|
||||
mainColor = messageTheme.accentTextColor
|
||||
}
|
||||
}
|
||||
|
||||
var titleString: NSAttributedString?
|
||||
var textString: NSAttributedString?
|
||||
var updatedContactInfo: String?
|
||||
@ -159,8 +201,8 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
updatedContactInfo = info
|
||||
|
||||
titleString = NSAttributedString(string: displayName, font: titleFont, textColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.accentTextColor : item.presentationData.theme.theme.chat.message.outgoing.accentTextColor)
|
||||
textString = NSAttributedString(string: info, font: textFont, textColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.primaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.primaryTextColor)
|
||||
titleString = NSAttributedString(string: displayName, font: titleFont, textColor: mainColor)
|
||||
textString = NSAttributedString(string: info, font: textFont, textColor: messageTheme.primaryTextColor)
|
||||
} else {
|
||||
updatedContactInfo = nil
|
||||
}
|
||||
@ -247,42 +289,47 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
))
|
||||
}
|
||||
|
||||
let titleColor: UIColor
|
||||
let avatarPlaceholderColor: UIColor
|
||||
if incoming {
|
||||
titleColor = item.presentationData.theme.theme.chat.message.incoming.accentTextColor
|
||||
avatarPlaceholderColor = item.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor
|
||||
} else {
|
||||
titleColor = item.presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
||||
avatarPlaceholderColor = item.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor
|
||||
}
|
||||
|
||||
let (buttonWidth, continueLayout) = makeButtonLayout(constrainedSize.width, nil, false, item.presentationData.strings.Conversation_ViewContactDetails, titleColor, false, true)
|
||||
let (messageButtonWidth, messageContinueLayout) = makeMessageButtonLayout(constrainedSize.width, nil, false, item.presentationData.strings.Conversation_ContactMessage.uppercased(), mainColor, false, false)
|
||||
let (addButtonWidth, addContinueLayout) = makeAddButtonLayout(constrainedSize.width, nil, false, item.presentationData.strings.Conversation_ContactAddContact.uppercased(), mainColor, false, false)
|
||||
|
||||
let maxButtonWidth = max(messageButtonWidth, addButtonWidth)
|
||||
var maxContentWidth: CGFloat = avatarSize.width + 7.0
|
||||
if let statusSuggestedWidthAndContinue = statusSuggestedWidthAndContinue {
|
||||
maxContentWidth = max(maxContentWidth, statusSuggestedWidthAndContinue.0)
|
||||
}
|
||||
maxContentWidth = max(maxContentWidth, avatarSize.width + 7.0 + titleLayout.size.width)
|
||||
maxContentWidth = max(maxContentWidth, avatarSize.width + 7.0 + textLayout.size.width)
|
||||
maxContentWidth = max(maxContentWidth, buttonWidth)
|
||||
maxContentWidth = max(maxContentWidth, maxButtonWidth * 2.0)
|
||||
maxContentWidth = max(maxContentWidth, 240.0)
|
||||
|
||||
let contentWidth = maxContentWidth + layoutConstants.text.bubbleInsets.right * 2.0
|
||||
|
||||
return (contentWidth, { boundingWidth in
|
||||
let baseAvatarFrame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.right, y: layoutConstants.text.bubbleInsets.top), size: avatarSize)
|
||||
|
||||
let (buttonSize, buttonApply) = continueLayout(boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0, 33.0)
|
||||
let lineWidth: CGFloat = 3.0
|
||||
let buttonWidth = floor((boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0 - lineWidth) / 2.0)
|
||||
let (messageButtonSize, messageButtonApply) = messageContinueLayout(buttonWidth, 33.0)
|
||||
let (addButtonSize, addButtonApply) = addContinueLayout(buttonWidth, 33.0)
|
||||
|
||||
let buttonSpacing: CGFloat = 4.0
|
||||
|
||||
let statusSizeAndApply = statusSuggestedWidthAndContinue?.1(boundingWidth - sideInsets)
|
||||
|
||||
var layoutSize = CGSize(width: contentWidth, height: 49.0 + textLayout.size.height + buttonSize.height + buttonSpacing)
|
||||
var layoutSize = CGSize(width: contentWidth, height: 64.0 + textLayout.size.height + addButtonSize.height + buttonSpacing)
|
||||
if let statusSizeAndApply = statusSizeAndApply {
|
||||
layoutSize.height += statusSizeAndApply.0.height - 4.0
|
||||
}
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.right, y: layoutSize.height - 9.0 - buttonSize.height), size: buttonSize)
|
||||
let avatarFrame = baseAvatarFrame.offsetBy(dx: 0.0, dy: 5.0)
|
||||
let messageButtonFrame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.right + lineWidth, y: layoutSize.height - 24.0 - messageButtonSize.height), size: messageButtonSize)
|
||||
let addButtonFrame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.right + lineWidth + buttonWidth, y: layoutSize.height - 24.0 - addButtonSize.height), size: addButtonSize)
|
||||
let avatarFrame = baseAvatarFrame.offsetBy(dx: 9.0, dy: 14.0)
|
||||
|
||||
var customLetters: [String] = []
|
||||
if let selectedContact = selectedContact, selectedContact.peerId == nil {
|
||||
@ -309,14 +356,20 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
let _ = titleApply()
|
||||
let _ = textApply()
|
||||
let _ = buttonApply(animation)
|
||||
let _ = messageButtonApply(animation)
|
||||
let _ = addButtonApply(animation)
|
||||
|
||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: avatarFrame.maxX + 7.0, y: avatarFrame.minY + 1.0), size: titleLayout.size)
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: avatarFrame.maxX + 7.0, y: avatarFrame.minY + 20.0), size: textLayout.size)
|
||||
strongSelf.buttonNode.frame = buttonFrame
|
||||
strongSelf.addButtonNode.frame = addButtonFrame
|
||||
|
||||
strongSelf.messageButtonNode.frame = messageButtonFrame
|
||||
|
||||
let backgroundInsets = layoutConstants.text.bubbleInsets
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top + 5.0), size: CGSize(width: contentWidth - layoutConstants.text.bubbleInsets.right * 2.0, height: 94.0))
|
||||
|
||||
if let statusSizeAndApply = statusSizeAndApply {
|
||||
strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.left, y: strongSelf.textNode.frame.maxY + 2.0), size: statusSizeAndApply.0)
|
||||
strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.left, y: backgroundFrame.maxY + 3.0), size: statusSizeAndApply.0)
|
||||
if strongSelf.dateAndStatusNode.supernode == nil {
|
||||
strongSelf.addSubnode(strongSelf.dateAndStatusNode)
|
||||
statusSizeAndApply.1(.None)
|
||||
@ -359,6 +412,48 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
} else {
|
||||
strongSelf.dateAndStatusNode.pressed = nil
|
||||
}
|
||||
|
||||
var pattern: MessageInlineBlockBackgroundView.Pattern?
|
||||
if let contactPeer, let backgroundEmojiId = contactPeer.backgroundEmojiId {
|
||||
pattern = MessageInlineBlockBackgroundView.Pattern(
|
||||
context: item.context,
|
||||
fileId: backgroundEmojiId,
|
||||
file: item.message.associatedMedia[MediaId(
|
||||
namespace: Namespaces.Media.CloudFile,
|
||||
id: backgroundEmojiId
|
||||
)] as? TelegramMediaFile
|
||||
)
|
||||
}
|
||||
|
||||
let patternTopRightPosition = CGPoint()
|
||||
|
||||
let backgroundView: MessageInlineBlockBackgroundView
|
||||
if let current = strongSelf.backgroundView {
|
||||
backgroundView = current
|
||||
animation.animator.updateFrame(layer: backgroundView.layer, frame: backgroundFrame, completion: nil)
|
||||
backgroundView.update(size: backgroundFrame.size, isTransparent: false, primaryColor: mainColor, secondaryColor: secondaryColor, thirdColor: tertiaryColor, backgroundColor: nil, pattern: pattern, patternTopRightPosition: patternTopRightPosition, animation: animation)
|
||||
} else {
|
||||
backgroundView = MessageInlineBlockBackgroundView()
|
||||
strongSelf.backgroundView = backgroundView
|
||||
backgroundView.frame = backgroundFrame
|
||||
strongSelf.view.insertSubview(backgroundView, at: 0)
|
||||
backgroundView.update(size: backgroundFrame.size, isTransparent: false, primaryColor: mainColor, secondaryColor: secondaryColor, thirdColor: tertiaryColor, backgroundColor: nil, pattern: pattern, patternTopRightPosition: patternTopRightPosition, animation: .None)
|
||||
}
|
||||
|
||||
let separatorFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + 9.0, y: backgroundFrame.maxY - 36.0), size: CGSize(width: backgroundFrame.width - 18.0, height: UIScreenPixel))
|
||||
|
||||
let actionButtonSeparator: SimpleLayer
|
||||
if let current = strongSelf.actionButtonSeparator {
|
||||
actionButtonSeparator = current
|
||||
animation.animator.updateFrame(layer: actionButtonSeparator, frame: separatorFrame, completion: nil)
|
||||
} else {
|
||||
actionButtonSeparator = SimpleLayer()
|
||||
strongSelf.actionButtonSeparator = actionButtonSeparator
|
||||
strongSelf.layer.addSublayer(actionButtonSeparator)
|
||||
actionButtonSeparator.frame = separatorFrame
|
||||
}
|
||||
|
||||
actionButtonSeparator.backgroundColor = mainColor.withMultipliedAlpha(0.2).cgColor
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -379,7 +474,10 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
|
||||
if self.buttonNode.frame.contains(point) {
|
||||
if self.messageButtonNode.frame.contains(point) {
|
||||
return ChatMessageBubbleContentTapAction(content: .ignore)
|
||||
}
|
||||
if self.addButtonNode.frame.contains(point) {
|
||||
return ChatMessageBubbleContentTapAction(content: .openMessage)
|
||||
}
|
||||
if self.dateAndStatusNode.supernode != nil, let _ = self.dateAndStatusNode.hitTest(self.view.convert(point, to: self.dateAndStatusNode.view), with: nil) {
|
||||
@ -396,12 +494,26 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
@objc private func addButtonPressed() {
|
||||
if let item = self.item {
|
||||
let _ = item.controllerInteraction.openMessage(item.message, OpenMessageParams(mode: .default))
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func messageButtonPressed() {
|
||||
if let item = self.item {
|
||||
var selectedContact: TelegramMediaContact?
|
||||
for media in item.message.media {
|
||||
if let media = media as? TelegramMediaContact {
|
||||
selectedContact = media
|
||||
}
|
||||
}
|
||||
if let peerId = selectedContact?.peerId, let peer = item.message.peers[peerId] {
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func reactionTargetView(value: MessageReaction.Reaction) -> UIView? {
|
||||
if !self.dateAndStatusNode.isHidden {
|
||||
return self.dateAndStatusNode.reactionView(value: value)
|
||||
|
@ -102,6 +102,7 @@ public final class ChatRecentActionsController: TelegramBaseController {
|
||||
}, finishMediaRecording: { _ in
|
||||
}, stopMediaRecording: {
|
||||
}, lockMediaRecording: {
|
||||
}, resumeMediaRecording: {
|
||||
}, deleteRecordedMedia: {
|
||||
}, sendRecordedMedia: { _, _ in
|
||||
}, displayRestrictedInfo: { _, _ in
|
||||
|
@ -2473,7 +2473,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
messageEntity.secondaryRenderImage = result.nightImage
|
||||
messageEntity.overlayRenderImage = result.overlayImage
|
||||
messageEntity.referenceDrawingSize = storyDimensions
|
||||
messageEntity.position = CGPoint(x: storyDimensions.width / 2.0 - 16.0, y: storyDimensions.height / 2.0)
|
||||
messageEntity.position = CGPoint(x: storyDimensions.width / 2.0 - 54.0, y: storyDimensions.height / 2.0)
|
||||
|
||||
let fraction = max(result.size.width, result.size.height) / 353.0
|
||||
messageEntity.scale = min(6.0, 3.3 * fraction)
|
||||
@ -3375,6 +3375,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
let entity = DrawingStickerEntity(content: .image(updatedImage, .rectangle))
|
||||
entity.canCutOut = false
|
||||
|
||||
let _ = (cutoutStickerImage(from: image)
|
||||
|> deliverOnMainQueue).start(next: { [weak entity] result in
|
||||
if result != nil, let entity {
|
||||
entity.canCutOut = true
|
||||
}
|
||||
})
|
||||
|
||||
self?.interaction?.insertEntity(entity, scale: 2.5)
|
||||
}
|
||||
|
||||
|
@ -347,6 +347,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
||||
}, finishMediaRecording: { _ in
|
||||
}, stopMediaRecording: {
|
||||
}, lockMediaRecording: {
|
||||
}, resumeMediaRecording: {
|
||||
}, deleteRecordedMedia: {
|
||||
}, sendRecordedMedia: { _, _ in
|
||||
}, displayRestrictedInfo: { _, _ in
|
||||
@ -2589,7 +2590,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
|
||||
if message.isCopyProtected() {
|
||||
|
||||
} else if message.id.peerId.namespace != Namespaces.Peer.SecretChat {
|
||||
} else if message.id.peerId.namespace != Namespaces.Peer.SecretChat && message.minAutoremoveOrClearTimeout == nil {
|
||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuForward, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||
c.dismiss(completion: {
|
||||
if let strongSelf = self {
|
||||
|
@ -591,6 +591,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
}, finishMediaRecording: { _ in
|
||||
}, stopMediaRecording: {
|
||||
}, lockMediaRecording: {
|
||||
}, resumeMediaRecording: {
|
||||
}, deleteRecordedMedia: {
|
||||
}, sendRecordedMedia: { _, _ in
|
||||
}, displayRestrictedInfo: { _, _ in
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
12
submodules/TelegramUI/Images.xcassets/Chat/Input/Recording/Pause.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Input/Recording/Pause.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "pausevoicecideo_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
97
submodules/TelegramUI/Images.xcassets/Chat/Input/Recording/Pause.imageset/pausevoicecideo_30.pdf
vendored
Normal file
97
submodules/TelegramUI/Images.xcassets/Chat/Input/Recording/Pause.imageset/pausevoicecideo_30.pdf
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 8.000000 8.000000 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
0.038429 12.390181 m
|
||||
0.000000 12.196983 0.000000 11.964655 0.000000 11.500000 c
|
||||
0.000000 2.500000 l
|
||||
0.000000 2.035345 0.000000 1.803018 0.038429 1.609819 c
|
||||
0.196243 0.816438 0.816438 0.196242 1.609819 0.038429 c
|
||||
1.803017 0.000000 2.035345 0.000000 2.500000 0.000000 c
|
||||
2.964655 0.000000 3.196983 0.000000 3.390181 0.038429 c
|
||||
4.183562 0.196242 4.803757 0.816438 4.961571 1.609819 c
|
||||
5.000000 1.803018 5.000000 2.035345 5.000000 2.500000 c
|
||||
5.000000 11.500000 l
|
||||
5.000000 11.964655 5.000000 12.196983 4.961571 12.390181 c
|
||||
4.803757 13.183562 4.183562 13.803758 3.390181 13.961571 c
|
||||
3.196983 14.000000 2.964655 14.000000 2.500000 14.000000 c
|
||||
2.035345 14.000000 1.803017 14.000000 1.609819 13.961571 c
|
||||
0.816438 13.803758 0.196243 13.183562 0.038429 12.390181 c
|
||||
h
|
||||
9.038429 12.390181 m
|
||||
9.000000 12.196983 9.000000 11.964655 9.000000 11.500000 c
|
||||
9.000000 2.500000 l
|
||||
9.000000 2.035345 9.000000 1.803018 9.038429 1.609819 c
|
||||
9.196242 0.816438 9.816438 0.196242 10.609819 0.038429 c
|
||||
10.803018 0.000000 11.035345 0.000000 11.500000 0.000000 c
|
||||
11.964655 0.000000 12.196982 0.000000 12.390181 0.038429 c
|
||||
13.183562 0.196242 13.803758 0.816438 13.961571 1.609819 c
|
||||
14.000000 1.803018 14.000000 2.035345 14.000000 2.500000 c
|
||||
14.000000 11.500000 l
|
||||
14.000000 11.964655 14.000000 12.196983 13.961571 12.390181 c
|
||||
13.803758 13.183562 13.183562 13.803758 12.390181 13.961571 c
|
||||
12.196982 14.000000 11.964655 14.000000 11.500000 14.000000 c
|
||||
11.035345 14.000000 10.803018 14.000000 10.609819 13.961571 c
|
||||
9.816438 13.803758 9.196242 13.183562 9.038429 12.390181 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1660
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001750 00000 n
|
||||
0000001773 00000 n
|
||||
0000001946 00000 n
|
||||
0000002020 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
2079
|
||||
%%EOF
|
12
submodules/TelegramUI/Images.xcassets/Chat/Input/Recording/SwitchCamera.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Input/Recording/SwitchCamera.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "switchcamera_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
188
submodules/TelegramUI/Images.xcassets/Chat/Input/Recording/SwitchCamera.imageset/switchcamera_30.pdf
vendored
Normal file
188
submodules/TelegramUI/Images.xcassets/Chat/Input/Recording/SwitchCamera.imageset/switchcamera_30.pdf
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 3.084961 5.584656 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
9.490515 20.080341 m
|
||||
9.422689 20.080362 l
|
||||
8.999260 20.080563 8.679590 20.080715 8.371326 20.006708 c
|
||||
8.099400 19.941423 7.839443 19.833746 7.600999 19.687628 c
|
||||
7.330693 19.521984 7.104758 19.295835 6.805490 18.996283 c
|
||||
6.757546 18.948309 l
|
||||
5.959852 18.150616 l
|
||||
5.772345 17.963108 5.716480 17.908821 5.660614 17.864992 c
|
||||
5.468593 17.714350 5.238950 17.619228 4.996650 17.589970 c
|
||||
4.926156 17.581457 4.848266 17.580341 4.583089 17.580341 c
|
||||
4.484859 17.580357 l
|
||||
3.725908 17.580544 3.223788 17.580666 2.792615 17.474993 c
|
||||
1.466152 17.149897 0.430476 16.114222 0.105380 14.787757 c
|
||||
-0.000294 14.356585 -0.000171 13.854465 0.000015 13.095512 c
|
||||
0.000031 12.997284 l
|
||||
0.000031 5.465342 l
|
||||
0.000031 5.436520 l
|
||||
0.000025 4.620880 0.000020 3.968216 0.043112 3.440796 c
|
||||
0.087345 2.899416 0.180274 2.431705 0.399492 2.001467 c
|
||||
0.750868 1.311853 1.311542 0.751179 2.001156 0.399803 c
|
||||
2.431395 0.180586 2.899104 0.087656 3.440485 0.043423 c
|
||||
3.967894 0.000332 4.620543 0.000336 5.436161 0.000341 c
|
||||
5.436225 0.000341 l
|
||||
5.465031 0.000341 l
|
||||
18.365032 0.000341 l
|
||||
18.393837 0.000341 l
|
||||
18.393900 0.000341 l
|
||||
19.209520 0.000336 19.862169 0.000332 20.389578 0.043423 c
|
||||
20.930958 0.087656 21.398668 0.180586 21.828907 0.399803 c
|
||||
22.518520 0.751179 23.079195 1.311853 23.430571 2.001467 c
|
||||
23.649788 2.431705 23.742718 2.899416 23.786951 3.440796 c
|
||||
23.830042 3.968204 23.830038 4.620852 23.830032 5.436470 c
|
||||
23.830032 5.436536 l
|
||||
23.830032 5.465342 l
|
||||
23.830032 12.997283 l
|
||||
23.830048 13.095503 l
|
||||
23.830235 13.854461 23.830357 14.356583 23.724682 14.787757 c
|
||||
23.399588 16.114222 22.363911 17.149897 21.037447 17.474993 c
|
||||
20.606276 17.580666 20.104155 17.580544 19.345201 17.580357 c
|
||||
19.246973 17.580341 l
|
||||
18.981796 17.580341 18.903906 17.581457 18.833412 17.589970 c
|
||||
18.591112 17.619228 18.361469 17.714350 18.169449 17.864992 c
|
||||
18.113583 17.908821 18.057718 17.963108 17.870209 18.150616 c
|
||||
17.072515 18.948311 l
|
||||
17.024576 18.996279 l
|
||||
16.725307 19.295834 16.499371 19.521982 16.229063 19.687628 c
|
||||
15.990620 19.833746 15.730661 19.941423 15.458736 20.006708 c
|
||||
15.150473 20.080715 14.830803 20.080563 14.407373 20.080362 c
|
||||
14.339548 20.080341 l
|
||||
9.490515 20.080341 l
|
||||
h
|
||||
8.681808 18.713455 m
|
||||
8.817650 18.746067 8.969681 18.750341 9.490515 18.750341 c
|
||||
14.339548 18.750341 l
|
||||
14.860382 18.750341 15.012413 18.746067 15.148252 18.713455 c
|
||||
15.284472 18.680752 15.414694 18.626812 15.534140 18.553616 c
|
||||
15.653254 18.480623 15.763777 18.376143 16.132063 18.007858 c
|
||||
16.929756 17.210163 l
|
||||
16.954220 17.185692 l
|
||||
16.954237 17.185675 l
|
||||
17.106804 17.033035 17.221756 16.918030 17.348524 16.818577 c
|
||||
17.731848 16.517857 18.190273 16.327971 18.673967 16.269562 c
|
||||
18.833941 16.250244 18.996555 16.250284 19.212389 16.250336 c
|
||||
19.246973 16.250341 l
|
||||
20.139725 16.250341 20.466656 16.245523 20.720854 16.183224 c
|
||||
21.565954 15.976102 22.225792 15.316265 22.432913 14.471165 c
|
||||
22.495213 14.216966 22.500031 13.890034 22.500031 12.997283 c
|
||||
22.500031 5.465342 l
|
||||
22.500031 4.614289 22.499514 4.015995 22.461367 3.549101 c
|
||||
22.423855 3.089968 22.353294 2.816771 22.245531 2.605274 c
|
||||
22.021667 2.165916 21.664457 1.808706 21.225100 1.584843 c
|
||||
21.013603 1.477079 20.740406 1.406519 20.281273 1.369007 c
|
||||
19.814379 1.330860 19.216084 1.330343 18.365032 1.330343 c
|
||||
5.465031 1.330343 l
|
||||
4.613979 1.330343 4.015684 1.330860 3.548789 1.369007 c
|
||||
3.089657 1.406519 2.816460 1.477079 2.604963 1.584843 c
|
||||
2.165605 1.808706 1.808395 2.165916 1.584531 2.605274 c
|
||||
1.476768 2.816771 1.406208 3.089968 1.368695 3.549101 c
|
||||
1.330548 4.015995 1.330031 4.614290 1.330031 5.465342 c
|
||||
1.330031 12.997284 l
|
||||
1.330031 13.890034 1.334849 14.216966 1.397150 14.471165 c
|
||||
1.604271 15.316265 2.264108 15.976102 3.109208 16.183224 c
|
||||
3.363407 16.245523 3.690338 16.250341 4.583089 16.250341 c
|
||||
4.617674 16.250336 l
|
||||
4.617689 16.250336 l
|
||||
4.833515 16.250284 4.996125 16.250244 5.156096 16.269562 c
|
||||
5.639788 16.327971 6.098214 16.517857 6.481537 16.818577 c
|
||||
6.608315 16.918037 6.723272 17.033049 6.875851 17.185703 c
|
||||
6.900304 17.210163 l
|
||||
7.697999 18.007858 l
|
||||
8.066284 18.376143 8.176808 18.480623 8.295923 18.553616 c
|
||||
8.415368 18.626812 8.545589 18.680752 8.681808 18.713455 c
|
||||
h
|
||||
8.819138 12.449797 m
|
||||
9.606685 13.253181 10.702194 13.750379 11.915030 13.750379 c
|
||||
13.964883 13.750379 15.683014 12.327185 16.134258 10.415339 c
|
||||
14.848875 10.415339 l
|
||||
14.636918 10.415339 14.521129 10.168130 14.656816 10.005297 c
|
||||
16.622980 7.645809 l
|
||||
16.722927 7.525869 16.907139 7.525866 17.007090 7.645802 c
|
||||
18.973408 10.005290 l
|
||||
19.109104 10.168120 18.993317 10.415339 18.781355 10.415339 c
|
||||
17.491955 10.415339 l
|
||||
17.019377 13.067384 14.702655 15.080379 11.915030 15.080379 c
|
||||
10.330412 15.080379 8.896755 14.428887 7.869370 13.380838 c
|
||||
7.612269 13.118567 7.616461 12.697534 7.878733 12.440434 c
|
||||
8.141004 12.183333 8.562037 12.187525 8.819138 12.449797 c
|
||||
h
|
||||
6.338119 8.415344 m
|
||||
5.048842 8.415344 l
|
||||
4.836884 8.415344 4.721095 8.662557 4.856786 8.825389 c
|
||||
6.822981 11.184870 l
|
||||
6.922931 11.304811 7.107148 11.304810 7.207097 11.184867 c
|
||||
9.173254 8.825387 l
|
||||
9.308942 8.662554 9.193151 8.415344 8.981194 8.415344 c
|
||||
7.695821 8.415344 l
|
||||
8.147092 6.503536 9.865205 5.080379 11.915030 5.080379 c
|
||||
13.096758 5.080379 14.166923 5.552347 14.949532 6.319569 c
|
||||
15.211796 6.576675 15.632830 6.572495 15.889937 6.310231 c
|
||||
16.147045 6.047967 16.142864 5.626933 15.880600 5.369825 c
|
||||
14.859452 4.368757 13.458792 3.750378 11.915030 3.750378 c
|
||||
9.127432 3.750378 6.810725 5.763336 6.338119 8.415344 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
5480
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000005570 00000 n
|
||||
0000005593 00000 n
|
||||
0000005766 00000 n
|
||||
0000005840 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
5899
|
||||
%%EOF
|
@ -9499,13 +9499,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
strongSelf.beginMediaRecordingRequestId += 1
|
||||
strongSelf.lockMediaRecordingRequestId = nil
|
||||
strongSelf.stopMediaRecorder()
|
||||
strongSelf.stopMediaRecorder(pause: true)
|
||||
}, lockMediaRecording: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.lockMediaRecordingRequestId = strongSelf.beginMediaRecordingRequestId
|
||||
strongSelf.lockMediaRecorder()
|
||||
}, resumeMediaRecording: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.resumeMediaRecorder()
|
||||
}, deleteRecordedMedia: { [weak self] in
|
||||
self?.deleteMediaRecording()
|
||||
}, sendRecordedMedia: { [weak self] silentPosting, viewOnce in
|
||||
@ -15546,98 +15551,106 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if let audioRecorderValue = self.audioRecorderValue {
|
||||
audioRecorderValue.stop()
|
||||
switch action {
|
||||
case .pause:
|
||||
audioRecorderValue.pause()
|
||||
default:
|
||||
audioRecorderValue.stop()
|
||||
}
|
||||
|
||||
switch updatedAction {
|
||||
case .dismiss:
|
||||
self.chatDisplayNode.updateRecordedMediaDeleted(true)
|
||||
case .dismiss:
|
||||
self.chatDisplayNode.updateRecordedMediaDeleted(true)
|
||||
self.audioRecorder.set(.single(nil))
|
||||
case .preview, .pause:
|
||||
if case .preview = updatedAction {
|
||||
self.audioRecorder.set(.single(nil))
|
||||
case .preview:
|
||||
self.audioRecorder.set(.single(nil))
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
$0.updatedInputTextPanelState { panelState in
|
||||
return panelState.withUpdatedMediaRecordingState(.waitingForPreview)
|
||||
}
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
$0.updatedInputTextPanelState { panelState in
|
||||
return panelState.withUpdatedMediaRecordingState(.waitingForPreview)
|
||||
}
|
||||
})
|
||||
let _ = (audioRecorderValue.takenRecordedData()
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] data in
|
||||
if let strongSelf = self, let data = data {
|
||||
if data.duration < 0.5 {
|
||||
strongSelf.recorderFeedback?.error()
|
||||
strongSelf.recorderFeedback = nil
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
$0.updatedInputTextPanelState { panelState in
|
||||
return panelState.withUpdatedMediaRecordingState(nil)
|
||||
}
|
||||
})
|
||||
} else if let waveform = data.waveform {
|
||||
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max), size: Int64(data.compressedData.count))
|
||||
|
||||
strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data.compressedData)
|
||||
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
$0.updatedRecordedMediaPreview(ChatRecordedMediaPreview(resource: resource, duration: Int32(data.duration), fileSize: Int32(data.compressedData.count), waveform: AudioWaveform(bitstream: waveform, bitsPerSample: 5))).updatedInputTextPanelState { panelState in
|
||||
return panelState.withUpdatedMediaRecordingState(nil)
|
||||
}
|
||||
})
|
||||
strongSelf.recorderFeedback = nil
|
||||
strongSelf.updateDownButtonVisibility()
|
||||
}
|
||||
})
|
||||
let _ = (audioRecorderValue.takenRecordedData() |> deliverOnMainQueue).startStandalone(next: { [weak self] data in
|
||||
if let strongSelf = self, let data = data {
|
||||
if data.duration < 0.5 {
|
||||
strongSelf.recorderFeedback?.error()
|
||||
strongSelf.recorderFeedback = nil
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
$0.updatedInputTextPanelState { panelState in
|
||||
return panelState.withUpdatedMediaRecordingState(nil)
|
||||
}
|
||||
})
|
||||
case let .send(viewOnce):
|
||||
self.chatDisplayNode.updateRecordedMediaDeleted(false)
|
||||
let _ = (audioRecorderValue.takenRecordedData()
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] data in
|
||||
if let strongSelf = self, let data = data {
|
||||
if data.duration < 0.5 {
|
||||
strongSelf.recorderFeedback?.error()
|
||||
strongSelf.recorderFeedback = nil
|
||||
strongSelf.audioRecorder.set(.single(nil))
|
||||
} else {
|
||||
let randomId = Int64.random(in: Int64.min ... Int64.max)
|
||||
|
||||
let resource = LocalFileMediaResource(fileId: randomId)
|
||||
strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data.compressedData)
|
||||
|
||||
let waveformBuffer: Data? = data.waveform
|
||||
|
||||
let correlationId = Int64.random(in: 0 ..< Int64.max)
|
||||
var usedCorrelationId = false
|
||||
|
||||
if strongSelf.chatDisplayNode.shouldAnimateMessageTransition, let textInputPanelNode = strongSelf.chatDisplayNode.textInputPanelNode, let micButton = textInputPanelNode.micButton {
|
||||
usedCorrelationId = true
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .audioMicInput(ChatMessageTransitionNodeImpl.Source.AudioMicInput(micButton: micButton)), initiated: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
})
|
||||
} else if let waveform = data.waveform {
|
||||
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max), size: Int64(data.compressedData.count))
|
||||
|
||||
strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data.compressedData)
|
||||
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
$0.updatedRecordedMediaPreview(ChatRecordedMediaPreview(resource: resource, duration: Int32(data.duration), fileSize: Int32(data.compressedData.count), waveform: AudioWaveform(bitstream: waveform, bitsPerSample: 5))).updatedInputTextPanelState { panelState in
|
||||
return panelState.withUpdatedMediaRecordingState(nil)
|
||||
}
|
||||
})
|
||||
strongSelf.recorderFeedback = nil
|
||||
strongSelf.updateDownButtonVisibility()
|
||||
}
|
||||
}
|
||||
})
|
||||
case let .send(viewOnce):
|
||||
self.chatDisplayNode.updateRecordedMediaDeleted(false)
|
||||
let _ = (audioRecorderValue.takenRecordedData()
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] data in
|
||||
if let strongSelf = self, let data = data {
|
||||
if data.duration < 0.5 {
|
||||
strongSelf.recorderFeedback?.error()
|
||||
strongSelf.recorderFeedback = nil
|
||||
strongSelf.audioRecorder.set(.single(nil))
|
||||
} else {
|
||||
let randomId = Int64.random(in: Int64.min ... Int64.max)
|
||||
|
||||
let resource = LocalFileMediaResource(fileId: randomId)
|
||||
strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data.compressedData)
|
||||
|
||||
let waveformBuffer: Data? = data.waveform
|
||||
|
||||
let correlationId = Int64.random(in: 0 ..< Int64.max)
|
||||
var usedCorrelationId = false
|
||||
|
||||
if strongSelf.chatDisplayNode.shouldAnimateMessageTransition, let textInputPanelNode = strongSelf.chatDisplayNode.textInputPanelNode, let micButton = textInputPanelNode.micButton {
|
||||
usedCorrelationId = true
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .audioMicInput(ChatMessageTransitionNodeImpl.Source.AudioMicInput(micButton: micButton)), initiated: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.audioRecorder.set(.single(nil))
|
||||
})
|
||||
} else {
|
||||
strongSelf.audioRecorder.set(.single(nil))
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatDisplayNode.collapseInput()
|
||||
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) }
|
||||
})
|
||||
}
|
||||
}, usedCorrelationId ? correlationId : nil)
|
||||
|
||||
var attributes: [MessageAttribute] = []
|
||||
if viewOnce {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: viewOnceTimeout, countdownBeginTime: nil))
|
||||
}
|
||||
|
||||
strongSelf.sendMessages([.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(data.compressedData.count), attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)])), threadId: strongSelf.chatLocation.threadId, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: [])])
|
||||
|
||||
strongSelf.recorderFeedback?.tap()
|
||||
strongSelf.recorderFeedback = nil
|
||||
})
|
||||
} else {
|
||||
strongSelf.audioRecorder.set(.single(nil))
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatDisplayNode.collapseInput()
|
||||
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) }
|
||||
})
|
||||
}
|
||||
}, usedCorrelationId ? correlationId : nil)
|
||||
|
||||
var attributes: [MessageAttribute] = []
|
||||
if viewOnce {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: viewOnceTimeout, countdownBeginTime: nil))
|
||||
}
|
||||
|
||||
strongSelf.sendMessages([.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(data.compressedData.count), attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)])), threadId: strongSelf.chatLocation.threadId, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: [])])
|
||||
|
||||
strongSelf.recorderFeedback?.tap()
|
||||
strongSelf.recorderFeedback = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if let videoRecorderValue = self.videoRecorderValue {
|
||||
if case .send = updatedAction {
|
||||
@ -15660,10 +15673,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
func stopMediaRecorder() {
|
||||
func stopMediaRecorder(pause: Bool = false) {
|
||||
if let audioRecorderValue = self.audioRecorderValue {
|
||||
if let _ = self.presentationInterfaceState.inputTextPanelState.mediaRecordingState {
|
||||
self.dismissMediaRecorder(.preview)
|
||||
self.dismissMediaRecorder(pause ? .pause : .preview)
|
||||
} else {
|
||||
audioRecorderValue.stop()
|
||||
self.audioRecorder.set(.single(nil))
|
||||
@ -15681,6 +15694,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
func resumeMediaRecorder() {
|
||||
if let audioRecorderValue = self.audioRecorderValue {
|
||||
audioRecorderValue.resume()
|
||||
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
$0.updatedInputTextPanelState { panelState in
|
||||
return panelState.withUpdatedMediaRecordingState(.audio(recorder: audioRecorderValue, isLocked: true))
|
||||
}.updatedRecordedMediaPreview(nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func lockMediaRecorder() {
|
||||
if self.presentationInterfaceState.inputTextPanelState.mediaRecordingState != nil {
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
|
@ -21,6 +21,7 @@ func chatHistoryEntriesForView(
|
||||
selectedMessages: Set<MessageId>?,
|
||||
presentationData: ChatPresentationData,
|
||||
historyAppearsCleared: Bool,
|
||||
skipViewOnceMedia: Bool,
|
||||
pendingUnpinnedAllMessages: Bool,
|
||||
pendingRemovedMessages: Set<MessageId>,
|
||||
associatedData: ChatMessageItemAssociatedData,
|
||||
@ -152,6 +153,10 @@ func chatHistoryEntriesForView(
|
||||
}
|
||||
}
|
||||
|
||||
if skipViewOnceMedia, message.minAutoremoveOrClearTimeout != nil {
|
||||
continue loop
|
||||
}
|
||||
|
||||
var contentTypeHint: ChatMessageEntryContentType = .generic
|
||||
|
||||
for media in message.media {
|
||||
|
@ -1588,6 +1588,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
||||
selectedMessages: selectedMessages,
|
||||
presentationData: chatPresentationData,
|
||||
historyAppearsCleared: historyAppearsCleared,
|
||||
skipViewOnceMedia: mode != .bubbles,
|
||||
pendingUnpinnedAllMessages: pendingUnpinnedAllMessages,
|
||||
pendingRemovedMessages: pendingRemovedMessages,
|
||||
associatedData: associatedData,
|
||||
|
@ -909,7 +909,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
actions.insert(.separator, at: 1)
|
||||
}
|
||||
|
||||
if !hasRateTranscription {
|
||||
if !hasRateTranscription && message.minAutoremoveOrClearTimeout == nil {
|
||||
for media in message.media {
|
||||
if let file = media as? TelegramMediaFile, let size = file.size, size < 1 * 1024 * 1024, let duration = file.duration, duration < 60, (["audio/mpeg", "audio/mp3", "audio/mpeg3", "audio/ogg"] as [String]).contains(file.mimeType.lowercased()) {
|
||||
let fileName = file.fileName ?? "Tone"
|
||||
|
@ -69,6 +69,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
||||
|
||||
private var viewOnce = false
|
||||
let viewOnceButton: ChatRecordingViewOnceButtonNode
|
||||
let recordMoreButton: ChatRecordingViewOnceButtonNode
|
||||
|
||||
private let waveformNode: AudioWaveformNode
|
||||
private let waveformForegroundNode: AudioWaveformNode
|
||||
@ -104,7 +105,8 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
||||
self.sendButton.displaysAsynchronously = false
|
||||
self.sendButton.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(theme), for: [])
|
||||
|
||||
self.viewOnceButton = ChatRecordingViewOnceButtonNode()
|
||||
self.viewOnceButton = ChatRecordingViewOnceButtonNode(icon: .viewOnce)
|
||||
self.recordMoreButton = ChatRecordingViewOnceButtonNode(icon: .recordMore)
|
||||
|
||||
self.waveformBackgroundNode = ASImageNode()
|
||||
self.waveformBackgroundNode.isLayerBacked = true
|
||||
@ -173,6 +175,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
||||
self.deleteButton.addTarget(self, action: #selector(self.deletePressed), forControlEvents: [.touchUpInside])
|
||||
self.sendButton.addTarget(self, action: #selector(self.sendPressed), forControlEvents: [.touchUpInside])
|
||||
self.viewOnceButton.addTarget(self, action: #selector(self.viewOncePressed), forControlEvents: [.touchUpInside])
|
||||
self.recordMoreButton.addTarget(self, action: #selector(self.recordMorePressed), forControlEvents: [.touchUpInside])
|
||||
|
||||
self.waveformButton.addTarget(self, action: #selector(self.waveformPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
@ -197,6 +200,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
||||
|
||||
if let viewForOverlayContent = self.viewForOverlayContent {
|
||||
viewForOverlayContent.addSubnode(self.viewOnceButton)
|
||||
viewForOverlayContent.addSubnode(self.recordMoreButton)
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,7 +275,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
}
|
||||
|
||||
if isFirstTime {
|
||||
if isFirstTime, !self.viewOnceButton.isHidden {
|
||||
self.maybePresentViewOnceTooltip()
|
||||
}
|
||||
|
||||
@ -282,9 +286,13 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
||||
self.binNode.frame = self.deleteButton.bounds
|
||||
|
||||
let viewOnceSize = self.viewOnceButton.update(theme: interfaceState.theme)
|
||||
let viewOnceButtonFrame = CGRect(origin: CGPoint(x: width - rightInset - 44.0 - UIScreenPixel, y: -64.0), size: viewOnceSize)
|
||||
let viewOnceButtonFrame = CGRect(origin: CGPoint(x: width - rightInset - 44.0 - UIScreenPixel, y: -64.0 - 53.0), size: viewOnceSize)
|
||||
transition.updateFrame(node: self.viewOnceButton, frame: viewOnceButtonFrame)
|
||||
|
||||
let recordMoreSize = self.recordMoreButton.update(theme: interfaceState.theme)
|
||||
let recordMoreButtonFrame = CGRect(origin: CGPoint(x: width - rightInset - 44.0 - UIScreenPixel, y: -64.0), size: recordMoreSize)
|
||||
transition.updateFrame(node: self.recordMoreButton, frame: recordMoreButtonFrame)
|
||||
|
||||
var isScheduledMessages = false
|
||||
if case .scheduledMessages = interfaceState.subject {
|
||||
isScheduledMessages = true
|
||||
@ -332,6 +340,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
||||
|
||||
prevTextInputPanelNode.viewOnceButton.isHidden = true
|
||||
prevTextInputPanelNode.viewOnce = false
|
||||
|
||||
self.viewOnceButton.layer.animatePosition(from: prevTextInputPanelNode.viewOnceButton.position, to: self.viewOnceButton.position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
|
||||
prevTextInputPanelNode.viewOnceButton.isHidden = false
|
||||
prevTextInputPanelNode.viewOnceButton.update(isSelected: false, animated: false)
|
||||
@ -420,6 +429,12 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func recordMorePressed() {
|
||||
self.tooltipController?.dismiss()
|
||||
|
||||
self.interfaceInteraction?.resumeMediaRecording()
|
||||
}
|
||||
|
||||
private func displayViewOnceTooltip(text: String, hasIcon: Bool) {
|
||||
guard let context = self.context, let parentController = self.interfaceInteraction?.chatController() else {
|
||||
return
|
||||
@ -515,19 +530,28 @@ private final class PlayPauseIconNode: ManagedAnimationNode {
|
||||
|
||||
|
||||
final class ChatRecordingViewOnceButtonNode: HighlightTrackingButtonNode {
|
||||
enum Icon {
|
||||
case viewOnce
|
||||
case recordMore
|
||||
}
|
||||
|
||||
private let icon: Icon
|
||||
|
||||
private let backgroundNode: ASImageNode
|
||||
private let iconNode: ASImageNode
|
||||
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
override init(pointerStyle: PointerStyle? = nil) {
|
||||
init(icon: Icon) {
|
||||
self.icon = icon
|
||||
|
||||
self.backgroundNode = ASImageNode()
|
||||
self.backgroundNode.isUserInteractionEnabled = false
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.isUserInteractionEnabled = false
|
||||
|
||||
super.init(pointerStyle: pointerStyle)
|
||||
super.init(pointerStyle: .default)
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
@ -576,7 +600,9 @@ final class ChatRecordingViewOnceButtonNode: HighlightTrackingButtonNode {
|
||||
}
|
||||
|
||||
if updated {
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: self.innerIsSelected ? "Media Gallery/ViewOnceEnabled" : "Media Gallery/ViewOnce"), color: theme.chat.inputPanel.panelControlAccentColor)
|
||||
if case .viewOnce = self.icon {
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: self.innerIsSelected ? "Media Gallery/ViewOnceEnabled" : "Media Gallery/ViewOnce"), color: theme.chat.inputPanel.panelControlAccentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -588,9 +614,16 @@ final class ChatRecordingViewOnceButtonNode: HighlightTrackingButtonNode {
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundNode.image = generateFilledCircleImage(diameter: innerSize.width, color: theme.rootController.navigationBar.opaqueBackgroundColor, strokeColor: theme.chat.inputPanel.panelSeparatorColor, strokeWidth: 0.5, backgroundColor: nil)
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: self.innerIsSelected ? "Media Gallery/ViewOnceEnabled" : "Media Gallery/ViewOnce"), color: theme.chat.inputPanel.panelControlAccentColor)
|
||||
}
|
||||
|
||||
switch self.icon {
|
||||
case .viewOnce:
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: self.innerIsSelected ? "Media Gallery/ViewOnceEnabled" : "Media Gallery/ViewOnce"), color: theme.chat.inputPanel.panelControlAccentColor)
|
||||
|
||||
case .recordMore:
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconMicrophone"), color: theme.chat.inputPanel.panelControlAccentColor)
|
||||
}
|
||||
}
|
||||
|
||||
if let backgroundImage = self.backgroundNode.image {
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(size.width / 2.0 - backgroundImage.size.width / 2.0), y: floorToScreenPixels(size.height / 2.0 - backgroundImage.size.height / 2.0)), size: backgroundImage.size)
|
||||
self.backgroundNode.frame = backgroundFrame
|
||||
|
@ -850,7 +850,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
||||
self.counterTextNode = ImmediateTextNode()
|
||||
self.counterTextNode.textAlignment = .center
|
||||
|
||||
self.viewOnceButton = ChatRecordingViewOnceButtonNode()
|
||||
self.viewOnceButton = ChatRecordingViewOnceButtonNode(icon: .viewOnce)
|
||||
|
||||
super.init()
|
||||
|
||||
@ -2634,7 +2634,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
||||
}
|
||||
transition.updateAlpha(node: self.viewOnceButton, alpha: viewOnceIsVisible ? 1.0 : 0.0)
|
||||
transition.updateTransformScale(node: self.viewOnceButton, scale: viewOnceIsVisible ? 1.0 : 0.01)
|
||||
if let _ = interfaceState.renderedPeer?.peer as? TelegramUser {
|
||||
if let user = interfaceState.renderedPeer?.peer as? TelegramUser, user.id != interfaceState.accountPeerId && user.botInfo == nil {
|
||||
self.viewOnceButton.isHidden = false
|
||||
} else {
|
||||
self.viewOnceButton.isHidden = true
|
||||
|
@ -447,6 +447,18 @@ final class ManagedAudioRecorderContext {
|
||||
}
|
||||
}
|
||||
|
||||
func pause() {
|
||||
assert(self.queue.isCurrent())
|
||||
|
||||
self.paused = true
|
||||
}
|
||||
|
||||
func resume() {
|
||||
assert(self.queue.isCurrent())
|
||||
|
||||
self.paused = false
|
||||
}
|
||||
|
||||
func stop() {
|
||||
assert(self.queue.isCurrent())
|
||||
|
||||
@ -488,7 +500,7 @@ final class ManagedAudioRecorderContext {
|
||||
free(buffer.mData)
|
||||
}
|
||||
|
||||
if !self.processSamples {
|
||||
if !self.processSamples && !self.paused {
|
||||
return
|
||||
}
|
||||
|
||||
@ -506,7 +518,7 @@ final class ManagedAudioRecorderContext {
|
||||
var currentEncoderPacketSize = 0
|
||||
|
||||
while currentEncoderPacketSize < encoderPacketSizeInBytes {
|
||||
if audioBuffer.count != 0 {
|
||||
if self.audioBuffer.count != 0 {
|
||||
let takenBytes = min(self.audioBuffer.count, encoderPacketSizeInBytes - currentEncoderPacketSize)
|
||||
if takenBytes != 0 {
|
||||
self.audioBuffer.withUnsafeBytes { rawBytes -> Void in
|
||||
@ -699,6 +711,22 @@ final class ManagedAudioRecorderImpl: ManagedAudioRecorder {
|
||||
}
|
||||
}
|
||||
|
||||
func pause() {
|
||||
self.queue.async {
|
||||
if let context = self.contextRef?.takeUnretainedValue() {
|
||||
context.pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resume() {
|
||||
self.queue.async {
|
||||
if let context = self.contextRef?.takeUnretainedValue() {
|
||||
context.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
self.queue.async {
|
||||
if let context = self.contextRef?.takeUnretainedValue() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user