diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 9bc79ab039..c15d30700e 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -870,7 +870,7 @@ public protocol SharedAccountContext: AnyObject { func makeStorageManagementController(context: AccountContext) -> ViewController func makeAttachmentFileController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileController func makeGalleryCaptionPanelView(context: AccountContext, chatLocation: ChatLocation, customEmojiAvailable: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void) -> NSObject? - func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String) -> ViewController + func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String, all: Bool) -> ViewController func makeMyStoriesController(context: AccountContext, isArchive: Bool) -> ViewController func navigateToChatController(_ params: NavigateToChatControllerParams) func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController) diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 70ca54d052..e439f42aa0 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -3368,13 +3368,13 @@ private final class ShimmerEffectNode: ASDisplayNode { } } -private final class ChatListSearchShimmerNode: ASDisplayNode { +public final class ChatListSearchShimmerNode: ASDisplayNode { private let backgroundColorNode: ASDisplayNode private let effectNode: ShimmerEffectNode private let maskNode: ASImageNode private var currentParams: (size: CGSize, presentationData: PresentationData, key: ChatListSearchPaneKey)? - init(key: ChatListSearchPaneKey) { + public init(key: ChatListSearchPaneKey) { self.backgroundColorNode = ASDisplayNode() self.effectNode = ShimmerEffectNode() self.maskNode = ASImageNode() @@ -3388,7 +3388,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode { self.addSubnode(self.maskNode) } - func update(context: AccountContext, size: CGSize, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, key: ChatListSearchPaneKey, hasSelection: Bool, transition: ContainedViewLayoutTransition) { + public func update(context: AccountContext, size: CGSize, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, key: ChatListSearchPaneKey, hasSelection: Bool, transition: ContainedViewLayoutTransition) { if self.currentParams?.size != size || self.currentParams?.presentationData !== presentationData || self.currentParams?.key != key { self.currentParams = (size, presentationData, key) diff --git a/submodules/DrawingUI/Sources/DrawingEntitiesView.swift b/submodules/DrawingUI/Sources/DrawingEntitiesView.swift index 9b55725ac3..4e0637b984 100644 --- a/submodules/DrawingUI/Sources/DrawingEntitiesView.swift +++ b/submodules/DrawingUI/Sources/DrawingEntitiesView.swift @@ -52,6 +52,7 @@ private func prepareForRendering(entityView: DrawingEntityView) { public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { private let context: AccountContext private let size: CGSize + private let hasBin: Bool weak var drawingView: DrawingView? public weak var selectionContainerView: DrawingSelectionContainerView? @@ -85,9 +86,10 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { private let hapticFeedback = HapticFeedback() - public init(context: AccountContext, size: CGSize) { + public init(context: AccountContext, size: CGSize, hasBin: Bool = false) { self.context = context self.size = size + self.hasBin = hasBin super.init(frame: CGRect(origin: .zero, size: size)) @@ -690,67 +692,71 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { public func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) { let location = gestureRecognizer.location(in: self) if let selectedEntityView = self.selectedEntityView, let selectionView = selectedEntityView.selectionView { - var isTrappedInBin = false - let scale = 100.0 / selectedEntityView.bounds.size.width - switch gestureRecognizer.state { - case .changed: - if self.updateBin(location: location) { - isTrappedInBin = true - } - case .ended, .cancelled: - let _ = self.updateBin(location: nil) - if selectedEntityView.isTrappedInBin { - selectedEntityView.layer.animateScale(from: scale, to: 0.01, duration: 0.2, removeOnCompletion: false) - selectedEntityView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in - self.remove(uuid: selectedEntityView.entity.uuid) - }) - self.selectEntity(nil) - - Queue.mainQueue().after(0.3, { - self.onInteractionUpdated(false) - }) - return - } - default: - break - } - - let transition = Transition.easeInOut(duration: 0.2) - if isTrappedInBin, let binView = self.bin.view { - if !selectedEntityView.isTrappedInBin { - let refs = [ - self.xAxisView, - self.yAxisView, - self.topEdgeView, - self.leftEdgeView, - self.rightEdgeView, - self.bottomEdgeView - ] - for ref in refs { - transition.setAlpha(view: ref, alpha: 0.0) - } - self.edgePreviewUpdated(false) - - selectedEntityView.isTrappedInBin = true - transition.setAlpha(view: selectionView, alpha: 0.0) - transition.animatePosition(view: selectionView, from: selectionView.center, to: self.convert(binView.center, to: selectionView.superview)) - transition.animateScale(view: selectionView, from: 0.0, to: -0.5, additive: true) - - transition.setPosition(view: selectedEntityView, position: binView.center) - - let rotation = selectedEntityView.layer.transform.decompose().rotation - var transform = CATransform3DMakeScale(scale, scale, 1.0) - transform = CATransform3DRotate(transform, CGFloat(rotation.z), 0.0, 0.0, 1.0) - - transition.setTransform(view: selectedEntityView, transform: transform) - } - } else { - if selectedEntityView.isTrappedInBin { - selectedEntityView.isTrappedInBin = false - transition.setAlpha(view: selectionView, alpha: 1.0) - selectedEntityView.layer.animateScale(from: scale, to: selectedEntityView.entity.scale, duration: 0.13) - } + if !self.hasBin { selectionView.handlePan(gestureRecognizer) + } else { + var isTrappedInBin = false + let scale = 100.0 / selectedEntityView.bounds.size.width + switch gestureRecognizer.state { + case .changed: + if self.updateBin(location: location) { + isTrappedInBin = true + } + case .ended, .cancelled: + let _ = self.updateBin(location: nil) + if selectedEntityView.isTrappedInBin { + selectedEntityView.layer.animateScale(from: scale, to: 0.01, duration: 0.2, removeOnCompletion: false) + selectedEntityView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + self.remove(uuid: selectedEntityView.entity.uuid) + }) + self.selectEntity(nil) + + Queue.mainQueue().after(0.3, { + self.onInteractionUpdated(false) + }) + return + } + default: + break + } + + let transition = Transition.easeInOut(duration: 0.2) + if isTrappedInBin, let binView = self.bin.view { + if !selectedEntityView.isTrappedInBin { + let refs = [ + self.xAxisView, + self.yAxisView, + self.topEdgeView, + self.leftEdgeView, + self.rightEdgeView, + self.bottomEdgeView + ] + for ref in refs { + transition.setAlpha(view: ref, alpha: 0.0) + } + self.edgePreviewUpdated(false) + + selectedEntityView.isTrappedInBin = true + transition.setAlpha(view: selectionView, alpha: 0.0) + transition.animatePosition(view: selectionView, from: selectionView.center, to: self.convert(binView.center, to: selectionView.superview)) + transition.animateScale(view: selectionView, from: 0.0, to: -0.5, additive: true) + + transition.setPosition(view: selectedEntityView, position: binView.center) + + let rotation = selectedEntityView.layer.transform.decompose().rotation + var transform = CATransform3DMakeScale(scale, scale, 1.0) + transform = CATransform3DRotate(transform, CGFloat(rotation.z), 0.0, 0.0, 1.0) + + transition.setTransform(view: selectedEntityView, transform: transform) + } + } else { + if selectedEntityView.isTrappedInBin { + selectedEntityView.isTrappedInBin = false + transition.setAlpha(view: selectionView, alpha: 1.0) + selectedEntityView.layer.animateScale(from: scale, to: selectedEntityView.entity.scale, duration: 0.13) + } + selectionView.handlePan(gestureRecognizer) + } } } else if gestureRecognizer.numberOfTouches == 1, let viewToSelect = self.entity(at: location) { self.selectEntity(viewToSelect.entity) diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index 545c690156..a705a4675c 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -2965,7 +2965,7 @@ public final class DrawingToolsInteraction { private let onInteractionUpdated: (Bool) -> Void private let onTextEditingEnded: (Bool) -> Void - private let getCurrentImage: () -> UIImage? + public let getCurrentImage: () -> UIImage? private let getControllerNode: () -> ASDisplayNode? private let present: (ViewController, PresentationContextType, Any?) -> Void private let addSubview: (UIView) -> Void diff --git a/submodules/DrawingUI/Sources/DrawingStickerEntity.swift b/submodules/DrawingUI/Sources/DrawingStickerEntity.swift index 0713392185..5bd95201b5 100644 --- a/submodules/DrawingUI/Sources/DrawingStickerEntity.swift +++ b/submodules/DrawingUI/Sources/DrawingStickerEntity.swift @@ -329,7 +329,7 @@ public final class DrawingStickerEntityView: DrawingEntityView { if size.width > 0 && self.currentSize != size { self.currentSize = size - let sideSize: CGFloat = size.width + let sideSize: CGFloat = max(size.width, size.height) let boundingSize = CGSize(width: sideSize, height: sideSize) let imageSize = self.dimensions.aspectFitted(boundingSize) @@ -656,7 +656,47 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView { let handleSize = CGSize(width: 9.0 / self.scale, height: 9.0 / self.scale) let handlePath = CGPath(ellipseIn: CGRect(origin: CGPoint(x: (bounds.width - handleSize.width) / 2.0, y: (bounds.height - handleSize.height) / 2.0), size: handleSize), transform: nil) let lineWidth = (1.0 + UIScreenPixel) / self.scale - + + let radius = (self.bounds.width - inset * 2.0) / 2.0 + let circumference: CGFloat = 2.0 * .pi * radius + let relativeDashLength: CGFloat = 0.25 + + self.border.lineWidth = 2.0 / self.scale + + let actualInset: CGFloat + if entity.isRectangle { + let aspectRatio = entity.baseSize.width / entity.baseSize.height + + let width: CGFloat + let height: CGFloat + + if entity.baseSize.width > entity.baseSize.height { + width = self.bounds.width - inset * 2.0 + height = self.bounds.height / aspectRatio - inset * 2.0 + } else { + width = self.bounds.width * aspectRatio - inset * 2.0 + height = self.bounds.height - inset * 2.0 + } + + actualInset = floorToScreenPixels((self.bounds.width - width) / 2.0) + + let cornerRadius: CGFloat = 12.0 - self.scale + let perimeter: CGFloat = 2.0 * (width + height - cornerRadius * (4.0 - .pi)) + let count = 12 + let dashLength = perimeter / CGFloat(count) + self.border.lineDashPattern = [dashLength * relativeDashLength, dashLength * relativeDashLength] as [NSNumber] + + self.border.path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: floorToScreenPixels((self.bounds.width - width) / 2.0), y: floorToScreenPixels((self.bounds.height - height) / 2.0)), size: CGSize(width: width, height: height)), cornerRadius: cornerRadius).cgPath + } else { + actualInset = inset + + let count = 10 + let dashLength = circumference / CGFloat(count) + self.border.lineDashPattern = [dashLength * relativeDashLength, dashLength * relativeDashLength] as [NSNumber] + + self.border.path = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: self.bounds.width - inset * 2.0, height: self.bounds.height - inset * 2.0))).cgPath + } + let handles = [ self.leftHandle, self.rightHandle @@ -668,29 +708,8 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView { handle.lineWidth = lineWidth } - self.leftHandle.position = CGPoint(x: inset, y: self.bounds.midY) - self.rightHandle.position = CGPoint(x: self.bounds.maxX - inset, y: self.bounds.midY) - - let radius = (self.bounds.width - inset * 2.0) / 2.0 - let circumference: CGFloat = 2.0 * .pi * radius - let count = 10 - let relativeDashLength: CGFloat = 0.25 - let dashLength = circumference / CGFloat(count) - self.border.lineDashPattern = [dashLength * relativeDashLength, dashLength * relativeDashLength] as [NSNumber] - - self.border.lineWidth = 2.0 / self.scale - - if entity.isRectangle { - let aspectRatio = entity.baseSize.width / entity.baseSize.height - - let width: CGFloat = self.bounds.width - inset * 2.0 - let height: CGFloat = self.bounds.height / aspectRatio - inset * 2.0 - - let cornerRadius: CGFloat = 12.0 - self.scale - self.border.path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: floorToScreenPixels((self.bounds.width - width) / 2.0), y: floorToScreenPixels((self.bounds.height - height) / 2.0)), size: CGSize(width: width, height: height)), cornerRadius: cornerRadius).cgPath - } else { - self.border.path = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: self.bounds.width - inset * 2.0, height: self.bounds.height - inset * 2.0))).cgPath - } + self.leftHandle.position = CGPoint(x: actualInset, y: self.bounds.midY) + self.rightHandle.position = CGPoint(x: self.bounds.maxX - actualInset, y: self.bounds.midY) } } diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index 1da6531b2e..3610eef1ad 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -17,6 +17,7 @@ public final class HashtagSearchController: TelegramBaseController { private let context: AccountContext private let peer: EnginePeer? private let query: String + let all: Bool private var transitionDisposable: Disposable? private let openMessageFromSearchDisposable = MetaDisposable() @@ -30,10 +31,11 @@ public final class HashtagSearchController: TelegramBaseController { return self.displayNode as! HashtagSearchControllerNode } - public init(context: AccountContext, peer: EnginePeer?, query: String) { + public init(context: AccountContext, peer: EnginePeer?, query: String, all: Bool = false) { self.context = context self.peer = peer self.query = query + self.all = all self.animationCache = context.animationCache self.animationRenderer = context.animationRenderer diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift index 289a388de6..7bf1f75596 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift @@ -11,17 +11,16 @@ import ChatListSearchItemHeader final class HashtagSearchControllerNode: ASDisplayNode { private let context: AccountContext private weak var controller: HashtagSearchController? + private let query: String private let navigationBar: NavigationBar? private let segmentedControlNode: SegmentedControlNode let listNode: ListView + let shimmerNode: ChatListSearchShimmerNode let chatController: ChatController? - - private let query: String - private var containerLayout: (ContainerViewLayout, CGFloat)? private var enqueuedTransitions: [(ChatListSearchContainerTransition, Bool)] = [] private var hasValidLayout = false @@ -34,6 +33,10 @@ final class HashtagSearchControllerNode: ASDisplayNode { let presentationData = context.sharedContext.currentPresentationData.with { $0 } + self.shimmerNode = ChatListSearchShimmerNode(key: .chats) + self.shimmerNode.isUserInteractionEnabled = false + self.shimmerNode.allowsGroupOpacity = true + self.listNode = ListView() self.listNode.accessibilityPageScrolledString = { row, count in return presentationData.strings.VoiceOver_ScrollStatus(row, count).string @@ -48,7 +51,7 @@ final class HashtagSearchControllerNode: ASDisplayNode { items.append(peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) ?? "") } items.append(presentationData.strings.HashtagSearch_AllChats) - self.segmentedControlNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: presentationData.theme), items: items.map { SegmentedControlItem(title: $0) }, selectedIndex: 0) + self.segmentedControlNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: presentationData.theme), items: items.map { SegmentedControlItem(title: $0) }, selectedIndex: controller.all ? 1 : 0) if let peer = peer { self.chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .inline(navigationController)) @@ -65,7 +68,15 @@ final class HashtagSearchControllerNode: ASDisplayNode { self.backgroundColor = presentationData.theme.chatList.backgroundColor self.addSubnode(self.listNode) - self.listNode.isHidden = true +// self.addSubnode(self.shimmerNode) + + if controller.all { + self.chatController?.displayNode.isHidden = true + self.listNode.isHidden = false + } else { + self.chatController?.displayNode.isHidden = false + self.listNode.isHidden = true + } self.segmentedControlNode.selectedIndexChanged = { [weak self] index in if let strongSelf = self { @@ -164,6 +175,11 @@ final class HashtagSearchControllerNode: ASDisplayNode { self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height) self.listNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0) + let overflowInset: CGFloat = 0.0 + let topInset = navigationBarHeight + self.shimmerNode.frame = CGRect(origin: CGPoint(x: overflowInset, y: topInset), size: CGSize(width: layout.size.width - overflowInset * 2.0, height: layout.size.height)) + self.shimmerNode.update(context: self.context, size: CGSize(width: layout.size.width - overflowInset * 2.0, height: layout.size.height), presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, key: .chats, hasSelection: false, transition: transition) + insets.top += 4.0 let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) diff --git a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift index 394a0b0219..ef5bb68a76 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift @@ -188,6 +188,10 @@ final class MediaPickerGridItemNode: GridItemNode { } else { return nil } + } else if let (draft, _) = self.currentDraftState { + let tag = Month(localTimestamp: draft.timestamp).packedValue + self._cachedTag = tag + return tag } else { return nil } diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index 61aa9be035..d13d1c827d 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -433,7 +433,7 @@ private final class CameraScreenComponent: CombinedComponent { case .began: return .single(.pendingImage) case let .finished(image, additionalImage, _): - return .single(.image(CameraScreen.Result.Image(image: image, additionalImage: additionalImage, additionalImagePosition: .bottomRight))) + return .single(.image(CameraScreen.Result.Image(image: image, additionalImage: additionalImage, additionalImagePosition: .topRight))) case .failed: return .complete() } @@ -470,7 +470,7 @@ private final class CameraScreenComponent: CombinedComponent { self.resultDisposable.set((self.camera.stopRecording() |> deliverOnMainQueue).start(next: { [weak self] result in if let self, case let .finished(mainResult, additionalResult, duration, positionChangeTimestamps, _) = result { - self.completion.invoke(.single(.video(CameraScreen.Result.Video(videoPath: mainResult.0, coverImage: mainResult.1, mirror: mainResult.2, additionalVideoPath: additionalResult?.0, additionalCoverImage: additionalResult?.1, dimensions: PixelDimensions(width: 1080, height: 1920), duration: duration, positionChangeTimestamps: positionChangeTimestamps, additionalVideoPosition: .bottomRight)))) + self.completion.invoke(.single(.video(CameraScreen.Result.Video(videoPath: mainResult.0, coverImage: mainResult.1, mirror: mainResult.2, additionalVideoPath: additionalResult?.0, additionalCoverImage: additionalResult?.1, dimensions: PixelDimensions(width: 1080, height: 1920), duration: duration, positionChangeTimestamps: positionChangeTimestamps, additionalVideoPosition: .topRight)))) } })) self.isTransitioning = true @@ -1075,7 +1075,7 @@ public class CameraScreen: ViewController { private var appliedDualCam = false private var cameraPosition: Camera.Position = .back - private var pipPosition: PIPPosition = .bottomRight + private var pipPosition: PIPPosition = .topRight fileprivate var previewBlurPromise = ValuePromise(false) private let animateFlipAction = ActionSlot() @@ -1941,7 +1941,9 @@ public class CameraScreen: ViewController { transition.setPosition(view: self.backgroundView, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)) transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: .zero, size: layout.size)) - transition.setPosition(view: self.containerView, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)) + if !self.hasGallery { + transition.setPosition(view: self.containerView, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)) + } transition.setBounds(view: self.containerView, bounds: CGRect(origin: .zero, size: layout.size)) transition.setFrame(view: self.transitionDimView, frame: CGRect(origin: .zero, size: layout.size)) @@ -2293,11 +2295,16 @@ public class CameraScreen: ViewController { let transitionFraction = max(0.0, min(1.0, transitionFraction)) let offsetX = floorToScreenPixels((1.0 - transitionFraction) * self.node.frame.width * -1.0) transition.updateTransform(layer: self.node.backgroundView.layer, transform: CGAffineTransform(translationX: offsetX, y: 0.0)) - transition.updateTransform(layer: self.node.containerView.layer, transform: CGAffineTransform(translationX: offsetX, y: 0.0)) + let scale: CGFloat = max(0.8, min(1.0, 0.8 + 0.2 * transitionFraction)) - transition.updateSublayerTransformScaleAndOffset(layer: self.node.containerView.layer, scale: scale, offset: CGPoint(x: -offsetX * 1.0 / scale * 0.5, y: 0.0), completion: { _ in + if !self.node.hasGallery { + transition.updateTransform(layer: self.node.containerView.layer, transform: CGAffineTransform(translationX: offsetX, y: 0.0)) + transition.updateSublayerTransformScaleAndOffset(layer: self.node.containerView.layer, scale: scale, offset: CGPoint(x: -offsetX * 1.0 / scale * 0.5, y: 0.0), completion: { _ in + completion() + }) + } else { completion() - }) + } let dimAlpha = 0.6 * (1.0 - transitionFraction) transition.updateAlpha(layer: self.node.transitionDimView.layer, alpha: dimAlpha) @@ -2549,7 +2556,7 @@ private func pipPositionForLocation(layout: ContainerViewLayout, position: CGPoi } } - var position: CameraScreen.PIPPosition = .bottomRight + var position: CameraScreen.PIPPosition = .topRight if result.x == 0.0 && result.y == 0.0 { position = .topLeft } else if result.x == 1.0 && result.y == 0.0 { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift index d0afbe9468..3fb2979f19 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift @@ -271,9 +271,6 @@ private func makeEditorImageFrameComposition(context: CIContext, inputImage: CII baseScale = entityBaseScale } else if let baseSize = entity.baseSize { baseScale = baseSize.width / image.extent.width - if baseSize.width != baseSize.height { - baseScale *= min(baseSize.width, baseSize.height) / max(baseSize.width, baseSize.height) - } } var transform = CGAffineTransform.identity diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift index c2ed9ea2b2..40cbe7784e 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift @@ -66,6 +66,7 @@ public final class MediaEditorDraft: Codable, Equatable { case values case caption case privacy + case timestamp } public let path: String @@ -76,8 +77,9 @@ public final class MediaEditorDraft: Codable, Equatable { public let values: MediaEditorValues public let caption: NSAttributedString public let privacy: MediaEditorResultPrivacy? + public let timestamp: Int32 - public init(path: String, isVideo: Bool, thumbnail: UIImage, dimensions: PixelDimensions, duration: Double?, values: MediaEditorValues, caption: NSAttributedString, privacy: MediaEditorResultPrivacy?) { + public init(path: String, isVideo: Bool, thumbnail: UIImage, dimensions: PixelDimensions, duration: Double?, values: MediaEditorValues, caption: NSAttributedString, privacy: MediaEditorResultPrivacy?, timestamp: Int32) { self.path = path self.isVideo = isVideo self.thumbnail = thumbnail @@ -86,6 +88,7 @@ public final class MediaEditorDraft: Codable, Equatable { self.values = values self.caption = caption self.privacy = privacy + self.timestamp = timestamp } public init(from decoder: Decoder) throws { @@ -117,6 +120,8 @@ public final class MediaEditorDraft: Codable, Equatable { } else { self.privacy = nil } + + self.timestamp = try container.decodeIfPresent(Int32.self, forKey: .timestamp) ?? 1688909663 } public func encode(to encoder: Encoder) throws { @@ -145,6 +150,7 @@ public final class MediaEditorDraft: Codable, Equatable { } else { try container.encodeNil(forKey: .privacy) } + try container.encode(self.timestamp, forKey: .timestamp) } } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 1cfefcb619..cacda0236e 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -341,7 +341,7 @@ final class MediaEditorScreenComponent: Component { } }, dismissTextInput: { - + }, insertText: { [weak self] text in if let self { @@ -1719,7 +1719,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.gradientView = UIImageView() self.entitiesContainerView = UIView(frame: CGRect(origin: .zero, size: storyDimensions)) - self.entitiesView = DrawingEntitiesView(context: controller.context, size: storyDimensions) + self.entitiesView = DrawingEntitiesView(context: controller.context, size: storyDimensions, hasBin: true) self.entitiesView.getEntityCenterPosition = { return CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0) } @@ -2100,8 +2100,37 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate }).start() } }, - getCurrentImage: { - return nil + getCurrentImage: { [weak self] in + guard let self else { + return nil + } + let colorSpace = CGColorSpaceCreateDeviceRGB() + let imageSize = CGSize(width: 1080, height: 1920) + let context = DrawingContext(size: imageSize, scale: 1.0, opaque: true, colorSpace: colorSpace) + + context?.withFlippedContext { context in + if let gradientImage = self.gradientView.image?.cgImage { + context.draw(gradientImage, in: CGRect(origin: .zero, size: imageSize)) + } + if let image = self.mediaEditor?.resultImage, let values = self.mediaEditor?.values { + let initialScale: CGFloat + if image.size.height > image.size.width { + initialScale = max(imageSize.width / image.size.width, imageSize.height / image.size.height) + } else { + initialScale = imageSize.width / image.size.width + } + let scale = initialScale * values.cropScale + context.translateBy(x: imageSize.width / 2.0 + values.cropOffset.x, y: imageSize.height / 2.0 - values.cropOffset.y) + context.rotate(by: -values.cropRotation) + context.scaleBy(x: scale, y: scale) + + if let cgImage = image.cgImage { + context.draw(cgImage, in: CGRect(x: -image.size.width / 2.0, y: -image.size.height / 2.0, width: image.size.width, height: image.size.height)) + } + } + } + + return context?.generateImage(colorSpace: colorSpace) }, getControllerNode: { [weak self] in return self @@ -2718,6 +2747,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } }) + galleryController.customModalStyleOverlayTransitionFactorUpdated = { [weak self, weak galleryController] transition in + if let self, let galleryController { + let transitionFactor = galleryController.modalStyleOverlayTransitionFactor + self.updateModalTransitionFactor(transitionFactor, transition: transition) + } + } controller.push(galleryController) } @@ -2906,36 +2941,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate existingStickerPickerInputData: self.stickerPickerInputData ) controller.getCurrentImage = { [weak self] in - guard let self else { - return nil - } - let colorSpace = CGColorSpaceCreateDeviceRGB() - let imageSize = CGSize(width: 1080, height: 1920) - let context = DrawingContext(size: imageSize, scale: 1.0, opaque: true, colorSpace: colorSpace) - - context?.withFlippedContext { context in - if let gradientImage = self.gradientView.image?.cgImage { - context.draw(gradientImage, in: CGRect(origin: .zero, size: imageSize)) - } - if let image = self.mediaEditor?.resultImage, let values = self.mediaEditor?.values { - let initialScale: CGFloat - if image.size.height > image.size.width { - initialScale = max(imageSize.width / image.size.width, imageSize.height / image.size.height) - } else { - initialScale = imageSize.width / image.size.width - } - let scale = initialScale * values.cropScale - context.translateBy(x: imageSize.width / 2.0 + values.cropOffset.x, y: imageSize.height / 2.0 - values.cropOffset.y) - context.rotate(by: -values.cropRotation) - context.scaleBy(x: scale, y: scale) - - if let cgImage = image.cgImage { - context.draw(cgImage, in: CGRect(x: -image.size.width / 2.0, y: -image.size.height / 2.0, width: image.size.width, height: image.size.height)) - } - } - } - - return context?.generateImage(colorSpace: colorSpace) + return self?.interaction?.getCurrentImage() } controller.updateVideoPlayback = { [weak self] play in guard let self else { @@ -3592,6 +3598,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let caption = self.getCaption() let duration = mediaEditor.duration ?? 0.0 + var timestamp: Int32 + if case let .draft(draft, _) = subject { + timestamp = draft.timestamp + } else { + timestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) + } + if let resultImage = mediaEditor.resultImage { mediaEditor.seek(0.0, andPlay: false) makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: resultImage, dimensions: storyDimensions, values: values, time: .zero, completion: { resultImage in @@ -3604,7 +3617,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) { let path = "\(Int64.random(in: .min ... .max)).jpg" if let data = image.jpegData(compressionQuality: 0.87) { - let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, duration: nil, values: values, caption: caption, privacy: privacy) + let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, duration: nil, values: values, caption: caption, privacy: privacy, timestamp: timestamp) try? data.write(to: URL(fileURLWithPath: draft.fullPath())) if let id { saveStorySource(engine: self.context.engine, item: draft, id: id) @@ -3618,7 +3631,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let saveVideoDraft: (String, PixelDimensions, Double) -> Void = { videoPath, dimensions, duration in if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) { let path = "\(Int64.random(in: .min ... .max)).mp4" - let draft = MediaEditorDraft(path: path, isVideo: true, thumbnail: thumbnailImage, dimensions: dimensions, duration: duration, values: values, caption: caption, privacy: privacy) + let draft = MediaEditorDraft(path: path, isVideo: true, thumbnail: thumbnailImage, dimensions: dimensions, duration: duration, values: values, caption: caption, privacy: privacy, timestamp: timestamp) try? FileManager.default.moveItem(atPath: videoPath, toPath: draft.fullPath()) if let id { saveStorySource(engine: self.context.engine, item: draft, id: id) @@ -3860,7 +3873,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate }) if case let .draft(draft, id) = subject, id == nil { - removeStoryDraft(engine: self.context.engine, path: draft.path, delete: !draft.isVideo) + removeStoryDraft(engine: self.context.engine, path: draft.path, delete: false) } } else { if let image = mediaEditor.resultImage { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index ff56715752..585d6c7a18 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -2472,7 +2472,7 @@ final class StoryItemSetContainerSendMessage { return } if !hashtag.isEmpty { - let searchController = component.context.sharedContext.makeHashtagSearchController(context: component.context, peer: peer.flatMap(EnginePeer.init), query: hashtag) + let searchController = component.context.sharedContext.makeHashtagSearchController(context: component.context, peer: peer.flatMap(EnginePeer.init), query: hashtag, all: true) navigationController.pushViewController(searchController) } })) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 60593dc748..df4de04972 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1720,8 +1720,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { return inputPanelNode } - public func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String) -> ViewController { - return HashtagSearchController(context: context, peer: peer, query: query) + public func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String, all: Bool) -> ViewController { + return HashtagSearchController(context: context, peer: peer, query: query, all: all) } public func makeMyStoriesController(context: AccountContext, isArchive: Bool) -> ViewController {