Various fixes

This commit is contained in:
Ilya Laktyushin 2023-07-08 19:09:22 +02:00
parent e71ce0b516
commit 2a56be9fe0
5 changed files with 167 additions and 43 deletions

View File

@ -21,7 +21,6 @@ public final class AvatarStoryIndicatorComponent: Component {
public let activeLineWidth: CGFloat
public let inactiveLineWidth: CGFloat
public let isGlassBackground: Bool
public let backgroundColor: UIColor?
public let counters: Counters?
public init(
@ -31,7 +30,6 @@ public final class AvatarStoryIndicatorComponent: Component {
activeLineWidth: CGFloat,
inactiveLineWidth: CGFloat,
isGlassBackground: Bool = false,
backgroundColor: UIColor? = nil,
counters: Counters?
) {
self.hasUnseen = hasUnseen
@ -40,7 +38,6 @@ public final class AvatarStoryIndicatorComponent: Component {
self.activeLineWidth = activeLineWidth
self.inactiveLineWidth = inactiveLineWidth
self.isGlassBackground = isGlassBackground
self.backgroundColor = backgroundColor
self.counters = counters
}
@ -63,9 +60,6 @@ public final class AvatarStoryIndicatorComponent: Component {
if lhs.isGlassBackground != rhs.isGlassBackground {
return false
}
if lhs.backgroundColor != rhs.backgroundColor {
return false
}
if lhs.counters != rhs.counters {
return false
}
@ -120,14 +114,6 @@ public final class AvatarStoryIndicatorComponent: Component {
var locations: [CGFloat] = [0.0, 1.0]
if let backgroundColor = component.backgroundColor {
context.setLineWidth(lineWidth)
context.setStrokeColor(backgroundColor.cgColor)
context.strokeEllipse(in: CGRect(origin: CGPoint(x: size.width * 0.5 - diameter * 0.5, y: size.height * 0.5 - diameter * 0.5), size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5).insetBy(dx: lineWidth, dy: lineWidth))
}
context.setLineWidth(lineWidth)
if let counters = component.counters, counters.totalCount > 1 {
let center = CGPoint(x: size.width * 0.5, y: size.height * 0.5)
let radius = (diameter - component.activeLineWidth) * 0.5

View File

@ -74,6 +74,8 @@ swift_library(
"//submodules/StickerPackPreviewUI",
"//submodules/Components/AnimatedStickerComponent",
"//submodules/OpenInExternalAppUI",
"//submodules/MediaPasteboardUI",
"//submodules/WebPBinding",
],
visibility = [
"//visibility:public",

View File

@ -1803,7 +1803,23 @@ public final class StoryItemSetContainerComponent: Component {
self.voiceMessagesRestrictedTooltipController = controller
self.state?.updated(transition: Transition(animation: .curve(duration: 0.2, curve: .easeInOut)))
},
paste: { _ in
paste: { [weak self] data in
guard let self else {
return
}
switch data {
case let .images(images):
self.sendMessageContext.presentMediaPasteboard(view: self, subjects: images.map { .image($0) })
case let .video(data):
let tempFilePath = NSTemporaryDirectory() + "\(Int64.random(in: 0...Int64.max)).mp4"
let url = NSURL(fileURLWithPath: tempFilePath) as URL
try? data.write(to: url)
self.sendMessageContext.presentMediaPasteboard(view: self, subjects: [.video(url)])
case let .gif(data):
self.sendMessageContext.enqueueGifData(view: self, data: data)
case let .sticker(image, isMemoji):
self.sendMessageContext.enqueueStickerImage(view: self, image: image, isMemoji: isMemoji)
}
},
audioRecorder: self.sendMessageContext.audioRecorderValue,
videoRecordingStatus: !self.sendMessageContext.hasRecordedVideoPreview ? self.sendMessageContext.videoRecorderValue?.audioStatus : nil,

View File

@ -38,6 +38,8 @@ import TextFieldComponent
import StickerPackPreviewUI
import OpenInExternalAppUI
import SafariServices
import MediaPasteboardUI
import WebPBinding
final class StoryItemSetContainerSendMessage {
enum InputMode {
@ -525,6 +527,75 @@ final class StoryItemSetContainerSendMessage {
controller?.requestLayout(forceUpdate: true, transition: .animated(duration: 0.3, curve: .spring))
}
func enqueueGifData(view: StoryItemSetContainerComponent.View, data: Data) {
guard let component = view.component else {
return
}
let peer = component.slice.peer
let _ = (legacyEnqueueGifMessage(account: component.context.account, data: data) |> deliverOnMainQueue).start(next: { [weak self, weak view] message in
if let self, let view {
let messages = self.transformEnqueueMessages(view: view, messages: [message], silentPosting: false)
self.sendMessages(view: view, peer: peer, messages: messages)
}
})
}
func enqueueStickerImage(view: StoryItemSetContainerComponent.View, image: UIImage, isMemoji: Bool) {
guard let component = view.component else {
return
}
let peer = component.slice.peer
let size = image.size.aspectFitted(CGSize(width: 512.0, height: 512.0))
func scaleImage(_ image: UIImage, size: CGSize, boundiingSize: CGSize) -> UIImage? {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
let format = UIGraphicsImageRendererFormat()
format.scale = 1.0
let renderer = UIGraphicsImageRenderer(size: size, format: format)
return renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: size))
}
} else {
return TGScaleImageToPixelSize(image, size)
}
}
func convertToWebP(image: UIImage, targetSize: CGSize?, targetBoundingSize: CGSize?, quality: CGFloat) -> Signal<Data, NoError> {
var image = image
if let targetSize = targetSize, let scaledImage = scaleImage(image, size: targetSize, boundiingSize: targetSize) {
image = scaledImage
}
return Signal { subscriber in
if let data = try? WebP.convert(toWebP: image, quality: quality * 100.0) {
subscriber.putNext(data)
}
subscriber.putCompletion()
return EmptyDisposable
} |> runOn(Queue.concurrentDefaultQueue())
}
let _ = (convertToWebP(image: image, targetSize: size, targetBoundingSize: size, quality: 0.9) |> deliverOnMainQueue).start(next: { [weak self, weak view] data in
if let self, let view, !data.isEmpty {
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
component.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
var fileAttributes: [TelegramMediaFileAttribute] = []
fileAttributes.append(.FileName(fileName: "sticker.webp"))
fileAttributes.append(.Sticker(displayText: "", packReference: nil, maskData: nil))
fileAttributes.append(.ImageSize(size: PixelDimensions(size)))
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: Int64(data.count), attributes: fileAttributes)
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
let messages = self.transformEnqueueMessages(view: view, messages: [message], silentPosting: false)
self.sendMessages(view: view, peer: peer, messages: messages)
}
})
}
func setMediaRecordingActive(
view: StoryItemSetContainerComponent.View,
isActive: Bool,
@ -1763,6 +1834,69 @@ final class StoryItemSetContainerSendMessage {
})
}
func presentMediaPasteboard(view: StoryItemSetContainerComponent.View, subjects: [MediaPickerScreen.Subject.Media]) {
guard let component = view.component else {
return
}
let focusedItem = component.slice.item
guard let peerId = focusedItem.peerId else {
return
}
let focusedStoryId = StoryId(peerId: peerId, id: focusedItem.storyItem.id)
guard let inputPanelView = view.inputPanel.view as? MessageInputPanelComponent.View else {
return
}
var inputText = NSAttributedString(string: "")
switch inputPanelView.getSendMessageInput() {
case let .text(text):
inputText = text
}
let peer = component.slice.peer
let theme = defaultDarkPresentationTheme
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>) = (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) })
let controller = mediaPasteboardScreen(
context: component.context,
updatedPresentationData: updatedPresentationData,
peer: peer,
subjects: subjects,
presentMediaPicker: { [weak self] subject, saveEditedPhotos, bannedSendPhotos, bannedSendVideos, present in
if let self {
self.presentMediaPicker(
view: view,
peer: peer,
replyToMessageId: nil,
replyToStoryId: focusedStoryId,
subject: subject,
saveEditedPhotos: saveEditedPhotos,
bannedSendPhotos: bannedSendPhotos,
bannedSendVideos: bannedSendVideos,
present: { controller, mediaPickerContext in
if !inputText.string.isEmpty {
mediaPickerContext?.setCaption(inputText)
}
present(controller, mediaPickerContext)
},
updateMediaPickerContext: { _ in },
completion: { [weak self, weak view] signals, silentPosting, scheduleTime, getAnimatedTransitionSource, completion in
guard let self, let view else {
return
}
if !inputText.string.isEmpty {
self.clearInputText(view: view)
}
self.enqueueMediaMessages(view: view, peer: peer, replyToMessageId: nil, replyToStoryId: focusedStoryId, signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
}
)
}
},
getSourceRect: nil
)
controller.navigationPresentation = .flatModal
component.controller()?.push(controller)
}
private func enqueueChatContextResult(view: StoryItemSetContainerComponent.View, peer: EnginePeer, replyMessageId: EngineMessage.Id?, storyId: StoryId?, results: ChatContextResultCollection, result: ChatContextResult, hideVia: Bool = false, closeMediaInput: Bool = false, silentPosting: Bool = false, resetTextInputState: Bool = true) {
if !canSendMessagesToPeer(peer._asPeer()) {
return

View File

@ -454,7 +454,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
self.playbackStartDisposable.dispose()
}
func updateStoryView(transition: ContainedViewLayoutTransition, theme: PresentationTheme, avatarMaskValue: CGFloat) {
func updateStoryView(transition: ContainedViewLayoutTransition, theme: PresentationTheme) {
if let storyData = self.storyData {
let avatarStoryView: ComponentView<Empty>
if let current = self.avatarStoryView {
@ -464,8 +464,6 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
self.avatarStoryView = avatarStoryView
}
let inset: CGFloat = storyData.hasUnseen ? 3.0 ? 2.0
let avatarFrame = self.avatarNode.frame.insetBy(dx: inset, dy: inset)
let _ = avatarStoryView.update(
transition: Transition(transition),
component: AnyComponent(AvatarStoryIndicatorComponent(
@ -474,23 +472,16 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
theme: theme,
activeLineWidth: 3.0,
inactiveLineWidth: 2.0,
backgroundColor: theme.list.blocksBackgroundColor,
counters: nil
)),
environment: {},
containerSize: avatarFrame.size
containerSize: self.avatarNode.bounds.size
)
if let avatarStoryComponentView = avatarStoryView.view {
if avatarStoryComponentView.superview == nil {
self.containerNode.view.addSubview(avatarStoryComponentView)
self.containerNode.view.insertSubview(avatarStoryComponentView, at: 0)
}
avatarStoryComponentView.bounds = CGRect(origin: .zero, size: avatarFrame.size)
let scaleValue = avatarMaskValue * 0.15
let scale = 1.0 - scaleValue
let offset = min(1.5, avatarMaskValue * 2.5)
avatarStoryComponentView.transform = CGAffineTransformMakeScale(scale, scale)
avatarStoryComponentView.center = avatarFrame.center.offsetBy(dx: 0.0, dy: offset)
avatarStoryComponentView.frame = self.avatarNode.frame
}
} else {
if let avatarStoryView = self.avatarStoryView {
@ -538,17 +529,12 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
}
var removedPhotoResourceIds = Set<String>()
func update(peer: Peer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, avatarMaskValue: CGFloat, isSettings: Bool) {
func update(peer: Peer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) {
if let peer = peer {
let previousItem = self.item
var item = item
self.item = item
var avatarSize = avatarSize
if self.storyData != nil {
avatarSize = avatarSize - 6.0
}
var overrideImage: AvatarNodeImageOverride?
if peer.isDeleted {
overrideImage = .deletedIcon
@ -802,7 +788,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
}
}
self.updateStoryView(transition: .immediate, theme: theme, avatarMaskValue: avatarMaskValue)
self.updateStoryView(transition: .immediate, theme: theme)
}
}
@ -1176,7 +1162,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
let isReady = Promise<Bool>()
var arguments: (Peer?, Int64?, EngineMessageHistoryThread.Info?, PresentationTheme, CGFloat, Bool, CGFloat)?
var arguments: (Peer?, Int64?, EngineMessageHistoryThread.Info?, PresentationTheme, CGFloat, Bool)?
var item: PeerInfoAvatarListItem?
var itemsUpdated: (([PeerInfoAvatarListItem]) -> Void)?
@ -1247,14 +1233,14 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
if let strongSelf = self {
strongSelf.item = items.first
strongSelf.itemsUpdated?(items)
if let (peer, threadId, threadInfo, theme, avatarSize, isExpanded, avatarMaskValue) = strongSelf.arguments {
strongSelf.avatarContainerNode.update(peer: peer, threadId: threadId, threadInfo: threadInfo, item: strongSelf.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, avatarMaskValue: avatarMaskValue, isSettings: strongSelf.isSettings)
if let (peer, threadId, threadInfo, theme, avatarSize, isExpanded) = strongSelf.arguments {
strongSelf.avatarContainerNode.update(peer: peer, threadId: threadId, threadInfo: threadInfo, item: strongSelf.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: strongSelf.isSettings)
}
}
}
self.pinchSourceNode.activate = { [weak self] sourceNode in
guard let strongSelf = self, let (_, _, _, _, _, isExpanded, _) = strongSelf.arguments, isExpanded else {
guard let strongSelf = self, let (_, _, _, _, _, isExpanded) = strongSelf.arguments, isExpanded else {
return
}
let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
@ -1280,13 +1266,13 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
}
}
func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, isForum: Bool, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, theme: PresentationTheme, avatarMaskValue: CGFloat, transition: ContainedViewLayoutTransition) {
self.arguments = (peer, threadId, threadInfo, theme, avatarSize, isExpanded, avatarMaskValue)
func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, isForum: Bool, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) {
self.arguments = (peer, threadId, threadInfo, theme, avatarSize, isExpanded)
self.maskNode.isForum = isForum
self.pinchSourceNode.update(size: size, transition: transition)
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
self.pinchSourceNode.frame = CGRect(origin: CGPoint(), size: size)
self.avatarContainerNode.update(peer: peer, threadId: threadId, threadInfo: threadInfo, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, avatarMaskValue: avatarMaskValue, isSettings: self.isSettings)
self.avatarContainerNode.update(peer: peer, threadId: threadId, threadInfo: threadInfo, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: self.isSettings)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
@ -3435,7 +3421,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
let avatarMaskValue = max(0.0, min(1.0, contentOffset / 120.0))
self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, isForum: isForum, threadId: self.forumTopicThreadId, threadInfo: threadData?.info, theme: presentationData.theme, avatarMaskValue: avatarMaskValue, transition: transition)
self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, isForum: isForum, threadId: self.forumTopicThreadId, threadInfo: threadData?.info, theme: presentationData.theme, transition: transition)
self.editingContentNode.avatarNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
self.avatarOverlayNode.update(peer: peer, threadData: threadData, chatLocation: self.chatLocation, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
if additive {