Video avatar fixes

This commit is contained in:
Ilya Laktyushin 2020-06-24 18:06:08 +03:00
parent 4755ee4261
commit a34b7402be
62 changed files with 753 additions and 1962 deletions

View File

@ -3,6 +3,8 @@ import Postbox
import TelegramCore import TelegramCore
import SyncCore import SyncCore
private let minimalStreamableSize: Int = 384 * 1024
public func isMediaStreamable(message: Message, media: TelegramMediaFile) -> Bool { public func isMediaStreamable(message: Message, media: TelegramMediaFile) -> Bool {
if message.containsSecretMedia { if message.containsSecretMedia {
return false return false
@ -13,7 +15,7 @@ public func isMediaStreamable(message: Message, media: TelegramMediaFile) -> Boo
guard let size = media.size else { guard let size = media.size else {
return false return false
} }
if size < 256 * 1024 { if size < minimalStreamableSize {
return false return false
} }
for attribute in media.attributes { for attribute in media.attributes {
@ -36,7 +38,7 @@ public func isMediaStreamable(media: TelegramMediaFile) -> Bool {
guard let size = media.size else { guard let size = media.size else {
return false return false
} }
if size < 1 * 1024 * 1024 { if size < minimalStreamableSize {
return false return false
} }
for attribute in media.attributes { for attribute in media.attributes {
@ -49,3 +51,11 @@ public func isMediaStreamable(media: TelegramMediaFile) -> Bool {
} }
return false return false
} }
public func isMediaStreamable(resource: MediaResource) -> Bool {
if let size = resource.size, size >= minimalStreamableSize {
return true
} else {
return false
}
}

View File

@ -184,7 +184,7 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
node?.itemChanged = { [weak self] index in node?.itemChanged = { [weak self] index in
if let strongSelf = self { if let strongSelf = self {
let pagerIndex = indexes[index] let pagerIndex = indexes[index]
strongSelf.pager.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: pagerIndex)) strongSelf.pager.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: pagerIndex, synchronous: false))
} }
} }
} }

View File

@ -23,7 +23,7 @@ public struct GalleryItemIndexData: Equatable {
public protocol GalleryItem { public protocol GalleryItem {
var id: AnyHashable { get } var id: AnyHashable { get }
func node() -> GalleryItemNode func node(synchronous: Bool) -> GalleryItemNode
func updateNode(node: GalleryItemNode) func updateNode(node: GalleryItemNode, synchronous: Bool)
func thumbnailItem() -> (Int64, GalleryThumbnailItem)? func thumbnailItem() -> (Int64, GalleryThumbnailItem)?
} }

View File

@ -64,12 +64,14 @@ public struct GalleryPagerTransaction {
public let insertItems: [GalleryPagerInsertItem] public let insertItems: [GalleryPagerInsertItem]
public let updateItems: [GalleryPagerUpdateItem] public let updateItems: [GalleryPagerUpdateItem]
public let focusOnItem: Int? public let focusOnItem: Int?
public let synchronous: Bool
public init(deleteItems: [Int], insertItems: [GalleryPagerInsertItem], updateItems: [GalleryPagerUpdateItem], focusOnItem: Int?) { public init(deleteItems: [Int], insertItems: [GalleryPagerInsertItem], updateItems: [GalleryPagerUpdateItem], focusOnItem: Int?, synchronous: Bool) {
self.deleteItems = deleteItems self.deleteItems = deleteItems
self.insertItems = insertItems self.insertItems = insertItems
self.updateItems = updateItems self.updateItems = updateItems
self.focusOnItem = focusOnItem self.focusOnItem = focusOnItem
self.synchronous = synchronous
} }
} }
@ -310,12 +312,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
} }
} }
public func replaceItems(_ items: [GalleryItem], centralItemIndex: Int?, keepFirst: Bool = false) { public func replaceItems(_ items: [GalleryItem], centralItemIndex: Int?, synchronous: Bool = false) {
var items = items
if keepFirst && !self.items.isEmpty && !items.isEmpty {
items[0] = self.items[0]
}
var updateItems: [GalleryPagerUpdateItem] = [] var updateItems: [GalleryPagerUpdateItem] = []
var deleteItems: [Int] = [] var deleteItems: [Int] = []
var insertItems: [GalleryPagerInsertItem] = [] var insertItems: [GalleryPagerInsertItem] = []
@ -333,7 +330,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
insertItems.append(GalleryPagerInsertItem(index: i, item: items[i], previousIndex: previousIndexById[items[i].id])) insertItems.append(GalleryPagerInsertItem(index: i, item: items[i], previousIndex: previousIndexById[items[i].id]))
} }
self.transaction(GalleryPagerTransaction(deleteItems: deleteItems, insertItems: insertItems, updateItems: updateItems, focusOnItem: centralItemIndex)) self.transaction(GalleryPagerTransaction(deleteItems: deleteItems, insertItems: insertItems, updateItems: updateItems, focusOnItem: centralItemIndex, synchronous: synchronous))
} }
public func transaction(_ transaction: GalleryPagerTransaction) { public func transaction(_ transaction: GalleryPagerTransaction) {
@ -341,7 +338,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
self.items[updatedItem.previousIndex] = updatedItem.item self.items[updatedItem.previousIndex] = updatedItem.item
if let itemNode = self.visibleItemNode(at: updatedItem.previousIndex) { if let itemNode = self.visibleItemNode(at: updatedItem.previousIndex) {
//print("update visible node at \(updatedItem.previousIndex)") //print("update visible node at \(updatedItem.previousIndex)")
updatedItem.item.updateNode(node: itemNode) updatedItem.item.updateNode(node: itemNode, synchronous: transaction.synchronous)
} }
} }
@ -395,7 +392,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
self.centralItemIndex = focusOnItem self.centralItemIndex = focusOnItem
} }
self.updateItemNodes(transition: .immediate, notify: transaction.focusOnItem != nil) self.updateItemNodes(transition: .immediate, notify: transaction.focusOnItem != nil, synchronous: transaction.synchronous)
//print("visible indices after update \(self.itemNodes.map { $0.index })") //print("visible indices after update \(self.itemNodes.map { $0.index })")
} }
@ -425,18 +422,18 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
private func goToPreviousItem() { private func goToPreviousItem() {
if let index = self.centralItemIndex, index > 0 { if let index = self.centralItemIndex, index > 0 {
self.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index - 1)) self.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false))
} }
} }
private func goToNextItem() { private func goToNextItem() {
if let index = self.centralItemIndex, index < self.items.count - 1 { if let index = self.centralItemIndex, index < self.items.count - 1 {
self.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index + 1)) self.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index + 1, synchronous: false))
} }
} }
private func makeNodeForItem(at index: Int) -> GalleryItemNode { private func makeNodeForItem(at index: Int, synchronous: Bool) -> GalleryItemNode {
let node = self.items[index].node() let node = self.items[index].node(synchronous: synchronous)
node.toggleControlsVisibility = self.toggleControlsVisibility node.toggleControlsVisibility = self.toggleControlsVisibility
node.dismiss = self.dismiss node.dismiss = self.dismiss
node.beginCustomDismiss = self.beginCustomDismiss node.beginCustomDismiss = self.beginCustomDismiss
@ -475,7 +472,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
self.itemNodes.remove(at: internalIndex) self.itemNodes.remove(at: internalIndex)
} }
private func updateItemNodes(transition: ContainedViewLayoutTransition, forceOffsetReset: Bool = false, notify: Bool = false, forceLoad: Bool = false) { private func updateItemNodes(transition: ContainedViewLayoutTransition, forceOffsetReset: Bool = false, notify: Bool = false, forceLoad: Bool = false, synchronous: Bool = false) {
if self.items.isEmpty || self.containerLayout == nil { if self.items.isEmpty || self.containerLayout == nil {
return return
} }
@ -487,7 +484,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
} while self.itemNodes.count > 0 } while self.itemNodes.count > 0
} }
if self.itemNodes.isEmpty { if self.itemNodes.isEmpty {
let node = self.makeNodeForItem(at: self.centralItemIndex ?? 0) let node = self.makeNodeForItem(at: self.centralItemIndex ?? 0, synchronous: synchronous)
node.frame = CGRect(origin: CGPoint(), size: scrollView.bounds.size) node.frame = CGRect(origin: CGPoint(), size: scrollView.bounds.size)
if let containerLayout = self.containerLayout { if let containerLayout = self.containerLayout {
node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate)
@ -502,7 +499,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
if let centralItemIndex = self.centralItemIndex, let centralItemNode = self.visibleItemNode(at: centralItemIndex) { if let centralItemIndex = self.centralItemIndex, let centralItemNode = self.visibleItemNode(at: centralItemIndex) {
if centralItemIndex != 0 { if centralItemIndex != 0 {
if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemIndex - 1) == nil { if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemIndex - 1) == nil {
let node = self.makeNodeForItem(at: centralItemIndex - 1) let node = self.makeNodeForItem(at: centralItemIndex - 1, synchronous: synchronous)
node.frame = centralItemNode.frame.offsetBy(dx: -centralItemNode.frame.size.width - self.pageGap, dy: 0.0) node.frame = centralItemNode.frame.offsetBy(dx: -centralItemNode.frame.size.width - self.pageGap, dy: 0.0)
if let containerLayout = self.containerLayout { if let containerLayout = self.containerLayout {
node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate)
@ -513,7 +510,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
if centralItemIndex != self.items.count - 1 { if centralItemIndex != self.items.count - 1 {
if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemIndex + 1) == nil { if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemIndex + 1) == nil {
let node = self.makeNodeForItem(at: centralItemIndex + 1) let node = self.makeNodeForItem(at: centralItemIndex + 1, synchronous: synchronous)
node.frame = centralItemNode.frame.offsetBy(dx: centralItemNode.frame.size.width + self.pageGap, dy: 0.0) node.frame = centralItemNode.frame.offsetBy(dx: centralItemNode.frame.size.width + self.pageGap, dy: 0.0)
if let containerLayout = self.containerLayout { if let containerLayout = self.containerLayout {
node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate)
@ -549,7 +546,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
if centralItemCandidateNode.index != 0 { if centralItemCandidateNode.index != 0 {
if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemCandidateNode.index - 1) == nil { if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemCandidateNode.index - 1) == nil {
let node = self.makeNodeForItem(at: centralItemCandidateNode.index - 1) let node = self.makeNodeForItem(at: centralItemCandidateNode.index - 1, synchronous: synchronous)
node.frame = centralItemCandidateNode.frame.offsetBy(dx: -centralItemCandidateNode.frame.size.width - self.pageGap, dy: 0.0) node.frame = centralItemCandidateNode.frame.offsetBy(dx: -centralItemCandidateNode.frame.size.width - self.pageGap, dy: 0.0)
if let containerLayout = self.containerLayout { if let containerLayout = self.containerLayout {
node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate)
@ -560,7 +557,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
if centralItemCandidateNode.index != items.count - 1 { if centralItemCandidateNode.index != items.count - 1 {
if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemCandidateNode.index + 1) == nil { if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemCandidateNode.index + 1) == nil {
let node = self.makeNodeForItem(at: centralItemCandidateNode.index + 1) let node = self.makeNodeForItem(at: centralItemCandidateNode.index + 1, synchronous: synchronous)
node.frame = centralItemCandidateNode.frame.offsetBy(dx: centralItemCandidateNode.frame.size.width + self.pageGap, dy: 0.0) node.frame = centralItemCandidateNode.frame.offsetBy(dx: centralItemCandidateNode.frame.size.width + self.pageGap, dy: 0.0)
if let containerLayout = self.containerLayout { if let containerLayout = self.containerLayout {
node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate)

View File

@ -31,7 +31,7 @@ class ChatAnimationGalleryItem: GalleryItem {
self.location = location self.location = location
} }
func node() -> GalleryItemNode { func node(synchronous: Bool) -> GalleryItemNode {
let node = ChatAnimationGalleryItemNode(context: self.context, presentationData: self.presentationData) let node = ChatAnimationGalleryItemNode(context: self.context, presentationData: self.presentationData)
for media in self.message.media { for media in self.message.media {
@ -46,7 +46,7 @@ class ChatAnimationGalleryItem: GalleryItem {
return node return node
} }
func updateNode(node: GalleryItemNode) { func updateNode(node: GalleryItemNode, synchronous: Bool) {
if let node = node as? ChatAnimationGalleryItemNode { if let node = node as? ChatAnimationGalleryItemNode {
node.setMessage(self.message) node.setMessage(self.message)
} }

View File

@ -28,7 +28,7 @@ class ChatDocumentGalleryItem: GalleryItem {
self.location = location self.location = location
} }
func node() -> GalleryItemNode { func node(synchronous: Bool) -> GalleryItemNode {
let node = ChatDocumentGalleryItemNode(context: self.context, presentationData: self.presentationData) let node = ChatDocumentGalleryItemNode(context: self.context, presentationData: self.presentationData)
for media in self.message.media { for media in self.message.media {
@ -51,7 +51,7 @@ class ChatDocumentGalleryItem: GalleryItem {
return node return node
} }
func updateNode(node: GalleryItemNode) { func updateNode(node: GalleryItemNode, synchronous: Bool) {
if let node = node as? ChatDocumentGalleryItemNode, let location = self.location { if let node = node as? ChatDocumentGalleryItemNode, let location = self.location {
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0)) node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0))
node.setMessage(self.message) node.setMessage(self.message)

View File

@ -29,7 +29,7 @@ class ChatExternalFileGalleryItem: GalleryItem {
self.location = location self.location = location
} }
func node() -> GalleryItemNode { func node(synchronous: Bool) -> GalleryItemNode {
let node = ChatExternalFileGalleryItemNode(context: self.context, presentationData: self.presentationData) let node = ChatExternalFileGalleryItemNode(context: self.context, presentationData: self.presentationData)
for media in self.message.media { for media in self.message.media {
@ -52,7 +52,7 @@ class ChatExternalFileGalleryItem: GalleryItem {
return node return node
} }
func updateNode(node: GalleryItemNode) { func updateNode(node: GalleryItemNode, synchronous: Bool) {
if let node = node as? ChatExternalFileGalleryItemNode, let location = self.location { if let node = node as? ChatExternalFileGalleryItemNode, let location = self.location {
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0)) node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0))
node.setMessage(self.message) node.setMessage(self.message)

View File

@ -101,7 +101,7 @@ class ChatImageGalleryItem: GalleryItem {
self.present = present self.present = present
} }
func node() -> GalleryItemNode { func node(synchronous: Bool) -> GalleryItemNode {
let node = ChatImageGalleryItemNode(context: self.context, presentationData: self.presentationData, performAction: self.performAction, openActionOptions: self.openActionOptions, present: self.present) let node = ChatImageGalleryItemNode(context: self.context, presentationData: self.presentationData, performAction: self.performAction, openActionOptions: self.openActionOptions, present: self.present)
for media in self.message.media { for media in self.message.media {
@ -131,7 +131,7 @@ class ChatImageGalleryItem: GalleryItem {
return node return node
} }
func updateNode(node: GalleryItemNode) { func updateNode(node: GalleryItemNode, synchronous: Bool) {
if let node = node as? ChatImageGalleryItemNode, let location = self.location { if let node = node as? ChatImageGalleryItemNode, let location = self.location {
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0)) node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0))

View File

@ -66,7 +66,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
self.present = present self.present = present
} }
public func node() -> GalleryItemNode { public func node(synchronous: Bool) -> GalleryItemNode {
let node = UniversalVideoGalleryItemNode(context: self.context, presentationData: self.presentationData, performAction: self.performAction, openActionOptions: self.openActionOptions, present: self.present) let node = UniversalVideoGalleryItemNode(context: self.context, presentationData: self.presentationData, performAction: self.performAction, openActionOptions: self.openActionOptions, present: self.present)
if let indexData = self.indexData { if let indexData = self.indexData {
@ -78,7 +78,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
return node return node
} }
public func updateNode(node: GalleryItemNode) { public func updateNode(node: GalleryItemNode, synchronous: Bool) {
if let node = node as? UniversalVideoGalleryItemNode { if let node = node as? UniversalVideoGalleryItemNode {
if let indexData = self.indexData { if let indexData = self.indexData {
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0)) node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0))

View File

@ -62,7 +62,7 @@ class InstantImageGalleryItem: GalleryItem {
self.openUrlOptions = openUrlOptions self.openUrlOptions = openUrlOptions
} }
func node() -> GalleryItemNode { func node(synchronous: Bool) -> GalleryItemNode {
let node = InstantImageGalleryItemNode(context: self.context, presentationData: self.presentationData, openUrl: self.openUrl, openUrlOptions: self.openUrlOptions) let node = InstantImageGalleryItemNode(context: self.context, presentationData: self.presentationData, openUrl: self.openUrl, openUrlOptions: self.openUrlOptions)
node.setImage(imageReference: self.imageReference) node.setImage(imageReference: self.imageReference)
@ -76,7 +76,7 @@ class InstantImageGalleryItem: GalleryItem {
return node return node
} }
func updateNode(node: GalleryItemNode) { func updateNode(node: GalleryItemNode, synchronous: Bool) {
if let node = node as? InstantImageGalleryItemNode { if let node = node as? InstantImageGalleryItemNode {
if let location = self.location { if let location = self.location {
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.position + 1)", "\(location.totalCount)").0)) node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.position + 1)", "\(location.totalCount)").0))

View File

@ -221,7 +221,7 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable
if strongSelf.isViewLoaded { if strongSelf.isViewLoaded {
strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({ strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({
$0.item(context: context, webPage: webPage, message: message, presentationData: strongSelf.presentationData, fromPlayingVideo: fromPlayingVideo, landscape: landscape, openUrl: strongSelf.innerOpenUrl, openUrlOptions: strongSelf.openUrlOptions) $0.item(context: context, webPage: webPage, message: message, presentationData: strongSelf.presentationData, fromPlayingVideo: fromPlayingVideo, landscape: landscape, openUrl: strongSelf.innerOpenUrl, openUrlOptions: strongSelf.openUrlOptions)
}), centralItemIndex: centralIndex, keepFirst: false) }), centralItemIndex: centralIndex)
let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in
strongSelf?.didSetReady = true strongSelf?.didSetReady = true

View File

@ -81,7 +81,6 @@
#import <LegacyComponents/TGCameraMainTabletView.h> #import <LegacyComponents/TGCameraMainTabletView.h>
#import <LegacyComponents/TGCameraMainView.h> #import <LegacyComponents/TGCameraMainView.h>
#import <LegacyComponents/TGCameraModeControl.h> #import <LegacyComponents/TGCameraModeControl.h>
#import <LegacyComponents/TGCameraPhotoPreviewController.h>
#import <LegacyComponents/TGCameraPreviewView.h> #import <LegacyComponents/TGCameraPreviewView.h>
#import <LegacyComponents/TGCameraSegmentsView.h> #import <LegacyComponents/TGCameraSegmentsView.h>
#import <LegacyComponents/TGCameraShutterButton.h> #import <LegacyComponents/TGCameraShutterButton.h>

View File

@ -25,7 +25,8 @@ typedef enum
PGCameraModePhoto, PGCameraModePhoto,
PGCameraModeVideo, PGCameraModeVideo,
PGCameraModeSquarePhoto, PGCameraModeSquarePhoto,
PGCameraModeSquareVideo PGCameraModeSquareVideo,
PGCameraModeSquareSwing
} PGCameraMode; } PGCameraMode;
typedef enum typedef enum

View File

@ -1,33 +0,0 @@
#import <LegacyComponents/TGOverlayController.h>
#import <LegacyComponents/LegacyComponentsContext.h>
@class PGCameraShotMetadata;
@class PGPhotoEditorValues;
@class TGSuggestionContext;
@interface TGCameraPhotoPreviewController : TGOverlayController
@property (nonatomic, assign) bool allowCaptions;
@property (nonatomic, copy) CGRect(^beginTransitionIn)(void);
@property (nonatomic, copy) CGRect(^beginTransitionOut)(CGRect referenceFrame);
@property (nonatomic, copy) void(^finishedTransitionIn)(void);
@property (nonatomic, copy) void (^photoEditorShown)(void);
@property (nonatomic, copy) void (^photoEditorHidden)(void);
@property (nonatomic, copy) void(^retakePressed)(void);
@property (nonatomic, copy) void(^sendPressed)(TGOverlayController *controller, UIImage *resultImage, NSString *caption, NSArray *entities, NSArray *stickers, NSNumber *timer);
@property (nonatomic, strong) TGSuggestionContext *suggestionContext;
@property (nonatomic, assign) bool shouldStoreAssets;
@property (nonatomic, assign) bool hasTimer;
@property (nonatomic, assign) bool hasSilentPosting;
@property (nonatomic, assign) bool hasSchedule;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context image:(UIImage *)image metadata:(PGCameraShotMetadata *)metadata recipientName:(NSString *)recipientName saveCapturedMedia:(bool)saveCapturedMedia saveEditedPhotos:(bool)saveEditedPhotos;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context image:(UIImage *)image metadata:(PGCameraShotMetadata *)metadata recipientName:(NSString *)recipientName backButtonTitle:(NSString *)backButtonTitle doneButtonTitle:(NSString *)doneButtonTitle saveCapturedMedia:(bool)saveCapturedMedia saveEditedPhotos:(bool)saveEditedPhotos;
@end

View File

@ -38,10 +38,11 @@ typedef enum
@property (nonatomic, readonly) UIImage *badge; @property (nonatomic, readonly) UIImage *badge;
@property (nonatomic, readonly) UIColor *badgeTextColor; @property (nonatomic, readonly) UIColor *badgeTextColor;
@property (nonatomic, readonly) UIImage *sendIconImage; @property (nonatomic, readonly) UIImage *sendIconImage;
@property (nonatomic, readonly) UIImage *doneIconImage;
@property (nonatomic, readonly) UIColor *maybeAccentColor; @property (nonatomic, readonly) UIColor *maybeAccentColor;
+ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage maybeAccentColor:(UIColor *)maybeAccentColor; + (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage doneIconImage:(UIImage *)doneIconImage maybeAccentColor:(UIColor *)maybeAccentColor;
@end @end

View File

@ -10,6 +10,7 @@
@class PGCameraShotMetadata; @class PGCameraShotMetadata;
@class TGSuggestionContext; @class TGSuggestionContext;
@class TGPhotoEditorController; @class TGPhotoEditorController;
@class AVPlayer;
@protocol TGPhotoPaintStickersContext; @protocol TGPhotoPaintStickersContext;
@class TGPhotoEntitiesContainerView; @class TGPhotoEntitiesContainerView;
@ -57,6 +58,10 @@ typedef enum {
@property (nonatomic, strong) PGCameraShotMetadata *metadata; @property (nonatomic, strong) PGCameraShotMetadata *metadata;
@property (nonatomic, strong) NSArray *faces; @property (nonatomic, strong) NSArray *faces;
@property (nonatomic, strong) NSArray *cachedVideoThumbnails;
@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) TGPhotoEntitiesContainerView *entitiesView; @property (nonatomic, strong) TGPhotoEntitiesContainerView *entitiesView;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id<TGMediaEditableItem>)item intent:(TGPhotoEditorControllerIntent)intent adjustments:(id<TGMediaEditAdjustments>)adjustments caption:(NSString *)caption screenImage:(UIImage *)screenImage availableTabs:(TGPhotoEditorTab)availableTabs selectedTab:(TGPhotoEditorTab)selectedTab; - (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id<TGMediaEditableItem>)item intent:(TGPhotoEditorControllerIntent)intent adjustments:(id<TGMediaEditAdjustments>)adjustments caption:(NSString *)caption screenImage:(UIImage *)screenImage availableTabs:(TGPhotoEditorTab)availableTabs selectedTab:(TGPhotoEditorTab)selectedTab;

View File

@ -63,6 +63,7 @@
- (bool)isDismissAllowed; - (bool)isDismissAllowed;
- (bool)hasOnScreenNavigation;
- (UIInterfaceOrientation)effectiveOrientation; - (UIInterfaceOrientation)effectiveOrientation;
- (UIInterfaceOrientation)effectiveOrientation:(UIInterfaceOrientation)orientation; - (UIInterfaceOrientation)effectiveOrientation:(UIInterfaceOrientation)orientation;

View File

@ -28,7 +28,8 @@ typedef enum
typedef enum typedef enum
{ {
TGPhotoEditorDoneButtonSend, TGPhotoEditorDoneButtonSend,
TGPhotoEditorDoneButtonCheck TGPhotoEditorDoneButtonCheck,
TGPhotoEditorDoneButtonDone
} TGPhotoEditorDoneButton; } TGPhotoEditorDoneButton;
@interface TGPhotoToolbarView : UIView @interface TGPhotoToolbarView : UIView
@ -47,6 +48,9 @@ typedef enum
@property (nonatomic, readonly) CGRect cancelButtonFrame; @property (nonatomic, readonly) CGRect cancelButtonFrame;
@property (nonatomic, readonly) CGRect doneButtonFrame; @property (nonatomic, readonly) CGRect doneButtonFrame;
@property (nonatomic, assign) TGPhotoEditorBackButton backButtonType;
@property (nonatomic, assign) TGPhotoEditorDoneButton doneButtonType;
- (instancetype)initWithBackButton:(TGPhotoEditorBackButton)backButton doneButton:(TGPhotoEditorDoneButton)doneButton solidBackground:(bool)solidBackground; - (instancetype)initWithBackButton:(TGPhotoEditorBackButton)backButton doneButton:(TGPhotoEditorDoneButton)doneButton solidBackground:(bool)solidBackground;
- (void)transitionInAnimated:(bool)animated; - (void)transitionInAnimated:(bool)animated;

View File

@ -384,7 +384,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus";
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData]; UIImage *image = [[UIImage alloc] initWithData:imageData];
if (self.cameraMode == PGCameraModeSquarePhoto || self.cameraMode == PGCameraModeSquareVideo) if (self.cameraMode == PGCameraModeSquarePhoto || self.cameraMode == PGCameraModeSquareVideo || self.cameraMode == PGCameraModeSquareSwing)
{ {
CGFloat shorterSide = MIN(image.size.width, image.size.height); CGFloat shorterSide = MIN(image.size.width, image.size.height);
CGFloat longerSide = MAX(image.size.width, image.size.height); CGFloat longerSide = MAX(image.size.width, image.size.height);
@ -636,7 +636,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus";
- (bool)flashActive - (bool)flashActive
{ {
if (self.cameraMode == PGCameraModeVideo || self.cameraMode == PGCameraModeSquareVideo) if (self.cameraMode == PGCameraModeVideo || self.cameraMode == PGCameraModeSquareVideo || self.cameraMode == PGCameraModeSquareSwing)
return self.captureSession.videoDevice.torchActive; return self.captureSession.videoDevice.torchActive;
return self.captureSession.videoDevice.flashActive; return self.captureSession.videoDevice.flashActive;
@ -644,7 +644,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus";
- (bool)flashAvailable - (bool)flashAvailable
{ {
if (self.cameraMode == PGCameraModeVideo || self.cameraMode == PGCameraModeSquareVideo) if (self.cameraMode == PGCameraModeVideo || self.cameraMode == PGCameraModeSquareVideo || self.cameraMode == PGCameraModeSquareSwing)
return self.captureSession.videoDevice.torchAvailable; return self.captureSession.videoDevice.torchAvailable;
return self.captureSession.videoDevice.flashAvailable; return self.captureSession.videoDevice.flashAvailable;

View File

@ -188,7 +188,7 @@ const NSInteger PGCameraFrameRate = 30;
if (self.currentCameraPosition != _preferredCameraPosition) if (self.currentCameraPosition != _preferredCameraPosition)
return true; return true;
if (self.currentMode == PGCameraModeVideo || self.currentMode == PGCameraModeSquareVideo) if (self.currentMode == PGCameraModeVideo || self.currentMode == PGCameraModeSquareVideo || self.currentMode == PGCameraModeSquareSwing)
return true; return true;
if (self.zoomLevel > FLT_EPSILON) if (self.zoomLevel > FLT_EPSILON)
@ -270,11 +270,12 @@ const NSInteger PGCameraFrameRate = 30;
case PGCameraModeVideo: case PGCameraModeVideo:
case PGCameraModeSquareVideo: case PGCameraModeSquareVideo:
case PGCameraModeSquareSwing:
{ {
self.sessionPreset = AVCaptureSessionPresetInputPriority; self.sessionPreset = AVCaptureSessionPresetInputPriority;
[self switchToBestVideoFormatForDevice:_videoDevice]; [self switchToBestVideoFormatForDevice:_videoDevice];
[self _addAudioInputRequestAudioSession:true]; [self _addAudioInputRequestAudioSession:true];
[self setFrameRate:PGCameraFrameRate forDevice:_videoDevice]; [self setFrameRate:mode == PGCameraFrameRate forDevice:_videoDevice];
} }
break; break;
@ -529,6 +530,7 @@ const NSInteger PGCameraFrameRate = 30;
{ {
case PGCameraModeVideo: case PGCameraModeVideo:
case PGCameraModeSquareVideo: case PGCameraModeSquareVideo:
case PGCameraModeSquareSwing:
return _videoFlashMode; return _videoFlashMode;
default: default:
@ -544,6 +546,7 @@ const NSInteger PGCameraFrameRate = 30;
{ {
case PGCameraModeVideo: case PGCameraModeVideo:
case PGCameraModeSquareVideo: case PGCameraModeSquareVideo:
case PGCameraModeSquareSwing:
{ {
AVCaptureTorchMode torchMode = [PGCameraCaptureSession _deviceTorchModeForCameraFlashMode:mode]; AVCaptureTorchMode torchMode = [PGCameraCaptureSession _deviceTorchModeForCameraFlashMode:mode];
if (device.hasTorch && [device isTorchModeSupported:torchMode]) if (device.hasTorch && [device isTorchModeSupported:torchMode])
@ -660,7 +663,7 @@ const NSInteger PGCameraFrameRate = 30;
[self commitConfiguration]; [self commitConfiguration];
if (self.currentMode == PGCameraModeVideo || self.currentMode == PGCameraModeSquareVideo) if (self.currentMode == PGCameraModeVideo || self.currentMode == PGCameraModeSquareVideo || self.currentMode == PGCameraModeSquareSwing)
[self setFrameRate:PGCameraFrameRate forDevice:deviceForTargetPosition]; [self setFrameRate:PGCameraFrameRate forDevice:deviceForTargetPosition];
else else
[self setFrameRate:0 forDevice:deviceForTargetPosition]; [self setFrameRate:0 forDevice:deviceForTargetPosition];

View File

@ -1,6 +1,7 @@
#import "PGPhotoBlurPass.h" #import "PGPhotoBlurPass.h"
#import "GPUImageTwoInputFilter.h" #import "GPUImageTwoInputFilter.h"
#import "GPUImageThreeInputFilter.h"
#import "PGPhotoGaussianBlurFilter.h" #import "PGPhotoGaussianBlurFilter.h"
NSString *const PGPhotoRadialBlurShaderString = PGShaderString NSString *const PGPhotoRadialBlurShaderString = PGShaderString
@ -54,12 +55,33 @@ NSString *const PGPhotoLinearBlurShaderString = PGShaderString
} }
); );
NSString *const PGPhotoMaskedBlurShaderString = PGShaderString
(
varying highp vec2 texCoord;
varying highp vec2 texCoord2;
varying highp vec2 texCoord3;
uniform sampler2D sourceImage;
uniform sampler2D inputImageTexture2;
uniform sampler2D inputImageTexture3;
void main()
{
lowp vec4 sharpImageColor = texture2D(sourceImage, texCoord);
lowp vec4 blurredImageColor = texture2D(inputImageTexture2, texCoord2);
lowp vec4 maskImageColor = texture2D(inputImageTexture3, texCoord3);
gl_FragColor = mix(blurredImageColor, sharpImageColor, maskImageColor.r);
}
);
@interface PGPhotoBlurFilter : GPUImageOutput <GPUImageInput> @interface PGPhotoBlurFilter : GPUImageOutput <GPUImageInput>
{ {
PGPhotoGaussianBlurFilter *_blurFilter; PGPhotoGaussianBlurFilter *_blurFilter;
GPUImageTwoInputFilter *_radialFocusFilter; GPUImageTwoInputFilter *_radialFocusFilter;
GPUImageTwoInputFilter *_linearFocusFilter; GPUImageTwoInputFilter *_linearFocusFilter;
GPUImageThreeInputFilter *_maskedFilter;
GPUImageOutput <GPUImageInput> *_currentFocusFilter; GPUImageOutput <GPUImageInput> *_currentFocusFilter;
@ -104,6 +126,10 @@ NSString *const PGPhotoLinearBlurShaderString = PGShaderString
} }
break; break;
case PGBlurToolTypePortrait:
{
_currentFocusFilter = _maskedFilter;
}
default: default:
break; break;
} }
@ -192,6 +218,7 @@ NSString *const PGPhotoLinearBlurShaderString = PGShaderString
[_blurFilter setInputSize:newSize atIndex:textureIndex]; [_blurFilter setInputSize:newSize atIndex:textureIndex];
[_radialFocusFilter setInputSize:newSize atIndex:textureIndex]; [_radialFocusFilter setInputSize:newSize atIndex:textureIndex];
[_linearFocusFilter setInputSize:newSize atIndex:textureIndex]; [_linearFocusFilter setInputSize:newSize atIndex:textureIndex];
[_maskedFilter setInputSize:newSize atIndex:textureIndex];
CGFloat aspectRatio = newSize.height / newSize.width; CGFloat aspectRatio = newSize.height / newSize.width;
[_radialFocusFilter setFloat:(float)aspectRatio forUniformName:@"aspectRatio"]; [_radialFocusFilter setFloat:(float)aspectRatio forUniformName:@"aspectRatio"];
@ -203,6 +230,7 @@ NSString *const PGPhotoLinearBlurShaderString = PGShaderString
[_blurFilter setInputRotation:newInputRotation atIndex:textureIndex]; [_blurFilter setInputRotation:newInputRotation atIndex:textureIndex];
[_radialFocusFilter setInputRotation:newInputRotation atIndex:textureIndex]; [_radialFocusFilter setInputRotation:newInputRotation atIndex:textureIndex];
[_linearFocusFilter setInputRotation:newInputRotation atIndex:textureIndex]; [_linearFocusFilter setInputRotation:newInputRotation atIndex:textureIndex];
[_maskedFilter setInputRotation:newInputRotation atIndex:textureIndex];
} }
- (CGSize)maximumOutputSize - (CGSize)maximumOutputSize

View File

@ -937,7 +937,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
{ {
if (editableItem.isVideo) { if (editableItem.isVideo) {
if ([editableItem isKindOfClass:[TGMediaAsset class]]) { if ([editableItem isKindOfClass:[TGMediaAsset class]]) {
return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem]; return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem allowNetworkAccess:true];
} else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) { } else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) {
return ((TGCameraCapturedVideo *)editableItem).avAsset; return ((TGCameraCapturedVideo *)editableItem).avAsset;
} else { } else {

View File

@ -921,6 +921,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
case PGCameraModeVideo: case PGCameraModeVideo:
case PGCameraModeSquareVideo: case PGCameraModeSquareVideo:
case PGCameraModeSquareSwing:
{ {
if (!_camera.isRecordingVideo) if (!_camera.isRecordingVideo)
{ {
@ -994,7 +995,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
}); });
}]; }];
} }
else if (cameraMode == PGCameraModeVideo) else if (cameraMode == PGCameraModeVideo || cameraMode == PGCameraModeSquareVideo || cameraMode == PGCameraModeSquareSwing)
{ {
if (!_camera.isRecordingVideo) if (!_camera.isRecordingVideo)
{ {
@ -1009,14 +1010,18 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
if (success) if (success)
{ {
TGCameraCapturedVideo *capturedVideo = [[TGCameraCapturedVideo alloc] initWithURL:outputURL]; TGCameraCapturedVideo *capturedVideo = [[TGCameraCapturedVideo alloc] initWithURL:outputURL];
if (strongSelf->_intent == TGCameraControllerAvatarIntent || strongSelf->_intent == TGCameraControllerSignupAvatarIntent)
{
[strongSelf presentPhotoResultControllerWithImage:capturedVideo metadata:nil completion:^{}];
} else {
[strongSelf addResultItem:capturedVideo]; [strongSelf addResultItem:capturedVideo];
if (![strongSelf maybePresentResultControllerForItem:capturedVideo completion:nil]) if (![strongSelf maybePresentResultControllerForItem:capturedVideo completion:nil])
{ {
strongSelf->_camera.disabled = false; strongSelf->_camera.disabled = false;
[strongSelf->_interfaceView setRecordingVideo:false animated:true]; [strongSelf->_interfaceView setRecordingVideo:false animated:true];
} }
} }
}
else else
{ {
[strongSelf->_interfaceView setRecordingVideo:false animated:false]; [strongSelf->_interfaceView setRecordingVideo:false animated:false];
@ -1655,16 +1660,30 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
#pragma mark - Legacy Photo Result #pragma mark - Legacy Photo Result
- (void)presentPhotoResultControllerWithImage:(UIImage *)image metadata:(PGCameraShotMetadata *)metadata completion:(void (^)(void))completion - (void)presentPhotoResultControllerWithImage:(id<TGMediaEditableItem>)input metadata:(PGCameraShotMetadata *)metadata completion:(void (^)(void))completion
{ {
[[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:false]; [[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:false];
if (image == nil || image.size.width < FLT_EPSILON) if (input == nil || ([input isKindOfClass:[UIImage class]] && ((UIImage *)input).size.width < FLT_EPSILON))
{ {
[self beginTransitionOutWithVelocity:0.0f]; [self beginTransitionOutWithVelocity:0.0f];
return; return;
} }
UIImage *image = nil;
if ([input isKindOfClass:[UIImage class]]) {
image = (UIImage *)input;
} else if ([input isKindOfClass:[TGCameraCapturedVideo class]]) {
AVAsset *asset = ((TGCameraCapturedVideo *)input).immediateAVAsset;
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generator.appliesPreferredTrackTransform = true;
generator.maximumSize = CGSizeMake(640.0f, 640.0f);
CGImageRef imageRef = [generator copyCGImageAtTime:kCMTimeZero actualTime:NULL error:NULL];
image = [[UIImage alloc] initWithCGImage:imageRef];
CGImageRelease(imageRef);
}
id<LegacyComponentsOverlayWindowManager> windowManager = nil; id<LegacyComponentsOverlayWindowManager> windowManager = nil;
id<LegacyComponentsContext> windowContext = nil; id<LegacyComponentsContext> windowContext = nil;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) { if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
@ -1679,16 +1698,11 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
_focusControl.ignoreAutofocusing = true; _focusControl.ignoreAutofocusing = true;
switch (_intent)
{
case TGCameraControllerAvatarIntent:
case TGCameraControllerSignupAvatarIntent:
{
TGPhotoEditorControllerIntent intent = TGPhotoEditorControllerAvatarIntent; TGPhotoEditorControllerIntent intent = TGPhotoEditorControllerAvatarIntent;
if (_intent == TGCameraControllerSignupAvatarIntent) { if (_intent == TGCameraControllerSignupAvatarIntent) {
intent = TGPhotoEditorControllerSignupAvatarIntent; intent = TGPhotoEditorControllerSignupAvatarIntent;
} }
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:windowContext item:image intent:(TGPhotoEditorControllerFromCameraIntent | intent) adjustments:nil caption:nil screenImage:image availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab]; TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:windowContext item:input intent:(TGPhotoEditorControllerFromCameraIntent | intent) adjustments:nil caption:nil screenImage:image availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
controller.stickersContext = _stickersContext; controller.stickersContext = _stickersContext;
__weak TGPhotoEditorController *weakController = controller; __weak TGPhotoEditorController *weakController = controller;
controller.beginTransitionIn = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView) controller.beginTransitionIn = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView)
@ -1738,7 +1752,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
if (strongSelf.finishedWithPhoto != nil) if (strongSelf.finishedWithPhoto != nil)
strongSelf.finishedWithPhoto(nil, resultImage, nil, nil, nil, nil); strongSelf.finishedWithPhoto(nil, resultImage, nil, nil, nil, nil);
if (strongSelf.shouldStoreCapturedAssets) if (strongSelf.shouldStoreCapturedAssets && [input isKindOfClass:[UIImage class]])
{ {
[strongSelf _savePhotoToCameraRollWithOriginalImage:image editedImage:[editorValues toolsApplied] ? resultImage : nil]; [strongSelf _savePhotoToCameraRollWithOriginalImage:image editedImage:[editorValues toolsApplied] ? resultImage : nil];
} }
@ -1766,7 +1780,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
{ {
if (editableItem.isVideo) { if (editableItem.isVideo) {
if ([editableItem isKindOfClass:[TGMediaAsset class]]) { if ([editableItem isKindOfClass:[TGMediaAsset class]]) {
return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem]; return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem allowNetworkAccess:true];
} else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) { } else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) {
return ((TGCameraCapturedVideo *)editableItem).avAsset; return ((TGCameraCapturedVideo *)editableItem).avAsset;
} else { } else {
@ -1778,79 +1792,6 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
}; };
overlayController = (TGOverlayController *)controller; overlayController = (TGOverlayController *)controller;
}
break;
default:
{
TGCameraPhotoPreviewController *controller = _shortcut ? [[TGCameraPhotoPreviewController alloc] initWithContext:windowContext image:image metadata:metadata recipientName:self.recipientName backButtonTitle:TGLocalized(@"Camera.Retake") doneButtonTitle:TGLocalized(@"Common.Next") saveCapturedMedia:_saveCapturedMedia saveEditedPhotos:_saveEditedPhotos] : [[TGCameraPhotoPreviewController alloc] initWithContext:windowContext image:image metadata:metadata recipientName:self.recipientName saveCapturedMedia:_saveCapturedMedia saveEditedPhotos:_saveEditedPhotos];
controller.allowCaptions = self.allowCaptions;
controller.shouldStoreAssets = self.shouldStoreCapturedAssets;
controller.suggestionContext = self.suggestionContext;
controller.hasTimer = self.hasTimer;
controller.hasSilentPosting = self.hasSilentPosting;
controller.hasSchedule = self.hasSchedule;
__weak TGCameraPhotoPreviewController *weakController = controller;
controller.beginTransitionIn = ^CGRect
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return CGRectZero;
strongSelf->_previewView.hidden = true;
return strongSelf->_previewView.frame;
};
controller.finishedTransitionIn = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf->_camera stopCaptureForPause:true completion:nil];
};
controller.beginTransitionOut = ^CGRect(CGRect referenceFrame)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return CGRectZero;
[strongSelf->_camera startCaptureForResume:true completion:nil];
return [strongSelf transitionBackFromResultControllerWithReferenceFrame:referenceFrame];
};
controller.retakePressed = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:true];
};
controller.sendPressed = ^(TGOverlayController *controller, UIImage *resultImage, NSString *caption, NSArray *entities, NSArray *stickers, NSNumber *timer)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (strongSelf.finishedWithPhoto != nil)
strongSelf.finishedWithPhoto(controller, resultImage, caption, entities, stickers, timer);
if (strongSelf->_shortcut)
return;
__strong TGOverlayController *strongController = weakController;
if (strongController != nil)
[strongSelf _dismissTransitionForResultController:strongController];
};
overlayController = controller;
}
break;
}
if (windowManager != nil) if (windowManager != nil)
{ {
@ -2375,6 +2316,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
case PGCameraModeSquarePhoto: case PGCameraModeSquarePhoto:
case PGCameraModeSquareVideo: case PGCameraModeSquareVideo:
case PGCameraModeSquareSwing:
{ {
CGRect rect = [self _cameraPreviewFrameForScreenSize:screenSize mode:PGCameraModePhoto]; CGRect rect = [self _cameraPreviewFrameForScreenSize:screenSize mode:PGCameraModePhoto];
CGFloat topOffset = CGRectGetMidY(rect) - rect.size.width / 2; CGFloat topOffset = CGRectGetMidY(rect) - rect.size.width / 2;
@ -2406,7 +2348,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
} }
else else
{ {
if (mode == PGCameraModeSquarePhoto || mode == PGCameraModeSquareVideo) if (mode == PGCameraModeSquarePhoto || mode == PGCameraModeSquareVideo || mode == PGCameraModeSquareSwing)
return CGRectMake(0, (screenSize.height - screenSize.width) / 2, screenSize.width, screenSize.width); return CGRectMake(0, (screenSize.height - screenSize.width) / 2, screenSize.width, screenSize.width);
return CGRectMake(0, 0, screenSize.width, screenSize.height); return CGRectMake(0, 0, screenSize.width, screenSize.height);

View File

@ -50,17 +50,17 @@
break; break;
case PGCameraModeVideo: case PGCameraModeVideo:
case PGCameraModeSquareVideo:
{ {
[_shutterButton setButtonMode:TGCameraShutterButtonVideoMode animated:true]; [_shutterButton setButtonMode:TGCameraShutterButtonVideoMode animated:true];
[_timecodeView setHidden:false animated:true]; [_timecodeView setHidden:false animated:true];
} }
break; break;
case PGCameraModeSquareVideo: case PGCameraModeSquareSwing:
{ {
[_shutterButton setButtonMode:TGCameraShutterButtonVideoMode animated:true]; [_shutterButton setButtonMode:TGCameraShutterButtonVideoMode animated:true];
[_timecodeView setHidden:true animated:true]; [_timecodeView setHidden:true animated:true];
} }
break; break;

View File

@ -9,7 +9,6 @@ const CGFloat TGCameraModeControlVerticalInteritemSpace = 29.0f;
@interface TGCameraModeControl () @interface TGCameraModeControl ()
{ {
UIImageView *_dotView;
UIControl *_wrapperView; UIControl *_wrapperView;
CGFloat _kerning; CGFloat _kerning;
@ -27,24 +26,6 @@ const CGFloat TGCameraModeControlVerticalInteritemSpace = 29.0f;
self = [super initWithFrame:frame]; self = [super initWithFrame:frame];
if (self != nil) if (self != nil)
{ {
static UIImage *dotImage = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(6, 6), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [TGCameraInterfaceAssets accentColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0, 0, 6, 6));
dotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
_dotView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 6, 6)];
_dotView.image = dotImage;
//[self addSubview:_dotView];
if (frame.size.width > frame.size.height) if (frame.size.width > frame.size.height)
_kerning = 3.5f; _kerning = 3.5f;
else else
@ -64,7 +45,8 @@ const CGFloat TGCameraModeControlVerticalInteritemSpace = 29.0f;
_buttons = @ _buttons = @
[ [
[self _createButtonForMode:PGCameraModeSquareVideo title:TGLocalized(@"Camera.VideoMode")], [self _createButtonForMode:PGCameraModeSquareVideo title:TGLocalized(@"Camera.VideoMode")],
[self _createButtonForMode:PGCameraModePhoto title:TGLocalized(@"Camera.PhotoMode")] [self _createButtonForMode:PGCameraModePhoto title:TGLocalized(@"Camera.PhotoMode")],
[self _createButtonForMode:PGCameraModeSquareSwing title:@"SWING"]
]; ];
} else { } else {
_buttons = @ _buttons = @
@ -119,7 +101,6 @@ const CGFloat TGCameraModeControlVerticalInteritemSpace = 29.0f;
+ (CGFloat)_buttonHorizontalSpacing + (CGFloat)_buttonHorizontalSpacing
{ {
//return 22;
return 19; return 19;
} }
@ -282,14 +263,7 @@ const CGFloat TGCameraModeControlVerticalInteritemSpace = 29.0f;
- (void)layoutSubviews - (void)layoutSubviews
{ {
if (self.frame.size.width > self.frame.size.height) if (self.frame.size.width > self.frame.size.height)
{
_dotView.frame = CGRectMake((self.frame.size.width - _dotView.frame.size.width) / 2, self.frame.size.height / 2 - 12, _dotView.frame.size.width, _dotView.frame.size.height);
_maskLayer.frame = CGRectMake(0, 0, _maskView.frame.size.width, _maskView.frame.size.height); _maskLayer.frame = CGRectMake(0, 0, _maskView.frame.size.width, _maskView.frame.size.height);
}
else
{
_dotView.frame = CGRectMake(13, (self.frame.size.height - _dotView.frame.size.height) / 2, _dotView.frame.size.width, _dotView.frame.size.height);
}
} }
@end @end

View File

@ -1374,7 +1374,7 @@
@implementation TGMediaAssetsPallete @implementation TGMediaAssetsPallete
+ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage maybeAccentColor:(UIColor *)maybeAccentColor + (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage doneIconImage:(UIImage *)doneIconImage maybeAccentColor:(UIColor *)maybeAccentColor
{ {
TGMediaAssetsPallete *pallete = [[TGMediaAssetsPallete alloc] init]; TGMediaAssetsPallete *pallete = [[TGMediaAssetsPallete alloc] init];
pallete->_isDark = dark; pallete->_isDark = dark;
@ -1390,6 +1390,7 @@
pallete->_badge = badge; pallete->_badge = badge;
pallete->_badgeTextColor = badgeTextColor; pallete->_badgeTextColor = badgeTextColor;
pallete->_sendIconImage = sendIconImage; pallete->_sendIconImage = sendIconImage;
pallete->_doneIconImage = doneIconImage;
pallete->_maybeAccentColor = maybeAccentColor; pallete->_maybeAccentColor = maybeAccentColor;
return pallete; return pallete;
} }

View File

@ -441,7 +441,7 @@
{ {
if (editableItem.isVideo) { if (editableItem.isVideo) {
if ([editableItem isKindOfClass:[TGMediaAsset class]]) { if ([editableItem isKindOfClass:[TGMediaAsset class]]) {
return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem]; return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem allowNetworkAccess:true];
} else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) { } else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) {
return ((TGCameraCapturedVideo *)editableItem).avAsset; return ((TGCameraCapturedVideo *)editableItem).avAsset;
} else { } else {

View File

@ -94,6 +94,7 @@
{ {
}]; }];
TGPhotoEditorController *controller = _controller;
void (^imageReady)(void) = self.imageReady; void (^imageReady)(void) = self.imageReady;
_toTransitionView = [[TGImageView alloc] initWithFrame:fromTransitionFrame]; _toTransitionView = [[TGImageView alloc] initWithFrame:fromTransitionFrame];
[_toTransitionView setSignal:[[[self.referenceScreenImageSignal() deliverOn:[SQueue mainQueue]] filter:^bool(id result) [_toTransitionView setSignal:[[[self.referenceScreenImageSignal() deliverOn:[SQueue mainQueue]] filter:^bool(id result)
@ -101,7 +102,7 @@
return [result isKindOfClass:[UIImage class]]; return [result isKindOfClass:[UIImage class]];
}] onNext:^(UIImage *next) }] onNext:^(UIImage *next)
{ {
[_controller _setScreenImage:next]; [controller _setScreenImage:next];
if (imageReady != nil) if (imageReady != nil)
imageReady(); imageReady();
}]]; }]];

View File

@ -577,7 +577,7 @@
{ {
if (editableItem.isVideo) { if (editableItem.isVideo) {
if ([editableItem isKindOfClass:[TGMediaAsset class]]) { if ([editableItem isKindOfClass:[TGMediaAsset class]]) {
return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem]; return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem allowNetworkAccess:true];
} else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) { } else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) {
return ((TGCameraCapturedVideo *)editableItem).avAsset; return ((TGCameraCapturedVideo *)editableItem).avAsset;
} else { } else {

View File

@ -15,11 +15,12 @@
@property (nonatomic, assign) NSTimeInterval trimStartValue; @property (nonatomic, assign) NSTimeInterval trimStartValue;
@property (nonatomic, assign) NSTimeInterval trimEndValue; @property (nonatomic, assign) NSTimeInterval trimEndValue;
@property (nonatomic, assign) NSTimeInterval dotValue; @property (nonatomic, assign) bool hasDotPicker;
- (void)setDotVideoView:(UIView *)dotVideoView;
- (void)setDotImage:(UIImage *)dotImage;
@property (nonatomic, assign) NSTimeInterval maximumLength; @property (nonatomic, assign) NSTimeInterval maximumLength;
@property (nonatomic, assign) bool disableZoom; @property (nonatomic, assign) bool disableZoom;
@property (nonatomic, assign) bool disableTimeDisplay; @property (nonatomic, assign) bool disableTimeDisplay;

View File

@ -39,7 +39,10 @@ typedef enum
UIView *_rightCurtainView; UIView *_rightCurtainView;
UIControl *_scrubberHandle; UIControl *_scrubberHandle;
UIImageView *_dotView; UIControl *_dotHandle;
UIImageView *_dotImageView;
__weak UIView *_dotVideoView;
UIImageView *_dotFrameView;
UIPanGestureRecognizer *_panGestureRecognizer; UIPanGestureRecognizer *_panGestureRecognizer;
UILongPressGestureRecognizer *_pressGestureRecognizer; UILongPressGestureRecognizer *_pressGestureRecognizer;
@ -126,11 +129,6 @@ typedef enum
_rightCurtainView.backgroundColor = [[TGPhotoEditorInterfaceAssets toolbarBackgroundColor] colorWithAlphaComponent:0.8f]; _rightCurtainView.backgroundColor = [[TGPhotoEditorInterfaceAssets toolbarBackgroundColor] colorWithAlphaComponent:0.8f];
[_wrapperView addSubview:_rightCurtainView]; [_wrapperView addSubview:_rightCurtainView];
_dotView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 8, 8)];
_dotView.image = TGCircleImage(8.0, [TGPhotoEditorInterfaceAssets accentColor]);
_dotView.hidden = true;
[self addSubview:_dotView];
__weak TGMediaPickerGalleryVideoScrubber *weakSelf = self; __weak TGMediaPickerGalleryVideoScrubber *weakSelf = self;
_trimView = [[TGMediaPickerGalleryVideoTrimView alloc] initWithFrame:CGRectZero]; _trimView = [[TGMediaPickerGalleryVideoTrimView alloc] initWithFrame:CGRectZero];
_trimView.exclusiveTouch = true; _trimView.exclusiveTouch = true;
@ -158,7 +156,11 @@ typedef enum
[strongSelf->_trimView setTrimming:true animated:true]; [strongSelf->_trimView setTrimming:true animated:true];
if (strongSelf->_hasDotPicker) {
[strongSelf setDotHandleHidden:true animated:false];
} else {
[strongSelf setScrubberHandleHidden:true animated:false]; [strongSelf setScrubberHandleHidden:true animated:false];
}
}; };
_trimView.didEndEditing = ^ _trimView.didEndEditing = ^
{ {
@ -206,7 +208,11 @@ typedef enum
[strongSelf->_trimView setTrimming:isTrimmed animated:true]; [strongSelf->_trimView setTrimming:isTrimmed animated:true];
if (strongSelf->_hasDotPicker) {
[strongSelf setDotHandleHidden:false animated:true];
} else {
[strongSelf setScrubberHandleHidden:false animated:true]; [strongSelf setScrubberHandleHidden:false animated:true];
}
[strongSelf cancelZoomIn]; [strongSelf cancelZoomIn];
if (strongSelf->_zoomedIn) if (strongSelf->_zoomedIn)
@ -260,8 +266,13 @@ typedef enum
strongSelf->_trimStartValue = trimStartPosition; strongSelf->_trimStartValue = trimStartPosition;
strongSelf->_trimEndValue = trimEndPosition; strongSelf->_trimEndValue = trimEndPosition;
[strongSelf setValue:strongSelf->_trimStartValue]; if (strongSelf->_hasDotPicker) {
if (strongSelf->_value < trimStartPosition) {
strongSelf->_value = trimStartPosition;
}
} else {
[strongSelf setValue:trimStartPosition];
}
UIView *handle = strongSelf->_scrubberHandle; UIView *handle = strongSelf->_scrubberHandle;
handle.center = CGPointMake(trimView.frame.origin.x + 12 + handle.frame.size.width / 2, handle.center.y); handle.center = CGPointMake(trimView.frame.origin.x + 12 + handle.frame.size.width / 2, handle.center.y);
@ -323,7 +334,13 @@ typedef enum
strongSelf->_trimStartValue = trimStartPosition; strongSelf->_trimStartValue = trimStartPosition;
strongSelf->_trimEndValue = trimEndPosition; strongSelf->_trimEndValue = trimEndPosition;
[strongSelf setValue:strongSelf->_trimEndValue]; if (strongSelf->_hasDotPicker) {
if (strongSelf->_value > trimEndPosition) {
strongSelf->_value = trimEndPosition;
}
} else {
[strongSelf setValue:trimEndPosition];
}
UIView *handle = strongSelf->_scrubberHandle; UIView *handle = strongSelf->_scrubberHandle;
handle.center = CGPointMake(CGRectGetMaxX(trimView.frame) - 12 - handle.frame.size.width / 2, handle.center.y); handle.center = CGPointMake(CGRectGetMaxX(trimView.frame) - 12 - handle.frame.size.width / 2, handle.center.y);
@ -341,13 +358,44 @@ typedef enum
}; };
[_wrapperView addSubview:_trimView]; [_wrapperView addSubview:_trimView];
_dotHandle = [[UIControl alloc] initWithFrame:CGRectMake(0, -4.0f, 26.0f, 44.0f)];
_dotHandle.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -12, -5, -12);
_dotHandle.hidden = true;
[_wrapperView addSubview:_dotHandle];
static UIImage *dotFrameImage = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(_dotHandle.frame.size.width, _dotHandle.frame.size.height), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextSetLineWidth(context, 3.0);
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(1.5f, 1.5f, _dotHandle.frame.size.width - 3.0, _dotHandle.frame.size.height - 3.0f) cornerRadius:4.0f];
CGContextAddPath(context, path.CGPath);
CGContextStrokePath(context);
dotFrameImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
_dotImageView = [[UIImageView alloc] initWithFrame:CGRectInset(_dotHandle.bounds, 2.0, 2.0)];
_dotImageView.clipsToBounds = true;
_dotImageView.contentMode = UIViewContentModeScaleAspectFill;
[_dotHandle addSubview:_dotImageView];
_dotFrameView = [[UIImageView alloc] initWithFrame:_dotHandle.bounds];
_dotFrameView.image = dotFrameImage;
[_dotHandle addSubview:_dotFrameView];
_scrubberHandle = [[UIControl alloc] initWithFrame:CGRectMake(0, -4.0f, 5.0f, 44.0f)]; _scrubberHandle = [[UIControl alloc] initWithFrame:CGRectMake(0, -4.0f, 5.0f, 44.0f)];
_scrubberHandle.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -12, -5, -12); _scrubberHandle.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -12, -5, -12);
[_wrapperView addSubview:_scrubberHandle]; [_wrapperView addSubview:_scrubberHandle];
static UIImage *handleViewImage = nil; static UIImage *handleViewImage = nil;
static dispatch_once_t onceToken; static dispatch_once_t onceToken2;
dispatch_once(&onceToken, ^ dispatch_once(&onceToken2, ^
{ {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(_scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height), false, 0.0f); UIGraphicsBeginImageContextWithOptions(CGSizeMake(_scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext(); CGContextRef context = UIGraphicsGetCurrentContext();
@ -373,6 +421,7 @@ typedef enum
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
_panGestureRecognizer.delegate = self; _panGestureRecognizer.delegate = self;
[_scrubberHandle addGestureRecognizer:_panGestureRecognizer]; [_scrubberHandle addGestureRecognizer:_panGestureRecognizer];
[_dotHandle addGestureRecognizer:_panGestureRecognizer];
_arrowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"PhotoPickerArrow")]; _arrowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"PhotoPickerArrow")];
_arrowView.alpha = 0.45f; _arrowView.alpha = 0.45f;
@ -400,6 +449,22 @@ typedef enum
[self _layoutRecipientLabel]; [self _layoutRecipientLabel];
} }
- (void)setHasDotPicker:(bool)hasDotPicker {
_hasDotPicker = hasDotPicker;
_dotHandle.hidden = !hasDotPicker;
_scrubberHandle.hidden = true;
}
- (void)setDotVideoView:(UIView *)dotVideoView {
_dotVideoView = dotVideoView;
_dotVideoView.frame = CGRectInset(_dotHandle.bounds, 2.0, 2.0);
[_dotHandle insertSubview:dotVideoView belowSubview:_dotFrameView];
}
- (void)setDotImage:(UIImage *)dotImage {
_dotImageView.image = dotImage;
}
- (bool)zoomAvailable - (bool)zoomAvailable
{ {
if (_disableZoom || _zoomedIn || _preparingToZoomIn || _summaryTimestamps.count == 0) if (_disableZoom || _zoomedIn || _preparingToZoomIn || _summaryTimestamps.count == 0)
@ -886,13 +951,18 @@ typedef enum
CGPoint point = [self _scrubberPositionForPosition:_value duration:_duration zoomedIn:zoomedIn]; CGPoint point = [self _scrubberPositionForPosition:_value duration:_duration zoomedIn:zoomedIn];
CGRect frame = CGRectMake(CGFloor(point.x) - _scrubberHandle.frame.size.width / 2, _scrubberHandle.frame.origin.y, _scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height); CGRect frame = CGRectMake(CGFloor(point.x) - _scrubberHandle.frame.size.width / 2, _scrubberHandle.frame.origin.y, _scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height);
CGPoint dotPoint = [self _dotPositionForPosition:_value duration:_duration];
CGRect dotFrame = CGRectMake(CGFloor(dotPoint.x) - _dotHandle.frame.size.width / 2, _dotHandle.frame.origin.y, _dotHandle.frame.size.width, _dotHandle.frame.size.height);
if (_trimStartValue > DBL_EPSILON && fabs(_value - _trimStartValue) < 0.01) if (_trimStartValue > DBL_EPSILON && fabs(_value - _trimStartValue) < 0.01)
{ {
frame = CGRectMake(_trimView.frame.origin.x + [self _scrubbingRectZoomedIn:zoomedIn].origin.x, _scrubberHandle.frame.origin.y, _scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height); frame = CGRectMake(_trimView.frame.origin.x + [self _scrubbingRectZoomedIn:zoomedIn].origin.x, _scrubberHandle.frame.origin.y, _scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height);
dotFrame = CGRectMake(_trimView.frame.origin.x + [self _scrubbingRectZoomedIn:false].origin.x, _dotHandle.frame.origin.y, _dotHandle.frame.size.width, _dotHandle.frame.size.height);
} }
else if (fabs(_value - _trimEndValue) < 0.01) else if (fabs(_value - _trimEndValue) < 0.01)
{ {
frame = CGRectMake(_trimView.frame.origin.x + _trimView.frame.size.width - [self _scrubbingRectZoomedIn:zoomedIn].origin.x - _scrubberHandle.frame.size.width, _scrubberHandle.frame.origin.y, _scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height); frame = CGRectMake(_trimView.frame.origin.x + _trimView.frame.size.width - [self _scrubbingRectZoomedIn:zoomedIn].origin.x - _scrubberHandle.frame.size.width, _scrubberHandle.frame.origin.y, _scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height);
dotFrame = CGRectMake(_trimView.frame.origin.x + _trimView.frame.size.width - [self _scrubbingRectZoomedIn:false].origin.x - _dotHandle.frame.size.width, _dotHandle.frame.origin.y, _dotHandle.frame.size.width, _dotHandle.frame.size.height);
} }
if (_isPlaying) if (_isPlaying)
@ -921,6 +991,8 @@ typedef enum
[self removeHandleAnimation]; [self removeHandleAnimation];
_scrubberHandle.frame = frame; _scrubberHandle.frame = frame;
} }
_dotHandle.frame = dotFrame;
} }
- (void)addHandleAnimationFromFrame:(CGRect)fromFrame toFrame:(CGRect)toFrame duration:(NSTimeInterval)duration - (void)addHandleAnimationFromFrame:(CGRect)fromFrame toFrame:(CGRect)toFrame duration:(NSTimeInterval)duration
@ -1030,6 +1102,8 @@ typedef enum
CGPoint translation = [gestureRecognizer translationInView:self]; CGPoint translation = [gestureRecognizer translationInView:self];
[gestureRecognizer setTranslation:CGPointZero inView:self]; [gestureRecognizer setTranslation:CGPointZero inView:self];
UIView *handle = gestureRecognizer.view;
switch (gestureRecognizer.state) switch (gestureRecognizer.state)
{ {
case UIGestureRecognizerStateBegan: case UIGestureRecognizerStateBegan:
@ -1059,23 +1133,23 @@ typedef enum
CGRect scrubbingRect = [self _scrubbingRect]; CGRect scrubbingRect = [self _scrubbingRect];
CGRect normalScrubbingRect = [self _scrubbingRectZoomedIn:false]; CGRect normalScrubbingRect = [self _scrubbingRectZoomedIn:false];
CGFloat minPosition = scrubbingRect.origin.x + _scrubberHandle.frame.size.width / 2; CGFloat minPosition = scrubbingRect.origin.x + handle.frame.size.width / 2;
CGFloat maxPosition = scrubbingRect.origin.x + scrubbingRect.size.width - _scrubberHandle.frame.size.width / 2; CGFloat maxPosition = scrubbingRect.origin.x + scrubbingRect.size.width - handle.frame.size.width / 2;
if (self.allowsTrimming) if (self.allowsTrimming)
{ {
minPosition = MAX(minPosition, _trimView.frame.origin.x + normalScrubbingRect.origin.x + _scrubberHandle.frame.size.width / 2); minPosition = MAX(minPosition, _trimView.frame.origin.x + normalScrubbingRect.origin.x + handle.frame.size.width / 2);
maxPosition = MIN(maxPosition, CGRectGetMaxX(_trimView.frame) - normalScrubbingRect.origin.x - _scrubberHandle.frame.size.width / 2); maxPosition = MIN(maxPosition, CGRectGetMaxX(_trimView.frame) - normalScrubbingRect.origin.x - handle.frame.size.width / 2);
} }
_scrubberHandle.center = CGPointMake(MIN(MAX(_scrubberHandle.center.x + translation.x, minPosition), maxPosition), _scrubberHandle.center.y); handle.center = CGPointMake(MIN(MAX(handle.center.x + translation.x, minPosition), maxPosition), handle.center.y);
NSTimeInterval position = [self _positionForScrubberPosition:_scrubberHandle.center duration:_duration]; NSTimeInterval position = [self _positionForScrubberPosition:handle.center duration:_duration];
if (self.allowsTrimming) if (self.allowsTrimming)
{ {
if (ABS(_scrubberHandle.center.x - minPosition) < FLT_EPSILON) if (ABS(handle.center.x - minPosition) < FLT_EPSILON)
position = _trimStartValue; position = _trimStartValue;
else if (ABS(_scrubberHandle.center.x - maxPosition) < FLT_EPSILON) else if (ABS(handle.center.x - maxPosition) < FLT_EPSILON)
position = _trimEndValue; position = _trimEndValue;
} }
@ -1105,8 +1179,6 @@ typedef enum
_scrubbing = false; _scrubbing = false;
[self setDotValue:_value];
id<TGMediaPickerGalleryVideoScrubberDelegate> delegate = self.delegate; id<TGMediaPickerGalleryVideoScrubberDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(videoScrubberDidEndScrubbing:)]) if ([delegate respondsToSelector:@selector(videoScrubberDidEndScrubbing:)])
[delegate videoScrubberDidEndScrubbing:self]; [delegate videoScrubberDidEndScrubbing:self];
@ -1145,6 +1217,27 @@ typedef enum
} }
} }
- (void)setDotHandleHidden:(bool)hidden animated:(bool)animated
{
if (animated)
{
_dotHandle.hidden = false;
[UIView animateWithDuration:0.25f animations:^
{
_dotHandle.alpha = hidden ? 0.0f : 1.0f;
} completion:^(BOOL finished)
{
if (finished)
_dotHandle.hidden = hidden;
}];
}
else
{
_dotHandle.hidden = hidden;
_dotHandle.alpha = hidden ? 0.0f : 1.0f;
}
}
- (CGPoint)_scrubberPositionForPosition:(NSTimeInterval)position duration:(NSTimeInterval)duration - (CGPoint)_scrubberPositionForPosition:(NSTimeInterval)position duration:(NSTimeInterval)duration
{ {
return [self _scrubberPositionForPosition:position duration:duration zoomedIn:_zoomedIn]; return [self _scrubberPositionForPosition:position duration:duration zoomedIn:_zoomedIn];
@ -1202,25 +1295,17 @@ typedef enum
#pragma mark - Dot #pragma mark - Dot
- (void)setDotValue:(NSTimeInterval)dotValue - (CGPoint)_dotPositionForPosition:(NSTimeInterval)position duration:(NSTimeInterval)duration
{ {
_dotValue = dotValue; CGRect scrubbingRect = [self _scrubbingRectZoomedIn:false];
if (dotValue > FLT_EPSILON) { if (duration < FLT_EPSILON)
_dotView.hidden = false; {
position = 0.0;
CGPoint point = [self _scrubberPositionForPosition:dotValue duration:_duration zoomedIn:false]; duration = 1.0;
_dotView.frame = CGRectMake(_wrapperView.frame.origin.x + point.x - _dotView.frame.size.width / 2.0, 8.0f, _dotView.frame.size.width, _dotView.frame.size.height);
_dotView.alpha = 0.0f;
_dotView.transform = CGAffineTransformMakeScale(0.25, 0.25);
[UIView animateWithDuration:0.2 animations:^{
_dotView.alpha = 1.0;
_dotView.transform = CGAffineTransformIdentity;
}];
} else {
_dotView.hidden = true;
} }
return CGPointMake(_dotHandle.frame.size.width / 2 + scrubbingRect.origin.x + (CGFloat)(position / duration) * (scrubbingRect.size.width - _dotHandle.frame.size.width), CGRectGetMidY([self _scrubbingRect]));
} }
#pragma mark - Trimming #pragma mark - Trimming

View File

@ -125,7 +125,7 @@
CGSize dimensions = [avAsset tracksWithMediaType:AVMediaTypeVideo].firstObject.naturalSize; CGSize dimensions = [avAsset tracksWithMediaType:AVMediaTypeVideo].firstObject.naturalSize;
TGMediaVideoConversionPreset preset = adjustments.sendAsGif ? TGMediaVideoConversionPresetAnimation : [self presetFromAdjustments:adjustments]; TGMediaVideoConversionPreset preset = adjustments.sendAsGif ? TGMediaVideoConversionPresetAnimation : [self presetFromAdjustments:adjustments];
if (!CGSizeEqualToSize(dimensions, CGSizeZero) && preset != TGMediaVideoConversionPresetAnimation && preset != TGMediaVideoConversionPresetVideoMessage) if (!CGSizeEqualToSize(dimensions, CGSizeZero) && preset != TGMediaVideoConversionPresetAnimation && preset != TGMediaVideoConversionPresetVideoMessage && preset != TGMediaVideoConversionPresetProfile)
{ {
TGMediaVideoConversionPreset bestPreset = [self bestAvailablePresetForDimensions:dimensions]; TGMediaVideoConversionPreset bestPreset = [self bestAvailablePresetForDimensions:dimensions];
if (preset > bestPreset) if (preset > bestPreset)
@ -344,8 +344,6 @@
if (TGOrientationIsSideward(adjustments.cropOrientation, NULL)) if (TGOrientationIsSideward(adjustments.cropOrientation, NULL))
outputDimensions = CGSizeMake(outputDimensions.height, outputDimensions.width); outputDimensions = CGSizeMake(outputDimensions.height, outputDimensions.width);
CMTimeRange instructionTimeRange = CMTimeRangeMake(kCMTimeZero, timeRange.duration);
AVMutableCompositionTrack *compositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; AVMutableCompositionTrack *compositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
if (adjustments.videoStartValue > 0.0 && adjustments.videoStartValue > adjustments.trimStartValue) { if (adjustments.videoStartValue > 0.0 && adjustments.videoStartValue > adjustments.trimStartValue) {
NSTimeInterval trimEndValue = adjustments.trimEndValue > adjustments.trimStartValue ? adjustments.trimEndValue : CMTimeGetSeconds(videoTrack.timeRange.duration); NSTimeInterval trimEndValue = adjustments.trimEndValue > adjustments.trimStartValue ? adjustments.trimEndValue : CMTimeGetSeconds(videoTrack.timeRange.duration);
@ -1340,7 +1338,7 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer,
return 300; return 300;
case TGMediaVideoConversionPresetProfile: case TGMediaVideoConversionPresetProfile:
return 1000; return 1800;
default: default:
return 900; return 900;

View File

@ -172,8 +172,11 @@
if ([_manager managesWindow]) { if ([_manager managesWindow]) {
_contentController = contentController; _contentController = contentController;
__weak TGOverlayControllerWindow *weakSelf = self; __weak TGOverlayControllerWindow *weakSelf = self;
__weak TGViewController *weakParentController = parentController;
contentController.customDismissBlock = ^{ contentController.customDismissBlock = ^{
__strong TGOverlayControllerWindow *strongSelf = weakSelf; __strong TGOverlayControllerWindow *strongSelf = weakSelf;
__strong TGViewController *strongParentController = weakParentController;
[strongParentController.associatedWindowStack removeObject:strongSelf];
[manager setHidden:true window:strongSelf]; [manager setHidden:true window:strongSelf];
}; };
[_manager bindController:contentController]; [_manager bindController:contentController];

View File

@ -277,6 +277,8 @@ const CGFloat TGPhotoAvatarCropButtonsWrapperSize = 61.0f;
{ {
[_cropView hideImageForCustomTransition]; [_cropView hideImageForCustomTransition];
[_cropView animateTransitionOutSwitching:false]; [_cropView animateTransitionOutSwitching:false];
[_cropView invalidateVideoView];
[UIView animateWithDuration:0.3f animations:^ [UIView animateWithDuration:0.3f animations:^
{ {
_buttonsWrapperView.alpha = 0.0f; _buttonsWrapperView.alpha = 0.0f;

View File

@ -14,6 +14,7 @@
#import "TGPhotoEditorSparseView.h" #import "TGPhotoEditorSparseView.h"
#import "TGMediaPickerGalleryVideoScrubber.h" #import "TGMediaPickerGalleryVideoScrubber.h"
#import "TGModernGalleryVideoView.h"
const CGFloat TGPhotoAvatarPreviewPanelSize = 96.0f; const CGFloat TGPhotoAvatarPreviewPanelSize = 96.0f;
const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanelSize + 40.0f; const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanelSize + 40.0f;
@ -36,6 +37,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
CGFloat _currentDiameter; CGFloat _currentDiameter;
TGMediaPickerGalleryVideoScrubber *_scrubberView; TGMediaPickerGalleryVideoScrubber *_scrubberView;
TGModernGalleryVideoView *_dotVideoView;
UILabel *_coverLabel; UILabel *_coverLabel;
bool _wasPlayingBeforeScrubbing; bool _wasPlayingBeforeScrubbing;
bool _requestingThumbnails; bool _requestingThumbnails;
@ -139,11 +141,13 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
{ {
[super viewDidLoad]; [super viewDidLoad];
_scrubberView.allowsTrimming = true; _scrubberView.allowsTrimming = self.item.originalDuration >= TGVideoEditMinimumTrimmableDuration;
_scrubberView.hasDotPicker = true;
_scrubberView.disableZoom = true; _scrubberView.disableZoom = true;
_scrubberView.disableTimeDisplay = true; _scrubberView.disableTimeDisplay = true;
_scrubberView.trimStartValue = 0.0; _scrubberView.trimStartValue = 0.0;
_scrubberView.trimEndValue = self.item.originalDuration; _scrubberView.trimEndValue = MIN(10.0, self.item.originalDuration);
_scrubberView.maximumLength = 10.0;
[_scrubberView reloadData]; [_scrubberView reloadData];
[_scrubberView resetToStart]; [_scrubberView resetToStart];
} }
@ -169,11 +173,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_landscapeToolsWrapperView.alpha = 1.0f; _landscapeToolsWrapperView.alpha = 1.0f;
}]; }];
UIInterfaceOrientation orientation = self.interfaceOrientation; switch (self.effectiveOrientation)
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
orientation = UIInterfaceOrientationPortrait;
switch (orientation)
{ {
case UIInterfaceOrientationLandscapeLeft: case UIInterfaceOrientationLandscapeLeft:
{ {
@ -217,11 +217,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
[_videoAreaView.superview bringSubviewToFront:_videoAreaView]; [_videoAreaView.superview bringSubviewToFront:_videoAreaView];
UIInterfaceOrientation orientation = self.interfaceOrientation; switch (self.effectiveOrientation)
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
orientation = UIInterfaceOrientationPortrait;
switch (orientation)
{ {
case UIInterfaceOrientationLandscapeLeft: case UIInterfaceOrientationLandscapeLeft:
{ {
@ -383,11 +379,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
- (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation - (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation
{ {
bool hasOnScreenNavigation = false; CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:self.hasOnScreenNavigation];
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON;
CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:hasOnScreenNavigation];
CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size); CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size);
CGRect sourceFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); CGRect sourceFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
@ -397,16 +389,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
- (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame - (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame
{ {
CGSize referenceSize = [self referenceViewSize]; CGSize referenceSize = [self referenceViewSize];
UIInterfaceOrientation orientation = self.interfaceOrientation; CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:0 hasOnScreenNavigation:self.hasOnScreenNavigation];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
orientation = UIInterfaceOrientationPortrait;
bool hasOnScreenNavigation = false;
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON;
CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:hasOnScreenNavigation];
CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size); CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size);
CGRect toFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); CGRect toFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
@ -441,11 +424,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
CGFloat panelToolbarPortraitSize = panelSize + TGPhotoEditorToolbarSize; CGFloat panelToolbarPortraitSize = panelSize + TGPhotoEditorToolbarSize;
CGFloat panelToolbarLandscapeSize = panelSize + TGPhotoEditorToolbarSize; CGFloat panelToolbarLandscapeSize = panelSize + TGPhotoEditorToolbarSize;
bool hasOnScreenNavigation = false; UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:self.hasOnScreenNavigation];
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON;
UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation];
UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2); UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2);
screenEdges.top += safeAreaInset.top; screenEdges.top += safeAreaInset.top;
screenEdges.left += safeAreaInset.left; screenEdges.left += safeAreaInset.left;
@ -517,10 +496,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
- (void)updatePreviewView - (void)updatePreviewView
{ {
UIInterfaceOrientation orientation = self.interfaceOrientation;
if ([self inFormSheet] || TGIsPad())
orientation = UIInterfaceOrientationPortrait;
CGSize referenceSize = [self referenceViewSize]; CGSize referenceSize = [self referenceViewSize];
PGPhotoEditor *photoEditor = self.photoEditor; PGPhotoEditor *photoEditor = self.photoEditor;
@ -529,11 +504,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
if (_dismissing || previewView.superview != self.view) if (_dismissing || previewView.superview != self.view)
return; return;
bool hasOnScreenNavigation = false; CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:0 hasOnScreenNavigation:self.hasOnScreenNavigation];
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON;
CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:hasOnScreenNavigation];
CGSize fittedSize = TGScaleToSize(photoEditor.rotatedCropSize, containerFrame.size); CGSize fittedSize = TGScaleToSize(photoEditor.rotatedCropSize, containerFrame.size);
previewView.frame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); previewView.frame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
@ -588,12 +559,12 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
- (TGPhotoEditorTab)availableTabs - (TGPhotoEditorTab)availableTabs
{ {
return TGPhotoEditorPaintTab | TGPhotoEditorToolsTab; return TGPhotoEditorCropTab | TGPhotoEditorPaintTab | TGPhotoEditorToolsTab;
} }
- (TGPhotoEditorTab)activeTab - (TGPhotoEditorTab)activeTab
{ {
return TGPhotoEditorCropTab; return TGPhotoEditorNoneTab;
} }
- (TGPhotoEditorTab)highlightedTabs - (TGPhotoEditorTab)highlightedTabs
@ -681,7 +652,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_wasPlayingBeforeScrubbing = true; _wasPlayingBeforeScrubbing = true;
self.controlVideoPlayback(false); self.controlVideoPlayback(false);
_scrubberView.dotValue = 0.0; _dotVideoView.hidden = false;
_coverLabel.alpha = 1.0f; _coverLabel.alpha = 1.0f;
@ -694,6 +665,19 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
- (void)videoScrubberDidEndScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber - (void)videoScrubberDidEndScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{ {
AVPlayer *player = ((TGPhotoEditorController *)self.parentViewController).player;
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:player.currentItem.asset];
generator.appliesPreferredTrackTransform = true;
generator.maximumSize = CGSizeMake(128.0f, 128.0f);
generator.requestedTimeToleranceAfter = kCMTimeZero;
generator.requestedTimeToleranceBefore = kCMTimeZero;
CGImageRef imageRef = [generator copyCGImageAtTime:player.currentItem.currentTime actualTime:NULL error:NULL];
UIImage *thumbnailImage = [[UIImage alloc] initWithCGImage:imageRef];
CGImageRelease(imageRef);
[_scrubberView setDotImage:thumbnailImage];
_dotVideoView.hidden = true;
[UIView animateWithDuration:0.12 animations:^{ [UIView animateWithDuration:0.12 animations:^{
_flashView.alpha = 1.0f; _flashView.alpha = 1.0f;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
@ -791,18 +775,30 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
id<TGMediaEditAdjustments> adjustments = [self.photoEditor exportAdjustments]; id<TGMediaEditAdjustments> adjustments = [self.photoEditor exportAdjustments];
NSArray *cachedThumbnails = ((TGPhotoEditorController *)self.parentViewController).cachedVideoThumbnails;
SSignal *thumbnailsSignal = nil; SSignal *thumbnailsSignal = nil;
if ([self.item isKindOfClass:[TGMediaAsset class]]) if (cachedThumbnails.count > 0) {
thumbnailsSignal = [SSignal single:cachedThumbnails];
} else if ([self.item isKindOfClass:[TGMediaAsset class]]) {
thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:(TGMediaAsset *)self.item size:size timestamps:timestamps]; thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:(TGMediaAsset *)self.item size:size timestamps:timestamps];
else if ([self.item isKindOfClass:[TGCameraCapturedVideo class]]) } else if ([self.item isKindOfClass:[TGCameraCapturedVideo class]]) {
thumbnailsSignal = [((TGCameraCapturedVideo *)self.item).avAsset mapToSignal:^SSignal *(AVAsset *avAsset) { thumbnailsSignal = [((TGCameraCapturedVideo *)self.item).avAsset mapToSignal:^SSignal *(AVAsset *avAsset) {
return [TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps]; return [TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps];
}]; }];
}
_requestingThumbnails = true; _requestingThumbnails = true;
__weak TGPhotoAvatarPreviewController *weakSelf = self; __weak TGPhotoAvatarPreviewController *weakSelf = self;
[_thumbnailsDisposable setDisposable:[[[thumbnailsSignal map:^NSArray *(NSArray *images) { [_thumbnailsDisposable setDisposable:[[[[thumbnailsSignal onNext:^(NSArray *images) {
__strong TGPhotoAvatarPreviewController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGDispatchOnMainThread(^{
((TGPhotoEditorController *)strongSelf.parentViewController).cachedVideoThumbnails = images;
});
}] map:^NSArray *(NSArray *images) {
if (adjustments.toolsApplied) { if (adjustments.toolsApplied) {
NSMutableArray *editedImages = [[NSMutableArray alloc] init]; NSMutableArray *editedImages = [[NSMutableArray alloc] init];
PGPhotoEditor *editor = [[PGPhotoEditor alloc] initWithOriginalSize:adjustments.originalSize adjustments:adjustments forVideo:false enableStickers:true]; PGPhotoEditor *editor = [[PGPhotoEditor alloc] initWithOriginalSize:adjustments.originalSize adjustments:adjustments forVideo:false enableStickers:true];
@ -868,16 +864,22 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
- (void)setScrubberPosition:(NSTimeInterval)position reset:(bool)reset - (void)setScrubberPosition:(NSTimeInterval)position reset:(bool)reset
{ {
[_scrubberView setValue:_scrubberView.trimStartValue resetPosition:reset];
} }
- (void)setScrubberPlaying:(bool)value - (void)setScrubberPlaying:(bool)value
{ {
[_scrubberView setIsPlaying:value]; if (_dotVideoView == nil) {
AVPlayer *player = ((TGPhotoEditorController *)self.parentViewController).player;
_dotVideoView = [[TGModernGalleryVideoView alloc] initWithFrame:CGRectMake(0.0, 0.0, 27.0, 44.0) player:player];
_dotVideoView.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
_dotVideoView.hidden = true;
[_scrubberView setDotVideoView:_dotVideoView];
}
} }
- (NSTimeInterval)coverPosition { - (NSTimeInterval)coverPosition {
return _scrubberView.dotValue; return _scrubberView.value;
} }
@end @end

View File

@ -37,9 +37,6 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
CGFloat _autoRotationAngle; CGFloat _autoRotationAngle;
UIView *_buttonsWrapperView; UIView *_buttonsWrapperView;
TGModernButton *_rotateButton;
TGModernButton *_mirrorButton;
TGModernButton *_aspectRatioButton;
TGModernButton *_resetButton; TGModernButton *_resetButton;
TGPhotoCropView *_cropView; TGPhotoCropView *_cropView;
@ -107,7 +104,7 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
[self.view addSubview:_wrapperView]; [self.view addSubview:_wrapperView];
PGPhotoEditor *photoEditor = self.photoEditor; PGPhotoEditor *photoEditor = self.photoEditor;
_cropView = [[TGPhotoCropView alloc] initWithOriginalSize:photoEditor.originalSize hasArbitraryRotation:true]; _cropView = [[TGPhotoCropView alloc] initWithOriginalSize:photoEditor.originalSize hasArbitraryRotation:!_forVideo];
[_cropView setCropRect:photoEditor.cropRect]; [_cropView setCropRect:photoEditor.cropRect];
[_cropView setCropOrientation:photoEditor.cropOrientation]; [_cropView setCropOrientation:photoEditor.cropOrientation];
[_cropView setRotation:photoEditor.cropRotation]; [_cropView setRotation:photoEditor.cropRotation];
@ -153,31 +150,6 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
_buttonsWrapperView = [[UIView alloc] initWithFrame:CGRectZero]; _buttonsWrapperView = [[UIView alloc] initWithFrame:CGRectZero];
[_wrapperView addSubview:_buttonsWrapperView]; [_wrapperView addSubview:_buttonsWrapperView];
_rotateButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 36, 36)];
_rotateButton.exclusiveTouch = true;
_rotateButton.hitTestEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10);
[_rotateButton addTarget:self action:@selector(rotate) forControlEvents:UIControlEventTouchUpInside];
[_rotateButton setImage:TGComponentsImageNamed(@"PhotoEditorRotateIcon") forState:UIControlStateNormal];
//[_buttonsWrapperView addSubview:_rotateButton];
_mirrorButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 36, 36)];
_mirrorButton.exclusiveTouch = true;
_mirrorButton.imageEdgeInsets = UIEdgeInsetsMake(4.0f, 0.0f, 0.0f, 0.0f);
_mirrorButton.hitTestEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10);
[_mirrorButton addTarget:self action:@selector(mirror) forControlEvents:UIControlEventTouchUpInside];
[_mirrorButton setImage:TGComponentsImageNamed(@"PhotoEditorMirrorIcon") forState:UIControlStateNormal];
//[_buttonsWrapperView addSubview:_mirrorButton];
_aspectRatioButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 36, 36)];
_aspectRatioButton.exclusiveTouch = true;
_aspectRatioButton.hitTestEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10);
[_aspectRatioButton addTarget:self action:@selector(aspectRatioButtonPressed) forControlEvents:UIControlEventTouchUpInside];
UIImage *aspectRatioHighlightedImage = TGTintedImage(TGComponentsImageNamed(@"PhotoEditorAspectRatioIcon"), [TGPhotoEditorInterfaceAssets accentColor]);
[_aspectRatioButton setImage:TGComponentsImageNamed(@"PhotoEditorAspectRatioIcon") forState:UIControlStateNormal];
[_aspectRatioButton setImage:aspectRatioHighlightedImage forState:UIControlStateSelected];
[_aspectRatioButton setImage:aspectRatioHighlightedImage forState:UIControlStateSelected | UIControlStateHighlighted];
//[_buttonsWrapperView addSubview:_aspectRatioButton];
NSString *resetButtonTitle = TGLocalized(@"PhotoEditor.CropReset"); NSString *resetButtonTitle = TGLocalized(@"PhotoEditor.CropReset");
_resetButton = [[TGModernButton alloc] init]; _resetButton = [[TGModernButton alloc] init];
_resetButton.exclusiveTouch = true; _resetButton.exclusiveTouch = true;
@ -190,14 +162,10 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
_resetButton.frame = CGRectMake(0, 0, _resetButton.frame.size.width, 24); _resetButton.frame = CGRectMake(0, 0, _resetButton.frame.size.width, 24);
[_buttonsWrapperView addSubview:_resetButton]; [_buttonsWrapperView addSubview:_resetButton];
if ([resetButtonTitle respondsToSelector:@selector(sizeWithAttributes:)])
_resetButtonWidth = CGCeil([resetButtonTitle sizeWithAttributes:@{ NSFontAttributeName:TGSystemFontOfSize(13) }].width); _resetButtonWidth = CGCeil([resetButtonTitle sizeWithAttributes:@{ NSFontAttributeName:TGSystemFontOfSize(13) }].width);
else
_resetButtonWidth = CGCeil([resetButtonTitle sizeWithFont:TGSystemFontOfSize(13)].width);
if (photoEditor.cropLockedAspectRatio > FLT_EPSILON) if (photoEditor.cropLockedAspectRatio > FLT_EPSILON)
{ {
_aspectRatioButton.selected = true;
[_cropView setLockedAspectRatio:photoEditor.cropLockedAspectRatio performResize:false animated:false]; [_cropView setLockedAspectRatio:photoEditor.cropLockedAspectRatio performResize:false animated:false];
} }
else if ([photoEditor hasDefaultCropping] && ABS(_autoRotationAngle) > FLT_EPSILON) else if ([photoEditor hasDefaultCropping] && ABS(_autoRotationAngle) > FLT_EPSILON)
@ -376,16 +344,11 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
- (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame - (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame
{ {
CGSize referenceSize = [self referenceViewSize]; CGSize referenceSize = [self referenceViewSize];
UIInterfaceOrientation orientation = self.interfaceOrientation;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
orientation = UIInterfaceOrientationPortrait;
bool hasOnScreenNavigation = false; bool hasOnScreenNavigation = false;
if (iosMajorVersion() >= 11) if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON;
CGRect containerFrame = [TGPhotoCropController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation hasArbitraryRotation:_cropView.hasArbitraryRotation hasOnScreenNavigation:hasOnScreenNavigation]; CGRect containerFrame = [TGPhotoCropController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation hasArbitraryRotation:_cropView.hasArbitraryRotation hasOnScreenNavigation:hasOnScreenNavigation];
containerFrame = CGRectInset(containerFrame, TGPhotoCropAreaInsetSize.width, TGPhotoCropAreaInsetSize.height); containerFrame = CGRectInset(containerFrame, TGPhotoCropAreaInsetSize.width, TGPhotoCropAreaInsetSize.height);
CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size); CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size);
@ -576,7 +539,6 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
if (_cropView.isAspectRatioLocked) if (_cropView.isAspectRatioLocked)
{ {
[_cropView unlockAspectRatio]; [_cropView unlockAspectRatio];
_aspectRatioButton.selected = false;
} }
else else
{ {
@ -677,14 +639,14 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
}]]; }]];
[controller setItemViews:items]; [controller setItemViews:items];
controller.sourceRect = ^CGRect // controller.sourceRect = ^CGRect
{ // {
__strong TGPhotoCropController *strongSelf = weakSelf; // __strong TGPhotoCropController *strongSelf = weakSelf;
if (strongSelf != nil) // if (strongSelf != nil)
return [strongSelf.view convertRect:strongSelf->_aspectRatioButton.frame fromView:strongSelf->_aspectRatioButton.superview]; // return [strongSelf.view convertRect:strongSelf->_aspectRatioButton.frame fromView:strongSelf->_aspectRatioButton.superview];
//
return CGRectZero; // return CGRectZero;
}; // };
[controller presentInViewController:self.parentViewController sourceView:self.view animated:true]; [controller presentInViewController:self.parentViewController sourceView:self.view animated:true];
} }
@ -706,8 +668,6 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
} }
else else
{ {
_aspectRatioButton.selected = false;
[_cropView resetAnimated:true]; [_cropView resetAnimated:true];
if (hasAutorotationAngle) if (hasAutorotationAngle)
@ -796,8 +756,7 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
- (void)updateLayout:(UIInterfaceOrientation)orientation - (void)updateLayout:(UIInterfaceOrientation)orientation
{ {
if ([self inFormSheet] || [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) orientation = [self effectiveOrientation:orientation];
orientation = UIInterfaceOrientationPortrait;
CGSize referenceSize = [self referenceViewSize]; CGSize referenceSize = [self referenceViewSize];
@ -823,18 +782,7 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
{ {
case UIInterfaceOrientationLandscapeLeft: case UIInterfaceOrientationLandscapeLeft:
{ {
_buttonsWrapperView.frame = CGRectMake(screenEdges.left + self.toolbarLandscapeSize, _buttonsWrapperView.frame = CGRectMake(screenEdges.left + self.toolbarLandscapeSize, screenEdges.top, TGPhotoCropButtonsWrapperSize, referenceSize.height);
screenEdges.top,
TGPhotoCropButtonsWrapperSize,
referenceSize.height);
_rotateButton.frame = CGRectMake(25, 10, _rotateButton.frame.size.width, _rotateButton.frame.size.height);
_mirrorButton.frame = CGRectMake(25, 60, _mirrorButton.frame.size.width, _mirrorButton.frame.size.height);
_aspectRatioButton.frame = CGRectMake(25,
_buttonsWrapperView.frame.size.height - _aspectRatioButton.frame.size.height - 10,
_aspectRatioButton.frame.size.width,
_aspectRatioButton.frame.size.height);
_resetButton.transform = CGAffineTransformIdentity; _resetButton.transform = CGAffineTransformIdentity;
_resetButton.frame = CGRectMake(0, 0, _resetButtonWidth, 24); _resetButton.frame = CGRectMake(0, 0, _resetButtonWidth, 24);
@ -852,18 +800,7 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
case UIInterfaceOrientationLandscapeRight: case UIInterfaceOrientationLandscapeRight:
{ {
_buttonsWrapperView.frame = CGRectMake(screenEdges.right - self.toolbarLandscapeSize - TGPhotoCropButtonsWrapperSize, _buttonsWrapperView.frame = CGRectMake(screenEdges.right - self.toolbarLandscapeSize - TGPhotoCropButtonsWrapperSize, screenEdges.top, TGPhotoCropButtonsWrapperSize, referenceSize.height);
screenEdges.top,
TGPhotoCropButtonsWrapperSize,
referenceSize.height);
_rotateButton.frame = CGRectMake(_buttonsWrapperView.frame.size.width - _rotateButton.frame.size.width - 25, 10, _rotateButton.frame.size.width, _rotateButton.frame.size.height);
_mirrorButton.frame = CGRectMake(_buttonsWrapperView.frame.size.width - _mirrorButton.frame.size.width - 25, 60, _mirrorButton.frame.size.width, _mirrorButton.frame.size.height);
_aspectRatioButton.frame = CGRectMake(_buttonsWrapperView.frame.size.width - _aspectRatioButton.frame.size.width - 25,
_buttonsWrapperView.frame.size.height - _aspectRatioButton.frame.size.height - 10,
_aspectRatioButton.frame.size.width,
_aspectRatioButton.frame.size.height);
_resetButton.transform = CGAffineTransformIdentity; _resetButton.transform = CGAffineTransformIdentity;
_resetButton.frame = CGRectMake(0, 0, _resetButtonWidth, 24); _resetButton.frame = CGRectMake(0, 0, _resetButtonWidth, 24);
@ -881,18 +818,7 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original";
default: default:
{ {
_buttonsWrapperView.frame = CGRectMake(screenEdges.left, _buttonsWrapperView.frame = CGRectMake(screenEdges.left, screenEdges.bottom - TGPhotoEditorToolbarSize - TGPhotoCropButtonsWrapperSize, referenceSize.width, TGPhotoCropButtonsWrapperSize);
screenEdges.bottom - TGPhotoEditorToolbarSize - TGPhotoCropButtonsWrapperSize,
referenceSize.width,
TGPhotoCropButtonsWrapperSize);
_rotateButton.frame = CGRectMake(10, _buttonsWrapperView.frame.size.height - _rotateButton.frame.size.height - 25, _rotateButton.frame.size.width, _rotateButton.frame.size.height);
_mirrorButton.frame = CGRectMake(60, _buttonsWrapperView.frame.size.height - _mirrorButton.frame.size.height - 25, _mirrorButton.frame.size.width, _mirrorButton.frame.size.height);
_aspectRatioButton.frame = CGRectMake(_buttonsWrapperView.frame.size.width - _aspectRatioButton.frame.size.width - 10,
_buttonsWrapperView.frame.size.height - _aspectRatioButton.frame.size.height - 25,
_aspectRatioButton.frame.size.width,
_aspectRatioButton.frame.size.height);
_resetButton.transform = CGAffineTransformIdentity; _resetButton.transform = CGAffineTransformIdentity;
_resetButton.frame = CGRectMake((_buttonsWrapperView.frame.size.width - _resetButton.frame.size.width) / 2, 20, _resetButtonWidth, 24); _resetButton.frame = CGRectMake((_buttonsWrapperView.frame.size.width - _resetButton.frame.size.width) / 2, 20, _resetButtonWidth, 24);

View File

@ -72,12 +72,12 @@
UIImage *_thumbnailImage; UIImage *_thumbnailImage;
AVPlayerItem *_playerItem; AVPlayerItem *_playerItem;
AVPlayer *_player;
SMetaDisposable *_playerItemDisposable; SMetaDisposable *_playerItemDisposable;
id _playerStartedObserver; id _playerStartedObserver;
id _playerReachedEndObserver; id _playerReachedEndObserver;
bool _registeredKeypathObserver; bool _registeredKeypathObserver;
NSTimer *_positionTimer; NSTimer *_positionTimer;
bool _scheduledVideoPlayback;
id<TGMediaEditAdjustments> _initialAdjustments; id<TGMediaEditAdjustments> _initialAdjustments;
NSString *_caption; NSString *_caption;
@ -260,14 +260,6 @@
TGPhotoEditorBackButton backButton = TGPhotoEditorBackButtonCancel; TGPhotoEditorBackButton backButton = TGPhotoEditorBackButtonCancel;
if ([self presentedForAvatarCreation])
{
if ([self presentedFromCamera])
backButton = TGPhotoEditorBackButtonCancel;
else
backButton = TGPhotoEditorBackButtonCancel;
}
TGPhotoEditorDoneButton doneButton = TGPhotoEditorDoneButtonCheck; TGPhotoEditorDoneButton doneButton = TGPhotoEditorDoneButtonCheck;
_portraitToolbarView = [[TGPhotoToolbarView alloc] initWithBackButton:backButton doneButton:doneButton solidBackground:true]; _portraitToolbarView = [[TGPhotoToolbarView alloc] initWithBackButton:backButton doneButton:doneButton solidBackground:true];
[_portraitToolbarView setToolbarTabs:_availableTabs animated:false]; [_portraitToolbarView setToolbarTabs:_availableTabs animated:false];
@ -403,8 +395,11 @@
[signal startWithNext:^(id next) [signal startWithNext:^(id next)
{ {
CGFloat progress = 0.0;
bool progressVisible = false;
if ([next isKindOfClass:[UIImage class]]) { if ([next isKindOfClass:[UIImage class]]) {
[_photoEditor setImage:(UIImage *)next forCropRect:_photoEditor.cropRect cropRotation:_photoEditor.cropRotation cropOrientation:_photoEditor.cropOrientation cropMirrored:_photoEditor.cropMirrored fullSize:false]; [_photoEditor setImage:(UIImage *)next forCropRect:_photoEditor.cropRect cropRotation:_photoEditor.cropRotation cropOrientation:_photoEditor.cropOrientation cropMirrored:_photoEditor.cropMirrored fullSize:false];
progress = 1.0f;
} else if ([next isKindOfClass:[AVAsset class]]) { } else if ([next isKindOfClass:[AVAsset class]]) {
_playerItem = [AVPlayerItem playerItemWithAsset:(AVAsset *)next]; _playerItem = [AVPlayerItem playerItemWithAsset:(AVAsset *)next];
_player = [AVPlayer playerWithPlayerItem:_playerItem]; _player = [AVPlayer playerWithPlayerItem:_playerItem];
@ -421,8 +416,21 @@
[_previewView performTransitionInWithCompletion:^ [_previewView performTransitionInWithCompletion:^
{ {
}]; }];
});
if (_scheduledVideoPlayback) {
_scheduledVideoPlayback = false;
[self startVideoPlayback:true];
} }
});
progress = 1.0f;
} else if ([next isKindOfClass:[NSNumber class]]) {
progress = [next floatValue];
progressVisible = true;
}
TGDispatchOnMainThread(^{
[self setProgressVisible:progressVisible value:progress animated:true];
});
if (_ignoreDefaultPreviewViewTransitionIn) if (_ignoreDefaultPreviewViewTransitionIn)
{ {
@ -496,6 +504,11 @@
} }
- (void)startVideoPlayback:(bool)reset { - (void)startVideoPlayback:(bool)reset {
if (reset && _player == nil) {
_scheduledVideoPlayback = true;
return;
}
if (reset) { if (reset) {
NSTimeInterval startPosition = 0.0f; NSTimeInterval startPosition = 0.0f;
if (_photoEditor.trimStartValue > DBL_EPSILON) if (_photoEditor.trimStartValue > DBL_EPSILON)
@ -973,6 +986,9 @@
_switchingTab = true; _switchingTab = true;
TGPhotoEditorBackButton backButtonType = TGPhotoEditorBackButtonCancel;
TGPhotoEditorDoneButton doneButtonType = TGPhotoEditorDoneButtonCheck;
__weak TGPhotoEditorController *weakSelf = self; __weak TGPhotoEditorController *weakSelf = self;
TGPhotoEditorTabController *controller = nil; TGPhotoEditorTabController *controller = nil;
switch (tab) switch (tab)
@ -1358,6 +1374,8 @@
[strongSelf setVideoEndTime:endTime]; [strongSelf setVideoEndTime:endTime];
}; };
controller = previewController; controller = previewController;
doneButtonType = TGPhotoEditorDoneButtonDone;
} }
break; break;
@ -1365,6 +1383,10 @@
break; break;
} }
if (_intent == TGPhotoEditorControllerAvatarIntent && !isInitialAppearance && tab != TGPhotoEditorPreviewTab) {
backButtonType = TGPhotoEditorBackButtonBack;
}
_currentTabController = controller; _currentTabController = controller;
_currentTabController.item = _item; _currentTabController.item = _item;
_currentTabController.intent = _intent; _currentTabController.intent = _intent;
@ -1382,11 +1404,11 @@
if (currentController != nil) if (currentController != nil)
[_currentTabController viewWillAppear:true]; [_currentTabController viewWillAppear:true];
_currentTabController.view.frame = _containerView.bounds;
if (currentController != nil) if (currentController != nil)
[_currentTabController viewDidAppear:true]; [_currentTabController viewDidAppear:true];
_currentTabController.view.frame = _containerView.bounds;
_currentTabController.valuesChanged = ^ _currentTabController.valuesChanged = ^
{ {
__strong TGPhotoEditorController *strongSelf = weakSelf; __strong TGPhotoEditorController *strongSelf = weakSelf;
@ -1405,6 +1427,12 @@
[_portraitToolbarView setToolbarTabs:[_currentTabController availableTabs] animated:true]; [_portraitToolbarView setToolbarTabs:[_currentTabController availableTabs] animated:true];
[_landscapeToolbarView setToolbarTabs:[_currentTabController availableTabs] animated:true]; [_landscapeToolbarView setToolbarTabs:[_currentTabController availableTabs] animated:true];
[_portraitToolbarView setBackButtonType:backButtonType];
[_landscapeToolbarView setBackButtonType:backButtonType];
[_portraitToolbarView setDoneButtonType:doneButtonType];
[_landscapeToolbarView setDoneButtonType:doneButtonType];
[self updateEditorButtons]; [self updateEditorButtons];
if ([self respondsToSelector:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]) if ([self respondsToSelector:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)])
@ -1474,10 +1502,7 @@
- (void)dismissEditor - (void)dismissEditor
{ {
if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) { if ((![_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]] && ![_currentTabController isKindOfClass:[TGPhotoAvatarCropController class]]) && _intent == TGPhotoEditorControllerAvatarIntent) {
[self presentEditorTab:TGPhotoEditorCropTab];
return;
} else if (![_currentTabController isKindOfClass:[TGPhotoAvatarCropController class]] && _intent == TGPhotoEditorControllerAvatarIntent) {
[self presentEditorTab:TGPhotoEditorPreviewTab]; [self presentEditorTab:TGPhotoEditorPreviewTab];
return; return;
} }
@ -1570,9 +1595,7 @@
- (void)doneButtonPressed - (void)doneButtonPressed
{ {
if ([_currentTabController isKindOfClass:[TGPhotoAvatarCropController class]]) { if (_intent == TGPhotoEditorControllerAvatarIntent && ![_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) {
[self presentEditorTab:TGPhotoEditorPreviewTab];
} else if (_intent == TGPhotoEditorControllerAvatarIntent && ![_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) {
[self presentEditorTab:TGPhotoEditorPreviewTab]; [self presentEditorTab:TGPhotoEditorPreviewTab];
} else { } else {
[self applyEditor]; [self applyEditor];
@ -1895,7 +1918,7 @@
- (void)dismiss - (void)dismiss
{ {
if (self.overlayWindow != nil) if (self.overlayWindow != nil || self.customDismissBlock != nil)
{ {
[super dismiss]; [super dismiss];
} }

View File

@ -82,6 +82,13 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
} }
} }
- (bool)hasOnScreenNavigation {
bool hasOnScreenNavigation = false;
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON;
return hasOnScreenNavigation;
}
- (UIInterfaceOrientation)effectiveOrientation { - (UIInterfaceOrientation)effectiveOrientation {
return [self effectiveOrientation:self.interfaceOrientation]; return [self effectiveOrientation:self.interfaceOrientation];
} }

View File

@ -1830,16 +1830,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
- (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame - (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame
{ {
CGSize referenceSize = [self referenceViewSize]; CGSize referenceSize = [self referenceViewSize];
UIInterfaceOrientation orientation = self.interfaceOrientation; CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
orientation = UIInterfaceOrientationPortrait;
bool hasOnScreenNavigation = false;
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON;
CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation];
CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size); CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size);
CGRect toFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); CGRect toFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
@ -1920,11 +1911,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
- (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation - (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation
{ {
bool hasOnScreenNavigation = false; CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON;
CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation];
CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size); CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size);
return CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); return CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
@ -1942,15 +1929,8 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
TGPhotoEditorPreviewView *previewView = self.previewView; TGPhotoEditorPreviewView *previewView = self.previewView;
[previewView prepareForTransitionOut]; [previewView prepareForTransitionOut];
bool hasOnScreenNavigation = false; UIInterfaceOrientation orientation = self.effectiveOrientation;
if (iosMajorVersion() >= 11) CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON;
UIInterfaceOrientation orientation = self.interfaceOrientation;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
orientation = UIInterfaceOrientationPortrait;
CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation];
CGRect referenceFrame = CGRectMake(0, 0, self.photoEditor.rotatedCropSize.width, self.photoEditor.rotatedCropSize.height); CGRect referenceFrame = CGRectMake(0, 0, self.photoEditor.rotatedCropSize.width, self.photoEditor.rotatedCropSize.height);
CGRect rect = CGRectOffset([self transitionOutSourceFrameForReferenceFrame:referenceFrame orientation:orientation], -containerFrame.origin.x, -containerFrame.origin.y); CGRect rect = CGRectOffset([self transitionOutSourceFrameForReferenceFrame:referenceFrame orientation:orientation], -containerFrame.origin.x, -containerFrame.origin.y);
previewView.frame = rect; previewView.frame = rect;
@ -2183,18 +2163,14 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
CGFloat panelToolbarPortraitSize = TGPhotoPaintBottomPanelSize + TGPhotoEditorToolbarSize; CGFloat panelToolbarPortraitSize = TGPhotoPaintBottomPanelSize + TGPhotoEditorToolbarSize;
CGFloat panelToolbarLandscapeSize = TGPhotoPaintBottomPanelSize + self.toolbarLandscapeSize; CGFloat panelToolbarLandscapeSize = TGPhotoPaintBottomPanelSize + self.toolbarLandscapeSize;
bool hasOnScreenNavigation = false; UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:self.hasOnScreenNavigation];
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON;
UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation];
UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2); UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2);
screenEdges.top += safeAreaInset.top; screenEdges.top += safeAreaInset.top;
screenEdges.left += safeAreaInset.left; screenEdges.left += safeAreaInset.left;
screenEdges.bottom -= safeAreaInset.bottom; screenEdges.bottom -= safeAreaInset.bottom;
screenEdges.right -= safeAreaInset.right; screenEdges.right -= safeAreaInset.right;
CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
_settingsViewWrapper.frame = self.parentViewController.view.bounds; _settingsViewWrapper.frame = self.parentViewController.view.bounds;
@ -2361,18 +2337,10 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
- (void)keyboardHeightChangedTo:(CGFloat)height duration:(NSTimeInterval)duration curve:(NSInteger)curve - (void)keyboardHeightChangedTo:(CGFloat)height duration:(NSTimeInterval)duration curve:(NSInteger)curve
{ {
UIInterfaceOrientation orientation = self.interfaceOrientation;
if ([self inFormSheet] || [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
orientation = UIInterfaceOrientationPortrait;
bool hasOnScreenNavigation = false;
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON;
CGSize referenceSize = [self referenceViewSize]; CGSize referenceSize = [self referenceViewSize];
CGFloat screenSide = MAX(referenceSize.width, referenceSize.height) + 2 * TGPhotoPaintBottomPanelSize; CGFloat screenSide = MAX(referenceSize.width, referenceSize.height) + 2 * TGPhotoPaintBottomPanelSize;
CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
CGFloat visibleArea = self.view.frame.size.height - height; CGFloat visibleArea = self.view.frame.size.height - height;
CGFloat yCenter = visibleArea / 2.0f; CGFloat yCenter = visibleArea / 2.0f;

View File

@ -42,12 +42,34 @@
_buttonsWrapperView = [[UIView alloc] initWithFrame:_backgroundView.bounds]; _buttonsWrapperView = [[UIView alloc] initWithFrame:_backgroundView.bounds];
[_backgroundView addSubview:_buttonsWrapperView]; [_backgroundView addSubview:_buttonsWrapperView];
_cancelButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 49, 49)]; CGSize buttonSize = CGSizeMake(49.0f, 49.0f);
_cancelButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, buttonSize.width, buttonSize.height)];
_cancelButton.exclusiveTouch = true; _cancelButton.exclusiveTouch = true;
_cancelButton.adjustsImageWhenHighlighted = false; _cancelButton.adjustsImageWhenHighlighted = false;
[self setBackButtonType:backButton];
[_cancelButton addTarget:self action:@selector(cancelButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[_backgroundView addSubview:_cancelButton];
_doneButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, buttonSize.width, buttonSize.height)];
_doneButton.exclusiveTouch = true;
_doneButton.adjustsImageWhenHighlighted = false;
[self setDoneButtonType:doneButton];
[_doneButton addTarget:self action:@selector(doneButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[_backgroundView addSubview:_doneButton];
_longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(doneButtonLongPressed:)];
_longPressGestureRecognizer.minimumPressDuration = 0.4;
[_doneButton addGestureRecognizer:_longPressGestureRecognizer];
}
return self;
}
- (void)setBackButtonType:(TGPhotoEditorBackButton)backButtonType {
_backButtonType = backButtonType;
UIImage *cancelImage = nil; UIImage *cancelImage = nil;
switch (backButton) switch (backButtonType)
{ {
case TGPhotoEditorBackButtonCancel: case TGPhotoEditorBackButtonCancel:
cancelImage = TGTintedImage([UIImage imageNamed:@"Editor/Cancel"], [UIColor whiteColor]); cancelImage = TGTintedImage([UIImage imageNamed:@"Editor/Cancel"], [UIColor whiteColor]);
@ -58,17 +80,27 @@
break; break;
} }
[_cancelButton setImage:cancelImage forState:UIControlStateNormal]; [_cancelButton setImage:cancelImage forState:UIControlStateNormal];
[_cancelButton addTarget:self action:@selector(cancelButtonPressed) forControlEvents:UIControlEventTouchUpInside]; }
[_backgroundView addSubview:_cancelButton];
UIImage *doneImage = nil; - (void)setDoneButtonType:(TGPhotoEditorDoneButton)doneButtonType {
CGSize buttonSize = CGSizeMake(49.0f, 49.0f); _doneButtonType = doneButtonType;
switch (doneButton)
UIImage *doneImage;
switch (doneButtonType)
{ {
case TGPhotoEditorDoneButtonCheck: case TGPhotoEditorDoneButtonCheck:
doneImage = TGTintedImage([UIImage imageNamed:@"Editor/Commit"], [UIColor whiteColor]); doneImage = TGTintedImage([UIImage imageNamed:@"Editor/Commit"], [UIColor whiteColor]);
break; break;
case TGPhotoEditorDoneButtonDone:
{
TGMediaAssetsPallete *pallete = nil;
if ([[LegacyComponentsGlobals provider] respondsToSelector:@selector(mediaAssetsPallete)])
pallete = [[LegacyComponentsGlobals provider] mediaAssetsPallete];
doneImage = pallete != nil ? pallete.doneIconImage : TGTintedImage([UIImage imageNamed:@"Editor/Commit"], [UIColor whiteColor]);
break;
}
default: default:
{ {
TGMediaAssetsPallete *pallete = nil; TGMediaAssetsPallete *pallete = nil;
@ -79,20 +111,7 @@
} }
break; break;
} }
_doneButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, buttonSize.width, buttonSize.height)];
_doneButton.exclusiveTouch = true;
_doneButton.adjustsImageWhenHighlighted = false;
[_doneButton setImage:doneImage forState:UIControlStateNormal]; [_doneButton setImage:doneImage forState:UIControlStateNormal];
[_doneButton addTarget:self action:@selector(doneButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[_backgroundView addSubview:_doneButton];
_longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(doneButtonLongPressed:)];
_longPressGestureRecognizer.minimumPressDuration = 0.4;
[_doneButton addGestureRecognizer:_longPressGestureRecognizer];
}
return self;
} }
- (UIButton *)doneButton - (UIButton *)doneButton

View File

@ -30,6 +30,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
{ {
NSValue *_contentOffsetAfterRotation; NSValue *_contentOffsetAfterRotation;
bool _appeared; bool _appeared;
bool _scheduledTransitionIn;
CGFloat _cellWidth; CGFloat _cellWidth;
NSArray *_allTools; NSArray *_allTools;
@ -306,17 +307,17 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
- (void)transitionIn - (void)transitionIn
{ {
if (_portraitToolsWrapperView.frame.size.height < FLT_EPSILON) {
_scheduledTransitionIn = true;
return;
}
[UIView animateWithDuration:0.3f animations:^ [UIView animateWithDuration:0.3f animations:^
{ {
_portraitToolsWrapperView.alpha = 1.0f; _portraitToolsWrapperView.alpha = 1.0f;
_landscapeToolsWrapperView.alpha = 1.0f; _landscapeToolsWrapperView.alpha = 1.0f;
}]; }];
UIInterfaceOrientation orientation = self.interfaceOrientation; switch (self.effectiveOrientation)
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
orientation = UIInterfaceOrientationPortrait;
switch (orientation)
{ {
case UIInterfaceOrientationLandscapeLeft: case UIInterfaceOrientationLandscapeLeft:
{ {
@ -350,8 +351,12 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
} }
} }
- (void)transitionOutSwitching:(bool)__unused switching completion:(void (^)(void))completion - (void)transitionOutSwitching:(bool)switching completion:(void (^)(void))completion
{ {
if (switching) {
_dismissing = true;
}
TGPhotoEditorPreviewView *previewView = self.previewView; TGPhotoEditorPreviewView *previewView = self.previewView;
previewView.touchedUp = nil; previewView.touchedUp = nil;
previewView.touchedDown = nil; previewView.touchedDown = nil;
@ -360,11 +365,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
[_toolAreaView.superview bringSubviewToFront:_toolAreaView]; [_toolAreaView.superview bringSubviewToFront:_toolAreaView];
UIInterfaceOrientation orientation = self.interfaceOrientation; switch (self.effectiveOrientation)
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
orientation = UIInterfaceOrientationPortrait;
switch (orientation)
{ {
case UIInterfaceOrientationLandscapeLeft: case UIInterfaceOrientationLandscapeLeft:
{ {
@ -769,17 +770,18 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
[self updateLayout:[[LegacyComponentsGlobals provider] applicationStatusBarOrientation]]; [self updateLayout:[[LegacyComponentsGlobals provider] applicationStatusBarOrientation]];
if (_scheduledTransitionIn) {
_scheduledTransitionIn = false;
[self transitionIn];
}
if (![self inFormSheet]) if (![self inFormSheet])
[self _applyPreparedContentOffset]; [self _applyPreparedContentOffset];
} }
- (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation - (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation
{ {
bool hasOnScreenNavigation = false; CGRect containerFrame = [TGPhotoToolsController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON;
CGRect containerFrame = [TGPhotoToolsController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:hasOnScreenNavigation];
CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size); CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size);
CGRect sourceFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); CGRect sourceFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
@ -789,16 +791,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
- (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame - (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame
{ {
CGSize referenceSize = [self referenceViewSize]; CGSize referenceSize = [self referenceViewSize];
UIInterfaceOrientation orientation = self.interfaceOrientation; CGRect containerFrame = [TGPhotoToolsController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
orientation = UIInterfaceOrientationPortrait;
bool hasOnScreenNavigation = false;
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON;
CGRect containerFrame = [TGPhotoToolsController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:hasOnScreenNavigation];
CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size); CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size);
CGRect toFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); CGRect toFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);
@ -831,11 +824,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
CGFloat panelToolbarPortraitSize = panelSize + TGPhotoEditorToolbarSize; CGFloat panelToolbarPortraitSize = panelSize + TGPhotoEditorToolbarSize;
CGFloat panelToolbarLandscapeSize = panelSize + TGPhotoEditorToolbarSize; CGFloat panelToolbarLandscapeSize = panelSize + TGPhotoEditorToolbarSize;
bool hasOnScreenNavigation = false; UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:self.hasOnScreenNavigation];
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON;
UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation];
UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2); UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2);
screenEdges.top += safeAreaInset.top; screenEdges.top += safeAreaInset.top;
screenEdges.left += safeAreaInset.left; screenEdges.left += safeAreaInset.left;
@ -946,23 +935,14 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
- (void)updatePreviewView - (void)updatePreviewView
{ {
UIInterfaceOrientation orientation = self.interfaceOrientation;
if ([self inFormSheet] || TGIsPad())
orientation = UIInterfaceOrientationPortrait;
CGSize referenceSize = [self referenceViewSize];
PGPhotoEditor *photoEditor = self.photoEditor; PGPhotoEditor *photoEditor = self.photoEditor;
TGPhotoEditorPreviewView *previewView = self.previewView; TGPhotoEditorPreviewView *previewView = self.previewView;
if (_dismissing || previewView.superview != self.view) if (_dismissing || previewView.superview != self.view)
return; return;
bool hasOnScreenNavigation = false; CGSize referenceSize = [self referenceViewSize];
if (iosMajorVersion() >= 11) CGRect containerFrame = _preview ? CGRectMake(0.0f, 0.0f, referenceSize.width, referenceSize.height) : [TGPhotoToolsController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON;
CGRect containerFrame = _preview ? CGRectMake(0.0f, 0.0f, referenceSize.width, referenceSize.height) : [TGPhotoToolsController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:hasOnScreenNavigation];
CGSize fittedSize = TGScaleToSize(photoEditor.rotatedCropSize, containerFrame.size); CGSize fittedSize = TGScaleToSize(photoEditor.rotatedCropSize, containerFrame.size);
previewView.frame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); previewView.frame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height);

View File

@ -9,7 +9,7 @@
#import "TGPhotoPaintStickerEntity.h" #import "TGPhotoPaintStickerEntity.h"
#import "TGPhotoPaintTextEntity.h" #import "TGPhotoPaintTextEntity.h"
const NSTimeInterval TGVideoEditMinimumTrimmableDuration = 1.0; const NSTimeInterval TGVideoEditMinimumTrimmableDuration = 1.5;
const NSTimeInterval TGVideoEditMaximumGifDuration = 30.5; const NSTimeInterval TGVideoEditMaximumGifDuration = 30.5;
@implementation TGVideoEditAdjustments @implementation TGVideoEditAdjustments

View File

@ -27,7 +27,7 @@ public func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, t
} }
completion(image) completion(image)
} }
mixin.didFinishWithView = { [weak legacyController] in mixin.didFinishWithView = {
openCurrent?() openCurrent?()
} }
mixin.didDismiss = { [weak legacyController] in mixin.didDismiss = { [weak legacyController] in

View File

@ -346,7 +346,7 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone
let navigationBar = presentationTheme.rootController.navigationBar let navigationBar = presentationTheme.rootController.navigationBar
let tabBar = presentationTheme.rootController.tabBar let tabBar = presentationTheme.rootController.tabBar
return TGMediaAssetsPallete(dark: presentationTheme.overallDarkAppearance, backgroundColor: theme.plainBackgroundColor, selectionColor: theme.itemHighlightedBackgroundColor, separatorColor: theme.itemPlainSeparatorColor, textColor: theme.itemPrimaryTextColor, secondaryTextColor: theme.controlSecondaryColor, accentColor: theme.itemAccentColor, barBackgroundColor: tabBar.backgroundColor, barSeparatorColor: tabBar.separatorColor, navigationTitleColor: navigationBar.primaryTextColor, badge: generateStretchableFilledCircleImage(diameter: 22.0, color: navigationBar.accentTextColor), badgeTextColor: navigationBar.backgroundColor, sendIconImage: PresentationResourcesChat.chatInputPanelSendButtonImage(presentationTheme), maybeAccentColor: navigationBar.accentTextColor) return TGMediaAssetsPallete(dark: presentationTheme.overallDarkAppearance, backgroundColor: theme.plainBackgroundColor, selectionColor: theme.itemHighlightedBackgroundColor, separatorColor: theme.itemPlainSeparatorColor, textColor: theme.itemPrimaryTextColor, secondaryTextColor: theme.controlSecondaryColor, accentColor: theme.itemAccentColor, barBackgroundColor: tabBar.backgroundColor, barSeparatorColor: tabBar.separatorColor, navigationTitleColor: navigationBar.primaryTextColor, badge: generateStretchableFilledCircleImage(diameter: 22.0, color: navigationBar.accentTextColor), badgeTextColor: navigationBar.backgroundColor, sendIconImage: PresentationResourcesChat.chatInputPanelSendButtonImage(presentationTheme), doneIconImage: PresentationResourcesChat.chatInputPanelApplyButtonImage(presentationTheme), maybeAccentColor: navigationBar.accentTextColor)
} }
func checkButtonPallete() -> TGCheckButtonPallete! { func checkButtonPallete() -> TGCheckButtonPallete! {

View File

@ -105,7 +105,7 @@ class SecureIdDocumentGalleryController: ViewController, StandalonePresentableCo
$0.item(context: context, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, secureIdContext: strongSelf.secureIdContext, delete: { resource in $0.item(context: context, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, secureIdContext: strongSelf.secureIdContext, delete: { resource in
self?.deleteItem(resource) self?.deleteItem(resource)
}) })
}), centralItemIndex: centralIndex, keepFirst: false) }), centralItemIndex: centralIndex)
let ready = (strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void()))) |> afterNext { [weak strongSelf] _ in let ready = (strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void()))) |> afterNext { [weak strongSelf] _ in
strongSelf?.didSetReady = true strongSelf?.didSetReady = true

View File

@ -39,7 +39,7 @@ class SecureIdDocumentGalleryItem: GalleryItem {
self.delete = delete self.delete = delete
} }
func node() -> GalleryItemNode { func node(synchronous: Bool) -> GalleryItemNode {
let node = SecureIdDocumentGalleryItemNode(context: self.context, theme: self.theme, strings: self.strings) let node = SecureIdDocumentGalleryItemNode(context: self.context, theme: self.theme, strings: self.strings)
node.setResource(secureIdContext: self.secureIdContext, resource: self.resource) node.setResource(secureIdContext: self.secureIdContext, resource: self.resource)
@ -52,7 +52,7 @@ class SecureIdDocumentGalleryItem: GalleryItem {
return node return node
} }
func updateNode(node: GalleryItemNode) { func updateNode(node: GalleryItemNode, synchronous: Bool) {
if let node = node as? SecureIdDocumentGalleryItemNode { if let node = node as? SecureIdDocumentGalleryItemNode {
node._title.set(.single(self.strings.Items_NOfM("\(self.location.position + 1)", "\(self.location.totalCount)").0)) node._title.set(.single(self.strings.Items_NOfM("\(self.location.position + 1)", "\(self.location.totalCount)").0))

View File

@ -223,6 +223,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
self.disposable.set(combineLatest(entriesSignal, self.animatedIn.get()).start(next: { [weak self] entries, animatedIn in self.disposable.set(combineLatest(entriesSignal, self.animatedIn.get()).start(next: { [weak self] entries, animatedIn in
let f: () -> Void = { let f: () -> Void = {
if let strongSelf = self, animatedIn { if let strongSelf = self, animatedIn {
let isFirstTime = strongSelf.entries.isEmpty
strongSelf.entries = entries strongSelf.entries = entries
if strongSelf.centralEntryIndex == nil { if strongSelf.centralEntryIndex == nil {
strongSelf.centralEntryIndex = 0 strongSelf.centralEntryIndex = 0
@ -248,7 +249,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
} : nil, setMain: { [weak self] in } : nil, setMain: { [weak self] in
self?.setMainEntry(entry) self?.setMainEntry(entry)
}) })
}), centralItemIndex: 0, keepFirst: false) }), centralItemIndex: 0, synchronous: !isFirstTime)
let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in
strongSelf?.didSetReady = true strongSelf?.didSetReady = true
@ -597,7 +598,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
} else { } else {
if let index = self.entries.firstIndex(of: entry) { if let index = self.entries.firstIndex(of: entry) {
self.entries.remove(at: index) self.entries.remove(at: index)
self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1)) self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false))
} }
} }
} }
@ -611,7 +612,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
} else { } else {
if let index = self.entries.firstIndex(of: entry) { if let index = self.entries.firstIndex(of: entry) {
self.entries.remove(at: index) self.entries.remove(at: index)
self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1)) self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false))
} }
} }
} else { } else {
@ -625,7 +626,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
} else { } else {
if let index = self.entries.firstIndex(of: entry) { if let index = self.entries.firstIndex(of: entry) {
self.entries.remove(at: index) self.entries.remove(at: index)
self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1)) self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false))
} }
} }
} }

View File

@ -65,27 +65,27 @@ class PeerAvatarImageGalleryItem: GalleryItem {
self.setMain = setMain self.setMain = setMain
} }
func node() -> GalleryItemNode { func node(synchronous: Bool) -> GalleryItemNode {
let node = PeerAvatarImageGalleryItemNode(context: self.context, presentationData: self.presentationData, peer: self.peer, sourceHasRoundCorners: self.sourceHasRoundCorners) let node = PeerAvatarImageGalleryItemNode(context: self.context, presentationData: self.presentationData, peer: self.peer, sourceHasRoundCorners: self.sourceHasRoundCorners)
if let indexData = self.entry.indexData { if let indexData = self.entry.indexData {
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0)) node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0))
} }
node.setEntry(self.entry) node.setEntry(self.entry, synchronous: synchronous)
node.footerContentNode.delete = self.delete node.footerContentNode.delete = self.delete
node.footerContentNode.setMain = self.setMain node.footerContentNode.setMain = self.setMain
return node return node
} }
func updateNode(node: GalleryItemNode) { func updateNode(node: GalleryItemNode, synchronous: Bool) {
if let node = node as? PeerAvatarImageGalleryItemNode { if let node = node as? PeerAvatarImageGalleryItemNode {
if let indexData = self.entry.indexData { if let indexData = self.entry.indexData {
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0)) node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0))
} }
node.setEntry(self.entry) node.setEntry(self.entry, synchronous: synchronous)
node.footerContentNode.delete = self.delete node.footerContentNode.delete = self.delete
node.footerContentNode.setMain = self.setMain node.footerContentNode.setMain = self.setMain
} }
@ -201,7 +201,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(), size: statusSize)) transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(), size: statusSize))
} }
fileprivate func setEntry(_ entry: AvatarGalleryEntry) { fileprivate func setEntry(_ entry: AvatarGalleryEntry, synchronous: Bool) {
if self.entry != entry { if self.entry != entry {
self.entry = entry self.entry = entry
@ -224,7 +224,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
case let .image(_, _, imageRepresentations, _, _, _, _, _): case let .image(_, _, imageRepresentations, _, _, _, _, _):
representations = imageRepresentations representations = imageRepresentations
} }
self.imageNode.setSignal(chatAvatarGalleryPhoto(account: self.context.account, representations: representations), dispatchOnDisplayLink: false) self.imageNode.setSignal(chatAvatarGalleryPhoto(account: self.context.account, representations: representations, attemptSynchronously: synchronous), attemptSynchronously: synchronous, dispatchOnDisplayLink: false)
self.zoomableContent = (largestSize.dimensions.cgSize, self.contentNode) self.zoomableContent = (largestSize.dimensions.cgSize, self.contentNode)
if let largestIndex = representations.firstIndex(where: { $0.representation == largestSize }) { if let largestIndex = representations.firstIndex(where: { $0.representation == largestSize }) {
@ -278,8 +278,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
} }
if let video = entry.videoRepresentations.last, let id = id { if let video = entry.videoRepresentations.last, let id = id {
let mediaManager = self.context.sharedContext.mediaManager let mediaManager = self.context.sharedContext.mediaManager
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])])) let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .black) let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded) let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
videoNode.isUserInteractionEnabled = false videoNode.isUserInteractionEnabled = false
videoNode.ownsContentNodeUpdated = { [weak self] owns in videoNode.ownsContentNodeUpdated = { [weak self] owns in

View File

@ -774,7 +774,7 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi
if let profileImage = peer?.smallProfileImage { if let profileImage = peer?.smallProfileImage {
return $0.withUpdatedUpdatingAvatar(.image(profileImage, false)) return $0.withUpdatedUpdatingAvatar(.image(profileImage, false))
} else { } else {
return $0.withUpdatedUpdatingAvatar(.none) return $0.withUpdatedUpdatingAvatar(ItemListAvatarAndNameInfoItemUpdatingAvatar.none)
} }
} }
updateAvatarDisposable.set((updatePeerPhoto(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, accountPeerId: context.account.peerId, peerId: peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in updateAvatarDisposable.set((updatePeerPhoto(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, accountPeerId: context.account.peerId, peerId: peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in

View File

@ -628,7 +628,7 @@ func editSettingsController(context: AccountContext, currentName: ItemListAvatar
if let profileImage = peer?.smallProfileImage { if let profileImage = peer?.smallProfileImage {
return $0.withUpdatedUpdatingAvatar(.image(profileImage, false)) return $0.withUpdatedUpdatingAvatar(.image(profileImage, false))
} else { } else {
return $0.withUpdatedUpdatingAvatar(.none) return $0.withUpdatedUpdatingAvatar(ItemListAvatarAndNameInfoItemUpdatingAvatar.none)
} }
} }
updateAvatarDisposable.set((updateAccountPhoto(account: context.account, resource: nil, videoResource: nil, mapResourceToAvatarSizes: { resource, representations in updateAvatarDisposable.set((updateAccountPhoto(account: context.account, resource: nil, videoResource: nil, mapResourceToAvatarSizes: { resource, representations in

View File

@ -301,7 +301,7 @@ public class WallpaperGalleryController: ViewController {
updateItems.append(item) updateItems.append(item)
i += 1 i += 1
} }
return GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: updateItems, focusOnItem: self.galleryNode.pager.centralItemNode()?.index) return GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: updateItems, focusOnItem: self.galleryNode.pager.centralItemNode()?.index, synchronous: false)
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {

View File

@ -51,13 +51,13 @@ class WallpaperGalleryItem: GalleryItem {
self.source = source self.source = source
} }
func node() -> GalleryItemNode { func node(synchronous: Bool) -> GalleryItemNode {
let node = WallpaperGalleryItemNode(context: self.context) let node = WallpaperGalleryItemNode(context: self.context)
node.setEntry(self.entry, arguments: self.arguments, source: self.source) node.setEntry(self.entry, arguments: self.arguments, source: self.source)
return node return node
} }
func updateNode(node: GalleryItemNode) { func updateNode(node: GalleryItemNode, synchronous: Bool) {
if let node = node as? WallpaperGalleryItemNode { if let node = node as? WallpaperGalleryItemNode {
node.setEntry(self.entry, arguments: self.arguments, source: self.source) node.setEntry(self.entry, arguments: self.arguments, source: self.source)
} }

View File

@ -122,19 +122,21 @@ public final class CloudPhotoSizeMediaResource: TelegramMediaResource {
public let sizeSpec: String public let sizeSpec: String
public let volumeId: Int64 public let volumeId: Int64
public let localId: Int32 public let localId: Int32
public let size: Int?
public let fileReference: Data? public let fileReference: Data?
public var id: MediaResourceId { public var id: MediaResourceId {
return CloudPhotoSizeMediaResourceId(datacenterId: Int32(self.datacenterId), photoId: self.photoId, sizeSpec: self.sizeSpec) return CloudPhotoSizeMediaResourceId(datacenterId: Int32(self.datacenterId), photoId: self.photoId, sizeSpec: self.sizeSpec)
} }
public init(datacenterId: Int32, photoId: Int64, accessHash: Int64, sizeSpec: String, volumeId: Int64, localId: Int32, fileReference: Data?) { public init(datacenterId: Int32, photoId: Int64, accessHash: Int64, sizeSpec: String, volumeId: Int64, localId: Int32, size: Int?, fileReference: Data?) {
self.datacenterId = Int(datacenterId) self.datacenterId = Int(datacenterId)
self.photoId = photoId self.photoId = photoId
self.accessHash = accessHash self.accessHash = accessHash
self.sizeSpec = sizeSpec self.sizeSpec = sizeSpec
self.volumeId = volumeId self.volumeId = volumeId
self.localId = localId self.localId = localId
self.size = size
self.fileReference = fileReference self.fileReference = fileReference
} }
@ -145,6 +147,11 @@ public final class CloudPhotoSizeMediaResource: TelegramMediaResource {
self.sizeSpec = decoder.decodeStringForKey("s", orElse: "") self.sizeSpec = decoder.decodeStringForKey("s", orElse: "")
self.volumeId = decoder.decodeInt64ForKey("v", orElse: 0) self.volumeId = decoder.decodeInt64ForKey("v", orElse: 0)
self.localId = decoder.decodeInt32ForKey("l", orElse: 0) self.localId = decoder.decodeInt32ForKey("l", orElse: 0)
if let size = decoder.decodeOptionalInt32ForKey("n") {
self.size = Int(size)
} else {
self.size = nil
}
self.fileReference = decoder.decodeBytesForKey("fr")?.makeData() self.fileReference = decoder.decodeBytesForKey("fr")?.makeData()
} }
@ -155,6 +162,11 @@ public final class CloudPhotoSizeMediaResource: TelegramMediaResource {
encoder.encodeString(self.sizeSpec, forKey: "s") encoder.encodeString(self.sizeSpec, forKey: "s")
encoder.encodeInt64(self.volumeId, forKey: "v") encoder.encodeInt64(self.volumeId, forKey: "v")
encoder.encodeInt32(self.localId, forKey: "l") encoder.encodeInt32(self.localId, forKey: "l")
if let size = self.size {
encoder.encodeInt32(Int32(size), forKey: "n")
} else {
encoder.encodeNil(forKey: "n")
}
if let fileReference = self.fileReference { if let fileReference = self.fileReference {
encoder.encodeBytes(MemoryBuffer(data: fileReference), forKey: "fr") encoder.encodeBytes(MemoryBuffer(data: fileReference), forKey: "fr")
} else { } else {
@ -164,7 +176,7 @@ public final class CloudPhotoSizeMediaResource: TelegramMediaResource {
public func isEqual(to: MediaResource) -> Bool { public func isEqual(to: MediaResource) -> Bool {
if let to = to as? CloudPhotoSizeMediaResource { if let to = to as? CloudPhotoSizeMediaResource {
return self.datacenterId == to.datacenterId && self.photoId == to.photoId && self.accessHash == to.accessHash && self.sizeSpec == to.sizeSpec && self.volumeId == to.volumeId && self.localId == to.localId && self.fileReference == to.fileReference return self.datacenterId == to.datacenterId && self.photoId == to.photoId && self.accessHash == to.accessHash && self.sizeSpec == to.sizeSpec && self.volumeId == to.volumeId && self.localId == to.localId && self.size == to.size && self.fileReference == to.fileReference
} else { } else {
return false return false
} }

View File

@ -125,12 +125,13 @@ public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateMan
|> mapError { _ in return UploadPeerPhotoError.generic } |> mapError { _ in return UploadPeerPhotoError.generic }
|> mapToSignal { photo -> Signal<(UpdatePeerPhotoStatus, MediaResource?), UploadPeerPhotoError> in |> mapToSignal { photo -> Signal<(UpdatePeerPhotoStatus, MediaResource?), UploadPeerPhotoError> in
var representations: [TelegramMediaImageRepresentation] = [] var representations: [TelegramMediaImageRepresentation] = []
var videoRepresentations: [TelegramMediaImage.VideoRepresentation] = []
switch photo { switch photo {
case let .photo(photo: apiPhoto, users: _): case let .photo(photo: apiPhoto, users: _):
switch apiPhoto { switch apiPhoto {
case .photoEmpty: case .photoEmpty:
break break
case let .photo(_, _, _, _, _, sizes, videoSizes, dcId): case let .photo(_, id, accessHash, fileReference, _, sizes, videoSizes, dcId):
var sizes = sizes var sizes = sizes
if sizes.count == 3 { if sizes.count == 3 {
sizes.remove(at: 1) sizes.remove(at: 1)
@ -147,14 +148,32 @@ public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateMan
} }
} }
if let resource = photoResult.resource as? LocalFileReferenceMediaResource { if let videoSizes = videoSizes {
if let data = try? Data(contentsOf: URL(fileURLWithPath: resource.localFilePath)) { for size in videoSizes {
for representation in representations { switch size {
postbox.mediaBox.storeResourceData(representation.resource.id, data: data) case let .videoSize(type, location, w, h, size):
let resource: TelegramMediaResource
switch location {
case let .fileLocationToBeDeprecated(volumeId, localId):
resource = CloudPhotoSizeMediaResource(datacenterId: dcId, photoId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: Int(size), fileReference: fileReference.makeData())
}
videoRepresentations.append(TelegramMediaImage.VideoRepresentation(
dimensions: PixelDimensions(width: w, height: h),
resource: resource))
} }
} }
} }
for representation in representations {
postbox.mediaBox.copyResourceData(from: photoResult.resource.id, to: representation.resource.id)
}
if let resource = videoResult?.resource {
for representation in videoRepresentations {
postbox.mediaBox.copyResourceData(from: resource.id, to: representation.resource.id)
}
}
} }
} }
return postbox.transaction { transaction -> (UpdatePeerPhotoStatus, MediaResource?) in return postbox.transaction { transaction -> (UpdatePeerPhotoStatus, MediaResource?) in
@ -221,7 +240,7 @@ public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateMan
switch result { switch result {
case let .complete(representations): case let .complete(representations):
if let resource = resource as? LocalFileReferenceMediaResource { if let resource = resource as? LocalFileReferenceMediaResource {
if let data = try? Data(contentsOf: URL(fileURLWithPath: resource.localFilePath)) { if let data = try? Data(contentsOf: URL(fileURLWithPath: resource.localFilePath), options: [.mappedRead] ) {
for representation in representations { for representation in representations {
postbox.mediaBox.storeResourceData(representation.resource.id, data: data) postbox.mediaBox.storeResourceData(representation.resource.id, data: data)
} }
@ -284,16 +303,10 @@ public func updatePeerPhotoExisting(network: Network, reference: TelegramMediaIm
|> `catch` { _ -> Signal<Api.UserProfilePhoto, NoError> in |> `catch` { _ -> Signal<Api.UserProfilePhoto, NoError> in
return .complete() return .complete()
} }
|> mapToSignal { _ -> Signal<Void, NoError> in
return network.request(Api.functions.photos.deletePhotos(id: [.inputPhoto(id: imageId, accessHash: accessHash, fileReference: Buffer(data: fileReference))]))
|> `catch` { _ -> Signal<[Int64], NoError> in
return .single([])
}
|> mapToSignal { _ -> Signal<Void, NoError> in |> mapToSignal { _ -> Signal<Void, NoError> in
return .complete() return .complete()
} }
} }
}
} }
public func removeAccountPhoto(network: Network, reference: TelegramMediaImageReference?) -> Signal<Void, NoError> { public func removeAccountPhoto(network: Network, reference: TelegramMediaImageReference?) -> Signal<Void, NoError> {

View File

@ -12,13 +12,13 @@ func telegramMediaImageRepresentationsFromApiSizes(datacenterId: Int32, photoId:
case let .photoCachedSize(type, location, w, h, _): case let .photoCachedSize(type, location, w, h, _):
switch location { switch location {
case let .fileLocationToBeDeprecated(volumeId, localId): case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference) let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: nil, fileReference: fileReference)
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource)) representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource))
} }
case let .photoSize(type, location, w, h, _): case let .photoSize(type, location, w, h, size):
switch location { switch location {
case let .fileLocationToBeDeprecated(volumeId, localId): case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference) let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: Int(size), fileReference: fileReference)
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource)) representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource))
} }
case let .photoStrippedSize(_, data): case let .photoStrippedSize(_, data):
@ -44,11 +44,11 @@ func telegramMediaImageFromApiPhoto(_ photo: Api.Photo) -> TelegramMediaImage? {
if let videoSizes = videoSizes { if let videoSizes = videoSizes {
for size in videoSizes { for size in videoSizes {
switch size { switch size {
case let .videoSize(type, location, w, h, _): case let .videoSize(type, location, w, h, size):
let resource: TelegramMediaResource let resource: TelegramMediaResource
switch location { switch location {
case let .fileLocationToBeDeprecated(volumeId, localId): case let .fileLocationToBeDeprecated(volumeId, localId):
resource = CloudPhotoSizeMediaResource(datacenterId: dcId, photoId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference.makeData()) resource = CloudPhotoSizeMediaResource(datacenterId: dcId, photoId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: Int(size), fileReference: fileReference.makeData())
} }
videoRepresentations.append(TelegramMediaImage.VideoRepresentation( videoRepresentations.append(TelegramMediaImage.VideoRepresentation(

View File

@ -12,7 +12,7 @@ private func collectPreCachedResources(for photo: Api.Photo) -> [(MediaResource,
case let .photoCachedSize(type, location, _, _, bytes): case let .photoCachedSize(type, location, _, _, bytes):
switch location { switch location {
case let .fileLocationToBeDeprecated(volumeId, localId): case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudPhotoSizeMediaResource(datacenterId: dcId, photoId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference.makeData()) let resource = CloudPhotoSizeMediaResource(datacenterId: dcId, photoId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: nil, fileReference: fileReference.makeData())
let data = bytes.makeData() let data = bytes.makeData()
return [(resource, data)] return [(resource, data)]
} }

View File

@ -7,7 +7,10 @@ import TelegramUIPreferences
private func decodeColor<Key>(_ values: KeyedDecodingContainer<Key>, _ key: Key, decoder: Decoder? = nil, fallbackKey: String? = nil) throws -> UIColor { private func decodeColor<Key>(_ values: KeyedDecodingContainer<Key>, _ key: Key, decoder: Decoder? = nil, fallbackKey: String? = nil) throws -> UIColor {
if let decoder = decoder as? PresentationThemeDecoding, let fallbackKey = fallbackKey { if let decoder = decoder as? PresentationThemeDecoding, let fallbackKey = fallbackKey {
let key = (decoder.codingPath.map { $0.stringValue } + [key.stringValue]).joined(separator: ".") var codingPath = decoder.codingPath.map { $0.stringValue }
codingPath.append(key.stringValue)
let key = codingPath.joined(separator: ".")
decoder.fallbackKeys[key] = fallbackKey decoder.fallbackKeys[key] = fallbackKey
} }
@ -1021,9 +1024,17 @@ extension PresentationThemeBubbleColorComponents: Codable {
public convenience init(from decoder: Decoder) throws { public convenience init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self) let values = try decoder.container(keyedBy: CodingKeys.self)
let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".") let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".")
var fillColor = try decodeColor(values, .bg)
var gradientColor = try decodeColor(values, .gradientBg, decoder: decoder, fallbackKey: "\(codingPath).bg")
if gradientColor.rgb != fillColor.rgb {
fillColor = fillColor.withAlphaComponent(1.0)
gradientColor = gradientColor.withAlphaComponent(1.0)
}
self.init( self.init(
fill: try decodeColor(values, .bg), fill: fillColor,
gradientFill: try decodeColor(values, .gradientBg, decoder: decoder, fallbackKey: codingPath + ".bg"), gradientFill: gradientColor,
highlightedFill: try decodeColor(values, .highlightedBg), highlightedFill: try decodeColor(values, .highlightedBg),
stroke: try decodeColor(values, .stroke), stroke: try decodeColor(values, .stroke),
shadow: try? values.decode(PresentationThemeBubbleShadow.self, forKey: .shadow) shadow: try? values.decode(PresentationThemeBubbleShadow.self, forKey: .shadow)
@ -1162,7 +1173,7 @@ extension PresentationThemePartedColors: Codable {
accentControlDisabledColor: (try? decodeColor(values, .accentControlDisabled)) ?? accentControlColor.withAlphaComponent(0.5), accentControlDisabledColor: (try? decodeColor(values, .accentControlDisabled)) ?? accentControlColor.withAlphaComponent(0.5),
mediaActiveControlColor: try decodeColor(values, .mediaActiveControl), mediaActiveControlColor: try decodeColor(values, .mediaActiveControl),
mediaInactiveControlColor: try decodeColor(values, .mediaInactiveControl), mediaInactiveControlColor: try decodeColor(values, .mediaInactiveControl),
mediaControlInnerBackgroundColor: try decodeColor(values, .mediaControlInnerBg, decoder: decoder, fallbackKey: codingPath + ".bubble.withWp.bg"), mediaControlInnerBackgroundColor: try decodeColor(values, .mediaControlInnerBg, decoder: decoder, fallbackKey: "\(codingPath).bubble.withWp.bg"),
pendingActivityColor: try decodeColor(values, .pendingActivity), pendingActivityColor: try decodeColor(values, .pendingActivity),
fileTitleColor: try decodeColor(values, .fileTitle), fileTitleColor: try decodeColor(values, .fileTitle),
fileDescriptionColor: try decodeColor(values, .fileDescription), fileDescriptionColor: try decodeColor(values, .fileDescription),
@ -1389,7 +1400,7 @@ extension PresentationThemeChatInputPanel: Codable {
let values = try decoder.container(keyedBy: CodingKeys.self) let values = try decoder.container(keyedBy: CodingKeys.self)
let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".") let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".")
self.init(panelBackgroundColor: try decodeColor(values, .panelBg), self.init(panelBackgroundColor: try decodeColor(values, .panelBg),
panelBackgroundColorNoWallpaper: try decodeColor(values, .panelBg, decoder: decoder, fallbackKey: codingPath + ".panelBgNoWallpaper"), panelBackgroundColorNoWallpaper: try decodeColor(values, .panelBg, decoder: decoder, fallbackKey: "\(codingPath).panelBgNoWallpaper"),
panelSeparatorColor: try decodeColor(values, .panelSeparator), panelSeparatorColor: try decodeColor(values, .panelSeparator),
panelControlAccentColor: try decodeColor(values, .panelControlAccent), panelControlAccentColor: try decodeColor(values, .panelControlAccent),
panelControlColor: try decodeColor(values, .panelControl), panelControlColor: try decodeColor(values, .panelControl),

View File

@ -182,12 +182,20 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
} }
} }
var isExpanded: Bool = false {
didSet {
self.videoNode?.canAttachContent = self.isExpanded
}
}
init(context: AccountContext) { init(context: AccountContext) {
self.context = context self.context = context
self.imageNode = TransformImageNode() self.imageNode = TransformImageNode()
super.init() super.init()
self.clipsToBounds = true
self.imageNode.contentAnimations = [.firstUpdate, .subsequentUpdates] self.imageNode.contentAnimations = [.firstUpdate, .subsequentUpdates]
self.addSubnode(self.imageNode) self.addSubnode(self.imageNode)
@ -222,8 +230,8 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
if let video = videoRepresentations.last, let id = id { if let video = videoRepresentations.last, let id = id {
let mediaManager = self.context.sharedContext.mediaManager let mediaManager = self.context.sharedContext.mediaManager
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])])) let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .black) let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded) let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
videoNode.isUserInteractionEnabled = false videoNode.isUserInteractionEnabled = false
videoNode.ownsContentNodeUpdated = { [weak self] owns in videoNode.ownsContentNodeUpdated = { [weak self] owns in
@ -259,7 +267,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
videoNode.updateLayout(size: imageSize, transition: .immediate) videoNode.updateLayout(size: imageSize, transition: .immediate)
videoNode.frame = imageFrame videoNode.frame = imageFrame
videoNode.canAttachContent = true videoNode.canAttachContent = self.isExpanded
if videoNode.hasAttachedContext { if videoNode.hasAttachedContext {
videoNode.play() videoNode.play()
} }
@ -285,17 +293,17 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
private var items: [PeerInfoAvatarListItem] = [] private var items: [PeerInfoAvatarListItem] = []
private var itemNodes: [WrappedMediaResourceId: PeerInfoAvatarListItemNode] = [:] private var itemNodes: [WrappedMediaResourceId: PeerInfoAvatarListItemNode] = [:]
private var stripNodes: [ASImageNode] = [] private var stripNodes: [ASImageNode] = []
private var stripWidth: CGFloat = 0.0
private let activeStripImage: UIImage private let activeStripImage: UIImage
private var appliedStripNodeCurrentIndex: Int? private var appliedStripNodeCurrentIndex: Int?
private var currentIndex: Int = 0 private var currentIndex: Int = 0
private var transitionFraction: CGFloat = 0.0 private var transitionFraction: CGFloat = 0.0
private var validLayout: CGSize? private var validLayout: CGSize?
private var isExpanded = false
private let disposable = MetaDisposable() private let disposable = MetaDisposable()
private let positionDisposable = MetaDisposable()
private var initializedList = false private var initializedList = false
var itemsUpdated: (([PeerInfoAvatarListItem]) -> Void)?
let isReady = Promise<Bool>() let isReady = Promise<Bool>()
private var didSetReady = false private var didSetReady = false
@ -316,55 +324,6 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
} }
} }
private var playerUpdateTimer: SwiftSignalKit.Timer?
private var playerStatus: MediaPlayerStatus? {
didSet {
if self.playerStatus != oldValue {
if let playerStatus = playerStatus, case .playing = playerStatus.status {
self.ensureHasTimer()
} else {
self.stopTimer()
}
self.updateStatus()
}
}
}
private func ensureHasTimer() {
if self.playerUpdateTimer == nil {
let timer = SwiftSignalKit.Timer(timeout: 0.016, repeat: true, completion: { [weak self] in
self?.updateStatus()
}, queue: Queue.mainQueue())
self.playerUpdateTimer = timer
timer.start()
}
}
private func updateStatus() {
var position: CGFloat = 1.0
if let playerStatus = self.playerStatus {
var playerPosition: Double
if !playerStatus.generationTimestamp.isZero, case .playing = playerStatus.status {
playerPosition = playerStatus.timestamp + (CACurrentMediaTime() - playerStatus.generationTimestamp)
} else {
playerPosition = playerStatus.timestamp
}
position = CGFloat(playerPosition / playerStatus.duration)
}
if let appliedStripNodeCurrentIndex = self.appliedStripNodeCurrentIndex {
var frame = self.stripNodes[appliedStripNodeCurrentIndex].frame
frame.size.width = self.stripWidth * position
self.stripNodes[appliedStripNodeCurrentIndex].frame = frame
}
}
private func stopTimer() {
self.playerUpdateTimer?.invalidate()
self.playerUpdateTimer = nil
}
init(context: AccountContext) { init(context: AccountContext) {
self.context = context self.context = context
@ -537,7 +496,6 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
deinit { deinit {
self.disposable.dispose() self.disposable.dispose()
self.positionDisposable.dispose()
} }
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
@ -628,8 +586,9 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
} }
} }
func update(size: CGSize, peer: Peer?, transition: ContainedViewLayoutTransition) { func update(size: CGSize, peer: Peer?, isExpanded: Bool, transition: ContainedViewLayoutTransition) {
self.validLayout = size self.validLayout = size
self.isExpanded = isExpanded
self.leftHighlightNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: floor(size.width * 1.0 / 5.0), height: size.height)) self.leftHighlightNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: floor(size.width * 1.0 / 5.0), height: size.height))
self.rightHighlightNode.frame = CGRect(origin: CGPoint(x: size.width - floor(size.width * 1.0 / 5.0), y: 0.0), size: CGSize(width: floor(size.width * 1.0 / 5.0), height: size.height)) self.rightHighlightNode.frame = CGRect(origin: CGPoint(x: size.width - floor(size.width * 1.0 / 5.0), y: 0.0), size: CGSize(width: floor(size.width * 1.0 / 5.0), height: size.height))
@ -652,6 +611,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
} }
strongSelf.galleryEntries = entries strongSelf.galleryEntries = entries
strongSelf.items = items strongSelf.items = items
strongSelf.itemsUpdated?(items)
if let size = strongSelf.validLayout { if let size = strongSelf.validLayout {
strongSelf.updateItems(size: size, transition: .immediate, stripTransition: .immediate) strongSelf.updateItems(size: size, transition: .immediate, stripTransition: .immediate)
} }
@ -677,9 +637,11 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
var wasAdded = false var wasAdded = false
if let current = self.itemNodes[self.items[i].id] { if let current = self.itemNodes[self.items[i].id] {
itemNode = current itemNode = current
itemNode.isExpanded = self.isExpanded
} else { } else {
wasAdded = true wasAdded = true
itemNode = PeerInfoAvatarListItemNode(context: self.context) itemNode = PeerInfoAvatarListItemNode(context: self.context)
itemNode.isExpanded = self.isExpanded
itemNode.setup(item: self.items[i], synchronous: synchronous && i == self.currentIndex) itemNode.setup(item: self.items[i], synchronous: synchronous && i == self.currentIndex)
self.itemNodes[self.items[i].id] = itemNode self.itemNodes[self.items[i].id] = itemNode
self.contentNode.addSubnode(itemNode) self.contentNode.addSubnode(itemNode)
@ -687,6 +649,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
let indexOffset = CGFloat(i - self.currentIndex) let indexOffset = CGFloat(i - self.currentIndex)
let itemFrame = CGRect(origin: CGPoint(x: indexOffset * size.width + self.transitionFraction * size.width - size.width / 2.0, y: -size.height / 2.0), size: size) let itemFrame = CGRect(origin: CGPoint(x: indexOffset * size.width + self.transitionFraction * size.width - size.width / 2.0, y: -size.height / 2.0), size: size)
if wasAdded { if wasAdded {
addedItemNodesForAdditiveTransition.append(itemNode) addedItemNodesForAdditiveTransition.append(itemNode)
itemNode.frame = itemFrame itemNode.frame = itemFrame
@ -748,17 +711,6 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
if self.currentIndex >= 0 && self.currentIndex < self.stripNodes.count { if self.currentIndex >= 0 && self.currentIndex < self.stripNodes.count {
self.stripNodes[self.currentIndex].alpha = 1.0 self.stripNodes[self.currentIndex].alpha = 1.0
} }
if let currentItemNode = self.currentItemNode {
self.positionDisposable.set((currentItemNode.mediaStatus
|> deliverOnMainQueue).start(next: { [weak self] status in
if let strongSelf = self {
strongSelf.playerStatus = status
}
}))
} else {
self.positionDisposable.set(nil)
}
} }
if hadOneStripNode && self.stripNodes.count > 1 { if hadOneStripNode && self.stripNodes.count > 1 {
self.stripContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) self.stripContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
@ -766,7 +718,6 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
let stripInset: CGFloat = 8.0 let stripInset: CGFloat = 8.0
let stripSpacing: CGFloat = 4.0 let stripSpacing: CGFloat = 4.0
let stripWidth: CGFloat = max(5.0, floor((size.width - stripInset * 2.0 - stripSpacing * CGFloat(self.stripNodes.count - 1)) / CGFloat(self.stripNodes.count))) let stripWidth: CGFloat = max(5.0, floor((size.width - stripInset * 2.0 - stripSpacing * CGFloat(self.stripNodes.count - 1)) / CGFloat(self.stripNodes.count)))
self.stripWidth = stripWidth
let currentStripMinX = stripInset + CGFloat(self.currentIndex) * (stripWidth + stripSpacing) let currentStripMinX = stripInset + CGFloat(self.currentIndex) * (stripWidth + stripSpacing)
let currentStripMidX = floor(currentStripMinX + stripWidth / 2.0) let currentStripMidX = floor(currentStripMinX + stripWidth / 2.0)
let lastStripMaxX = stripInset + CGFloat(self.stripNodes.count - 1) * (stripWidth + stripSpacing) + stripWidth let lastStripMaxX = stripInset + CGFloat(self.stripNodes.count - 1) * (stripWidth + stripSpacing) + stripWidth
@ -795,6 +746,9 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
let context: AccountContext let context: AccountContext
let avatarNode: AvatarNode let avatarNode: AvatarNode
private var videoNode: UniversalVideoNode?
private var videoContent: NativeVideoContent?
var tapped: (() -> Void)? var tapped: (() -> Void)?
private var isFirstAvatarLoading = true private var isFirstAvatarLoading = true
@ -818,7 +772,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
} }
} }
func update(peer: Peer?, theme: PresentationTheme, avatarSize: CGFloat) { func update(peer: Peer?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool) {
if let peer = peer { if let peer = peer {
var overrideImage: AvatarNodeImageOverride? var overrideImage: AvatarNodeImageOverride?
if peer.isDeleted { if peer.isDeleted {
@ -829,6 +783,46 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0)) self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0))
if let item = item, case let .image(reference, _, videoRepresentations) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference {
let id = imageId
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
if videoContent.id != self.videoContent?.id {
let mediaManager = self.context.sharedContext.mediaManager
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
videoNode.isUserInteractionEnabled = false
videoNode.ownsContentNodeUpdated = { [weak self] owns in
if let strongSelf = self {
strongSelf.videoNode?.isHidden = !owns
}
}
self.videoContent = videoContent
self.videoNode = videoNode
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
let shape = CAShapeLayer()
shape.path = maskPath.cgPath
videoNode.layer.mask = shape
self.addSubnode(videoNode)
}
} else if let videoNode = self.videoNode {
self.videoContent = nil
self.videoNode = nil
videoNode.removeFromSupernode()
}
if let videoNode = self.videoNode {
videoNode.updateLayout(size: self.avatarNode.frame.size, transition: .immediate)
videoNode.frame = self.avatarNode.frame
videoNode.canAttachContent = !isExpanded
if videoNode.hasAttachedContext {
videoNode.play()
}
}
} }
} }
} }
@ -922,6 +916,9 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
let isReady = Promise<Bool>() let isReady = Promise<Bool>()
var arguments: (Peer?, PresentationTheme, CGFloat, Bool)?
var item: PeerInfoAvatarListItem?
init(context: AccountContext, readyWhenGalleryLoads: Bool) { init(context: AccountContext, readyWhenGalleryLoads: Bool) {
self.avatarContainerNode = PeerInfoAvatarTransformContainerNode(context: context) self.avatarContainerNode = PeerInfoAvatarTransformContainerNode(context: context)
self.listContainerTransformNode = ASDisplayNode() self.listContainerTransformNode = ASDisplayNode()
@ -965,10 +962,20 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
return value return value
} }
|> take(1)) |> take(1))
self.listContainerNode.itemsUpdated = { [weak self] items in
if let strongSelf = self {
strongSelf.item = items.first
if let (peer, theme, avatarSize, isExpanded) = strongSelf.arguments {
strongSelf.avatarContainerNode.update(peer: peer, item: strongSelf.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded)
}
}
}
} }
func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) { func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) {
self.avatarContainerNode.update(peer: peer, theme: theme, avatarSize: avatarSize) self.arguments = (peer, theme, avatarSize, isExpanded)
self.avatarContainerNode.update(peer: peer, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded)
} }
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
@ -2073,7 +2080,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transition.updateSublayerTransformScale(node: self.avatarListNode.listContainerTransformNode, scale: avatarListContainerScale) transition.updateSublayerTransformScale(node: self.avatarListNode.listContainerTransformNode, scale: avatarListContainerScale)
} }
self.avatarListNode.listContainerNode.update(size: expandedAvatarListSize, peer: peer, transition: transition) self.avatarListNode.listContainerNode.update(size: expandedAvatarListSize, peer: peer, isExpanded: self.isAvatarExpanded, transition: transition)
let panelWithAvatarHeight: CGFloat = 112.0 + avatarSize let panelWithAvatarHeight: CGFloat = 112.0 + avatarSize
let buttonsCollapseStart = titleCollapseOffset let buttonsCollapseStart = titleCollapseOffset

View File

@ -829,7 +829,9 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallp
let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ") let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ")
if incoming.fill.rgb != incoming.gradientFill.rgb { if incoming.fill.rgb != incoming.gradientFill.rgb {
let gradientColors = [incoming.fill, incoming.gradientFill].map { $0.cgColor } as CFArray c.clip()
let gradientColors = [incoming.fill.withAlphaComponent(1.0), incoming.gradientFill.withAlphaComponent(1.0)].map { $0.cgColor } as CFArray
var locations: [CGFloat] = [0.0, 1.0] var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB() let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
@ -855,7 +857,7 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallp
if outgoing.fill.rgb != outgoing.gradientFill.rgb { if outgoing.fill.rgb != outgoing.gradientFill.rgb {
c.clip() c.clip()
let gradientColors = [outgoing.fill, outgoing.gradientFill].map { $0.cgColor } as CFArray let gradientColors = [outgoing.fill.withAlphaComponent(1.0), outgoing.gradientFill.withAlphaComponent(1.0)].map { $0.cgColor } as CFArray
var locations: [CGFloat] = [0.0, 1.0] var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB() let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!

View File

@ -139,7 +139,7 @@ class WebSearchGalleryController: ViewController {
if strongSelf.isViewLoaded { if strongSelf.isViewLoaded {
strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({ strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({
$0.item(context: context, presentationData: strongSelf.presentationData, controllerInteraction: strongSelf.controllerInteraction) $0.item(context: context, presentationData: strongSelf.presentationData, controllerInteraction: strongSelf.controllerInteraction)
}), centralItemIndex: centralIndex, keepFirst: false) }), centralItemIndex: centralIndex)
let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in
strongSelf?.didSetReady = true strongSelf?.didSetReady = true

View File

@ -35,13 +35,13 @@ class WebSearchVideoGalleryItem: GalleryItem {
self.controllerInteraction = controllerInteraction self.controllerInteraction = controllerInteraction
} }
func node() -> GalleryItemNode { func node(synchronous: Bool) -> GalleryItemNode {
let node = WebSearchVideoGalleryItemNode(context: self.context, presentationData: self.presentationData, controllerInteraction: self.controllerInteraction) let node = WebSearchVideoGalleryItemNode(context: self.context, presentationData: self.presentationData, controllerInteraction: self.controllerInteraction)
node.setupItem(self) node.setupItem(self)
return node return node
} }
func updateNode(node: GalleryItemNode) { func updateNode(node: GalleryItemNode, synchronous: Bool) {
if let node = node as? WebSearchVideoGalleryItemNode { if let node = node as? WebSearchVideoGalleryItemNode {
node.setupItem(self) node.setupItem(self)
} }