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
e71ce0b516
commit
2a56be9fe0
@ -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
|
||||
|
@ -74,6 +74,8 @@ swift_library(
|
||||
"//submodules/StickerPackPreviewUI",
|
||||
"//submodules/Components/AnimatedStickerComponent",
|
||||
"//submodules/OpenInExternalAppUI",
|
||||
"//submodules/MediaPasteboardUI",
|
||||
"//submodules/WebPBinding",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user