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";
|
"Conversation.ViewEmojis" = "VIEW EMOJIS";
|
||||||
|
|
||||||
"MediaEditor.StickersTooMuch" = "Sorry, you've reached the maximum number of stickers in this set. Try a different one.";
|
"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 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 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
|
func makeStickerPickerScreen(context: AccountContext, inputData: Promise<StickerPickerInput>, completion: @escaping (FileMediaReference) -> Void) -> ViewController
|
||||||
|
@ -3,5 +3,4 @@ import Foundation
|
|||||||
public struct GlobalExperimentalSettings {
|
public struct GlobalExperimentalSettings {
|
||||||
public static var isAppStoreBuild: Bool = false
|
public static var isAppStoreBuild: Bool = false
|
||||||
public static var enableFeed: Bool = false
|
public static var enableFeed: Bool = false
|
||||||
public static var enableWIPStickers: Bool = true
|
|
||||||
}
|
}
|
||||||
|
@ -1066,15 +1066,28 @@ public final class Camera {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static var isIpad: Bool {
|
||||||
|
return DeviceModel.current.isIpad
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class CameraHolder {
|
public final class CameraHolder {
|
||||||
public let camera: Camera
|
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.camera = camera
|
||||||
self.previewView = previewView
|
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 {
|
- (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) {
|
if (!_formatContext) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avio_open(&_formatContext->pb, [outputPath UTF8String], AVIO_FLAG_WRITE) < 0) {
|
if (avio_open(&_formatContext->pb, [outputPath UTF8String], AVIO_FLAG_WRITE) < 0) {
|
||||||
|
avformat_free_context(_formatContext);
|
||||||
|
_formatContext = nil;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_VP9);
|
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_VP9);
|
||||||
if (!codec) {
|
if (!codec) {
|
||||||
|
avio_closep(&_formatContext->pb);
|
||||||
|
avformat_free_context(_formatContext);
|
||||||
|
_formatContext = nil;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_codecContext = avcodec_alloc_context3(codec);
|
_codecContext = avcodec_alloc_context3(codec);
|
||||||
if (!_codecContext) {
|
if (!_codecContext) {
|
||||||
|
avio_closep(&_formatContext->pb);
|
||||||
|
avformat_free_context(_formatContext);
|
||||||
|
_formatContext = nil;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,11 +69,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (avcodec_open2(_codecContext, codec, NULL) < 0) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stream = avformat_new_stream(_formatContext, codec);
|
_stream = avformat_new_stream(_formatContext, codec);
|
||||||
if (!_stream) {
|
if (!_stream) {
|
||||||
|
avcodec_close(_codecContext);
|
||||||
|
avcodec_free_context(&_codecContext);
|
||||||
|
_codecContext = nil;
|
||||||
|
avio_closep(&_formatContext->pb);
|
||||||
|
avformat_free_context(_formatContext);
|
||||||
|
_formatContext = nil;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,11 +97,23 @@
|
|||||||
|
|
||||||
int ret = avcodec_parameters_from_context(_stream->codecpar, _codecContext);
|
int ret = avcodec_parameters_from_context(_stream->codecpar, _codecContext);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
avcodec_close(_codecContext);
|
||||||
|
avcodec_free_context(&_codecContext);
|
||||||
|
_codecContext = nil;
|
||||||
|
avio_closep(&_formatContext->pb);
|
||||||
|
avformat_free_context(_formatContext);
|
||||||
|
_formatContext = nil;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = avformat_write_header(_formatContext, NULL);
|
ret = avformat_write_header(_formatContext, nil);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
avcodec_close(_codecContext);
|
||||||
|
avcodec_free_context(&_codecContext);
|
||||||
|
_codecContext = nil;
|
||||||
|
avio_closep(&_formatContext->pb);
|
||||||
|
avformat_free_context(_formatContext);
|
||||||
|
_formatContext = nil;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +141,7 @@
|
|||||||
|
|
||||||
AVPacket pkt;
|
AVPacket pkt;
|
||||||
av_init_packet(&pkt);
|
av_init_packet(&pkt);
|
||||||
pkt.data = NULL;
|
pkt.data = nil;
|
||||||
pkt.size = 0;
|
pkt.size = 0;
|
||||||
|
|
||||||
while (sendRet >= 0) {
|
while (sendRet >= 0) {
|
||||||
@ -118,6 +149,7 @@
|
|||||||
if (recvRet == AVERROR(EAGAIN) || recvRet == AVERROR_EOF) {
|
if (recvRet == AVERROR(EAGAIN) || recvRet == AVERROR_EOF) {
|
||||||
break;
|
break;
|
||||||
} else if (recvRet < 0) {
|
} else if (recvRet < 0) {
|
||||||
|
av_packet_unref(&pkt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,11 +167,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (bool)finalizeVideo {
|
- (bool)finalizeVideo {
|
||||||
|
if (!_codecContext) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int sendRet = avcodec_send_frame(_codecContext, NULL);
|
int sendRet = avcodec_send_frame(_codecContext, NULL);
|
||||||
if (sendRet >= 0) {
|
if (sendRet >= 0) {
|
||||||
AVPacket pkt;
|
AVPacket pkt;
|
||||||
av_init_packet(&pkt);
|
av_init_packet(&pkt);
|
||||||
pkt.data = NULL;
|
pkt.data = nil;
|
||||||
pkt.size = 0;
|
pkt.size = 0;
|
||||||
|
|
||||||
while (avcodec_receive_packet(_codecContext, &pkt) == 0) {
|
while (avcodec_receive_packet(_codecContext, &pkt) == 0) {
|
||||||
@ -151,14 +187,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_formatContext) {
|
||||||
av_write_trailer(_formatContext);
|
av_write_trailer(_formatContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_formatContext && _formatContext->pb) {
|
||||||
avio_closep(&_formatContext->pb);
|
avio_closep(&_formatContext->pb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_codecContext) {
|
||||||
avcodec_close(_codecContext);
|
avcodec_close(_codecContext);
|
||||||
|
|
||||||
avcodec_free_context(&_codecContext);
|
avcodec_free_context(&_codecContext);
|
||||||
|
_codecContext = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_formatContext) {
|
||||||
avformat_free_context(_formatContext);
|
avformat_free_context(_formatContext);
|
||||||
|
_formatContext = nil;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,7 +499,7 @@ open class ItemListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
|||||||
panRecognizer.delaysTouchesBegan = false
|
panRecognizer.delaysTouchesBegan = false
|
||||||
panRecognizer.cancelsTouchesInView = true
|
panRecognizer.cancelsTouchesInView = true
|
||||||
self.panRecognizer = panRecognizer
|
self.panRecognizer = panRecognizer
|
||||||
self.view.addGestureRecognizer(panRecognizer)
|
// self.view.addGestureRecognizer(panRecognizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
@ -45,6 +45,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/CameraScreen",
|
"//submodules/TelegramUI/Components/CameraScreen",
|
||||||
"//submodules/TelegramUI/Components/MediaEditor",
|
"//submodules/TelegramUI/Components/MediaEditor",
|
||||||
"//submodules/RadialStatusNode",
|
"//submodules/RadialStatusNode",
|
||||||
|
"//submodules/Camera",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -21,6 +21,7 @@ import SparseItemGrid
|
|||||||
import UndoUI
|
import UndoUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import MoreButtonNode
|
import MoreButtonNode
|
||||||
|
import Camera
|
||||||
import CameraScreen
|
import CameraScreen
|
||||||
import MediaEditor
|
import MediaEditor
|
||||||
|
|
||||||
@ -184,7 +185,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
|
|
||||||
public weak var webSearchController: WebSearchController?
|
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 presentSchedulePicker: (Bool, @escaping (Int32) -> Void) -> Void = { _, _ in }
|
||||||
public var presentTimerPicker: (@escaping (Int32) -> Void) -> Void = { _ in }
|
public var presentTimerPicker: (@escaping (Int32) -> Void) -> Void = { _ in }
|
||||||
public var presentWebSearch: (MediaGroupsScreen, Bool) -> 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 containerNode: ASDisplayNode
|
||||||
private let backgroundNode: NavigationBackgroundNode
|
private let backgroundNode: NavigationBackgroundNode
|
||||||
fileprivate let gridNode: GridNode
|
fileprivate let gridNode: GridNode
|
||||||
|
|
||||||
|
fileprivate let cameraWrapperView: UIView
|
||||||
fileprivate var cameraView: TGAttachmentCameraView?
|
fileprivate var cameraView: TGAttachmentCameraView?
|
||||||
|
|
||||||
|
fileprivate var modernCamera: Camera?
|
||||||
|
fileprivate var modernCameraView: CameraSimplePreviewView?
|
||||||
|
fileprivate var modernCameraTapGestureRecognizer: UITapGestureRecognizer?
|
||||||
|
|
||||||
private var cameraActivateAreaNode: AccessibilityAreaNode
|
private var cameraActivateAreaNode: AccessibilityAreaNode
|
||||||
private var placeholderNode: MediaPickerPlaceholderNode?
|
private var placeholderNode: MediaPickerPlaceholderNode?
|
||||||
private var manageNode: MediaPickerManageNode?
|
private var manageNode: MediaPickerManageNode?
|
||||||
@ -267,7 +275,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var isSuspended = false
|
fileprivate var isSuspended = false
|
||||||
private var hasGallery = false
|
fileprivate var hasGallery = false
|
||||||
private var isCameraPreviewVisible = true
|
private var isCameraPreviewVisible = true
|
||||||
|
|
||||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||||
@ -289,6 +297,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.gridNode = GridNode()
|
self.gridNode = GridNode()
|
||||||
self.scrollingArea = SparseItemGridScrollingArea()
|
self.scrollingArea = SparseItemGridScrollingArea()
|
||||||
|
|
||||||
|
self.cameraWrapperView = UIView()
|
||||||
|
|
||||||
self.cameraActivateAreaNode = AccessibilityAreaNode()
|
self.cameraActivateAreaNode = AccessibilityAreaNode()
|
||||||
self.cameraActivateAreaNode.accessibilityLabel = "Camera"
|
self.cameraActivateAreaNode.accessibilityLabel = "Camera"
|
||||||
self.cameraActivateAreaNode.accessibilityTraits = [.button]
|
self.cameraActivateAreaNode.accessibilityTraits = [.button]
|
||||||
@ -306,6 +316,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.containerNode.addSubnode(self.gridNode)
|
self.containerNode.addSubnode(self.gridNode)
|
||||||
self.containerNode.addSubnode(self.scrollingArea)
|
self.containerNode.addSubnode(self.scrollingArea)
|
||||||
|
|
||||||
|
self.gridNode.scrollView.addSubview(self.cameraWrapperView)
|
||||||
|
|
||||||
let selectedCollection = controller.selectedCollection.get()
|
let selectedCollection = controller.selectedCollection.get()
|
||||||
let preloadPromise = self.preloadPromise
|
let preloadPromise = self.preloadPromise
|
||||||
let updatedState: Signal<State, NoError>
|
let updatedState: Signal<State, NoError>
|
||||||
@ -497,11 +509,19 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.gridNode.visibleItemsUpdated = { [weak self] _ in
|
self.gridNode.visibleItemsUpdated = { [weak self] _ in
|
||||||
self?.updateScrollingArea()
|
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.isCameraPreviewVisible = self.gridNode.scrollView.bounds.intersects(cameraView.frame)
|
||||||
self.updateIsCameraActive()
|
self.updateIsCameraActive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
self.updateScrollingArea()
|
self.updateScrollingArea()
|
||||||
|
|
||||||
let throttledContentOffsetSignal = self.fastScrollContentOffset.get()
|
let throttledContentOffsetSignal = self.fastScrollContentOffset.get()
|
||||||
@ -539,17 +559,89 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
|
|
||||||
self.gridNode.scrollView.addSubview(cameraView)
|
self.gridNode.scrollView.addSubview(cameraView)
|
||||||
self.gridNode.addSubnode(self.cameraActivateAreaNode)
|
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 {
|
} else {
|
||||||
self.containerNode.clipsToBounds = true
|
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() {
|
func updateIsCameraActive() {
|
||||||
let isCameraActive = !self.isSuspended && !self.hasGallery && self.isCameraPreviewVisible
|
let isCameraActive = !self.isSuspended && !self.hasGallery && self.isCameraPreviewVisible
|
||||||
|
if let cameraView = self.cameraView {
|
||||||
if isCameraActive {
|
if isCameraActive {
|
||||||
self.cameraView?.resumePreview()
|
cameraView.resumePreview()
|
||||||
} else {
|
} 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))
|
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))
|
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
|
cameraRect = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1448,12 +1540,36 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
transition.updateFrame(node: selectionNode, frame: innerBounds)
|
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 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)
|
transition.updateFrame(view: cameraView, frame: cameraRect)
|
||||||
|
}
|
||||||
self.cameraActivateAreaNode.frame = cameraRect
|
self.cameraActivateAreaNode.frame = cameraRect
|
||||||
|
self.cameraWrapperView.isHidden = false
|
||||||
cameraView.isHidden = false
|
cameraView.isHidden = false
|
||||||
} else {
|
} else {
|
||||||
|
self.cameraWrapperView.isHidden = true
|
||||||
cameraView.isHidden = true
|
cameraView.isHidden = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2319,6 +2435,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func updateHiddenMediaId(_ id: String?) {
|
public func updateHiddenMediaId(_ id: String?) {
|
||||||
|
if self.customSelection != nil {
|
||||||
|
self.controllerNode.hasGallery = id != nil
|
||||||
|
self.controllerNode.updateIsCameraActive()
|
||||||
|
}
|
||||||
self.controllerNode.hiddenMediaId.set(.single(id))
|
self.controllerNode.hiddenMediaId.set(.single(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2644,8 +2764,8 @@ public func storyMediaPickerController(
|
|||||||
|
|
||||||
public func stickerMediaPickerController(
|
public func stickerMediaPickerController(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
getSourceRect: @escaping () -> CGRect,
|
getSourceRect: @escaping () -> CGRect?,
|
||||||
completion: @escaping (Any?, UIView?, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void,
|
completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void,
|
||||||
dismissed: @escaping () -> Void
|
dismissed: @escaping () -> Void
|
||||||
) -> ViewController {
|
) -> ViewController {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
|
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
|
||||||
@ -2674,16 +2794,16 @@ public func stickerMediaPickerController(
|
|||||||
}
|
}
|
||||||
return nil
|
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)
|
controller?.updateHiddenMediaId(nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mediaPickerController.createFromScratch = { [weak controller] in
|
mediaPickerController.createFromScratch = { [weak controller] in
|
||||||
completion(nil, nil, .zero, nil, { _ in return nil }, { [weak controller] in
|
completion(nil, nil, .zero, nil, false, { _ in return nil }, {
|
||||||
controller?.dismiss(animated: true)
|
|
||||||
})
|
})
|
||||||
|
controller?.dismiss(animated: true)
|
||||||
}
|
}
|
||||||
mediaPickerController.presentFilePicker = { [weak controller] in
|
mediaPickerController.presentFilePicker = { [weak controller] in
|
||||||
controller?.present(legacyICloudFilePicker(theme: presentationData.theme, mode: .import, documentTypes: ["public.image"], forceDarkTheme: false, dismissed: {
|
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) {
|
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)
|
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)
|
present(mediaPickerController, mediaPickerController.mediaPickerContext)
|
||||||
}
|
}
|
||||||
controller.willDismiss = {
|
controller.willDismiss = {
|
||||||
|
@ -497,9 +497,6 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
if let (info, _, _) = strongSelf.currentStickerPack, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) {
|
if let (info, _, _) = strongSelf.currentStickerPack, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) {
|
||||||
canEdit = true
|
canEdit = true
|
||||||
}
|
}
|
||||||
if !GlobalExperimentalSettings.enableWIPStickers {
|
|
||||||
canEdit = false
|
|
||||||
}
|
|
||||||
|
|
||||||
let accountPeerId = strongSelf.context.account.peerId
|
let accountPeerId = strongSelf.context.account.peerId
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
@ -552,30 +549,30 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
})))
|
})))
|
||||||
|
|
||||||
if canEdit {
|
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)
|
f(.default)
|
||||||
if let self {
|
if let self {
|
||||||
self.openEditSticker(item.file)
|
self.openEditSticker(item.file)
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
if !strongSelf.isEditing {
|
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)
|
f(.default)
|
||||||
if let self {
|
if let self {
|
||||||
self.updateIsEditing(true)
|
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
|
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 {
|
if let self {
|
||||||
let contextItems: [ContextMenuItem] = [
|
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)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor)
|
||||||
}, iconPosition: .left, action: { c ,f in
|
}, iconPosition: .left, action: { c ,f in
|
||||||
c.popItems()
|
c.popItems()
|
||||||
})),
|
})),
|
||||||
.separator,
|
.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)
|
f(.default)
|
||||||
|
|
||||||
if let self, let (info, items, installed) = self.currentStickerPack {
|
if let self, let (info, items, installed) = self.currentStickerPack {
|
||||||
@ -999,7 +996,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
case .none:
|
case .none:
|
||||||
buttonColor = self.presentationData.theme.list.itemAccentColor
|
buttonColor = self.presentationData.theme.list.itemAccentColor
|
||||||
case let .result(info, _, installed):
|
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
|
buttonColor = installed ? self.presentationData.theme.list.itemAccentColor : self.presentationData.theme.list.itemCheckColors.foregroundColor
|
||||||
} else {
|
} else {
|
||||||
buttonColor = installed ? self.presentationData.theme.list.itemDestructiveColor : self.presentationData.theme.list.itemCheckColors.foregroundColor
|
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) {
|
if let info = self.currentStickerPack?.0, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) {
|
||||||
isEditable = true
|
isEditable = true
|
||||||
}
|
}
|
||||||
if !GlobalExperimentalSettings.enableWIPStickers {
|
|
||||||
isEditable = false
|
|
||||||
}
|
|
||||||
|
|
||||||
let transaction: StickerPackPreviewGridTransaction
|
let transaction: StickerPackPreviewGridTransaction
|
||||||
if reload {
|
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) {
|
if let (info, packItems, _) = self.currentStickerPack, info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) {
|
||||||
//TODO:localize
|
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
if packItems.count > 0 {
|
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)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
f(.default)
|
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)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
@ -1153,18 +1146,18 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
self?.presentEditPackTitle()
|
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)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
|
||||||
}, action: { [weak self] c, f in
|
}, action: { [weak self] c, f in
|
||||||
if let self, let (_, _, isInstalled) = self.currentStickerPack {
|
if let self, let (_, _, isInstalled) = self.currentStickerPack {
|
||||||
if isInstalled {
|
if isInstalled {
|
||||||
let contextItems: [ContextMenuItem] = [
|
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)
|
f(.default)
|
||||||
|
|
||||||
self?.presentDeletePack()
|
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)
|
f(.default)
|
||||||
|
|
||||||
self?.togglePackInstalled()
|
self?.togglePackInstalled()
|
||||||
@ -1180,7 +1173,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
|
|
||||||
items.append(.separator)
|
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
|
return nil
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
@ -1199,10 +1192,9 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
|
|
||||||
private let stickerPickerInputData = Promise<StickerPickerInput>()
|
private let stickerPickerInputData = Promise<StickerPickerInput>()
|
||||||
private func presentAddStickerOptions() {
|
private func presentAddStickerOptions() {
|
||||||
//TODO:localize
|
|
||||||
let actionSheet = ActionSheetController(presentationData: self.presentationData)
|
let actionSheet = ActionSheetController(presentationData: self.presentationData)
|
||||||
var items: [ActionSheetItem] = []
|
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()
|
actionSheet?.dismissAnimated()
|
||||||
|
|
||||||
guard let self, let controller = self.controller else {
|
guard let self, let controller = self.controller else {
|
||||||
@ -1211,7 +1203,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
self.presentCreateSticker()
|
self.presentCreateSticker()
|
||||||
controller.controllerNode.dismiss()
|
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()
|
actionSheet?.dismissAnimated()
|
||||||
|
|
||||||
guard let self, let controller = self.controller else {
|
guard let self, let controller = self.controller else {
|
||||||
@ -1262,7 +1254,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
let mainController = context.sharedContext.makeStickerMediaPickerScreen(
|
let mainController = context.sharedContext.makeStickerMediaPickerScreen(
|
||||||
context: context,
|
context: context,
|
||||||
getSourceRect: { return .zero },
|
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(
|
let editorController = context.sharedContext.makeStickerEditorScreen(
|
||||||
context: context,
|
context: context,
|
||||||
source: result,
|
source: result,
|
||||||
@ -1286,7 +1278,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
(navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root))
|
(navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root))
|
||||||
|
|
||||||
Queue.mainQueue().after(0.1) {
|
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))
|
(navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root))
|
||||||
|
|
||||||
Queue.mainQueue().after(0.1) {
|
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))
|
(navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root))
|
||||||
|
|
||||||
Queue.mainQueue().after(0.1) {
|
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
|
return
|
||||||
}
|
}
|
||||||
let context = self.context
|
let context = self.context
|
||||||
//TODO:localize
|
|
||||||
var dismissImpl: (() -> Void)?
|
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 {
|
guard let self, let title else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1421,7 +1412,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let context = self.context
|
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))
|
let _ = (context.engine.stickers.deleteStickerSet(packReference: .id(id: info.id.id, accessHash: info.accessHash))
|
||||||
|> deliverOnMainQueue).startStandalone()
|
|> deliverOnMainQueue).startStandalone()
|
||||||
|
|
||||||
@ -1478,7 +1469,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
self.requestDismiss()
|
self.requestDismiss()
|
||||||
} else if let (info, _, installed) = self.currentStickerPack {
|
} 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)
|
self.updateIsEditing(!self.isEditing)
|
||||||
return
|
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 let currentContents = self.currentContents, currentContents.count == 1, let content = currentContents.first, case let .result(info, _, installed) = content {
|
||||||
if installed {
|
if installed {
|
||||||
let text: String
|
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 {
|
if self.isEditing {
|
||||||
var updated = false
|
var updated = false
|
||||||
if let current = self.buttonNode.attributedTitle(for: .normal)?.string, !current.isEmpty && current != self.presentationData.strings.Common_Done {
|
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.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: [])
|
self.buttonNode.setBackgroundImage(generateStretchableFilledCircleImage(radius: 11, color: self.presentationData.theme.list.itemCheckColors.fillColor), for: [])
|
||||||
} else {
|
} else {
|
||||||
|
let buttonTitle = self.presentationData.strings.StickerPack_EditStickers
|
||||||
var updated = false
|
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
|
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)
|
self.buttonNode.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:localize
|
text = buttonTitle
|
||||||
text = "Edit Stickers"
|
|
||||||
self.buttonNode.setTitle(text, with: Font.regular(17.0), with: self.presentationData.theme.list.itemAccentColor, for: .normal)
|
self.buttonNode.setTitle(text, with: Font.regular(17.0), with: self.presentationData.theme.list.itemAccentColor, for: .normal)
|
||||||
self.buttonNode.setBackgroundImage(nil, for: [])
|
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?.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)
|
self.controller?.dismiss(animated: true, completion: nil)
|
||||||
case let .result(info, items, installed):
|
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()
|
self.onReady()
|
||||||
if !items.isEmpty && self.currentStickerPack == nil {
|
if !items.isEmpty && self.currentStickerPack == nil {
|
||||||
if let _ = self.validLayout, abs(self.expandScrollProgress - 1.0) < .ulpOfOne {
|
if let _ = self.validLayout, abs(self.expandScrollProgress - 1.0) < .ulpOfOne {
|
||||||
@ -1816,7 +1807,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
self.updateButton(count: count)
|
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)
|
entries.append(.add)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1926,7 +1917,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
self.currentEntries = entries
|
self.currentEntries = entries
|
||||||
|
|
||||||
if let controller = self.controller {
|
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)
|
self.enqueueTransaction(transaction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1986,7 +1977,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
actionAreaBottomInset = 2.0
|
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
|
buttonHeight = 42.0
|
||||||
actionAreaTopInset = 1.0
|
actionAreaTopInset = 1.0
|
||||||
actionAreaBottomInset = 2.0
|
actionAreaBottomInset = 2.0
|
||||||
|
@ -446,14 +446,14 @@ final class EmojiStickerAccessoryNode: SparseNode, PeekControllerAccessoryNode {
|
|||||||
let items = topItems(selectedEmoji: selectedEmoji, recommendedEmoji: recommendedEmoji, count: 7)
|
let items = topItems(selectedEmoji: selectedEmoji, recommendedEmoji: recommendedEmoji, count: 7)
|
||||||
let selectedItems = ValuePromise<[String]>(selectedEmoji)
|
let selectedItems = ValuePromise<[String]>(selectedEmoji)
|
||||||
|
|
||||||
//TODO:localize
|
let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme)
|
||||||
let reactionContextNode = ReactionContextNode(
|
let reactionContextNode = ReactionContextNode(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
animationCache: self.context.animationCache,
|
animationCache: self.context.animationCache,
|
||||||
presentationData: self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme),
|
presentationData: presentationData,
|
||||||
items: items.map { .staticEmoji($0) },
|
items: items.map { .staticEmoji($0) },
|
||||||
selectedItems: Set(selectedEmoji),
|
selectedItems: Set(selectedEmoji),
|
||||||
title: "Set emoji that corresponds to your sticker",
|
title: presentationData.strings.MediaEditor_SetStickerEmoji,
|
||||||
reactionsLocked: false,
|
reactionsLocked: false,
|
||||||
alwaysAllowPremiumReactions: true,
|
alwaysAllowPremiumReactions: true,
|
||||||
allPresetReactionsAreAvailable: true,
|
allPresetReactionsAreAvailable: true,
|
||||||
|
@ -751,6 +751,13 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
state.cameraState = component.cameraState
|
state.cameraState = component.cameraState
|
||||||
state.volumeButtonsListenerActive = component.hasAppeared && component.isVisible
|
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
|
let isTablet: Bool
|
||||||
if case .regular = environment.metrics.widthClass {
|
if case .regular = environment.metrics.widthClass {
|
||||||
isTablet = true
|
isTablet = true
|
||||||
@ -879,6 +886,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
let captureControls = captureControls.update(
|
let captureControls = captureControls.update(
|
||||||
component: CaptureControlsComponent(
|
component: CaptureControlsComponent(
|
||||||
isTablet: isTablet,
|
isTablet: isTablet,
|
||||||
|
isSticker: isSticker,
|
||||||
hasAppeared: component.hasAppeared && hasAllRequiredAccess,
|
hasAppeared: component.hasAppeared && hasAllRequiredAccess,
|
||||||
hasAccess: hasAllRequiredAccess,
|
hasAccess: hasAllRequiredAccess,
|
||||||
tintColor: controlsTintColor,
|
tintColor: controlsTintColor,
|
||||||
@ -1063,7 +1071,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
.disappear(.default(scale: true))
|
.disappear(.default(scale: true))
|
||||||
)
|
)
|
||||||
|
|
||||||
if !isTablet && Camera.isDualCameraSupported(forRoundVideo: false) {
|
if !isSticker && !isTablet && Camera.isDualCameraSupported(forRoundVideo: false) {
|
||||||
let dualButton = dualButton.update(
|
let dualButton = dualButton.update(
|
||||||
component: CameraButton(
|
component: CameraButton(
|
||||||
content: AnyComponentWithIdentity(
|
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
|
let availableModeControlSize: CGSize
|
||||||
if isTablet {
|
if isTablet {
|
||||||
availableModeControlSize = CGSize(width: panelWidth, height: 120.0)
|
availableModeControlSize = CGSize(width: panelWidth, height: 120.0)
|
||||||
@ -1314,6 +1322,11 @@ private class BlurView: UIVisualEffectView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class CameraScreen: ViewController {
|
public class CameraScreen: ViewController {
|
||||||
|
public enum Mode {
|
||||||
|
case story
|
||||||
|
case sticker
|
||||||
|
}
|
||||||
|
|
||||||
public enum PIPPosition: Int32 {
|
public enum PIPPosition: Int32 {
|
||||||
case topLeft
|
case topLeft
|
||||||
case topRight
|
case topRight
|
||||||
@ -1735,11 +1748,18 @@ public class CameraScreen: ViewController {
|
|||||||
|
|
||||||
fileprivate var captureStartTimestamp: Double?
|
fileprivate var captureStartTimestamp: Double?
|
||||||
private func setupCamera() {
|
private func setupCamera() {
|
||||||
guard self.camera == nil else {
|
guard self.camera == nil, let controller = self.controller else {
|
||||||
return
|
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(
|
configuration: Camera.Configuration(
|
||||||
preset: .hd1920x1080,
|
preset: .hd1920x1080,
|
||||||
position: self.cameraState.position,
|
position: self.cameraState.position,
|
||||||
@ -1751,6 +1771,8 @@ public class CameraScreen: ViewController {
|
|||||||
previewView: self.mainPreviewView,
|
previewView: self.mainPreviewView,
|
||||||
secondaryPreviewView: self.additionalPreviewView
|
secondaryPreviewView: self.additionalPreviewView
|
||||||
)
|
)
|
||||||
|
isNew = true
|
||||||
|
}
|
||||||
|
|
||||||
self.cameraStateDisposable = combineLatest(
|
self.cameraStateDisposable = combineLatest(
|
||||||
queue: Queue.mainQueue(),
|
queue: Queue.mainQueue(),
|
||||||
@ -1841,12 +1863,14 @@ public class CameraScreen: ViewController {
|
|||||||
})
|
})
|
||||||
|
|
||||||
camera.focus(at: CGPoint(x: 0.5, y: 0.5), autoFocus: true)
|
camera.focus(at: CGPoint(x: 0.5, y: 0.5), autoFocus: true)
|
||||||
|
if isNew {
|
||||||
camera.startCapture()
|
camera.startCapture()
|
||||||
|
}
|
||||||
self.captureStartTimestamp = CACurrentMediaTime()
|
self.captureStartTimestamp = CACurrentMediaTime()
|
||||||
|
|
||||||
self.camera = camera
|
self.camera = camera
|
||||||
|
|
||||||
if self.hasAppeared {
|
if isNew && self.hasAppeared {
|
||||||
self.maybePresentTooltips()
|
self.maybePresentTooltips()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2025,21 +2049,30 @@ public class CameraScreen: ViewController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var animatedIn = false
|
||||||
func animateIn() {
|
func animateIn() {
|
||||||
|
guard let controller = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.transitionDimView.alpha = 0.0
|
self.transitionDimView.alpha = 0.0
|
||||||
self.backgroundView.alpha = 0.0
|
self.backgroundView.alpha = 0.0
|
||||||
UIView.animate(withDuration: 0.4, animations: {
|
UIView.animate(withDuration: 0.4, animations: {
|
||||||
self.backgroundView.alpha = 1.0
|
self.backgroundView.alpha = 1.0
|
||||||
})
|
})
|
||||||
|
|
||||||
if let layout = self.validLayout, layout.metrics.isTablet {
|
if let layout = self.validLayout {
|
||||||
self.controller?.statusBar.updateStatusBarStyle(.Hide, animated: true)
|
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 {
|
if let transitionIn = self.controller?.transitionIn, let sourceView = transitionIn.sourceView {
|
||||||
let sourceLocalFrame = sourceView.convert(transitionIn.sourceRect, to: self.view)
|
let sourceLocalFrame = sourceView.convert(transitionIn.sourceRect, to: self.view)
|
||||||
|
if case .story = controller.mode {
|
||||||
let sourceScale = sourceLocalFrame.width / self.previewContainerView.frame.width
|
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.previewContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: self.previewContainerView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
|
||||||
self.requestUpdateLayout(hasAppeared: true, transition: .immediate)
|
self.requestUpdateLayout(hasAppeared: true, transition: .immediate)
|
||||||
})
|
})
|
||||||
@ -2054,16 +2087,37 @@ public class CameraScreen: ViewController {
|
|||||||
timingFunction: kCAMediaTimingFunctionSpring,
|
timingFunction: kCAMediaTimingFunctionSpring,
|
||||||
duration: 0.3
|
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 {
|
if let view = self.componentHost.view {
|
||||||
view.layer.animatePosition(from: sourceLocalFrame.center, to: view.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
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.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) {
|
func animateOut(completion: @escaping () -> Void) {
|
||||||
self.camera?.stopCapture(invalidate: true)
|
guard let controller = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
UIView.animate(withDuration: 0.25, animations: {
|
UIView.animate(withDuration: 0.25, animations: {
|
||||||
self.backgroundView.alpha = 0.0
|
self.backgroundView.alpha = 0.0
|
||||||
@ -2071,8 +2125,9 @@ public class CameraScreen: ViewController {
|
|||||||
|
|
||||||
if let transitionOut = self.controller?.transitionOut(false), let destinationView = transitionOut.destinationView {
|
if let transitionOut = self.controller?.transitionOut(false), let destinationView = transitionOut.destinationView {
|
||||||
let destinationLocalFrame = destinationView.convert(transitionOut.destinationRect, to: self.view)
|
let destinationLocalFrame = destinationView.convert(transitionOut.destinationRect, to: self.view)
|
||||||
|
|
||||||
let targetScale = destinationLocalFrame.width / self.previewContainerView.frame.width
|
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
|
self.previewContainerView.layer.animatePosition(from: self.previewContainerView.center, to: destinationLocalFrame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
|
||||||
completion()
|
completion()
|
||||||
})
|
})
|
||||||
@ -2088,18 +2143,35 @@ public class CameraScreen: ViewController {
|
|||||||
duration: 0.3,
|
duration: 0.3,
|
||||||
removeOnCompletion: false
|
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 {
|
if let view = self.componentHost.view {
|
||||||
view.layer.animatePosition(from: view.center, to: destinationLocalFrame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
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: targetScale, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||||
|
view.layer.animateScale(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.componentHost.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
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)
|
self.previewContainerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func animateOutToEditor() {
|
func animateOutToEditor() {
|
||||||
self.cameraIsActive = false
|
self.cameraIsActive = false
|
||||||
@ -2344,7 +2416,7 @@ public class CameraScreen: ViewController {
|
|||||||
|
|
||||||
fileprivate var hasAppeared = false
|
fileprivate var hasAppeared = false
|
||||||
func containerLayoutUpdated(layout: ContainerViewLayout, forceUpdate: Bool = false, hasAppeared: Bool = false, transition: Transition) {
|
func containerLayoutUpdated(layout: ContainerViewLayout, forceUpdate: Bool = false, hasAppeared: Bool = false, transition: Transition) {
|
||||||
guard let _ = self.controller else {
|
guard let controller = self.controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let isFirstTime = self.validLayout == nil
|
let isFirstTime = self.validLayout == nil
|
||||||
@ -2578,7 +2650,14 @@ public class CameraScreen: ViewController {
|
|||||||
self.additionalPreviewContainerView.insertSubview(additionalPreviewView, at: 0)
|
self.additionalPreviewContainerView.insertSubview(additionalPreviewView, at: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case .sticker = controller.mode {
|
||||||
|
if self.animatedIn {
|
||||||
mainPreviewView.frame = mainPreviewInnerFrame
|
mainPreviewView.frame = mainPreviewInnerFrame
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mainPreviewView.frame = mainPreviewInnerFrame
|
||||||
|
}
|
||||||
|
|
||||||
additionalPreviewView.frame = additionalPreviewInnerFrame
|
additionalPreviewView.frame = additionalPreviewInnerFrame
|
||||||
|
|
||||||
self.previewFrameLeftDimView.isHidden = !isTablet
|
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.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)))
|
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()
|
self.animateIn()
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.cameraState.flashMode == .on && (self.cameraState.recording != .none || self.cameraState.mode == .video) {
|
if self.cameraState.flashMode == .on && (self.cameraState.recording != .none || self.cameraState.mode == .video) {
|
||||||
self.controller?.statusBarStyle = .Black
|
controller.statusBarStyle = .Black
|
||||||
} else {
|
} else {
|
||||||
self.controller?.statusBarStyle = .White
|
controller.statusBarStyle = .White
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2621,6 +2700,7 @@ public class CameraScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
|
fileprivate let mode: Mode
|
||||||
fileprivate let holder: CameraHolder?
|
fileprivate let holder: CameraHolder?
|
||||||
fileprivate let transitionIn: TransitionIn?
|
fileprivate let transitionIn: TransitionIn?
|
||||||
fileprivate let transitionOut: (Bool) -> TransitionOut?
|
fileprivate let transitionOut: (Bool) -> TransitionOut?
|
||||||
@ -2645,6 +2725,7 @@ public class CameraScreen: ViewController {
|
|||||||
}
|
}
|
||||||
fileprivate let completion: (Signal<CameraScreen.Result, NoError>, ResultTransition?, @escaping () -> Void) -> Void
|
fileprivate let completion: (Signal<CameraScreen.Result, NoError>, ResultTransition?, @escaping () -> Void) -> Void
|
||||||
public var transitionedIn: () -> Void = {}
|
public var transitionedIn: () -> Void = {}
|
||||||
|
public var transitionedOut: () -> Void = {}
|
||||||
|
|
||||||
private var audioSessionDisposable: Disposable?
|
private var audioSessionDisposable: Disposable?
|
||||||
|
|
||||||
@ -2663,6 +2744,8 @@ public class CameraScreen: ViewController {
|
|||||||
return self.node.cameraState
|
return self.node.cameraState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var isEmbedded = false
|
||||||
|
|
||||||
fileprivate func updateCameraState(_ f: (CameraState) -> CameraState, transition: Transition) {
|
fileprivate func updateCameraState(_ f: (CameraState) -> CameraState, transition: Transition) {
|
||||||
self.node.cameraState = f(self.node.cameraState)
|
self.node.cameraState = f(self.node.cameraState)
|
||||||
self.node.requestUpdateLayout(hasAppeared: self.node.hasAppeared, transition: transition)
|
self.node.requestUpdateLayout(hasAppeared: self.node.hasAppeared, transition: transition)
|
||||||
@ -2670,12 +2753,14 @@ public class CameraScreen: ViewController {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
|
mode: Mode,
|
||||||
holder: CameraHolder? = nil,
|
holder: CameraHolder? = nil,
|
||||||
transitionIn: TransitionIn?,
|
transitionIn: TransitionIn?,
|
||||||
transitionOut: @escaping (Bool) -> TransitionOut?,
|
transitionOut: @escaping (Bool) -> TransitionOut?,
|
||||||
completion: @escaping (Signal<CameraScreen.Result, NoError>, ResultTransition?, @escaping () -> Void) -> Void
|
completion: @escaping (Signal<CameraScreen.Result, NoError>, ResultTransition?, @escaping () -> Void) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
self.mode = mode
|
||||||
self.holder = holder
|
self.holder = holder
|
||||||
self.transitionIn = transitionIn
|
self.transitionIn = transitionIn
|
||||||
self.transitionOut = transitionOut
|
self.transitionOut = transitionOut
|
||||||
@ -2907,13 +2992,17 @@ public class CameraScreen: ViewController {
|
|||||||
self.hapticFeedback.impact(.light)
|
self.hapticFeedback.impact(.light)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case .story = self.mode {
|
||||||
self.node.camera?.stopCapture(invalidate: true)
|
self.node.camera?.stopCapture(invalidate: true)
|
||||||
|
}
|
||||||
|
|
||||||
self.isDismissed = true
|
self.isDismissed = true
|
||||||
if animated {
|
if animated {
|
||||||
self.ignoreStatusBar = true
|
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.node.animateOut(completion: {
|
||||||
self.dismiss(animated: false)
|
self.dismiss(animated: false)
|
||||||
|
self.transitionedOut()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
if !interactive {
|
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.updateTransitionProgress(0.0, transition: .animated(duration: 0.4, curve: .spring), completion: { [weak self] in
|
||||||
self?.dismiss(animated: false)
|
self?.dismiss(animated: false)
|
||||||
|
self?.transitionedOut()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -459,6 +459,7 @@ final class CaptureControlsComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let isTablet: Bool
|
let isTablet: Bool
|
||||||
|
let isSticker: Bool
|
||||||
let hasAppeared: Bool
|
let hasAppeared: Bool
|
||||||
let hasAccess: Bool
|
let hasAccess: Bool
|
||||||
let tintColor: UIColor
|
let tintColor: UIColor
|
||||||
@ -478,6 +479,7 @@ final class CaptureControlsComponent: Component {
|
|||||||
|
|
||||||
init(
|
init(
|
||||||
isTablet: Bool,
|
isTablet: Bool,
|
||||||
|
isSticker: Bool,
|
||||||
hasAppeared: Bool,
|
hasAppeared: Bool,
|
||||||
hasAccess: Bool,
|
hasAccess: Bool,
|
||||||
tintColor: UIColor,
|
tintColor: UIColor,
|
||||||
@ -496,6 +498,7 @@ final class CaptureControlsComponent: Component {
|
|||||||
flipAnimationAction: ActionSlot<Void>
|
flipAnimationAction: ActionSlot<Void>
|
||||||
) {
|
) {
|
||||||
self.isTablet = isTablet
|
self.isTablet = isTablet
|
||||||
|
self.isSticker = isSticker
|
||||||
self.hasAppeared = hasAppeared
|
self.hasAppeared = hasAppeared
|
||||||
self.hasAccess = hasAccess
|
self.hasAccess = hasAccess
|
||||||
self.tintColor = tintColor
|
self.tintColor = tintColor
|
||||||
@ -518,6 +521,9 @@ final class CaptureControlsComponent: Component {
|
|||||||
if lhs.isTablet != rhs.isTablet {
|
if lhs.isTablet != rhs.isTablet {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.isSticker != rhs.isSticker {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.hasAppeared != rhs.hasAppeared {
|
if lhs.hasAppeared != rhs.hasAppeared {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -912,7 +918,9 @@ final class CaptureControlsComponent: Component {
|
|||||||
isTransitioning = true
|
isTransitioning = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let galleryButtonFrame: CGRect
|
||||||
let gallerySize: CGSize
|
let gallerySize: CGSize
|
||||||
|
if !component.isSticker {
|
||||||
let galleryCornerRadius: CGFloat
|
let galleryCornerRadius: CGFloat
|
||||||
if component.isTablet {
|
if component.isTablet {
|
||||||
gallerySize = CGSize(width: 72.0, height: 72.0)
|
gallerySize = CGSize(width: 72.0, height: 72.0)
|
||||||
@ -950,7 +958,6 @@ final class CaptureControlsComponent: Component {
|
|||||||
environment: {},
|
environment: {},
|
||||||
containerSize: gallerySize
|
containerSize: gallerySize
|
||||||
)
|
)
|
||||||
let galleryButtonFrame: CGRect
|
|
||||||
if component.isTablet {
|
if component.isTablet {
|
||||||
galleryButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - galleryButtonSize.width) / 2.0), y: size.height - galleryButtonSize.height - 56.0), size: galleryButtonSize)
|
galleryButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - galleryButtonSize.width) / 2.0), y: size.height - galleryButtonSize.height - 56.0), size: galleryButtonSize)
|
||||||
} else {
|
} else {
|
||||||
@ -970,6 +977,10 @@ final class CaptureControlsComponent: Component {
|
|||||||
transition.setScale(view: galleryButtonView, scale: isRecording || isTransitioning ? 0.1 : 1.0)
|
transition.setScale(view: galleryButtonView, scale: isRecording || isTransitioning ? 0.1 : 1.0)
|
||||||
transition.setAlpha(view: galleryButtonView, alpha: isRecording || isTransitioning ? 0.0 : normalAlpha)
|
transition.setAlpha(view: galleryButtonView, alpha: isRecording || isTransitioning ? 0.0 : normalAlpha)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
galleryButtonFrame = .zero
|
||||||
|
gallerySize = .zero
|
||||||
|
}
|
||||||
|
|
||||||
if !component.isTablet && component.hasAccess {
|
if !component.isTablet && component.hasAccess {
|
||||||
let flipButtonOriginX = availableSize.width - 48.0 - buttonSideInset
|
let flipButtonOriginX = availableSize.width - 48.0 - buttonSideInset
|
||||||
@ -1173,6 +1184,7 @@ final class CaptureControlsComponent: Component {
|
|||||||
|
|
||||||
if let shutterButtonView = self.shutterButtonView.view {
|
if let shutterButtonView = self.shutterButtonView.view {
|
||||||
if shutterButtonView.superview == nil {
|
if shutterButtonView.superview == nil {
|
||||||
|
if !component.isSticker {
|
||||||
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
|
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
|
||||||
panGestureRecognizer.delegate = self
|
panGestureRecognizer.delegate = self
|
||||||
shutterButtonView.addGestureRecognizer(panGestureRecognizer)
|
shutterButtonView.addGestureRecognizer(panGestureRecognizer)
|
||||||
@ -1181,7 +1193,7 @@ final class CaptureControlsComponent: Component {
|
|||||||
pressGestureRecognizer.minimumPressDuration = 0.3
|
pressGestureRecognizer.minimumPressDuration = 0.3
|
||||||
pressGestureRecognizer.delegate = self
|
pressGestureRecognizer.delegate = self
|
||||||
shutterButtonView.addGestureRecognizer(pressGestureRecognizer)
|
shutterButtonView.addGestureRecognizer(pressGestureRecognizer)
|
||||||
|
}
|
||||||
self.addSubview(shutterButtonView)
|
self.addSubview(shutterButtonView)
|
||||||
}
|
}
|
||||||
let alpha: CGFloat = component.hasAccess ? 1.0 : 0.3
|
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))
|
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 patternTopRightPosition = CGPoint()
|
||||||
|
var patternAlpha: CGFloat = 1.0
|
||||||
|
|
||||||
if !contentAnimatedFilesValue.isEmpty, let (_, inlineMediaSize) = inlineMediaAndSize {
|
if !contentAnimatedFilesValue.isEmpty, let (_, inlineMediaSize) = inlineMediaAndSize {
|
||||||
var inlineMediaFrame = CGRect(origin: CGPoint(x: actualSize.width - insets.right - inlineMediaSize.width, y: backgroundInsets.top + inlineMediaEdgeInset), size: inlineMediaSize)
|
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
|
inlineMediaFrame.origin.x = insets.left
|
||||||
}
|
}
|
||||||
|
|
||||||
patternTopRightPosition.x = insets.right + inlineMediaSize.width - 6.0
|
patternAlpha = 0.5
|
||||||
|
|
||||||
if !contentAnimatedFilesValue.isEmpty {
|
if !contentAnimatedFilesValue.isEmpty {
|
||||||
if contentAnimatedFilesValue.count < 4, let file = contentAnimatedFilesValue.first {
|
if contentAnimatedFilesValue.count < 4, let file = contentAnimatedFilesValue.first {
|
||||||
@ -1486,13 +1487,13 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
if let current = self.backgroundView {
|
if let current = self.backgroundView {
|
||||||
backgroundView = current
|
backgroundView = current
|
||||||
animation.animator.updateFrame(layer: backgroundView.layer, frame: backgroundFrame, completion: nil)
|
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 {
|
} else {
|
||||||
backgroundView = MessageInlineBlockBackgroundView()
|
backgroundView = MessageInlineBlockBackgroundView()
|
||||||
self.backgroundView = backgroundView
|
self.backgroundView = backgroundView
|
||||||
backgroundView.frame = backgroundFrame
|
backgroundView.frame = backgroundFrame
|
||||||
self.transformContainer.view.insertSubview(backgroundView, at: 0)
|
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 {
|
} else {
|
||||||
if let backgroundView = self.backgroundView {
|
if let backgroundView = self.backgroundView {
|
||||||
|
@ -460,6 +460,7 @@ public final class MessageInlineBlockBackgroundView: UIView {
|
|||||||
var backgroundColor: UIColor?
|
var backgroundColor: UIColor?
|
||||||
var pattern: Pattern?
|
var pattern: Pattern?
|
||||||
var patternTopRightPosition: CGPoint?
|
var patternTopRightPosition: CGPoint?
|
||||||
|
var patternAlpha: CGFloat
|
||||||
var displayProgress: Bool
|
var displayProgress: Bool
|
||||||
|
|
||||||
init(
|
init(
|
||||||
@ -471,6 +472,7 @@ public final class MessageInlineBlockBackgroundView: UIView {
|
|||||||
backgroundColor: UIColor?,
|
backgroundColor: UIColor?,
|
||||||
pattern: Pattern?,
|
pattern: Pattern?,
|
||||||
patternTopRightPosition: CGPoint?,
|
patternTopRightPosition: CGPoint?,
|
||||||
|
patternAlpha: CGFloat,
|
||||||
displayProgress: Bool
|
displayProgress: Bool
|
||||||
) {
|
) {
|
||||||
self.size = size
|
self.size = size
|
||||||
@ -481,6 +483,7 @@ public final class MessageInlineBlockBackgroundView: UIView {
|
|||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
self.pattern = pattern
|
self.pattern = pattern
|
||||||
self.patternTopRightPosition = patternTopRightPosition
|
self.patternTopRightPosition = patternTopRightPosition
|
||||||
|
self.patternAlpha = patternAlpha
|
||||||
self.displayProgress = displayProgress
|
self.displayProgress = displayProgress
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -612,6 +615,7 @@ public final class MessageInlineBlockBackgroundView: UIView {
|
|||||||
backgroundColor: UIColor?,
|
backgroundColor: UIColor?,
|
||||||
pattern: Pattern?,
|
pattern: Pattern?,
|
||||||
patternTopRightPosition: CGPoint? = nil,
|
patternTopRightPosition: CGPoint? = nil,
|
||||||
|
patternAlpha: CGFloat = 1.0,
|
||||||
animation: ListViewItemUpdateAnimation
|
animation: ListViewItemUpdateAnimation
|
||||||
) {
|
) {
|
||||||
let params = Params(
|
let params = Params(
|
||||||
@ -623,6 +627,7 @@ public final class MessageInlineBlockBackgroundView: UIView {
|
|||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
pattern: pattern,
|
pattern: pattern,
|
||||||
patternTopRightPosition: patternTopRightPosition,
|
patternTopRightPosition: patternTopRightPosition,
|
||||||
|
patternAlpha: patternAlpha,
|
||||||
displayProgress: self.displayProgress
|
displayProgress: self.displayProgress
|
||||||
)
|
)
|
||||||
if self.params == params {
|
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)
|
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)
|
var alphaFraction = abs(placement.position.x / 3.0) / min(500.0, size.width)
|
||||||
alphaFraction = min(1.0, max(0.0, alphaFraction))
|
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
|
maxIndex += 1
|
||||||
}
|
}
|
||||||
|
@ -215,7 +215,7 @@ public final class EntityKeyboardAnimationData: Equatable {
|
|||||||
self.isTemplate = isTemplate
|
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
|
let type: ItemType
|
||||||
if file.isVideoSticker || file.isVideoEmoji {
|
if file.isVideoSticker || file.isVideoEmoji {
|
||||||
type = .video
|
type = .video
|
||||||
@ -225,7 +225,14 @@ public final class EntityKeyboardAnimationData: Equatable {
|
|||||||
type = .still
|
type = .still
|
||||||
}
|
}
|
||||||
let isTemplate = file.isCustomTemplateEmoji
|
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 {
|
public static func ==(lhs: EntityKeyboardAnimationData, rhs: EntityKeyboardAnimationData) -> Bool {
|
||||||
|
@ -1787,6 +1787,7 @@ public extension EmojiPagerContentComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let savedStickers = savedStickers {
|
if let savedStickers = savedStickers {
|
||||||
|
let groupId = "saved"
|
||||||
for item in savedStickers.items {
|
for item in savedStickers.items {
|
||||||
guard let item = item.contents.get(SavedStickerItem.self) else {
|
guard let item = item.contents.get(SavedStickerItem.self) else {
|
||||||
continue
|
continue
|
||||||
@ -1800,7 +1801,7 @@ public extension EmojiPagerContentComponent {
|
|||||||
tintMode = .primary
|
tintMode = .primary
|
||||||
}
|
}
|
||||||
|
|
||||||
let animationData = EntityKeyboardAnimationData(file: item.file)
|
let animationData = EntityKeyboardAnimationData(file: item.file, partialReference: .savedSticker)
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
animationData: animationData,
|
animationData: animationData,
|
||||||
content: .animation(animationData),
|
content: .animation(animationData),
|
||||||
@ -1810,7 +1811,6 @@ public extension EmojiPagerContentComponent {
|
|||||||
tintMode: tintMode
|
tintMode: tintMode
|
||||||
)
|
)
|
||||||
|
|
||||||
let groupId = "saved"
|
|
||||||
if let groupIndex = itemGroupIndexById[groupId] {
|
if let groupIndex = itemGroupIndexById[groupId] {
|
||||||
itemGroups[groupIndex].items.append(resultItem)
|
itemGroups[groupIndex].items.append(resultItem)
|
||||||
} else {
|
} else {
|
||||||
@ -1836,7 +1836,7 @@ public extension EmojiPagerContentComponent {
|
|||||||
tintMode = .primary
|
tintMode = .primary
|
||||||
}
|
}
|
||||||
|
|
||||||
let animationData = EntityKeyboardAnimationData(file: item.media)
|
let animationData = EntityKeyboardAnimationData(file: item.media, partialReference: .recentSticker)
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
animationData: animationData,
|
animationData: animationData,
|
||||||
content: .animation(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.animateAlpha(from: view.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2)
|
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 {
|
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.animateAlpha(from: view.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2)
|
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) {
|
func animateOutToTool(inPlace: Bool, transition: Transition) {
|
||||||
@ -1978,7 +2002,9 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
var hasRestoreButton = false
|
var hasRestoreButton = false
|
||||||
var hasOutlineButton = 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 {
|
if controller.node.isCutout || controller.node.stickerMaskDrawingView?.internalState.canUndo == true {
|
||||||
hasUndoButton = true
|
hasUndoButton = true
|
||||||
}
|
}
|
||||||
@ -2002,7 +2028,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
content: AnyComponent(CutoutButtonContentComponent(
|
content: AnyComponent(CutoutButtonContentComponent(
|
||||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||||
icon: state.image(.undo),
|
icon: state.image(.undo),
|
||||||
title: "Undo"
|
title: environment.strings.MediaEditor_Undo
|
||||||
)),
|
)),
|
||||||
effectAlignment: .center,
|
effectAlignment: .center,
|
||||||
action: {
|
action: {
|
||||||
@ -2048,7 +2074,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
content: AnyComponent(CutoutButtonContentComponent(
|
content: AnyComponent(CutoutButtonContentComponent(
|
||||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||||
icon: state.image(.cutout),
|
icon: state.image(.cutout),
|
||||||
title: "Cut Out an Object"
|
title: environment.strings.MediaEditor_Cutout
|
||||||
)),
|
)),
|
||||||
effectAlignment: .center,
|
effectAlignment: .center,
|
||||||
action: {
|
action: {
|
||||||
@ -2100,7 +2126,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
content: AnyComponent(CutoutButtonContentComponent(
|
content: AnyComponent(CutoutButtonContentComponent(
|
||||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||||
icon: state.image(.erase),
|
icon: state.image(.erase),
|
||||||
title: "Erase",
|
title: environment.strings.MediaEditor_Erase,
|
||||||
minWidth: 160.0,
|
minWidth: 160.0,
|
||||||
selected: component.isDisplayingTool == .cutoutErase
|
selected: component.isDisplayingTool == .cutoutErase
|
||||||
)),
|
)),
|
||||||
@ -2123,7 +2149,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
content: AnyComponent(CutoutButtonContentComponent(
|
content: AnyComponent(CutoutButtonContentComponent(
|
||||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||||
icon: state.image(.restore),
|
icon: state.image(.restore),
|
||||||
title: "Restore",
|
title: environment.strings.MediaEditor_Restore,
|
||||||
minWidth: 160.0,
|
minWidth: 160.0,
|
||||||
selected: component.isDisplayingTool == .cutoutRestore
|
selected: component.isDisplayingTool == .cutoutRestore
|
||||||
)),
|
)),
|
||||||
@ -2204,7 +2230,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
content: AnyComponent(CutoutButtonContentComponent(
|
content: AnyComponent(CutoutButtonContentComponent(
|
||||||
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.18),
|
||||||
icon: state.image(.outline),
|
icon: state.image(.outline),
|
||||||
title: "Add Outline",
|
title: environment.strings.MediaEditor_Outline,
|
||||||
minWidth: 160.0,
|
minWidth: 160.0,
|
||||||
selected: isOutlineActive
|
selected: isOutlineActive
|
||||||
)),
|
)),
|
||||||
@ -2898,7 +2924,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
if case .message = subject, self.context.sharedContext.currentPresentationData.with({$0}).autoNightModeTriggered {
|
if case .message = subject, self.context.sharedContext.currentPresentationData.with({$0}).autoNightModeTriggered {
|
||||||
mediaEditor.setNightTheme(true)
|
mediaEditor.setNightTheme(true)
|
||||||
}
|
}
|
||||||
mediaEditor.attachPreviewView(self.previewView)
|
|
||||||
mediaEditor.valuesUpdated = { [weak self] values in
|
mediaEditor.valuesUpdated = { [weak self] values in
|
||||||
if let self, let controller = self.controller, values.gradientColors != nil, controller.previousSavedValues != values {
|
if let self, let controller = self.controller, values.gradientColors != nil, controller.previousSavedValues != values {
|
||||||
if !isSavingAvailable && controller.previousSavedValues == nil {
|
if !isSavingAvailable && controller.previousSavedValues == nil {
|
||||||
@ -2936,6 +2961,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
self.controller?.stickerRecommendedEmoji = emojiForClasses(classes.map { $0.0 })
|
self.controller?.stickerRecommendedEmoji = emojiForClasses(classes.map { $0.0 })
|
||||||
}
|
}
|
||||||
|
mediaEditor.attachPreviewView(self.previewView)
|
||||||
|
|
||||||
if case .empty = effectiveSubject {
|
if case .empty = effectiveSubject {
|
||||||
self.stickerMaskDrawingView?.emptyColor = .black
|
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)
|
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
|
controller.completion = { [weak self] content in
|
||||||
if let self {
|
guard let self else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if let content {
|
if let content {
|
||||||
if case let .file(file, _) = content {
|
if case let .file(file, _) = content {
|
||||||
self.defaultToEmoji = file.media.isCustomEmoji
|
self.defaultToEmoji = file.media.isCustomEmoji
|
||||||
@ -4664,7 +4692,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
self.stickerScreen = nil
|
self.stickerScreen = nil
|
||||||
self.mediaEditor?.maybeUnpauseVideo()
|
self.mediaEditor?.maybeUnpauseVideo()
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
controller.customModalStyleOverlayTransitionFactorUpdated = { [weak self, weak controller] transition in
|
controller.customModalStyleOverlayTransitionFactorUpdated = { [weak self, weak controller] transition in
|
||||||
@ -6506,13 +6533,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
self.uploadSticker(file, action: .addToFavorites)
|
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 {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var contextItems: [ContextMenuItem] = []
|
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)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor)
|
||||||
}, iconPosition: .left, action: { c, _ in
|
}, iconPosition: .left, action: { c, _ in
|
||||||
c.popItems()
|
c.popItems()
|
||||||
@ -6520,7 +6547,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
contextItems.append(.separator)
|
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 {
|
if let self {
|
||||||
self.presentCreateStickerPack(file: file, completion: {
|
self.presentCreateStickerPack(file: file, completion: {
|
||||||
f(.default)
|
f(.default)
|
||||||
@ -6579,7 +6606,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
c.pushItems(items: .single(items))
|
c.pushItems(items: .single(items))
|
||||||
})))
|
})))
|
||||||
case .editing:
|
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 {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -6601,7 +6628,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
case .addingToPack:
|
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 {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -6691,11 +6718,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func presentCreateStickerPack(file: TelegramMediaFile, completion: @escaping () -> Void) {
|
private func presentCreateStickerPack(file: TelegramMediaFile, completion: @escaping () -> Void) {
|
||||||
//TODO:localize
|
|
||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme)
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||||
|
|
||||||
var dismissImpl: (() -> Void)?
|
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 {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -6942,7 +6968,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
(navigationController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
(navigationController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
||||||
|
|
||||||
Queue.mainQueue().after(0.1) {
|
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,
|
interaction: nil,
|
||||||
chatPeerId: nil,
|
chatPeerId: nil,
|
||||||
present: { c, a in
|
present: { c, a in
|
||||||
let _ = c
|
|
||||||
let _ = a
|
|
||||||
// controller?.presentInGlobalOverlay(c, with: a)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -309,7 +306,7 @@ private final class StickerSelectionComponent: Component {
|
|||||||
switchToGifSubject: { _ in },
|
switchToGifSubject: { _ in },
|
||||||
reorderItems: { _, _ in },
|
reorderItems: { _, _ in },
|
||||||
makeSearchContainerNode: { [weak self] content 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,7 +319,7 @@ private final class StickerSelectionComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
var presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
if controller?.forceDark == true {
|
if controller.forceDark == true {
|
||||||
presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme)
|
presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||||
}
|
}
|
||||||
let searchContainerNode = PaneSearchContainerNode(
|
let searchContainerNode = PaneSearchContainerNode(
|
||||||
@ -440,7 +437,7 @@ public class StickerPickerScreen: ViewController {
|
|||||||
let containerView: UIView
|
let containerView: UIView
|
||||||
let hostView: ComponentHostView<Empty>
|
let hostView: ComponentHostView<Empty>
|
||||||
|
|
||||||
private var content: StickerPickerInputData?
|
fileprivate var content: StickerPickerInputData?
|
||||||
private let contentDisposable = MetaDisposable()
|
private let contentDisposable = MetaDisposable()
|
||||||
private var hasRecentGifsDisposable: Disposable?
|
private var hasRecentGifsDisposable: Disposable?
|
||||||
fileprivate let trendingGifsPromise = Promise<ChatMediaInputGifPaneTrendingState?>(nil)
|
fileprivate let trendingGifsPromise = Promise<ChatMediaInputGifPaneTrendingState?>(nil)
|
||||||
@ -534,6 +531,7 @@ public class StickerPickerScreen: ViewController {
|
|||||||
self.wrappingView.addSubview(self.containerView)
|
self.wrappingView.addSubview(self.containerView)
|
||||||
self.containerView.addSubview(self.hostView)
|
self.containerView.addSubview(self.hostView)
|
||||||
|
|
||||||
|
if controller.hasInteractiveStickers {
|
||||||
self.storyStickersContentView = StoryStickersContentView(frame: .zero)
|
self.storyStickersContentView = StoryStickersContentView(frame: .zero)
|
||||||
self.storyStickersContentView?.locationAction = { [weak self] in
|
self.storyStickersContentView?.locationAction = { [weak self] in
|
||||||
self?.controller?.presentLocationPicker()
|
self?.controller?.presentLocationPicker()
|
||||||
@ -547,6 +545,7 @@ public class StickerPickerScreen: ViewController {
|
|||||||
self.storyStickersContentView?.cameraAction = { [weak self] in
|
self.storyStickersContentView?.cameraAction = { [weak self] in
|
||||||
self?.controller?.addCamera()
|
self?.controller?.addCamera()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let gifItems: Signal<EntityKeyboardGifContent?, NoError>
|
let gifItems: Signal<EntityKeyboardGifContent?, NoError>
|
||||||
if controller.hasGifs {
|
if controller.hasGifs {
|
||||||
@ -654,11 +653,11 @@ public class StickerPickerScreen: ViewController {
|
|||||||
|
|
||||||
self.contentDisposable.set(data.start(next: { [weak self] inputData, gifData, stickerSearchState, emojiSearchState in
|
self.contentDisposable.set(data.start(next: { [weak self] inputData, gifData, stickerSearchState, emojiSearchState in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let presentationData = strongSelf.presentationData
|
|
||||||
guard var inputData = inputData as? StickerPickerInputData else {
|
guard var inputData = inputData as? StickerPickerInputData else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let presentationData = strongSelf.presentationData
|
||||||
inputData.gifs = gifData?.component
|
inputData.gifs = gifData?.component
|
||||||
|
|
||||||
if let emoji = inputData.emoji {
|
if let emoji = inputData.emoji {
|
||||||
@ -1495,7 +1494,6 @@ public class StickerPickerScreen: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if group.items.isEmpty && !result.isFinalResult {
|
if group.items.isEmpty && !result.isFinalResult {
|
||||||
//strongSelf.stickerSearchStateValue.isSearching = true
|
|
||||||
strongSelf.stickerSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: [
|
strongSelf.stickerSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: [
|
||||||
EmojiPagerContentComponent.ItemGroup(
|
EmojiPagerContentComponent.ItemGroup(
|
||||||
supergroupId: "search",
|
supergroupId: "search",
|
||||||
@ -1535,7 +1533,7 @@ public class StickerPickerScreen: ViewController {
|
|||||||
customLayout: nil,
|
customLayout: nil,
|
||||||
externalBackground: nil,
|
externalBackground: nil,
|
||||||
externalExpansionView: nil,
|
externalExpansionView: nil,
|
||||||
customContentView: controller.hasInteractiveStickers ? self.storyStickersContentView : nil,
|
customContentView: self.storyStickersContentView,
|
||||||
useOpaqueTheme: false,
|
useOpaqueTheme: false,
|
||||||
hideBackground: true,
|
hideBackground: true,
|
||||||
stateContext: nil,
|
stateContext: nil,
|
||||||
@ -2050,8 +2048,7 @@ public class StickerPickerScreen: ViewController {
|
|||||||
self.statusBar.statusBarStyle = .Ignore
|
self.statusBar.statusBarStyle = .Ignore
|
||||||
|
|
||||||
if expanded {
|
if expanded {
|
||||||
//TODO:localize
|
self.title = presentationData.strings.Stickers_ChooseSticker_Title
|
||||||
self.title = "Choose Sticker"
|
|
||||||
self.navigationPresentation = .modal
|
self.navigationPresentation = .modal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1891,8 +1891,10 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
guard let self, let view else {
|
guard let self, let view else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if let cameraView = cameraView as? TGAttachmentCameraView {
|
||||||
self.openCamera(view: view, peer: peer, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, cameraView: cameraView)
|
self.openCamera(view: view, peer: peer, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, cameraView: cameraView)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
controller.presentWebSearch = { [weak self, weak view, weak controller] mediaGroups, activateOnDisplay in
|
controller.presentWebSearch = { [weak self, weak view, weak controller] mediaGroups, activateOnDisplay in
|
||||||
guard let self, let view, let controller else {
|
guard let self, let view, let controller else {
|
||||||
return
|
return
|
||||||
|
@ -31,6 +31,7 @@ import PremiumGiftAttachmentScreen
|
|||||||
import TelegramCallsUI
|
import TelegramCallsUI
|
||||||
import AutomaticBusinessMessageSetupScreen
|
import AutomaticBusinessMessageSetupScreen
|
||||||
import MediaEditorScreen
|
import MediaEditorScreen
|
||||||
|
import CameraScreen
|
||||||
|
|
||||||
extension ChatControllerImpl {
|
extension ChatControllerImpl {
|
||||||
enum AttachMenuSubject {
|
enum AttachMenuSubject {
|
||||||
@ -1160,8 +1161,10 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
let mediaPickerContext = controller.mediaPickerContext
|
let mediaPickerContext = controller.mediaPickerContext
|
||||||
controller.openCamera = { [weak self] cameraView in
|
controller.openCamera = { [weak self] cameraView in
|
||||||
|
if let cameraView = cameraView as? TGAttachmentCameraView {
|
||||||
self?.openCamera(cameraView: cameraView)
|
self?.openCamera(cameraView: cameraView)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
controller.presentWebSearch = { [weak self, weak controller] mediaGroups, activateOnDisplay in
|
controller.presentWebSearch = { [weak self, weak controller] mediaGroups, activateOnDisplay in
|
||||||
self?.presentWebSearch(editingMessage: false, attachment: true, activateOnDisplay: activateOnDisplay, present: { [weak controller] c, a in
|
self?.presentWebSearch(editingMessage: false, attachment: true, activateOnDisplay: activateOnDisplay, present: { [weak controller] c, a in
|
||||||
controller?.present(c, in: .current)
|
controller?.present(c, in: .current)
|
||||||
@ -1726,8 +1729,8 @@ extension ChatControllerImpl {
|
|||||||
var dismissImpl: (() -> Void)?
|
var dismissImpl: (() -> Void)?
|
||||||
let mainController = self.context.sharedContext.makeStickerMediaPickerScreen(
|
let mainController = self.context.sharedContext.makeStickerMediaPickerScreen(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
getSourceRect: { return .zero },
|
getSourceRect: { return nil },
|
||||||
completion: { [weak self] result, transitionView, transitionRect, transitionImage, transitionOut, dismissed in
|
completion: { [weak self] result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1736,6 +1739,18 @@ extension ChatControllerImpl {
|
|||||||
subject = .single(.asset(asset))
|
subject = .single(.asset(asset))
|
||||||
} else if let image = result as? UIImage {
|
} else if let image = result as? UIImage {
|
||||||
subject = .single(.image(image, PixelDimensions(image.size), nil, .bottomRight))
|
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 {
|
} else {
|
||||||
subject = .single(.empty(PixelDimensions(width: 1080, height: 1920)))
|
subject = .single(.empty(PixelDimensions(width: 1080, height: 1920)))
|
||||||
}
|
}
|
||||||
@ -1744,7 +1759,7 @@ extension ChatControllerImpl {
|
|||||||
context: self.context,
|
context: self.context,
|
||||||
mode: .stickerEditor(mode: .generic),
|
mode: .stickerEditor(mode: .generic),
|
||||||
subject: subject,
|
subject: subject,
|
||||||
transitionIn: transitionView.flatMap({ .gallery(
|
transitionIn: fromCamera ? .camera : transitionView.flatMap({ .gallery(
|
||||||
MediaEditorScreen.TransitionIn.GalleryTransitionIn(
|
MediaEditorScreen.TransitionIn.GalleryTransitionIn(
|
||||||
sourceView: $0,
|
sourceView: $0,
|
||||||
sourceRect: transitionRect,
|
sourceRect: transitionRect,
|
||||||
@ -1772,6 +1787,9 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
} as (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
} as (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
||||||
)
|
)
|
||||||
|
editorController.cancelled = { _ in
|
||||||
|
cancelled()
|
||||||
|
}
|
||||||
editorController.sendSticker = { [weak self] file, sourceView, sourceRect in
|
editorController.sendSticker = { [weak self] file, sourceView, sourceRect in
|
||||||
return self?.interfaceInteraction?.sendSticker(file, true, sourceView, sourceRect, nil, []) ?? false
|
return self?.interfaceInteraction?.sendSticker(file, true, sourceView, sourceRect, nil, []) ?? false
|
||||||
}
|
}
|
||||||
@ -1780,7 +1798,13 @@ extension ChatControllerImpl {
|
|||||||
dismissed: {}
|
dismissed: {}
|
||||||
)
|
)
|
||||||
dismissImpl = { [weak mainController] in
|
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.navigationPresentation = .flatModal
|
||||||
mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
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)
|
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)
|
return stickerMediaPickerController(context: context, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,6 +300,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
var showDraftTooltipImpl: (() -> Void)?
|
var showDraftTooltipImpl: (() -> Void)?
|
||||||
let cameraController = CameraScreen(
|
let cameraController = CameraScreen(
|
||||||
context: context,
|
context: context,
|
||||||
|
mode: .story,
|
||||||
transitionIn: transitionIn.flatMap {
|
transitionIn: transitionIn.flatMap {
|
||||||
if let sourceView = $0.sourceView {
|
if let sourceView = $0.sourceView {
|
||||||
return CameraScreen.TransitionIn(
|
return CameraScreen.TransitionIn(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user