diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index a63185d3cb..ce0ab57c8c 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -11910,3 +11910,47 @@ Sorry for the inconvenience."; "Conversation.ViewEmojis" = "VIEW EMOJIS"; "MediaEditor.StickersTooMuch" = "Sorry, you've reached the maximum number of stickers in this set. Try a different one."; + +"Stickers.ChooseSticker.Title" = "Choose Sticker"; +"Stickers.EditSticker" = "Edit Sticker"; +"Stickers.Reorder" = "Reorder"; +"Stickers.Delete" = "Delete"; +"Stickers.Delete.ForEveryone" = "Delete for Everyone"; + +"StickerPack.EditStickers" = "Edit Stickers"; +"StickerPack.EditName" = "Edit Name"; +"StickerPack.Reorder" = "Reorder"; +"StickerPack.Delete" = "Delete"; +"StickerPack.Delete.DeleteForEveyone" = "Delete for Everyone"; +"StickerPack.Delete.RemoveForMe" = "Remove for Me"; +"StickerPack.EditInfo" = "Check [@stickers]() bot for more options."; +"StickerPack.CreateNew" = "Create a New Sticker"; +"StickerPack.AddExisting" = "Add an Existing Sticker"; + +"StickerPack.EditName.Title" = "Edit Sticker Set Name"; +"StickerPack.EditName.Text" = "Choose a new name for your set."; + +"StickerPack.Delete.Title" = "Delete Sticker Set"; +"StickerPack.Delete.Text" = "This will delete the sticker set for all users."; +"StickerPack.Delete.Delete" = "Delete"; + +"StickerPack.StickerAdded" = "Sticker added to **%@** sticker set."; +"StickerPack.StickerUpdated" = "Sticker updated."; + +"MediaEditor.Undo" = "Undo"; +"MediaEditor.Erase" = "Erase"; +"MediaEditor.EraseInfo" = "Erase"; +"MediaEditor.Restore" = "Restore"; +"MediaEditor.RestoreInfo" = "Restore"; +"MediaEditor.Cutout" = "Cut Out an Object"; +"MediaEditor.CutoutInfo" = "Cut Out an Object"; +"MediaEditor.Outline" = "Add Outline"; +"MediaEditor.SetStickerEmoji" = "Set emoji that corresponds to your sticker"; + +"MediaEditor.CreateNewPack" = "New Sticker Set"; +"MediaEditor.ReplaceSticker" = "Replace Sticker"; +"MediaEditor.AddToStickerPack" = "Add to Sticker Set"; + +"MediaEditor.NewStickerPack.Title" = "New Sticker Set"; +"MediaEditor.NewStickerPack.Text" = "Choose a name for your sticker set."; + diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index ed954b3c6e..4a74a0ff8b 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1021,7 +1021,7 @@ public protocol SharedAccountContext: AnyObject { func makeStickerEditorScreen(context: AccountContext, source: Any?, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, [String], @escaping () -> Void) -> Void) -> ViewController - func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any?, UIView?, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController + func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController func makeStoryMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController func makeStickerPickerScreen(context: AccountContext, inputData: Promise, completion: @escaping (FileMediaReference) -> Void) -> ViewController diff --git a/submodules/AccountContext/Sources/GlobalExperimentalSettings.swift b/submodules/AccountContext/Sources/GlobalExperimentalSettings.swift index 8afb9aa45a..143bfa6a53 100644 --- a/submodules/AccountContext/Sources/GlobalExperimentalSettings.swift +++ b/submodules/AccountContext/Sources/GlobalExperimentalSettings.swift @@ -3,5 +3,4 @@ import Foundation public struct GlobalExperimentalSettings { public static var isAppStoreBuild: Bool = false public static var enableFeed: Bool = false - public static var enableWIPStickers: Bool = true } diff --git a/submodules/Camera/Sources/Camera.swift b/submodules/Camera/Sources/Camera.swift index b92bbb9320..d1f95d0bc9 100644 --- a/submodules/Camera/Sources/Camera.swift +++ b/submodules/Camera/Sources/Camera.swift @@ -1066,15 +1066,28 @@ public final class Camera { return false } } + + public static var isIpad: Bool { + return DeviceModel.current.isIpad + } } public final class CameraHolder { public let camera: Camera - public let previewView: CameraPreviewView + public let previewView: CameraSimplePreviewView + public let parentView: UIView + public let restore: () -> Void - public init(camera: Camera, previewView: CameraPreviewView) { + public init( + camera: Camera, + previewView: CameraSimplePreviewView, + parentView: UIView, + restore: @escaping () -> Void + ) { self.camera = camera self.previewView = previewView + self.parentView = parentView + self.restore = restore } } diff --git a/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m b/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m index 81ccc78103..0edf884377 100755 --- a/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m +++ b/submodules/FFMpegBinding/Sources/FFMpegVideoWriter.m @@ -25,22 +25,30 @@ } - (bool)setupWithOutputPath:(NSString *)outputPath width:(int)width height:(int)height bitrate:(int64_t)bitrate framerate:(int32_t)framerate { - avformat_alloc_output_context2(&_formatContext, NULL, "matroska", [outputPath UTF8String]); + avformat_alloc_output_context2(&_formatContext, nil, "matroska", [outputPath UTF8String]); if (!_formatContext) { return false; } if (avio_open(&_formatContext->pb, [outputPath UTF8String], AVIO_FLAG_WRITE) < 0) { + avformat_free_context(_formatContext); + _formatContext = nil; return false; } const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_VP9); if (!codec) { + avio_closep(&_formatContext->pb); + avformat_free_context(_formatContext); + _formatContext = nil; return false; } _codecContext = avcodec_alloc_context3(codec); if (!_codecContext) { + avio_closep(&_formatContext->pb); + avformat_free_context(_formatContext); + _formatContext = nil; return false; } @@ -61,11 +69,22 @@ } if (avcodec_open2(_codecContext, codec, NULL) < 0) { + avcodec_free_context(&_codecContext); + _codecContext = nil; + avio_closep(&_formatContext->pb); + avformat_free_context(_formatContext); + _formatContext = nil; return false; } _stream = avformat_new_stream(_formatContext, codec); if (!_stream) { + avcodec_close(_codecContext); + avcodec_free_context(&_codecContext); + _codecContext = nil; + avio_closep(&_formatContext->pb); + avformat_free_context(_formatContext); + _formatContext = nil; return false; } @@ -78,11 +97,23 @@ int ret = avcodec_parameters_from_context(_stream->codecpar, _codecContext); if (ret < 0) { + avcodec_close(_codecContext); + avcodec_free_context(&_codecContext); + _codecContext = nil; + avio_closep(&_formatContext->pb); + avformat_free_context(_formatContext); + _formatContext = nil; return false; } - ret = avformat_write_header(_formatContext, NULL); + ret = avformat_write_header(_formatContext, nil); if (ret < 0) { + avcodec_close(_codecContext); + avcodec_free_context(&_codecContext); + _codecContext = nil; + avio_closep(&_formatContext->pb); + avformat_free_context(_formatContext); + _formatContext = nil; return false; } @@ -110,7 +141,7 @@ AVPacket pkt; av_init_packet(&pkt); - pkt.data = NULL; + pkt.data = nil; pkt.size = 0; while (sendRet >= 0) { @@ -118,6 +149,7 @@ if (recvRet == AVERROR(EAGAIN) || recvRet == AVERROR_EOF) { break; } else if (recvRet < 0) { + av_packet_unref(&pkt); break; } @@ -135,11 +167,15 @@ } - (bool)finalizeVideo { + if (!_codecContext) { + return false; + } + int sendRet = avcodec_send_frame(_codecContext, NULL); if (sendRet >= 0) { AVPacket pkt; av_init_packet(&pkt); - pkt.data = NULL; + pkt.data = nil; pkt.size = 0; while (avcodec_receive_packet(_codecContext, &pkt) == 0) { @@ -151,14 +187,25 @@ } } - av_write_trailer(_formatContext); + if (_formatContext) { + av_write_trailer(_formatContext); + } - avio_closep(&_formatContext->pb); - - avcodec_close(_codecContext); + if (_formatContext && _formatContext->pb) { + avio_closep(&_formatContext->pb); + } + + if (_codecContext) { + avcodec_close(_codecContext); + avcodec_free_context(&_codecContext); + _codecContext = nil; + } + + if (_formatContext) { + avformat_free_context(_formatContext); + _formatContext = nil; + } - avcodec_free_context(&_codecContext); - avformat_free_context(_formatContext); return true; } diff --git a/submodules/ItemListUI/Sources/ItemListControllerNode.swift b/submodules/ItemListUI/Sources/ItemListControllerNode.swift index 2d1059536e..35785719f8 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerNode.swift @@ -499,7 +499,7 @@ open class ItemListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { panRecognizer.delaysTouchesBegan = false panRecognizer.cancelsTouchesInView = true self.panRecognizer = panRecognizer - self.view.addGestureRecognizer(panRecognizer) +// self.view.addGestureRecognizer(panRecognizer) } public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool { diff --git a/submodules/MediaPickerUI/BUILD b/submodules/MediaPickerUI/BUILD index b9a2a15a4f..893eaf9a2e 100644 --- a/submodules/MediaPickerUI/BUILD +++ b/submodules/MediaPickerUI/BUILD @@ -45,6 +45,7 @@ swift_library( "//submodules/TelegramUI/Components/CameraScreen", "//submodules/TelegramUI/Components/MediaEditor", "//submodules/RadialStatusNode", + "//submodules/Camera", ], visibility = [ "//visibility:public", diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index e6386b878a..96dc08b168 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -21,6 +21,7 @@ import SparseItemGrid import UndoUI import PresentationDataUtils import MoreButtonNode +import Camera import CameraScreen import MediaEditor @@ -184,7 +185,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { public weak var webSearchController: WebSearchController? - public var openCamera: ((TGAttachmentCameraView?) -> Void)? + public var openCamera: ((Any?) -> Void)? public var presentSchedulePicker: (Bool, @escaping (Int32) -> Void) -> Void = { _, _ in } public var presentTimerPicker: (@escaping (Int32) -> Void) -> Void = { _ in } public var presentWebSearch: (MediaGroupsScreen, Bool) -> Void = { _, _ in } @@ -232,7 +233,14 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { private let containerNode: ASDisplayNode private let backgroundNode: NavigationBackgroundNode fileprivate let gridNode: GridNode + + fileprivate let cameraWrapperView: UIView fileprivate var cameraView: TGAttachmentCameraView? + + fileprivate var modernCamera: Camera? + fileprivate var modernCameraView: CameraSimplePreviewView? + fileprivate var modernCameraTapGestureRecognizer: UITapGestureRecognizer? + private var cameraActivateAreaNode: AccessibilityAreaNode private var placeholderNode: MediaPickerPlaceholderNode? private var manageNode: MediaPickerManageNode? @@ -267,7 +275,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } fileprivate var isSuspended = false - private var hasGallery = false + fileprivate var hasGallery = false private var isCameraPreviewVisible = true private var validLayout: (ContainerViewLayout, CGFloat)? @@ -289,6 +297,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.gridNode = GridNode() self.scrollingArea = SparseItemGridScrollingArea() + self.cameraWrapperView = UIView() + self.cameraActivateAreaNode = AccessibilityAreaNode() self.cameraActivateAreaNode.accessibilityLabel = "Camera" self.cameraActivateAreaNode.accessibilityTraits = [.button] @@ -306,6 +316,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.containerNode.addSubnode(self.gridNode) self.containerNode.addSubnode(self.scrollingArea) + self.gridNode.scrollView.addSubview(self.cameraWrapperView) + let selectedCollection = controller.selectedCollection.get() let preloadPromise = self.preloadPromise let updatedState: Signal @@ -497,9 +509,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.gridNode.visibleItemsUpdated = { [weak self] _ in self?.updateScrollingArea() - if let self, let cameraView = self.cameraView { - self.isCameraPreviewVisible = self.gridNode.scrollView.bounds.intersects(cameraView.frame) - self.updateIsCameraActive() + if let self { + var cameraView: UIView? + if let view = self.cameraView { + cameraView = view + } else if let _ = self.modernCameraView { + cameraView = self.cameraWrapperView + } + if let cameraView { + self.isCameraPreviewVisible = self.gridNode.scrollView.bounds.intersects(cameraView.frame) + self.updateIsCameraActive() + } } } self.updateScrollingArea() @@ -539,17 +559,89 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.gridNode.scrollView.addSubview(cameraView) self.gridNode.addSubnode(self.cameraActivateAreaNode) + } else if let controller = self.controller, case .assets(nil, .createSticker) = controller.subject, !Camera.isIpad { + let cameraPreviewView = CameraSimplePreviewView(frame: .zero, main: true) + cameraPreviewView.resetPlaceholder(front: false) + self.modernCameraView = cameraPreviewView + + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.cameraTapped)) + cameraPreviewView.addGestureRecognizer(tapGestureRecognizer) + self.modernCameraTapGestureRecognizer = tapGestureRecognizer + + if #available(iOS 13.0, *) { + let _ = (cameraPreviewView.isPreviewing + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak self] _ in + self?.modernCameraView?.removePlaceholder(delay: 0.35) + }) + } else { + Queue.mainQueue().after(0.35) { + cameraPreviewView.removePlaceholder(delay: 0.15) + } + } + + self.cameraWrapperView.addSubview(cameraPreviewView) + + let camera = Camera( + configuration: Camera.Configuration( + preset: .hd1920x1080, + position: .back, + isDualEnabled: false, + audio: false, + photo: true, + metadata: false + ), + previewView: cameraPreviewView, + secondaryPreviewView: nil + ) + self.modernCamera = camera + + camera.startCapture() } else { self.containerNode.clipsToBounds = true } } + @objc private func cameraTapped() { + guard let camera = self.modernCamera, let previewView = self.modernCameraView else { + return + } + self.modernCameraTapGestureRecognizer?.isEnabled = false + self.controller?.openCamera?( + CameraHolder( + camera: camera, + previewView: previewView, + parentView: self.cameraWrapperView, + restore: { [weak self, weak previewView] in + guard let self else{ + return + } + self.modernCameraTapGestureRecognizer?.isEnabled = true + if let previewView { + self.cameraWrapperView.addSubview(previewView) + } + } + ) + ) + } + func updateIsCameraActive() { let isCameraActive = !self.isSuspended && !self.hasGallery && self.isCameraPreviewVisible - if isCameraActive { - self.cameraView?.resumePreview() - } else { - self.cameraView?.pausePreview() + if let cameraView = self.cameraView { + if isCameraActive { + cameraView.resumePreview() + } else { + cameraView.pausePreview() + } + } else if let camera = self.modernCamera, let cameraView = self.modernCameraView { + if isCameraActive { + cameraView.isEnabled = true + camera.startCapture() + } else { + cameraView.isEnabled = false + camera.stopCapture() + } } } @@ -1311,7 +1403,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { let itemWidth = floorToScreenPixels((width - itemSpacing * CGFloat(itemsPerRow - 1)) / CGFloat(itemsPerRow)) var cameraRect: CGRect? = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: 0.0), size: CGSize(width: itemWidth, height: itemWidth * 2.0 + 1.0)) - if self.cameraView == nil { + if self.cameraView == nil && self.modernCameraView == nil { cameraRect = nil } @@ -1448,12 +1540,36 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { transition.updateFrame(node: selectionNode, frame: innerBounds) } - if let cameraView = self.cameraView { + + var cameraView: UIView? + if let view = self.cameraView { + cameraView = view + } else if let view = self.modernCameraView { + cameraView = view + } + + if let cameraView { if let cameraRect = cameraRect { - transition.updateFrame(view: cameraView, frame: cameraRect) + if cameraView.superview == self.cameraWrapperView { + transition.updateFrame(view: self.cameraWrapperView, frame: cameraRect) + + let screenWidth = min(layout.deviceMetrics.screenSize.width, layout.deviceMetrics.screenSize.height) + let cameraFullSize = cameraRect.size.aspectFilled(CGSize(width: screenWidth, height: screenWidth)) + let cameraScale = cameraRect.width / cameraFullSize.width + + cameraView.bounds = CGRect(origin: .zero, size: cameraFullSize) + cameraView.center = CGPoint(x: cameraRect.size.width / 2.0, y: cameraRect.size.height / 2.0) + cameraView.transform = CGAffineTransform(scaleX: cameraScale, y: cameraScale) + + transition.updateFrame(view: cameraView, frame: CGRect(origin: .zero, size: cameraRect.size)) + } else { + transition.updateFrame(view: cameraView, frame: cameraRect) + } self.cameraActivateAreaNode.frame = cameraRect + self.cameraWrapperView.isHidden = false cameraView.isHidden = false } else { + self.cameraWrapperView.isHidden = true cameraView.isHidden = true } } @@ -2319,6 +2435,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } public func updateHiddenMediaId(_ id: String?) { + if self.customSelection != nil { + self.controllerNode.hasGallery = id != nil + self.controllerNode.updateIsCameraActive() + } self.controllerNode.hiddenMediaId.set(.single(id)) } @@ -2644,8 +2764,8 @@ public func storyMediaPickerController( public func stickerMediaPickerController( context: AccountContext, - getSourceRect: @escaping () -> CGRect, - completion: @escaping (Any?, UIView?, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, + getSourceRect: @escaping () -> CGRect?, + completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void ) -> ViewController { let presentationData = context.sharedContext.currentPresentationData.with({ $0 }) @@ -2674,16 +2794,16 @@ public func stickerMediaPickerController( } return nil } - completion(result, transitionView, transitionView.bounds, controller.transitionImage(for: result.localIdentifier), transitionOut, { [weak controller] in + completion(result, transitionView, transitionView.bounds, controller.transitionImage(for: result.localIdentifier), false, transitionOut, { [weak controller] in controller?.updateHiddenMediaId(nil) }) } } } mediaPickerController.createFromScratch = { [weak controller] in - completion(nil, nil, .zero, nil, { _ in return nil }, { [weak controller] in - controller?.dismiss(animated: true) + completion(nil, nil, .zero, nil, false, { _ in return nil }, { }) + controller?.dismiss(animated: true) } mediaPickerController.presentFilePicker = { [weak controller] in controller?.present(legacyICloudFilePicker(theme: presentationData.theme, mode: .import, documentTypes: ["public.image"], forceDarkTheme: false, dismissed: { @@ -2710,7 +2830,7 @@ public func stickerMediaPickerController( } if let image = UIImage(contentsOfFile: copyPath) { - completion(image, nil, .zero, nil, { _ in return nil }, {}) + completion(image, nil, .zero, nil, false, { _ in return nil }, {}) } }) } @@ -2718,6 +2838,47 @@ public func stickerMediaPickerController( controller?.dismiss(animated: true) } + mediaPickerController.openCamera = { [weak controller] cameraHolder in + guard let cameraHolder = cameraHolder as? CameraHolder else { + return + } + + var returnToCameraImpl: (() -> Void)? + let cameraScreen = CameraScreen( + context: context, + mode: .sticker, + holder: cameraHolder, + transitionIn: CameraScreen.TransitionIn( + sourceView: cameraHolder.parentView, + sourceRect: cameraHolder.parentView.bounds, + sourceCornerRadius: 0.0 + ), + transitionOut: { _ in + return CameraScreen.TransitionOut( + destinationView: cameraHolder.parentView, + destinationRect: cameraHolder.parentView.bounds, + destinationCornerRadius: 0.0 + ) + }, + completion: { result, _, commit in + completion(result, nil, .zero, nil, true, { _ in return nil }, { + returnToCameraImpl?() + }) + } + ) + cameraScreen.transitionedOut = { [weak cameraHolder] in + if let cameraHolder { + cameraHolder.restore() + } + } + controller?.push(cameraScreen) + + returnToCameraImpl = { [weak cameraScreen] in + if let cameraScreen { + cameraScreen.returnFromEditor() + } + } + } present(mediaPickerController, mediaPickerController.mediaPickerContext) } controller.willDismiss = { diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index a8d521cc7a..c90cd6a55b 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -497,9 +497,6 @@ private final class StickerPackContainer: ASDisplayNode { if let (info, _, _) = strongSelf.currentStickerPack, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { canEdit = true } - if !GlobalExperimentalSettings.enableWIPStickers { - canEdit = false - } let accountPeerId = strongSelf.context.account.peerId return combineLatest( @@ -552,30 +549,30 @@ private final class StickerPackContainer: ASDisplayNode { }))) if canEdit { - menuItems.append(.action(ContextMenuActionItem(text: "Edit Sticker", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Draw"), color: theme.contextMenu.primaryColor) }, action: { _, f in + menuItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Stickers_EditSticker, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Draw"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.default) if let self { self.openEditSticker(item.file) } }))) if !strongSelf.isEditing { - menuItems.append(.action(ContextMenuActionItem(text: "Reorder", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) }, action: { _, f in + menuItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Stickers_Reorder, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.default) if let self { self.updateIsEditing(true) } }))) } - menuItems.append(.action(ContextMenuActionItem(text: "Delete", textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, f in - if let _ = self { + menuItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Stickers_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, f in + if let self { let contextItems: [ContextMenuItem] = [ - .action(ContextMenuActionItem(text: "Back", icon: { theme in + .action(ContextMenuActionItem(text: self.presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c ,f in c.popItems() })), .separator, - .action(ContextMenuActionItem(text: "Delete for Everyone", textColor: .destructive, icon: { _ in return nil }, action: { [weak self] _ ,f in + .action(ContextMenuActionItem(text: self.presentationData.strings.Stickers_Delete_ForEveryone, textColor: .destructive, icon: { _ in return nil }, action: { [weak self] _ ,f in f(.default) if let self, let (info, items, installed) = self.currentStickerPack { @@ -999,7 +996,7 @@ private final class StickerPackContainer: ASDisplayNode { case .none: buttonColor = self.presentationData.theme.list.itemAccentColor case let .result(info, _, installed): - if GlobalExperimentalSettings.enableWIPStickers && info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { + if info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { buttonColor = installed ? self.presentationData.theme.list.itemAccentColor : self.presentationData.theme.list.itemCheckColors.foregroundColor } else { buttonColor = installed ? self.presentationData.theme.list.itemDestructiveColor : self.presentationData.theme.list.itemCheckColors.foregroundColor @@ -1038,9 +1035,6 @@ private final class StickerPackContainer: ASDisplayNode { if let info = self.currentStickerPack?.0, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { isEditable = true } - if !GlobalExperimentalSettings.enableWIPStickers { - isEditable = false - } let transaction: StickerPackPreviewGridTransaction if reload { @@ -1133,11 +1127,10 @@ private final class StickerPackContainer: ASDisplayNode { } }))) - if GlobalExperimentalSettings.enableWIPStickers, let (info, packItems, _) = self.currentStickerPack, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { - //TODO:localize + if let (info, packItems, _) = self.currentStickerPack, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { items.append(.separator) if packItems.count > 0 { - items.append(.action(ContextMenuActionItem(text: "Reorder", icon: { theme in + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StickerPack_Reorder, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.default) @@ -1145,7 +1138,7 @@ private final class StickerPackContainer: ASDisplayNode { }))) } - items.append(.action(ContextMenuActionItem(text: "Edit Name", icon: { theme in + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StickerPack_EditName, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.default) @@ -1153,18 +1146,18 @@ private final class StickerPackContainer: ASDisplayNode { self?.presentEditPackTitle() }))) - items.append(.action(ContextMenuActionItem(text: "Delete", textColor: .destructive, icon: { theme in + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StickerPack_Delete, textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, f in if let self, let (_, _, isInstalled) = self.currentStickerPack { if isInstalled { let contextItems: [ContextMenuItem] = [ - .action(ContextMenuActionItem(text: "Delete for Everyone", textColor: .destructive, icon: { _ in return nil }, action: { [weak self] _ ,f in + .action(ContextMenuActionItem(text: self.presentationData.strings.StickerPack_Delete_DeleteForEveyone, textColor: .destructive, icon: { _ in return nil }, action: { [weak self] _ ,f in f(.default) self?.presentDeletePack() })), - .action(ContextMenuActionItem(text: "Remove for Me", icon: { _ in return nil }, action: { [weak self] _ ,f in + .action(ContextMenuActionItem(text: self.presentationData.strings.StickerPack_Delete_RemoveForMe, icon: { _ in return nil }, action: { [weak self] _ ,f in f(.default) self?.togglePackInstalled() @@ -1180,7 +1173,7 @@ private final class StickerPackContainer: ASDisplayNode { items.append(.separator) - items.append(.action(ContextMenuActionItem(text: "Check [@stickers]() bot for more options.", textLayout: .multiline, textFont: .small, parseMarkdown: true, icon: { _ in + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StickerPack_EditInfo, textLayout: .multiline, textFont: .small, parseMarkdown: true, icon: { _ in return nil }, action: { [weak self] _, f in f(.default) @@ -1199,10 +1192,9 @@ private final class StickerPackContainer: ASDisplayNode { private let stickerPickerInputData = Promise() private func presentAddStickerOptions() { - //TODO:localize let actionSheet = ActionSheetController(presentationData: self.presentationData) var items: [ActionSheetItem] = [] - items.append(ActionSheetButtonItem(title: "Create a New Sticker", color: .accent, action: { [weak actionSheet, weak self] in + items.append(ActionSheetButtonItem(title: self.presentationData.strings.StickerPack_CreateNew, color: .accent, action: { [weak actionSheet, weak self] in actionSheet?.dismissAnimated() guard let self, let controller = self.controller else { @@ -1211,7 +1203,7 @@ private final class StickerPackContainer: ASDisplayNode { self.presentCreateSticker() controller.controllerNode.dismiss() })) - items.append(ActionSheetButtonItem(title: "Add an Existing Sticker", color: .accent, action: { [weak actionSheet, weak self] in + items.append(ActionSheetButtonItem(title: self.presentationData.strings.StickerPack_AddExisting, color: .accent, action: { [weak actionSheet, weak self] in actionSheet?.dismissAnimated() guard let self, let controller = self.controller else { @@ -1262,7 +1254,7 @@ private final class StickerPackContainer: ASDisplayNode { let mainController = context.sharedContext.makeStickerMediaPickerScreen( context: context, getSourceRect: { return .zero }, - completion: { result, transitionView, transitionRect, transitionImage, completion, dismissed in + completion: { result, transitionView, transitionRect, transitionImage, fromCamera, completion, cancelled in let editorController = context.sharedContext.makeStickerEditorScreen( context: context, source: result, @@ -1286,7 +1278,7 @@ private final class StickerPackContainer: ASDisplayNode { (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root)) Queue.mainQueue().after(0.1) { - packController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: "Sticker added to **\(info.title)** sticker set.", undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) + packController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: presentationData.strings.StickerPack_StickerAdded(info.title).string, undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) } }) } @@ -1335,7 +1327,7 @@ private final class StickerPackContainer: ASDisplayNode { (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root)) Queue.mainQueue().after(0.1) { - packController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file.media, loop: true, title: nil, text: "Sticker added to **\(info.title)** sticker set.", undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) + packController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file.media, loop: true, title: nil, text: presentationData.strings.StickerPack_StickerAdded(info.title).string, undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) } }) }) @@ -1383,7 +1375,7 @@ private final class StickerPackContainer: ASDisplayNode { (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root)) Queue.mainQueue().after(0.1) { - packController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: "Sticker updated.", undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) + packController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: presentationData.strings.StickerPack_StickerUpdated, undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) } }) } @@ -1396,9 +1388,8 @@ private final class StickerPackContainer: ASDisplayNode { return } let context = self.context - //TODO:localize var dismissImpl: (() -> Void)? - let controller = stickerPackEditTitleController(context: context, title: "Edit Sticker Set Name", text: "Choose a new name for your set.", placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: self.updatedTitle ?? info.title, maxLength: 64, apply: { [weak self] title in + let controller = stickerPackEditTitleController(context: context, title: self.presentationData.strings.StickerPack_EditName_Title, text: self.presentationData.strings.StickerPack_EditName_Text, placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: self.updatedTitle ?? info.title, maxLength: 64, apply: { [weak self] title in guard let self, let title else { return } @@ -1421,7 +1412,7 @@ private final class StickerPackContainer: ASDisplayNode { return } let context = self.context - controller.present(textAlertController(context: context, updatedPresentationData: controller.updatedPresentationData, title: "Delete Sticker Set", text: "This will delete the sticker set for all users.", actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .destructiveAction, title: "Delete", action: { [weak self] in + controller.present(textAlertController(context: context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.StickerPack_Delete_Title, text: self.presentationData.strings.StickerPack_Delete_Text, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .destructiveAction, title: self.presentationData.strings.StickerPack_Delete_Delete, action: { [weak self] in let _ = (context.engine.stickers.deleteStickerSet(packReference: .id(id: info.id.id, accessHash: info.accessHash)) |> deliverOnMainQueue).startStandalone() @@ -1478,7 +1469,7 @@ private final class StickerPackContainer: ASDisplayNode { } self.requestDismiss() } else if let (info, _, installed) = self.currentStickerPack { - if GlobalExperimentalSettings.enableWIPStickers, installed, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { + if installed, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { self.updateIsEditing(!self.isEditing) return } @@ -1553,7 +1544,7 @@ private final class StickerPackContainer: ASDisplayNode { if let currentContents = self.currentContents, currentContents.count == 1, let content = currentContents.first, case let .result(info, _, installed) = content { if installed { let text: String - if GlobalExperimentalSettings.enableWIPStickers, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { + if info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { if self.isEditing { var updated = false if let current = self.buttonNode.attributedTitle(for: .normal)?.string, !current.isEmpty && current != self.presentationData.strings.Common_Done { @@ -1572,8 +1563,9 @@ private final class StickerPackContainer: ASDisplayNode { self.buttonNode.setTitle(self.presentationData.strings.Common_Done, with: Font.semibold(17.0), with: self.presentationData.theme.list.itemCheckColors.foregroundColor, for: .normal) self.buttonNode.setBackgroundImage(generateStretchableFilledCircleImage(radius: 11, color: self.presentationData.theme.list.itemCheckColors.fillColor), for: []) } else { + let buttonTitle = self.presentationData.strings.StickerPack_EditStickers var updated = false - if let current = self.buttonNode.attributedTitle(for: .normal)?.string, !current.isEmpty && current != "Edit Stickers" { + if let current = self.buttonNode.attributedTitle(for: .normal)?.string, !current.isEmpty && current != buttonTitle { updated = true } @@ -1586,8 +1578,7 @@ private final class StickerPackContainer: ASDisplayNode { self.buttonNode.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } - //TODO:localize - text = "Edit Stickers" + text = buttonTitle self.buttonNode.setTitle(text, with: Font.regular(17.0), with: self.presentationData.theme.list.itemAccentColor, for: .normal) self.buttonNode.setBackgroundImage(nil, for: []) } @@ -1732,7 +1723,7 @@ private final class StickerPackContainer: ASDisplayNode { self.controller?.present(textAlertController(context: self.context, title: nil, text: self.presentationData.strings.StickerPack_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) self.controller?.dismiss(animated: true, completion: nil) case let .result(info, items, installed): - isEditable = GlobalExperimentalSettings.enableWIPStickers && info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) + isEditable = info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) self.onReady() if !items.isEmpty && self.currentStickerPack == nil { if let _ = self.validLayout, abs(self.expandScrollProgress - 1.0) < .ulpOfOne { @@ -1816,7 +1807,7 @@ private final class StickerPackContainer: ASDisplayNode { self.updateButton(count: count) } - if GlobalExperimentalSettings.enableWIPStickers && info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) && entries.count < maxStickersCount { + if info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) && entries.count < maxStickersCount { entries.append(.add) } } @@ -1926,7 +1917,7 @@ private final class StickerPackContainer: ASDisplayNode { self.currentEntries = entries if let controller = self.controller { - let transaction = StickerPackPreviewGridTransaction(previousList: previousEntries, list: entries, context: self.context, interaction: self.interaction, theme: self.presentationData.theme, strings: self.presentationData.strings, animationCache: controller.animationCache, animationRenderer: controller.animationRenderer, scrollToItem: nil, isEditable: GlobalExperimentalSettings.enableWIPStickers && info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji), isEditing: self.isEditing, invert: invert) + let transaction = StickerPackPreviewGridTransaction(previousList: previousEntries, list: entries, context: self.context, interaction: self.interaction, theme: self.presentationData.theme, strings: self.presentationData.strings, animationCache: controller.animationCache, animationRenderer: controller.animationRenderer, scrollToItem: nil, isEditable: info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji), isEditing: self.isEditing, invert: invert) self.enqueueTransaction(transaction) } } @@ -1986,7 +1977,7 @@ private final class StickerPackContainer: ASDisplayNode { actionAreaBottomInset = 2.0 } } - if let (info, _, isInstalled) = self.currentStickerPack, isInstalled, !GlobalExperimentalSettings.enableWIPStickers || (!info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji)) { + if let (info, _, isInstalled) = self.currentStickerPack, isInstalled, (!info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji)) { buttonHeight = 42.0 actionAreaTopInset = 1.0 actionAreaBottomInset = 2.0 diff --git a/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift b/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift index ef2701a5e5..833153052a 100644 --- a/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift +++ b/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift @@ -446,14 +446,14 @@ final class EmojiStickerAccessoryNode: SparseNode, PeekControllerAccessoryNode { let items = topItems(selectedEmoji: selectedEmoji, recommendedEmoji: recommendedEmoji, count: 7) let selectedItems = ValuePromise<[String]>(selectedEmoji) - //TODO:localize + let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme) let reactionContextNode = ReactionContextNode( context: self.context, animationCache: self.context.animationCache, - presentationData: self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme), + presentationData: presentationData, items: items.map { .staticEmoji($0) }, selectedItems: Set(selectedEmoji), - title: "Set emoji that corresponds to your sticker", + title: presentationData.strings.MediaEditor_SetStickerEmoji, reactionsLocked: false, alwaysAllowPremiumReactions: true, allPresetReactionsAreAvailable: true, diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index aa438ce2e2..2fd87adc12 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -751,6 +751,13 @@ private final class CameraScreenComponent: CombinedComponent { state.cameraState = component.cameraState state.volumeButtonsListenerActive = component.hasAppeared && component.isVisible + let isSticker: Bool + if let controller = controller() as? CameraScreen, case .sticker = controller.mode { + isSticker = true + } else { + isSticker = false + } + let isTablet: Bool if case .regular = environment.metrics.widthClass { isTablet = true @@ -879,6 +886,7 @@ private final class CameraScreenComponent: CombinedComponent { let captureControls = captureControls.update( component: CaptureControlsComponent( isTablet: isTablet, + isSticker: isSticker, hasAppeared: component.hasAppeared && hasAllRequiredAccess, hasAccess: hasAllRequiredAccess, tintColor: controlsTintColor, @@ -1063,7 +1071,7 @@ private final class CameraScreenComponent: CombinedComponent { .disappear(.default(scale: true)) ) - if !isTablet && Camera.isDualCameraSupported(forRoundVideo: false) { + if !isSticker && !isTablet && Camera.isDualCameraSupported(forRoundVideo: false) { let dualButton = dualButton.update( component: CameraButton( content: AnyComponentWithIdentity( @@ -1201,7 +1209,7 @@ private final class CameraScreenComponent: CombinedComponent { } } - if case .none = component.cameraState.recording, !state.isTransitioning && hasAllRequiredAccess { + if !isSticker, case .none = component.cameraState.recording, !state.isTransitioning && hasAllRequiredAccess { let availableModeControlSize: CGSize if isTablet { availableModeControlSize = CGSize(width: panelWidth, height: 120.0) @@ -1314,6 +1322,11 @@ private class BlurView: UIVisualEffectView { } public class CameraScreen: ViewController { + public enum Mode { + case story + case sticker + } + public enum PIPPosition: Int32 { case topLeft case topRight @@ -1443,7 +1456,7 @@ public class CameraScreen: ViewController { private var validLayout: ContainerViewLayout? fileprivate var didAppear: () -> Void = {} - + private let completion = ActionSlot>() var cameraState: CameraState { @@ -1735,22 +1748,31 @@ public class CameraScreen: ViewController { fileprivate var captureStartTimestamp: Double? private func setupCamera() { - guard self.camera == nil else { + guard self.camera == nil, let controller = self.controller else { return } - let camera = Camera( - configuration: Camera.Configuration( - preset: .hd1920x1080, - position: self.cameraState.position, - isDualEnabled: self.cameraState.isDualCameraEnabled, - audio: true, - photo: true, - metadata: false - ), - previewView: self.mainPreviewView, - secondaryPreviewView: self.additionalPreviewView - ) + var isNew = false + let camera: Camera + if let cameraHolder = controller.holder { + camera = cameraHolder.camera + self.mainPreviewView = cameraHolder.previewView + self.mainPreviewContainerView.addSubview(self.mainPreviewView) + } else { + camera = Camera( + configuration: Camera.Configuration( + preset: .hd1920x1080, + position: self.cameraState.position, + isDualEnabled: self.cameraState.isDualCameraEnabled, + audio: true, + photo: true, + metadata: false + ), + previewView: self.mainPreviewView, + secondaryPreviewView: self.additionalPreviewView + ) + isNew = true + } self.cameraStateDisposable = combineLatest( queue: Queue.mainQueue(), @@ -1841,12 +1863,14 @@ public class CameraScreen: ViewController { }) camera.focus(at: CGPoint(x: 0.5, y: 0.5), autoFocus: true) - camera.startCapture() + if isNew { + camera.startCapture() + } self.captureStartTimestamp = CACurrentMediaTime() self.camera = camera - if self.hasAppeared { + if isNew && self.hasAppeared { self.maybePresentTooltips() } } @@ -2025,45 +2049,75 @@ public class CameraScreen: ViewController { ) } + var animatedIn = false func animateIn() { + guard let controller = self.controller else { + return + } self.transitionDimView.alpha = 0.0 self.backgroundView.alpha = 0.0 UIView.animate(withDuration: 0.4, animations: { self.backgroundView.alpha = 1.0 }) - if let layout = self.validLayout, layout.metrics.isTablet { - self.controller?.statusBar.updateStatusBarStyle(.Hide, animated: true) + if let layout = self.validLayout { + if layout.metrics.isTablet { + controller.statusBar.updateStatusBarStyle(.Hide, animated: true) + } else { + controller.statusBar.updateStatusBarStyle(.White, animated: true) + } } if let transitionIn = self.controller?.transitionIn, let sourceView = transitionIn.sourceView { let sourceLocalFrame = sourceView.convert(transitionIn.sourceRect, to: self.view) - - let sourceScale = sourceLocalFrame.width / self.previewContainerView.frame.width - self.previewContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: self.previewContainerView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in - self.requestUpdateLayout(hasAppeared: true, transition: .immediate) - }) - self.previewContainerView.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) - - let minSide = min(self.previewContainerView.bounds.width, self.previewContainerView.bounds.height) - self.previewContainerView.layer.animateBounds(from: CGRect(origin: CGPoint(x: (self.previewContainerView.bounds.width - minSide) / 2.0, y: (self.previewContainerView.bounds.height - minSide) / 2.0), size: CGSize(width: minSide, height: minSide)), to: self.previewContainerView.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) - self.previewContainerView.layer.animate( - from: minSide / 2.0 as NSNumber, - to: self.previewContainerView.layer.cornerRadius as NSNumber, - keyPath: "cornerRadius", - timingFunction: kCAMediaTimingFunctionSpring, - duration: 0.3 - ) + if case .story = controller.mode { + let sourceScale = sourceLocalFrame.width / self.previewContainerView.frame.width + + self.previewContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: self.previewContainerView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in + self.requestUpdateLayout(hasAppeared: true, transition: .immediate) + }) + self.previewContainerView.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + + let minSide = min(self.previewContainerView.bounds.width, self.previewContainerView.bounds.height) + self.previewContainerView.layer.animateBounds(from: CGRect(origin: CGPoint(x: (self.previewContainerView.bounds.width - minSide) / 2.0, y: (self.previewContainerView.bounds.height - minSide) / 2.0), size: CGSize(width: minSide, height: minSide)), to: self.previewContainerView.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + self.previewContainerView.layer.animate( + from: minSide / 2.0 as NSNumber, + to: self.previewContainerView.layer.cornerRadius as NSNumber, + keyPath: "cornerRadius", + timingFunction: kCAMediaTimingFunctionSpring, + duration: 0.3 + ) + } else { + let sourceInnerFrame = sourceView.convert(transitionIn.sourceRect, to: self.previewContainerView) + let sourceCenter = sourceInnerFrame.center + self.mainPreviewView.layer.position = CGPoint(x: self.previewContainerView.frame.width / 2.0, y: self.previewContainerView.frame.height / 2.0) + self.mainPreviewView.layer.animatePosition(from: sourceCenter, to: self.mainPreviewView.layer.position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in + self.requestUpdateLayout(hasAppeared: true, transition: .immediate) + }) + + self.mainPreviewView.layer.animateBounds(from: self.mainPreviewView.bounds, to: CGRect(origin: .zero, size: self.previewContainerView.frame.size), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + + let sourceScale = self.mainPreviewView.layer.value(forKeyPath: "transform.scale.x") as? CGFloat ?? 1.0 + self.mainPreviewView.transform = CGAffineTransform.identity + self.mainPreviewView.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in + Queue.mainQueue().justDispatch { + self.animatedIn = true + } + }) + } if let view = self.componentHost.view { view.layer.animatePosition(from: sourceLocalFrame.center, to: view.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } } } func animateOut(completion: @escaping () -> Void) { - self.camera?.stopCapture(invalidate: true) + guard let controller = self.controller else { + return + } UIView.animate(withDuration: 0.25, animations: { self.backgroundView.alpha = 0.0 @@ -2071,34 +2125,52 @@ public class CameraScreen: ViewController { if let transitionOut = self.controller?.transitionOut(false), let destinationView = transitionOut.destinationView { let destinationLocalFrame = destinationView.convert(transitionOut.destinationRect, to: self.view) - let targetScale = destinationLocalFrame.width / self.previewContainerView.frame.width - self.previewContainerView.layer.animatePosition(from: self.previewContainerView.center, to: destinationLocalFrame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in - completion() - }) - self.previewContainerView.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) - let minSide = min(self.previewContainerView.bounds.width, self.previewContainerView.bounds.height) - self.previewContainerView.layer.animateBounds(from: self.previewContainerView.bounds, to: CGRect(origin: CGPoint(x: (self.previewContainerView.bounds.width - minSide) / 2.0, y: (self.previewContainerView.bounds.height - minSide) / 2.0), size: CGSize(width: minSide, height: minSide)), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) - self.previewContainerView.layer.animate( - from: self.previewContainerView.layer.cornerRadius as NSNumber, - to: minSide / 2.0 as NSNumber, - keyPath: "cornerRadius", - timingFunction: kCAMediaTimingFunctionSpring, - duration: 0.3, - removeOnCompletion: false - ) + if case .story = controller.mode { + self.previewContainerView.layer.animatePosition(from: self.previewContainerView.center, to: destinationLocalFrame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in + completion() + }) + self.previewContainerView.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + + let minSide = min(self.previewContainerView.bounds.width, self.previewContainerView.bounds.height) + self.previewContainerView.layer.animateBounds(from: self.previewContainerView.bounds, to: CGRect(origin: CGPoint(x: (self.previewContainerView.bounds.width - minSide) / 2.0, y: (self.previewContainerView.bounds.height - minSide) / 2.0), size: CGSize(width: minSide, height: minSide)), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + self.previewContainerView.layer.animate( + from: self.previewContainerView.layer.cornerRadius as NSNumber, + to: minSide / 2.0 as NSNumber, + keyPath: "cornerRadius", + timingFunction: kCAMediaTimingFunctionSpring, + duration: 0.3, + removeOnCompletion: false + ) + } else { + let destinationInnerFrame = destinationView.convert(transitionOut.destinationRect, to: self.previewContainerView) + let initialCenter = self.mainPreviewView.layer.position + self.mainPreviewView.layer.position = destinationInnerFrame.center + self.mainPreviewView.layer.animatePosition(from: initialCenter, to: self.mainPreviewView.layer.position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in + completion() + }) + + self.mainPreviewView.layer.animateBounds(from: self.mainPreviewView.bounds, to: CGRect(origin: .zero, size: self.previewContainerView.frame.size), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + + let targetScale = destinationInnerFrame.width / self.previewContainerView.frame.width +// self.mainPreviewView.transform = CGAffineTransform.identity + self.mainPreviewView.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + } if let view = self.componentHost.view { view.layer.animatePosition(from: view.center, to: destinationLocalFrame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) view.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + view.layer.animateScale(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) } } else { completion() } self.componentHost.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) - self.previewContainerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false) + if case .story = controller.mode { + self.previewContainerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false) + } } func animateOutToEditor() { @@ -2344,7 +2416,7 @@ public class CameraScreen: ViewController { fileprivate var hasAppeared = false func containerLayoutUpdated(layout: ContainerViewLayout, forceUpdate: Bool = false, hasAppeared: Bool = false, transition: Transition) { - guard let _ = self.controller else { + guard let controller = self.controller else { return } let isFirstTime = self.validLayout == nil @@ -2578,7 +2650,14 @@ public class CameraScreen: ViewController { self.additionalPreviewContainerView.insertSubview(additionalPreviewView, at: 0) } - mainPreviewView.frame = mainPreviewInnerFrame + if case .sticker = controller.mode { + if self.animatedIn { + mainPreviewView.frame = mainPreviewInnerFrame + } + } else { + mainPreviewView.frame = mainPreviewInnerFrame + } + additionalPreviewView.frame = additionalPreviewInnerFrame self.previewFrameLeftDimView.isHidden = !isTablet @@ -2604,14 +2683,14 @@ public class CameraScreen: ViewController { transition.setPosition(view: self.transitionCornersView, position: CGPoint(x: layout.size.width + screenCornerRadius / 2.0, y: layout.size.height / 2.0)) transition.setBounds(view: self.transitionCornersView, bounds: CGRect(origin: .zero, size: CGSize(width: screenCornerRadius, height: layout.size.height))) - if isTablet && isFirstTime { + if (controller.mode == .sticker || isTablet) && isFirstTime { self.animateIn() } if self.cameraState.flashMode == .on && (self.cameraState.recording != .none || self.cameraState.mode == .video) { - self.controller?.statusBarStyle = .Black + controller.statusBarStyle = .Black } else { - self.controller?.statusBarStyle = .White + controller.statusBarStyle = .White } } } @@ -2621,6 +2700,7 @@ public class CameraScreen: ViewController { } private let context: AccountContext + fileprivate let mode: Mode fileprivate let holder: CameraHolder? fileprivate let transitionIn: TransitionIn? fileprivate let transitionOut: (Bool) -> TransitionOut? @@ -2645,6 +2725,7 @@ public class CameraScreen: ViewController { } fileprivate let completion: (Signal, ResultTransition?, @escaping () -> Void) -> Void public var transitionedIn: () -> Void = {} + public var transitionedOut: () -> Void = {} private var audioSessionDisposable: Disposable? @@ -2663,6 +2744,8 @@ public class CameraScreen: ViewController { return self.node.cameraState } + public var isEmbedded = false + fileprivate func updateCameraState(_ f: (CameraState) -> CameraState, transition: Transition) { self.node.cameraState = f(self.node.cameraState) self.node.requestUpdateLayout(hasAppeared: self.node.hasAppeared, transition: transition) @@ -2670,12 +2753,14 @@ public class CameraScreen: ViewController { public init( context: AccountContext, + mode: Mode, holder: CameraHolder? = nil, transitionIn: TransitionIn?, transitionOut: @escaping (Bool) -> TransitionOut?, completion: @escaping (Signal, ResultTransition?, @escaping () -> Void) -> Void ) { self.context = context + self.mode = mode self.holder = holder self.transitionIn = transitionIn self.transitionOut = transitionOut @@ -2907,13 +2992,17 @@ public class CameraScreen: ViewController { self.hapticFeedback.impact(.light) } - self.node.camera?.stopCapture(invalidate: true) + if case .story = self.mode { + self.node.camera?.stopCapture(invalidate: true) + } + self.isDismissed = true if animated { self.ignoreStatusBar = true - if let layout = self.validLayout, layout.metrics.isTablet { + if let layout = self.validLayout, layout.metrics.isTablet || self.mode == .sticker { self.node.animateOut(completion: { self.dismiss(animated: false) + self.transitionedOut() }) } else { if !interactive { @@ -2923,6 +3012,7 @@ public class CameraScreen: ViewController { } self.updateTransitionProgress(0.0, transition: .animated(duration: 0.4, curve: .spring), completion: { [weak self] in self?.dismiss(animated: false) + self?.transitionedOut() }) } } else { diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift index 6c68b85610..fcd1b04b8d 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift @@ -459,6 +459,7 @@ final class CaptureControlsComponent: Component { } let isTablet: Bool + let isSticker: Bool let hasAppeared: Bool let hasAccess: Bool let tintColor: UIColor @@ -478,6 +479,7 @@ final class CaptureControlsComponent: Component { init( isTablet: Bool, + isSticker: Bool, hasAppeared: Bool, hasAccess: Bool, tintColor: UIColor, @@ -496,6 +498,7 @@ final class CaptureControlsComponent: Component { flipAnimationAction: ActionSlot ) { self.isTablet = isTablet + self.isSticker = isSticker self.hasAppeared = hasAppeared self.hasAccess = hasAccess self.tintColor = tintColor @@ -518,6 +521,9 @@ final class CaptureControlsComponent: Component { if lhs.isTablet != rhs.isTablet { return false } + if lhs.isSticker != rhs.isSticker { + return false + } if lhs.hasAppeared != rhs.hasAppeared { return false } @@ -911,66 +917,71 @@ final class CaptureControlsComponent: Component { } else if case .transition = component.shutterState { isTransitioning = true } - - let gallerySize: CGSize - let galleryCornerRadius: CGFloat - if component.isTablet { - gallerySize = CGSize(width: 72.0, height: 72.0) - galleryCornerRadius = 16.0 - } else { - gallerySize = CGSize(width: 50.0, height: 50.0) - galleryCornerRadius = 10.0 - } - let galleryButtonId: String - if let (identifier, _) = state.cachedAssetImage, identifier == "" { - galleryButtonId = "placeholder" - } else { - galleryButtonId = "gallery" - } - let galleryButtonSize = self.galleryButtonView.update( - transition: transition, - component: AnyComponent( - CameraButton( - content: AnyComponentWithIdentity( - id: galleryButtonId, - component: AnyComponent( - Image( - image: state.cachedAssetImage?.1, - size: gallerySize, - contentMode: .scaleAspectFill - ) - ) - ), - tag: component.galleryButtonTag, - action: { - component.galleryTapped() - } - ) - ), - environment: {}, - containerSize: gallerySize - ) + let galleryButtonFrame: CGRect - if component.isTablet { - galleryButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - galleryButtonSize.width) / 2.0), y: size.height - galleryButtonSize.height - 56.0), size: galleryButtonSize) - } else { - galleryButtonFrame = CGRect(origin: CGPoint(x: buttonSideInset, y: floorToScreenPixels((size.height - galleryButtonSize.height) / 2.0)), size: galleryButtonSize) - } - if let galleryButtonView = self.galleryButtonView.view as? CameraButton.View { - galleryButtonView.contentView.clipsToBounds = true - galleryButtonView.contentView.layer.cornerRadius = galleryCornerRadius - if galleryButtonView.superview == nil { - self.addSubview(galleryButtonView) + let gallerySize: CGSize + if !component.isSticker { + let galleryCornerRadius: CGFloat + if component.isTablet { + gallerySize = CGSize(width: 72.0, height: 72.0) + galleryCornerRadius = 16.0 + } else { + gallerySize = CGSize(width: 50.0, height: 50.0) + galleryCornerRadius = 10.0 } - transition.setBounds(view: galleryButtonView, bounds: CGRect(origin: .zero, size: galleryButtonFrame.size)) - transition.setPosition(view: galleryButtonView, position: galleryButtonFrame.center) - - let normalAlpha = component.tintColor.rgb == 0xffffff ? 1.0 : 0.6 - - transition.setScale(view: galleryButtonView, scale: isRecording || isTransitioning ? 0.1 : 1.0) - transition.setAlpha(view: galleryButtonView, alpha: isRecording || isTransitioning ? 0.0 : normalAlpha) + let galleryButtonId: String + if let (identifier, _) = state.cachedAssetImage, identifier == "" { + galleryButtonId = "placeholder" + } else { + galleryButtonId = "gallery" + } + let galleryButtonSize = self.galleryButtonView.update( + transition: transition, + component: AnyComponent( + CameraButton( + content: AnyComponentWithIdentity( + id: galleryButtonId, + component: AnyComponent( + Image( + image: state.cachedAssetImage?.1, + size: gallerySize, + contentMode: .scaleAspectFill + ) + ) + ), + tag: component.galleryButtonTag, + action: { + component.galleryTapped() + } + ) + ), + environment: {}, + containerSize: gallerySize + ) + if component.isTablet { + galleryButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - galleryButtonSize.width) / 2.0), y: size.height - galleryButtonSize.height - 56.0), size: galleryButtonSize) + } else { + galleryButtonFrame = CGRect(origin: CGPoint(x: buttonSideInset, y: floorToScreenPixels((size.height - galleryButtonSize.height) / 2.0)), size: galleryButtonSize) + } + if let galleryButtonView = self.galleryButtonView.view as? CameraButton.View { + galleryButtonView.contentView.clipsToBounds = true + galleryButtonView.contentView.layer.cornerRadius = galleryCornerRadius + if galleryButtonView.superview == nil { + self.addSubview(galleryButtonView) + } + transition.setBounds(view: galleryButtonView, bounds: CGRect(origin: .zero, size: galleryButtonFrame.size)) + transition.setPosition(view: galleryButtonView, position: galleryButtonFrame.center) + + let normalAlpha = component.tintColor.rgb == 0xffffff ? 1.0 : 0.6 + + transition.setScale(view: galleryButtonView, scale: isRecording || isTransitioning ? 0.1 : 1.0) + transition.setAlpha(view: galleryButtonView, alpha: isRecording || isTransitioning ? 0.0 : normalAlpha) + } + } else { + galleryButtonFrame = .zero + gallerySize = .zero } - + if !component.isTablet && component.hasAccess { let flipButtonOriginX = availableSize.width - 48.0 - buttonSideInset let flipButtonMaskFrame: CGRect = CGRect(origin: CGPoint(x: availableSize.width / 2.0 - (flipButtonOriginX + 22.0) + 6.0 + self.shutterOffsetX, y: 8.0), size: CGSize(width: 32.0, height: 32.0)) @@ -1173,15 +1184,16 @@ final class CaptureControlsComponent: Component { if let shutterButtonView = self.shutterButtonView.view { if shutterButtonView.superview == nil { - let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:))) - panGestureRecognizer.delegate = self - shutterButtonView.addGestureRecognizer(panGestureRecognizer) - - let pressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handlePress(_:))) - pressGestureRecognizer.minimumPressDuration = 0.3 - pressGestureRecognizer.delegate = self - shutterButtonView.addGestureRecognizer(pressGestureRecognizer) - + if !component.isSticker { + let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:))) + panGestureRecognizer.delegate = self + shutterButtonView.addGestureRecognizer(panGestureRecognizer) + + let pressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handlePress(_:))) + pressGestureRecognizer.minimumPressDuration = 0.3 + pressGestureRecognizer.delegate = self + shutterButtonView.addGestureRecognizer(pressGestureRecognizer) + } self.addSubview(shutterButtonView) } let alpha: CGFloat = component.hasAccess ? 1.0 : 0.3 diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index bcbbe51e91..fb16b0f37e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -959,6 +959,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: actualSize.width - backgroundInsets.left - backgroundInsets.right, height: actualSize.height - backgroundInsets.top - backgroundInsets.bottom)) var patternTopRightPosition = CGPoint() + var patternAlpha: CGFloat = 1.0 if !contentAnimatedFilesValue.isEmpty, let (_, inlineMediaSize) = inlineMediaAndSize { var inlineMediaFrame = CGRect(origin: CGPoint(x: actualSize.width - insets.right - inlineMediaSize.width, y: backgroundInsets.top + inlineMediaEdgeInset), size: inlineMediaSize) @@ -966,8 +967,8 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { inlineMediaFrame.origin.x = insets.left } - patternTopRightPosition.x = insets.right + inlineMediaSize.width - 6.0 - + patternAlpha = 0.5 + if !contentAnimatedFilesValue.isEmpty { if contentAnimatedFilesValue.count < 4, let file = contentAnimatedFilesValue.first { let stickerLayer: InlineStickerItemLayer @@ -1486,13 +1487,13 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { if let current = self.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) + backgroundView.update(size: backgroundFrame.size, isTransparent: false, primaryColor: mainColor, secondaryColor: secondaryColor, thirdColor: tertiaryColor, backgroundColor: nil, pattern: pattern, patternTopRightPosition: patternTopRightPosition, patternAlpha: patternAlpha, animation: animation) } else { backgroundView = MessageInlineBlockBackgroundView() self.backgroundView = backgroundView backgroundView.frame = backgroundFrame self.transformContainer.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) + backgroundView.update(size: backgroundFrame.size, isTransparent: false, primaryColor: mainColor, secondaryColor: secondaryColor, thirdColor: tertiaryColor, backgroundColor: nil, pattern: pattern, patternTopRightPosition: patternTopRightPosition, patternAlpha: patternAlpha, animation: .None) } } else { if let backgroundView = self.backgroundView { diff --git a/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift b/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift index 9daa53bf3a..225d40505c 100644 --- a/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift +++ b/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift @@ -460,6 +460,7 @@ public final class MessageInlineBlockBackgroundView: UIView { var backgroundColor: UIColor? var pattern: Pattern? var patternTopRightPosition: CGPoint? + var patternAlpha: CGFloat var displayProgress: Bool init( @@ -471,6 +472,7 @@ public final class MessageInlineBlockBackgroundView: UIView { backgroundColor: UIColor?, pattern: Pattern?, patternTopRightPosition: CGPoint?, + patternAlpha: CGFloat, displayProgress: Bool ) { self.size = size @@ -481,6 +483,7 @@ public final class MessageInlineBlockBackgroundView: UIView { self.backgroundColor = backgroundColor self.pattern = pattern self.patternTopRightPosition = patternTopRightPosition + self.patternAlpha = patternAlpha self.displayProgress = displayProgress } } @@ -612,6 +615,7 @@ public final class MessageInlineBlockBackgroundView: UIView { backgroundColor: UIColor?, pattern: Pattern?, patternTopRightPosition: CGPoint? = nil, + patternAlpha: CGFloat = 1.0, animation: ListViewItemUpdateAnimation ) { let params = Params( @@ -623,6 +627,7 @@ public final class MessageInlineBlockBackgroundView: UIView { backgroundColor: backgroundColor, pattern: pattern, patternTopRightPosition: patternTopRightPosition, + patternAlpha: patternAlpha, displayProgress: self.displayProgress ) if self.params == params { @@ -766,7 +771,7 @@ public final class MessageInlineBlockBackgroundView: UIView { patternContentLayer.frame = CGRect(origin: CGPoint(x: patternOrigin.x - placement.position.x / 3.0 - itemSize.width * 0.5, y: patternOrigin.y + placement.position.y / 3.0 - itemSize.height * 0.5), size: itemSize) var alphaFraction = abs(placement.position.x / 3.0) / min(500.0, size.width) alphaFraction = min(1.0, max(0.0, alphaFraction)) - patternContentLayer.opacity = 0.3 * Float(1.0 - alphaFraction) + patternContentLayer.opacity = 0.3 * Float(1.0 - alphaFraction) * Float(patternAlpha) maxIndex += 1 } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index defb3c823a..f71777f833 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -215,7 +215,7 @@ public final class EntityKeyboardAnimationData: Equatable { self.isTemplate = isTemplate } - public convenience init(file: TelegramMediaFile, isReaction: Bool = false) { + public convenience init(file: TelegramMediaFile, isReaction: Bool = false, partialReference: PartialMediaReference? = nil) { let type: ItemType if file.isVideoSticker || file.isVideoEmoji { type = .video @@ -225,7 +225,14 @@ public final class EntityKeyboardAnimationData: Equatable { type = .still } let isTemplate = file.isCustomTemplateEmoji - self.init(id: .file(file.fileId), type: type, resource: .standalone(resource: file.resource), dimensions: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), immediateThumbnailData: file.immediateThumbnailData, isReaction: isReaction, isTemplate: isTemplate) + + let resourceReference: MediaResourceReference + if let partialReference { + resourceReference = partialReference.mediaReference(file).resourceReference(file.resource) + } else { + resourceReference = .standalone(resource: file.resource) + } + self.init(id: .file(file.fileId), type: type, resource: resourceReference, dimensions: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), immediateThumbnailData: file.immediateThumbnailData, isReaction: isReaction, isTemplate: isTemplate) } public static func ==(lhs: EntityKeyboardAnimationData, rhs: EntityKeyboardAnimationData) -> Bool { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift index 528fc17772..68aaa85996 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift @@ -1787,6 +1787,7 @@ public extension EmojiPagerContentComponent { } if let savedStickers = savedStickers { + let groupId = "saved" for item in savedStickers.items { guard let item = item.contents.get(SavedStickerItem.self) else { continue @@ -1800,7 +1801,7 @@ public extension EmojiPagerContentComponent { tintMode = .primary } - let animationData = EntityKeyboardAnimationData(file: item.file) + let animationData = EntityKeyboardAnimationData(file: item.file, partialReference: .savedSticker) let resultItem = EmojiPagerContentComponent.Item( animationData: animationData, content: .animation(animationData), @@ -1810,7 +1811,6 @@ public extension EmojiPagerContentComponent { tintMode: tintMode ) - let groupId = "saved" if let groupIndex = itemGroupIndexById[groupId] { itemGroups[groupIndex].items.append(resultItem) } else { @@ -1836,7 +1836,7 @@ public extension EmojiPagerContentComponent { tintMode = .primary } - let animationData = EntityKeyboardAnimationData(file: item.media) + let animationData = EntityKeyboardAnimationData(file: item.media, partialReference: .recentSticker) let resultItem = EmojiPagerContentComponent.Item( animationData: animationData, content: .animation(animationData), diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index cb07e90d5d..6f5fb0535a 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -587,12 +587,6 @@ final class MediaEditorScreenComponent: Component { view.layer.animateAlpha(from: view.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2) } - - if let view = self.scrubber?.view { - view.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 44.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) - view.layer.animateAlpha(from: view.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) - view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2) - } } if let view = self.saveButton.view { @@ -615,6 +609,36 @@ final class MediaEditorScreenComponent: Component { view.layer.animateAlpha(from: view.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false) view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2) } + + if let view = self.undoButton.view { + transition.setAlpha(view: view, alpha: 0.0) + transition.setScale(view: view, scale: 0.1) + } + + if let view = self.eraseButton.view { + transition.setAlpha(view: view, alpha: 0.0) + transition.setScale(view: view, scale: 0.1) + } + + if let view = self.restoreButton.view { + transition.setAlpha(view: view, alpha: 0.0) + transition.setScale(view: view, scale: 0.1) + } + + if let view = self.outlineButton.view { + transition.setAlpha(view: view, alpha: 0.0) + transition.setScale(view: view, scale: 0.1) + } + + if let view = self.cutoutButton.view { + transition.setAlpha(view: view, alpha: 0.0) + transition.setScale(view: view, scale: 0.1) + } + + if let view = self.textSize.view { + transition.setAlpha(view: view, alpha: 0.0) + transition.setScale(view: view, scale: 0.1) + } } func animateOutToTool(inPlace: Bool, transition: Transition) { @@ -1977,8 +2001,10 @@ final class MediaEditorScreenComponent: Component { var hasEraseButton = false var hasRestoreButton = false var hasOutlineButton = false - - if let canCutout = controller.node.canCutout { + + if let subject = controller.node.subject, case .empty = subject { + + } else if let canCutout = controller.node.canCutout { if controller.node.isCutout || controller.node.stickerMaskDrawingView?.internalState.canUndo == true { hasUndoButton = true } @@ -2002,7 +2028,7 @@ final class MediaEditorScreenComponent: Component { content: AnyComponent(CutoutButtonContentComponent( backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18), icon: state.image(.undo), - title: "Undo" + title: environment.strings.MediaEditor_Undo )), effectAlignment: .center, action: { @@ -2048,7 +2074,7 @@ final class MediaEditorScreenComponent: Component { content: AnyComponent(CutoutButtonContentComponent( backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18), icon: state.image(.cutout), - title: "Cut Out an Object" + title: environment.strings.MediaEditor_Cutout )), effectAlignment: .center, action: { @@ -2100,7 +2126,7 @@ final class MediaEditorScreenComponent: Component { content: AnyComponent(CutoutButtonContentComponent( backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18), icon: state.image(.erase), - title: "Erase", + title: environment.strings.MediaEditor_Erase, minWidth: 160.0, selected: component.isDisplayingTool == .cutoutErase )), @@ -2123,7 +2149,7 @@ final class MediaEditorScreenComponent: Component { content: AnyComponent(CutoutButtonContentComponent( backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18), icon: state.image(.restore), - title: "Restore", + title: environment.strings.MediaEditor_Restore, minWidth: 160.0, selected: component.isDisplayingTool == .cutoutRestore )), @@ -2204,7 +2230,7 @@ final class MediaEditorScreenComponent: Component { content: AnyComponent(CutoutButtonContentComponent( backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18), icon: state.image(.outline), - title: "Add Outline", + title: environment.strings.MediaEditor_Outline, minWidth: 160.0, selected: isOutlineActive )), @@ -2898,7 +2924,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if case .message = subject, self.context.sharedContext.currentPresentationData.with({$0}).autoNightModeTriggered { mediaEditor.setNightTheme(true) } - mediaEditor.attachPreviewView(self.previewView) mediaEditor.valuesUpdated = { [weak self] values in if let self, let controller = self.controller, values.gradientColors != nil, controller.previousSavedValues != values { if !isSavingAvailable && controller.previousSavedValues == nil { @@ -2936,6 +2961,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } self.controller?.stickerRecommendedEmoji = emojiForClasses(classes.map { $0.0 }) } + mediaEditor.attachPreviewView(self.previewView) if case .empty = effectiveSubject { self.stickerMaskDrawingView?.emptyColor = .black @@ -4641,30 +4667,31 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } let controller = StickerPickerScreen(context: self.context, inputData: self.stickerPickerInputData.get(), forceDark: true, defaultToEmoji: self.defaultToEmoji, hasGifs: true, hasInteractiveStickers: hasInteractiveStickers) controller.completion = { [weak self] content in - if let self { - if let content { - if case let .file(file, _) = content { - self.defaultToEmoji = file.media.isCustomEmoji - } - - let stickerEntity = DrawingStickerEntity(content: content) - let scale: CGFloat - if case .image = content { - scale = 2.5 - } else if case .video = content { - scale = 2.5 - } else { - scale = 1.33 - } - self.interaction?.insertEntity(stickerEntity, scale: scale) - - self.hasAnyChanges = true - self.controller?.isSavingAvailable = true - self.controller?.requestLayout(transition: .immediate) - } - self.stickerScreen = nil - self.mediaEditor?.maybeUnpauseVideo() + guard let self else { + return false } + if let content { + if case let .file(file, _) = content { + self.defaultToEmoji = file.media.isCustomEmoji + } + + let stickerEntity = DrawingStickerEntity(content: content) + let scale: CGFloat + if case .image = content { + scale = 2.5 + } else if case .video = content { + scale = 2.5 + } else { + scale = 1.33 + } + self.interaction?.insertEntity(stickerEntity, scale: scale) + + self.hasAnyChanges = true + self.controller?.isSavingAvailable = true + self.controller?.requestLayout(transition: .immediate) + } + self.stickerScreen = nil + self.mediaEditor?.maybeUnpauseVideo() return true } controller.customModalStyleOverlayTransitionFactorUpdated = { [weak self, weak controller] transition in @@ -6506,13 +6533,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.uploadSticker(file, action: .addToFavorites) }) }))) - menuItems.append(.action(ContextMenuActionItem(text: "Add to Sticker Set", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddSticker"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in + menuItems.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaEditor_AddToStickerPack, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddSticker"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in guard let self else { return } var contextItems: [ContextMenuItem] = [] - contextItems.append(.action(ContextMenuActionItem(text: "Back", icon: { theme in + contextItems.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in c.popItems() @@ -6520,7 +6547,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate contextItems.append(.separator) - contextItems.append(.action(ContextMenuActionItem(text: "New Sticker Set", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddCircle"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { [weak self] _, f in + contextItems.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaEditor_CreateNewPack, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddCircle"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { [weak self] _, f in if let self { self.presentCreateStickerPack(file: file, completion: { f(.default) @@ -6579,7 +6606,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate c.pushItems(items: .single(items)) }))) case .editing: - menuItems.append(.action(ContextMenuActionItem(text: "Replace Sticker", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replace"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + menuItems.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaEditor_ReplaceSticker, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replace"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in guard let self else { return } @@ -6601,7 +6628,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate }) }))) case .addingToPack: - menuItems.append(.action(ContextMenuActionItem(text: "Add to Sticker Set", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddSticker"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in + menuItems.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaEditor_AddToStickerPack, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddSticker"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in guard let self else { return } @@ -6691,11 +6718,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } private func presentCreateStickerPack(file: TelegramMediaFile, completion: @escaping () -> Void) { - //TODO:localize let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme) var dismissImpl: (() -> Void)? - let controller = stickerPackEditTitleController(context: self.context, forceDark: true, title: "New Sticker Set", text: "Choose a name for your sticker set.", placeholder: presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: nil, maxLength: 64, apply: { [weak self] title in + let controller = stickerPackEditTitleController(context: self.context, forceDark: true, title: presentationData.strings.MediaEditor_NewStickerPack_Title, text: presentationData.strings.MediaEditor_NewStickerPack_Text, placeholder: presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: nil, maxLength: 64, apply: { [weak self] title in guard let self else { return } @@ -6942,7 +6968,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate (navigationController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root)) Queue.mainQueue().after(0.1) { - controller.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: self.context, file: file, loop: true, title: nil, text: "Sticker added to **\(title)** sticker set.", undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) + controller.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: self.context, file: file, loop: true, title: nil, text: presentationData.strings.StickerPack_StickerAdded(title).string, undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) } } } diff --git a/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift b/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift index e99ea5ec77..f110a6643d 100644 --- a/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift +++ b/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift @@ -259,9 +259,6 @@ private final class StickerSelectionComponent: Component { interaction: nil, chatPeerId: nil, present: { c, a in - let _ = c - let _ = a -// controller?.presentInGlobalOverlay(c, with: a) } ) @@ -309,7 +306,7 @@ private final class StickerSelectionComponent: Component { switchToGifSubject: { _ in }, reorderItems: { _, _ in }, makeSearchContainerNode: { [weak self] content in - guard let self, let interaction = self.interaction, let inputNodeInteraction = self.inputNodeInteraction else { + guard let self, let interaction = self.interaction, let inputNodeInteraction = self.inputNodeInteraction, let component = self.component, let controller = component.getController() else { return nil } @@ -322,7 +319,7 @@ private final class StickerSelectionComponent: Component { } var presentationData = context.sharedContext.currentPresentationData.with { $0 } - if controller?.forceDark == true { + if controller.forceDark == true { presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme) } let searchContainerNode = PaneSearchContainerNode( @@ -440,7 +437,7 @@ public class StickerPickerScreen: ViewController { let containerView: UIView let hostView: ComponentHostView - private var content: StickerPickerInputData? + fileprivate var content: StickerPickerInputData? private let contentDisposable = MetaDisposable() private var hasRecentGifsDisposable: Disposable? fileprivate let trendingGifsPromise = Promise(nil) @@ -534,18 +531,20 @@ public class StickerPickerScreen: ViewController { self.wrappingView.addSubview(self.containerView) self.containerView.addSubview(self.hostView) - self.storyStickersContentView = StoryStickersContentView(frame: .zero) - self.storyStickersContentView?.locationAction = { [weak self] in - self?.controller?.presentLocationPicker() - } - self.storyStickersContentView?.audioAction = { [weak self] in - self?.controller?.presentAudioPicker() - } - self.storyStickersContentView?.reactionAction = { [weak self] in - self?.controller?.addReaction() - } - self.storyStickersContentView?.cameraAction = { [weak self] in - self?.controller?.addCamera() + if controller.hasInteractiveStickers { + self.storyStickersContentView = StoryStickersContentView(frame: .zero) + self.storyStickersContentView?.locationAction = { [weak self] in + self?.controller?.presentLocationPicker() + } + self.storyStickersContentView?.audioAction = { [weak self] in + self?.controller?.presentAudioPicker() + } + self.storyStickersContentView?.reactionAction = { [weak self] in + self?.controller?.addReaction() + } + self.storyStickersContentView?.cameraAction = { [weak self] in + self?.controller?.addCamera() + } } let gifItems: Signal @@ -654,11 +653,11 @@ public class StickerPickerScreen: ViewController { self.contentDisposable.set(data.start(next: { [weak self] inputData, gifData, stickerSearchState, emojiSearchState in if let strongSelf = self { - let presentationData = strongSelf.presentationData guard var inputData = inputData as? StickerPickerInputData else { return } + let presentationData = strongSelf.presentationData inputData.gifs = gifData?.component if let emoji = inputData.emoji { @@ -821,7 +820,7 @@ public class StickerPickerScreen: ViewController { guard let controller = self.controller else { return } - + content.emoji?.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction( performItemAction: { [weak self] groupId, item, _, _, _, _ in guard let strongSelf = self, let controller = strongSelf.controller else { @@ -1495,7 +1494,6 @@ public class StickerPickerScreen: ViewController { return } if group.items.isEmpty && !result.isFinalResult { - //strongSelf.stickerSearchStateValue.isSearching = true strongSelf.stickerSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: [ EmojiPagerContentComponent.ItemGroup( supergroupId: "search", @@ -1535,7 +1533,7 @@ public class StickerPickerScreen: ViewController { customLayout: nil, externalBackground: nil, externalExpansionView: nil, - customContentView: controller.hasInteractiveStickers ? self.storyStickersContentView : nil, + customContentView: self.storyStickersContentView, useOpaqueTheme: false, hideBackground: true, stateContext: nil, @@ -2050,8 +2048,7 @@ public class StickerPickerScreen: ViewController { self.statusBar.statusBarStyle = .Ignore if expanded { - //TODO:localize - self.title = "Choose Sticker" + self.title = presentationData.strings.Stickers_ChooseSticker_Title self.navigationPresentation = .modal } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index 09d7dac360..2c724d5882 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -1891,7 +1891,9 @@ final class StoryItemSetContainerSendMessage { guard let self, let view else { return } - self.openCamera(view: view, peer: peer, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, cameraView: cameraView) + if let cameraView = cameraView as? TGAttachmentCameraView { + self.openCamera(view: view, peer: peer, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, cameraView: cameraView) + } } controller.presentWebSearch = { [weak self, weak view, weak controller] mediaGroups, activateOnDisplay in guard let self, let view, let controller else { diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift index 965451a3ec..14a0bb84b1 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift @@ -31,6 +31,7 @@ import PremiumGiftAttachmentScreen import TelegramCallsUI import AutomaticBusinessMessageSetupScreen import MediaEditorScreen +import CameraScreen extension ChatControllerImpl { enum AttachMenuSubject { @@ -1160,7 +1161,9 @@ extension ChatControllerImpl { } let mediaPickerContext = controller.mediaPickerContext controller.openCamera = { [weak self] cameraView in - self?.openCamera(cameraView: cameraView) + if let cameraView = cameraView as? TGAttachmentCameraView { + self?.openCamera(cameraView: cameraView) + } } controller.presentWebSearch = { [weak self, weak controller] mediaGroups, activateOnDisplay in self?.presentWebSearch(editingMessage: false, attachment: true, activateOnDisplay: activateOnDisplay, present: { [weak controller] c, a in @@ -1726,8 +1729,8 @@ extension ChatControllerImpl { var dismissImpl: (() -> Void)? let mainController = self.context.sharedContext.makeStickerMediaPickerScreen( context: self.context, - getSourceRect: { return .zero }, - completion: { [weak self] result, transitionView, transitionRect, transitionImage, transitionOut, dismissed in + getSourceRect: { return nil }, + completion: { [weak self] result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in guard let self else { return } @@ -1736,6 +1739,18 @@ extension ChatControllerImpl { subject = .single(.asset(asset)) } else if let image = result as? UIImage { subject = .single(.image(image, PixelDimensions(image.size), nil, .bottomRight)) + } else if let result = result as? Signal { + subject = result + |> map { value -> MediaEditorScreen.Subject? in + switch value { + case .pendingImage: + return nil + case let .image(image): + return .image(image.image, PixelDimensions(image.image.size), nil, .topLeft) + default: + return nil + } + } } else { subject = .single(.empty(PixelDimensions(width: 1080, height: 1920))) } @@ -1744,7 +1759,7 @@ extension ChatControllerImpl { context: self.context, mode: .stickerEditor(mode: .generic), subject: subject, - transitionIn: transitionView.flatMap({ .gallery( + transitionIn: fromCamera ? .camera : transitionView.flatMap({ .gallery( MediaEditorScreen.TransitionIn.GalleryTransitionIn( sourceView: $0, sourceRect: transitionRect, @@ -1772,6 +1787,9 @@ extension ChatControllerImpl { } } as (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void ) + editorController.cancelled = { _ in + cancelled() + } editorController.sendSticker = { [weak self] file, sourceView, sourceRect in return self?.interfaceInteraction?.sendSticker(file, true, sourceView, sourceRect, nil, []) ?? false } @@ -1780,7 +1798,13 @@ extension ChatControllerImpl { dismissed: {} ) dismissImpl = { [weak mainController] in - mainController?.dismiss() + if let mainController, let navigationController = mainController.navigationController { + var viewControllers = navigationController.viewControllers + viewControllers = viewControllers.filter { c in + return !(c is CameraScreen) && c !== mainController + } + navigationController.setViewControllers(viewControllers, animated: false) + } } mainController.navigationPresentation = .flatModal mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index aa0c1c5df3..03ce2ee64d 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2421,7 +2421,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { return storyMediaPickerController(context: context, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed, groupsPresented: groupsPresented) } - public func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any?, UIView?, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController { + public func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController { return stickerMediaPickerController(context: context, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed) } diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 95f025960c..bb22222161 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -300,6 +300,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon var showDraftTooltipImpl: (() -> Void)? let cameraController = CameraScreen( context: context, + mode: .story, transitionIn: transitionIn.flatMap { if let sourceView = $0.sourceView { return CameraScreen.TransitionIn(