mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Stickers editor
This commit is contained in:
parent
8a11a281b7
commit
2d357afd12
@ -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.";
|
||||
|
||||
|
@ -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<StickerPickerInput>, completion: @escaping (FileMediaReference) -> Void) -> ViewController
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (_formatContext) {
|
||||
av_write_trailer(_formatContext);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -45,6 +45,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/CameraScreen",
|
||||
"//submodules/TelegramUI/Components/MediaEditor",
|
||||
"//submodules/RadialStatusNode",
|
||||
"//submodules/Camera",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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<State, NoError>
|
||||
@ -497,11 +509,19 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.gridNode.visibleItemsUpdated = { [weak self] _ in
|
||||
self?.updateScrollingArea()
|
||||
|
||||
if let self, let cameraView = self.cameraView {
|
||||
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()
|
||||
|
||||
let throttledContentOffsetSignal = self.fastScrollContentOffset.get()
|
||||
@ -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 let cameraView = self.cameraView {
|
||||
if isCameraActive {
|
||||
self.cameraView?.resumePreview()
|
||||
cameraView.resumePreview()
|
||||
} else {
|
||||
self.cameraView?.pausePreview()
|
||||
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 {
|
||||
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 = {
|
||||
|
@ -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<StickerPickerInput>()
|
||||
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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
@ -1735,11 +1748,18 @@ 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(
|
||||
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,
|
||||
@ -1751,6 +1771,8 @@ public class CameraScreen: ViewController {
|
||||
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)
|
||||
if isNew {
|
||||
camera.startCapture()
|
||||
}
|
||||
self.captureStartTimestamp = CACurrentMediaTime()
|
||||
|
||||
self.camera = camera
|
||||
|
||||
if self.hasAppeared {
|
||||
if isNew && self.hasAppeared {
|
||||
self.maybePresentTooltips()
|
||||
}
|
||||
}
|
||||
@ -2025,21 +2049,30 @@ 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)
|
||||
|
||||
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)
|
||||
})
|
||||
@ -2054,16 +2087,37 @@ public class CameraScreen: ViewController {
|
||||
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,8 +2125,9 @@ 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
|
||||
|
||||
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()
|
||||
})
|
||||
@ -2088,18 +2143,35 @@ public class CameraScreen: ViewController {
|
||||
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)
|
||||
if case .story = controller.mode {
|
||||
self.previewContainerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false)
|
||||
}
|
||||
}
|
||||
|
||||
func animateOutToEditor() {
|
||||
self.cameraIsActive = false
|
||||
@ -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)
|
||||
}
|
||||
|
||||
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<CameraScreen.Result, NoError>, 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<CameraScreen.Result, NoError>, 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)
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -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<Void>
|
||||
) {
|
||||
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
|
||||
}
|
||||
@ -912,7 +918,9 @@ final class CaptureControlsComponent: Component {
|
||||
isTransitioning = true
|
||||
}
|
||||
|
||||
let galleryButtonFrame: CGRect
|
||||
let gallerySize: CGSize
|
||||
if !component.isSticker {
|
||||
let galleryCornerRadius: CGFloat
|
||||
if component.isTablet {
|
||||
gallerySize = CGSize(width: 72.0, height: 72.0)
|
||||
@ -950,7 +958,6 @@ final class CaptureControlsComponent: Component {
|
||||
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 {
|
||||
@ -970,6 +977,10 @@ final class CaptureControlsComponent: Component {
|
||||
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
|
||||
@ -1173,6 +1184,7 @@ final class CaptureControlsComponent: Component {
|
||||
|
||||
if let shutterButtonView = self.shutterButtonView.view {
|
||||
if shutterButtonView.superview == nil {
|
||||
if !component.isSticker {
|
||||
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
|
||||
panGestureRecognizer.delegate = self
|
||||
shutterButtonView.addGestureRecognizer(panGestureRecognizer)
|
||||
@ -1181,7 +1193,7 @@ final class CaptureControlsComponent: Component {
|
||||
pressGestureRecognizer.minimumPressDuration = 0.3
|
||||
pressGestureRecognizer.delegate = self
|
||||
shutterButtonView.addGestureRecognizer(pressGestureRecognizer)
|
||||
|
||||
}
|
||||
self.addSubview(shutterButtonView)
|
||||
}
|
||||
let alpha: CGFloat = component.hasAccess ? 1.0 : 0.3
|
||||
|
@ -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,7 +967,7 @@ 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 {
|
||||
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
|
@ -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) {
|
||||
@ -1978,7 +2002,9 @@ final class MediaEditorScreenComponent: Component {
|
||||
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,7 +4667,9 @@ 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 {
|
||||
guard let self else {
|
||||
return false
|
||||
}
|
||||
if let content {
|
||||
if case let .file(file, _) = content {
|
||||
self.defaultToEmoji = file.media.isCustomEmoji
|
||||
@ -4664,7 +4692,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Empty>
|
||||
|
||||
private var content: StickerPickerInputData?
|
||||
fileprivate var content: StickerPickerInputData?
|
||||
private let contentDisposable = MetaDisposable()
|
||||
private var hasRecentGifsDisposable: Disposable?
|
||||
fileprivate let trendingGifsPromise = Promise<ChatMediaInputGifPaneTrendingState?>(nil)
|
||||
@ -534,6 +531,7 @@ public class StickerPickerScreen: ViewController {
|
||||
self.wrappingView.addSubview(self.containerView)
|
||||
self.containerView.addSubview(self.hostView)
|
||||
|
||||
if controller.hasInteractiveStickers {
|
||||
self.storyStickersContentView = StoryStickersContentView(frame: .zero)
|
||||
self.storyStickersContentView?.locationAction = { [weak self] in
|
||||
self?.controller?.presentLocationPicker()
|
||||
@ -547,6 +545,7 @@ public class StickerPickerScreen: ViewController {
|
||||
self.storyStickersContentView?.cameraAction = { [weak self] in
|
||||
self?.controller?.addCamera()
|
||||
}
|
||||
}
|
||||
|
||||
let gifItems: Signal<EntityKeyboardGifContent?, NoError>
|
||||
if controller.hasGifs {
|
||||
@ -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 {
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -1891,8 +1891,10 @@ final class StoryItemSetContainerSendMessage {
|
||||
guard let self, let view else {
|
||||
return
|
||||
}
|
||||
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 {
|
||||
return
|
||||
|
@ -31,6 +31,7 @@ import PremiumGiftAttachmentScreen
|
||||
import TelegramCallsUI
|
||||
import AutomaticBusinessMessageSetupScreen
|
||||
import MediaEditorScreen
|
||||
import CameraScreen
|
||||
|
||||
extension ChatControllerImpl {
|
||||
enum AttachMenuSubject {
|
||||
@ -1160,8 +1161,10 @@ extension ChatControllerImpl {
|
||||
}
|
||||
let mediaPickerContext = controller.mediaPickerContext
|
||||
controller.openCamera = { [weak self] cameraView in
|
||||
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
|
||||
controller?.present(c, in: .current)
|
||||
@ -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<CameraScreen.Result, NoError> {
|
||||
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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user