mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
New media pasteboard screen
This commit is contained in:
parent
f8c5bf2a6b
commit
1b2379fe8c
@ -8233,3 +8233,6 @@ Sorry for the inconvenience.";
|
||||
"Channel.AdminLog.TopicRenamedWithRemovedIcon" = "%1$@ renamed topic %2$@ to %3$@ and removed icon";
|
||||
"Channel.AdminLog.TopicChangedIcon" = "%1$@ changed topic %2$@ icon to %3$@";
|
||||
"Channel.AdminLog.TopicRemovedIcon" = "%1$@ removed topic %2$@ icon";
|
||||
|
||||
"Attachment.Pasteboard" = "Clipboard";
|
||||
"Attachment.DiscardPasteboardAlertText" = "Discard pasted items?";
|
||||
|
@ -168,6 +168,7 @@ public class AttachmentController: ViewController {
|
||||
private let buttons: [AttachmentButtonType]
|
||||
private let initialButton: AttachmentButtonType
|
||||
private let fromMenu: Bool
|
||||
private let hasTextInput: Bool
|
||||
private let makeEntityInputView: () -> AttachmentTextInputPanelInputView?
|
||||
|
||||
public var willDismiss: () -> Void = {}
|
||||
@ -437,10 +438,10 @@ public class AttachmentController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private func updateSelectionCount(_ count: Int) {
|
||||
fileprivate func updateSelectionCount(_ count: Int, animated: Bool = true) {
|
||||
self.selectionCount = count
|
||||
if let layout = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .spring))
|
||||
self.containerLayoutUpdated(layout, transition: animated ? .animated(duration: 0.4, curve: .spring) : .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -754,7 +755,7 @@ public class AttachmentController: ViewController {
|
||||
let previousHasButton = self.hasButton
|
||||
let hasButton = self.panel.isButtonVisible && !self.isDismissing
|
||||
self.hasButton = hasButton
|
||||
if let controller = self.controller, controller.buttons.count > 1 {
|
||||
if let controller = self.controller, controller.buttons.count > 1 || controller.hasTextInput {
|
||||
hasPanel = true
|
||||
}
|
||||
|
||||
@ -856,13 +857,14 @@ public class AttachmentController: ViewController {
|
||||
|
||||
public var getSourceRect: (() -> CGRect?)?
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, chatLocation: ChatLocation, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, chatLocation: ChatLocation, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, hasTextInput: Bool = true, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil}) {
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.chatLocation = chatLocation
|
||||
self.buttons = buttons
|
||||
self.initialButton = initialButton
|
||||
self.fromMenu = fromMenu
|
||||
self.hasTextInput = hasTextInput
|
||||
self.makeEntityInputView = makeEntityInputView
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
@ -886,6 +888,10 @@ public class AttachmentController: ViewController {
|
||||
return self.buttons.contains(.standalone)
|
||||
}
|
||||
|
||||
public func updateSelectionCount(_ count: Int) {
|
||||
self.node.updateSelectionCount(count, animated: false)
|
||||
}
|
||||
|
||||
private var node: Node {
|
||||
return self.displayNode as! Node
|
||||
}
|
||||
|
@ -83,9 +83,6 @@
|
||||
#import <LegacyComponents/TGChannelAdminRights.h>
|
||||
#import <LegacyComponents/TGChannelBannedRights.h>
|
||||
#import <LegacyComponents/TGCheckButtonView.h>
|
||||
#import <LegacyComponents/TGClipboardGalleryMixin.h>
|
||||
#import <LegacyComponents/TGClipboardGalleryPhotoItem.h>
|
||||
#import <LegacyComponents/TGClipboardMenu.h>
|
||||
#import <LegacyComponents/TGContactMediaAttachment.h>
|
||||
#import <LegacyComponents/TGConversation.h>
|
||||
#import <LegacyComponents/TGDataResource.h>
|
||||
|
@ -1,31 +0,0 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <LegacyComponents/TGModernGalleryController.h>
|
||||
|
||||
#import <LegacyComponents/LegacyComponentsContext.h>
|
||||
|
||||
@class TGClipboardGalleryPhotoItem;
|
||||
|
||||
@protocol TGPhotoPaintStickersContext;
|
||||
|
||||
@interface TGClipboardGalleryMixin : NSObject
|
||||
|
||||
@property (nonatomic, copy) void (^itemFocused)(TGClipboardGalleryPhotoItem *);
|
||||
|
||||
@property (nonatomic, copy) void (^willTransitionIn)();
|
||||
@property (nonatomic, copy) void (^willTransitionOut)();
|
||||
@property (nonatomic, copy) void (^didTransitionOut)();
|
||||
@property (nonatomic, copy) UIView *(^referenceViewForItem)(TGClipboardGalleryPhotoItem *);
|
||||
|
||||
@property (nonatomic, copy) void (^completeWithItem)(TGClipboardGalleryPhotoItem *item, bool silentPosting, int32_t scheduleTime);
|
||||
|
||||
@property (nonatomic, copy) void (^editorOpened)(void);
|
||||
@property (nonatomic, copy) void (^editorClosed)(void);
|
||||
|
||||
@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t));
|
||||
@property (nonatomic, copy) void (^presentTimerController)(void (^)(int32_t));
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context image:(UIImage *)image images:(NSArray *)images parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext hasCaptions:(bool)hasCaptions hasTimer:(bool)hasTimer hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder recipientName:(NSString *)recipientName;
|
||||
|
||||
- (void)present;
|
||||
|
||||
@end
|
@ -1,10 +0,0 @@
|
||||
#import <LegacyComponents/TGModernGallerySelectableItem.h>
|
||||
#import <LegacyComponents/TGModernGalleryEditableItem.h>
|
||||
|
||||
@interface TGClipboardGalleryPhotoItem : NSObject <TGModernGallerySelectableItem, TGModernGalleryEditableItem>
|
||||
|
||||
@property (nonatomic, strong) UIImage *image;
|
||||
|
||||
- (instancetype)initWithImage:(UIImage *)image;
|
||||
|
||||
@end
|
@ -1,20 +0,0 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <LegacyComponents/LegacyComponentsContext.h>
|
||||
|
||||
@class TGViewController;
|
||||
@class TGMenuSheetController;
|
||||
|
||||
@class TGMediaSelectionContext;
|
||||
@class TGMediaEditingContext;
|
||||
|
||||
@protocol TGMediaSelectableItem;
|
||||
@protocol TGPhotoPaintStickersContext;
|
||||
|
||||
@interface TGClipboardMenu : NSObject
|
||||
|
||||
+ (TGMenuSheetController *)presentInParentController:(TGViewController *)parentController context:(id<LegacyComponentsContext>)context images:(NSArray *)images allowGrouping:(bool)allowGrouping hasCaption:(bool)hasCaption hasTimer:(bool)hasTimer hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext presentScheduleController:(void (^)(void(^)(int32_t)))presentScheduleController presentTimerController:(void (^)(void(^)(int32_t)))presentTimerController completed:(void (^)(TGMediaSelectionContext *selectionContext, TGMediaEditingContext *editingContext, id<TGMediaSelectableItem> currentItem, bool silentPosting, int32_t scheduleTime))completed dismissed:(void (^)(void))dismissed sourceView:(UIView *)sourceView sourceRect:(CGRect (^)(void))sourceRect;
|
||||
|
||||
+ (NSArray *)resultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext currentItem:(id<TGMediaSelectableItem>)currentItem descriptionGenerator:(id (^)(id, NSAttributedString *, NSString *))descriptionGenerator;
|
||||
|
||||
@end
|
@ -109,4 +109,6 @@ typedef enum
|
||||
|
||||
+ (NSArray *)resultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext intent:(TGMediaAssetsControllerIntent)intent currentItem:(TGMediaAsset *)currentItem storeAssets:(bool)storeAssets convertToJpeg:(bool)convertToJpeg descriptionGenerator:(id (^)(id, NSAttributedString *, NSString *, NSString *))descriptionGenerator saveEditedPhotos:(bool)saveEditedPhotos;
|
||||
|
||||
+ (NSArray *)pasteboardResultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext intent:(TGMediaAssetsControllerIntent)intent currentItem:(id<TGMediaSelectableItem>)currentItem descriptionGenerator:(id (^)(id, NSAttributedString *, NSString *, NSString *))descriptionGenerator;
|
||||
|
||||
@end
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
@class TGMediaSelectionContext;
|
||||
@protocol TGMediaSelectableItem;
|
||||
@protocol TGModernGalleryEditableItem;
|
||||
|
||||
@protocol TGPhotoPaintStickersContext;
|
||||
|
||||
|
@ -1,281 +0,0 @@
|
||||
#import "TGClipboardGalleryMixin.h"
|
||||
|
||||
#import <LegacyComponents/LegacyComponents.h>
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
|
||||
#import <LegacyComponents/TGModernGalleryController.h>
|
||||
#import "TGClipboardGalleryPhotoItem.h"
|
||||
#import "TGClipboardGalleryModel.h"
|
||||
|
||||
#import <LegacyComponents/TGMediaEditingContext.h>
|
||||
#import <LegacyComponents/TGMediaSelectionContext.h>
|
||||
|
||||
#import <LegacyComponents/TGMediaAsset.h>
|
||||
#import <LegacyComponents/TGMediaAssetFetchResult.h>
|
||||
#import <LegacyComponents/TGMediaAssetMomentList.h>
|
||||
#import <LegacyComponents/TGMediaAssetMoment.h>
|
||||
|
||||
#import "TGMediaPickerSendActionSheetController.h"
|
||||
|
||||
@interface TGClipboardGalleryMixin ()
|
||||
{
|
||||
TGMediaEditingContext *_editingContext;
|
||||
bool _asFile;
|
||||
|
||||
__weak TGViewController *_parentController;
|
||||
__weak TGModernGalleryController *_galleryController;
|
||||
TGModernGalleryController *_strongGalleryController;
|
||||
|
||||
NSUInteger _itemsLimit;
|
||||
|
||||
id<LegacyComponentsContext> _context;
|
||||
}
|
||||
|
||||
@property (nonatomic, weak, readonly) TGClipboardGalleryModel *galleryModel;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TGClipboardGalleryMixin
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context image:(UIImage *)image images:(NSArray *)images parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext hasCaptions:(bool)hasCaptions hasTimer:(bool)hasTimer hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder recipientName:(NSString *)recipientName
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
_context = context;
|
||||
_parentController = parentController;
|
||||
_editingContext = editingContext;
|
||||
|
||||
__weak TGClipboardGalleryMixin *weakSelf = self;
|
||||
|
||||
TGModernGalleryController *modernGallery = [[TGModernGalleryController alloc] initWithContext:_context];
|
||||
_galleryController = modernGallery;
|
||||
_strongGalleryController = modernGallery;
|
||||
modernGallery.isImportant = true;
|
||||
|
||||
__block NSUInteger focusIndex = 0;
|
||||
[images enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * _Nonnull stop)
|
||||
{
|
||||
if (obj == image)
|
||||
{
|
||||
focusIndex = idx;
|
||||
*stop = true;
|
||||
}
|
||||
}];
|
||||
|
||||
TGClipboardGalleryModel *model = [[TGClipboardGalleryModel alloc] initWithContext:_context images:images focusIndex:focusIndex selectionContext:selectionContext editingContext:editingContext stickersContext:stickersContext hasCaptions:hasCaptions hasTimer:hasTimer hasSelectionPanel:false recipientName:recipientName];
|
||||
_galleryModel = model;
|
||||
model.controller = modernGallery;
|
||||
model.willFinishEditingItem = ^(id<TGMediaEditableItem> editableItem, id<TGMediaEditAdjustments> adjustments, id representation, bool hasChanges)
|
||||
{
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
if (hasChanges)
|
||||
{
|
||||
[editingContext setAdjustments:adjustments forItem:editableItem];
|
||||
[editingContext setTemporaryRep:representation forItem:editableItem];
|
||||
}
|
||||
|
||||
if (selectionContext != nil && adjustments != nil && [editableItem conformsToProtocol:@protocol(TGMediaSelectableItem)])
|
||||
[selectionContext setItem:(id<TGMediaSelectableItem>)editableItem selected:true];
|
||||
};
|
||||
|
||||
model.didFinishEditingItem = ^(id<TGMediaEditableItem> editableItem, __unused id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, UIImage *thumbnailImage)
|
||||
{
|
||||
[editingContext setImage:resultImage thumbnailImage:thumbnailImage forItem:editableItem synchronous:false];
|
||||
};
|
||||
|
||||
model.didFinishRenderingFullSizeImage = ^(id<TGMediaEditableItem> editableItem, UIImage *resultImage)
|
||||
{
|
||||
[editingContext setFullSizeImage:resultImage forItem:editableItem];
|
||||
};
|
||||
|
||||
model.saveItemCaption = ^(id<TGMediaEditableItem> editableItem, NSAttributedString *caption)
|
||||
{
|
||||
[editingContext setCaption:caption forItem:editableItem];
|
||||
|
||||
if (selectionContext != nil && caption.length > 0 && [editableItem conformsToProtocol:@protocol(TGMediaSelectableItem)])
|
||||
[selectionContext setItem:(id<TGMediaSelectableItem>)editableItem selected:true];
|
||||
};
|
||||
|
||||
[model.interfaceView updateSelectionInterface:selectionContext.count counterVisible:(selectionContext.count > 0) animated:false];
|
||||
model.interfaceView.donePressed = ^(id<TGModernGalleryItem> item)
|
||||
{
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_galleryModel.dismiss(true, false);
|
||||
|
||||
if (strongSelf.completeWithItem != nil)
|
||||
strongSelf.completeWithItem((TGClipboardGalleryPhotoItem *)item, false, 0);
|
||||
};
|
||||
|
||||
model.interfaceView.doneLongPressed = ^(id<TGModernGalleryItem> item) {
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil || !(hasSilentPosting || hasSchedule))
|
||||
return;
|
||||
|
||||
if (iosMajorVersion() >= 10) {
|
||||
UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
|
||||
[generator impactOccurred];
|
||||
}
|
||||
|
||||
bool effectiveHasSchedule = hasSchedule;
|
||||
for (id item in strongSelf->_galleryModel.selectionContext.selectedItems)
|
||||
{
|
||||
if ([item isKindOfClass:[TGMediaAsset class]])
|
||||
{
|
||||
if ([[strongSelf->_editingContext timerForItem:item] integerValue] > 0)
|
||||
{
|
||||
effectiveHasSchedule = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TGMediaPickerSendActionSheetController *controller = [[TGMediaPickerSendActionSheetController alloc] initWithContext:strongSelf->_context isDark:true sendButtonFrame:strongSelf.galleryModel.interfaceView.doneButtonFrame canSendSilently:hasSilentPosting canSchedule:effectiveHasSchedule reminder:reminder hasTimer:hasTimer];
|
||||
controller.send = ^{
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_galleryModel.dismiss(true, false);
|
||||
|
||||
if (strongSelf.completeWithItem != nil)
|
||||
strongSelf.completeWithItem((TGClipboardGalleryPhotoItem *)item, false, 0);
|
||||
};
|
||||
controller.sendSilently = ^{
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_galleryModel.dismiss(true, false);
|
||||
|
||||
if (strongSelf.completeWithItem != nil)
|
||||
strongSelf.completeWithItem((TGClipboardGalleryPhotoItem *)item, true, 0);
|
||||
};
|
||||
controller.schedule = ^{
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf.presentScheduleController(^(int32_t time) {
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_galleryModel.dismiss(true, false);
|
||||
|
||||
if (strongSelf.completeWithItem != nil)
|
||||
strongSelf.completeWithItem((TGClipboardGalleryPhotoItem *)item, false, time);
|
||||
});
|
||||
};
|
||||
controller.sendWithTimer = ^{
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf.presentTimerController(^(int32_t time) {
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_galleryModel.dismiss(true, false);
|
||||
|
||||
TGMediaEditingContext *editingContext = strongSelf->_editingContext;
|
||||
NSMutableArray *items = [strongSelf->_galleryModel.selectionContext.selectedItems mutableCopy];
|
||||
[items addObject:((TGClipboardGalleryPhotoItem *)item).image];
|
||||
|
||||
for (id<TGMediaEditableItem> editableItem in items) {
|
||||
[editingContext setTimer:@(time) forItem:editableItem];
|
||||
}
|
||||
|
||||
if (strongSelf.completeWithItem != nil)
|
||||
strongSelf.completeWithItem((TGClipboardGalleryPhotoItem *)item, false, 0);
|
||||
});
|
||||
};
|
||||
|
||||
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:[strongSelf->_context makeOverlayWindowManager] parentController:strongSelf->_parentController contentController:controller];
|
||||
controllerWindow.hidden = false;
|
||||
};
|
||||
|
||||
modernGallery.model = model;
|
||||
modernGallery.itemFocused = ^(id<TGModernGalleryItem> item)
|
||||
{
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf != nil && strongSelf.itemFocused != nil)
|
||||
strongSelf.itemFocused((TGClipboardGalleryPhotoItem *)item);
|
||||
};
|
||||
|
||||
modernGallery.beginTransitionIn = ^UIView *(id<TGModernGalleryItem> item, TGModernGalleryItemView *itemView)
|
||||
{
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return nil;
|
||||
|
||||
if (strongSelf.willTransitionIn != nil)
|
||||
strongSelf.willTransitionIn();
|
||||
|
||||
if (strongSelf.referenceViewForItem != nil)
|
||||
return strongSelf.referenceViewForItem((TGClipboardGalleryPhotoItem *)item);
|
||||
|
||||
return nil;
|
||||
};
|
||||
|
||||
modernGallery.finishedTransitionIn = ^(__unused id<TGModernGalleryItem> item, __unused TGModernGalleryItemView *itemView)
|
||||
{
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
[strongSelf->_galleryModel.interfaceView setSelectedItemsModel:strongSelf->_galleryModel.selectedItemsModel];
|
||||
};
|
||||
|
||||
modernGallery.beginTransitionOut = ^UIView *(id<TGModernGalleryItem> item, TGModernGalleryItemView *itemView)
|
||||
{
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
{
|
||||
if (strongSelf.willTransitionOut != nil)
|
||||
strongSelf.willTransitionOut();
|
||||
|
||||
if (strongSelf.referenceViewForItem != nil)
|
||||
return strongSelf.referenceViewForItem((TGClipboardGalleryPhotoItem *)item);
|
||||
}
|
||||
return nil;
|
||||
};
|
||||
|
||||
modernGallery.completedTransitionOut = ^
|
||||
{
|
||||
__strong TGClipboardGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf != nil && strongSelf.didTransitionOut != nil)
|
||||
strongSelf.didTransitionOut();
|
||||
};
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)present
|
||||
{
|
||||
_galleryModel.editorOpened = self.editorOpened;
|
||||
_galleryModel.editorClosed = self.editorClosed;
|
||||
|
||||
[_galleryController setPreviewMode:false];
|
||||
|
||||
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:[_context makeOverlayWindowManager] parentController:_parentController contentController:_galleryController];
|
||||
controllerWindow.hidden = false;
|
||||
_galleryController.view.clipsToBounds = true;
|
||||
|
||||
_strongGalleryController = nil;
|
||||
}
|
||||
|
||||
- (UIViewController *)galleryController
|
||||
{
|
||||
return _galleryController;
|
||||
}
|
||||
|
||||
@end
|
@ -1,31 +0,0 @@
|
||||
#import <LegacyComponents/TGModernGalleryModel.h>
|
||||
|
||||
#import <LegacyComponents/TGMediaPickerGalleryInterfaceView.h>
|
||||
#import <LegacyComponents/TGModernGalleryController.h>
|
||||
|
||||
#import <LegacyComponents/TGPhotoEditorController.h>
|
||||
|
||||
#import <LegacyComponents/LegacyComponentsContext.h>
|
||||
|
||||
@interface TGClipboardGalleryModel : TGModernGalleryModel
|
||||
|
||||
@property (nonatomic, copy) void (^willFinishEditingItem)(id<TGMediaEditableItem> item, id<TGMediaEditAdjustments> adjustments, id temporaryRep, bool hasChanges);
|
||||
@property (nonatomic, copy) void (^didFinishEditingItem)(id<TGMediaEditableItem>item, id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, UIImage *thumbnailImage);
|
||||
@property (nonatomic, copy) void (^didFinishRenderingFullSizeImage)(id<TGMediaEditableItem> item, UIImage *fullSizeImage);
|
||||
|
||||
@property (nonatomic, copy) void (^saveItemCaption)(id<TGMediaEditableItem> item, NSAttributedString *caption);
|
||||
|
||||
@property (nonatomic, copy) void (^editorOpened)(void);
|
||||
@property (nonatomic, copy) void (^editorClosed)(void);
|
||||
|
||||
@property (nonatomic, weak) TGModernGalleryController *controller;
|
||||
|
||||
@property (nonatomic, readonly, strong) TGMediaPickerGalleryInterfaceView *interfaceView;
|
||||
@property (nonatomic, readonly, strong) TGMediaPickerGallerySelectedItemsModel *selectedItemsModel;
|
||||
|
||||
@property (nonatomic, readonly) TGMediaSelectionContext *selectionContext;
|
||||
@property (nonatomic, strong) id<TGPhotoPaintStickersContext> stickersContext;
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context images:(NSArray *)images focusIndex:(NSUInteger)focusIndex selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext hasCaptions:(bool)hasCaptions hasTimer:(bool)hasTimer hasSelectionPanel:(bool)hasSelectionPanel recipientName:(NSString *)recipientName;
|
||||
|
||||
@end
|
@ -1,500 +0,0 @@
|
||||
#import "TGClipboardGalleryModel.h"
|
||||
|
||||
#import "TGMediaPickerGallerySelectedItemsModel.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
|
||||
#import <LegacyComponents/TGModernGalleryController.h>
|
||||
#import <LegacyComponents/TGModernGalleryItem.h>
|
||||
#import "TGModernGallerySelectableItem.h"
|
||||
#import "TGModernGalleryEditableItem.h"
|
||||
#import "TGModernGalleryEditableItemView.h"
|
||||
#import <LegacyComponents/TGModernGalleryZoomableItemView.h>
|
||||
|
||||
#import "TGClipboardGalleryPhotoItem.h"
|
||||
|
||||
#import "TGModernMediaListItem.h"
|
||||
#import "TGModernMediaListSelectableItem.h"
|
||||
|
||||
#import <LegacyComponents/PGPhotoEditorValues.h>
|
||||
|
||||
#import <LegacyComponents/TGSecretTimerMenu.h>
|
||||
|
||||
@interface TGClipboardGalleryModel ()
|
||||
{
|
||||
id<TGModernGalleryEditableItem> _itemBeingEdited;
|
||||
TGMediaEditingContext *_editingContext;
|
||||
|
||||
id<LegacyComponentsContext> _context;
|
||||
}
|
||||
|
||||
@property (nonatomic, weak) TGPhotoEditorController *editorController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TGClipboardGalleryModel
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context images:(NSArray *)images focusIndex:(NSUInteger)focusIndex selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext hasCaptions:(bool)hasCaptions hasTimer:(bool)hasTimer hasSelectionPanel:(bool)hasSelectionPanel recipientName:(NSString *)recipientName
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
NSMutableArray *items = [[NSMutableArray alloc] init];
|
||||
TGClipboardGalleryPhotoItem *focusItem = nil;
|
||||
NSUInteger i = 0;
|
||||
for (UIImage *image in images)
|
||||
{
|
||||
TGClipboardGalleryPhotoItem *item = [[TGClipboardGalleryPhotoItem alloc] initWithImage:image];
|
||||
item.selectionContext = selectionContext;
|
||||
item.editingContext = editingContext;
|
||||
item.stickersContext = stickersContext;
|
||||
[items addObject:item];
|
||||
|
||||
if (i == focusIndex)
|
||||
focusItem = item;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
[self _replaceItems:items focusingOnItem:focusItem];
|
||||
|
||||
_editingContext = editingContext;
|
||||
_selectionContext = selectionContext;
|
||||
_stickersContext = stickersContext;
|
||||
|
||||
__weak TGClipboardGalleryModel *weakSelf = self;
|
||||
if (selectionContext != nil)
|
||||
{
|
||||
_selectedItemsModel = [[TGMediaPickerGallerySelectedItemsModel alloc] initWithSelectionContext:selectionContext];
|
||||
_selectedItemsModel.selectionUpdated = ^(bool reload, bool incremental, bool add, NSInteger index)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
[strongSelf.interfaceView updateSelectionInterface:[strongSelf selectionCount] counterVisible:([strongSelf selectionCount] > 0) animated:incremental];
|
||||
[strongSelf.interfaceView updateSelectedPhotosView:reload incremental:incremental add:add index:index];
|
||||
};
|
||||
}
|
||||
|
||||
_interfaceView = [[TGMediaPickerGalleryInterfaceView alloc] initWithContext:_context focusItem:focusItem selectionContext:selectionContext editingContext:editingContext stickersContext:stickersContext hasSelectionPanel:hasSelectionPanel hasCameraButton:false recipientName:recipientName];
|
||||
_interfaceView.hasCaptions = hasCaptions;
|
||||
_interfaceView.hasTimer = hasTimer;
|
||||
[_interfaceView setEditorTabPressed:^(TGPhotoEditorTab tab)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
__strong TGModernGalleryController *controller = strongSelf.controller;
|
||||
if ([controller.currentItem conformsToProtocol:@protocol(TGModernGalleryEditableItem)])
|
||||
[strongSelf presentPhotoEditorForItem:(id<TGModernGalleryEditableItem>)controller.currentItem tab:tab];
|
||||
}];
|
||||
_interfaceView.photoStripItemSelected = ^(NSInteger index)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
[strongSelf setCurrentItemWithIndex:index];
|
||||
};
|
||||
_interfaceView.captionSet = ^(id<TGModernGalleryItem> item, NSAttributedString *caption)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil || strongSelf.saveItemCaption == nil)
|
||||
return;
|
||||
|
||||
__strong TGModernGalleryController *controller = strongSelf.controller;
|
||||
if ([controller.currentItem conformsToProtocol:@protocol(TGModernGalleryEditableItem)])
|
||||
strongSelf.saveItemCaption(((id<TGModernGalleryEditableItem>)item).editableMediaItem, caption);
|
||||
};
|
||||
_interfaceView.timerRequested = ^
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
__strong TGModernGalleryController *controller = strongSelf.controller;
|
||||
id<TGMediaEditableItem> editableMediaItem = ((id<TGModernGalleryEditableItem>)controller.currentItem).editableMediaItem;
|
||||
|
||||
NSString *description = editableMediaItem.isVideo ? TGLocalized(@"SecretTimer.VideoDescription") : TGLocalized(@"SecretTimer.ImageDescription");
|
||||
|
||||
NSString *lastValueKey = @"mediaPickerLastTimerValue_v0";
|
||||
NSNumber *value = [strongSelf->_editingContext timerForItem:editableMediaItem];
|
||||
if (value == nil)
|
||||
value = [[NSUserDefaults standardUserDefaults] objectForKey:lastValueKey];
|
||||
|
||||
[TGSecretTimerMenu presentInParentController:controller context:strongSelf->_context dark:true description:description values:[TGSecretTimerMenu secretMediaTimerValues] value:value completed:^(NSNumber *value)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
if (value == nil)
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:lastValueKey];
|
||||
else
|
||||
[[NSUserDefaults standardUserDefaults] setObject:value forKey:lastValueKey];
|
||||
|
||||
[strongSelf->_editingContext setTimer:value forItem:editableMediaItem];
|
||||
|
||||
if (value.integerValue != 0)
|
||||
{
|
||||
__strong TGModernGalleryController *controller = strongSelf.controller;
|
||||
id<TGMediaSelectableItem> selectableItem = nil;
|
||||
if ([controller.currentItem conformsToProtocol:@protocol(TGModernGallerySelectableItem)])
|
||||
{
|
||||
selectableItem = ((id<TGModernGallerySelectableItem>)controller.currentItem).selectableMediaItem;
|
||||
|
||||
if (selectableItem != nil) {
|
||||
[strongSelf->_selectionContext setItem:selectableItem selected:true animated:false sender:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
} dismissed:^
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
[strongSelf->_interfaceView setAllInterfaceHidden:false delay:0.0f animated:true];
|
||||
} sourceView:controller.view sourceRect:^CGRect
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return CGRectZero;
|
||||
|
||||
__strong TGModernGalleryController *controller = strongSelf.controller;
|
||||
return [strongSelf->_interfaceView.timerButton convertRect:strongSelf->_interfaceView.timerButton.bounds toView:controller.view];
|
||||
}];
|
||||
};
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSInteger)selectionCount
|
||||
{
|
||||
return _selectedItemsModel.selectedCount;
|
||||
}
|
||||
|
||||
- (void)setCurrentItem:(id<TGMediaSelectableItem>)item direction:(TGModernGalleryScrollAnimationDirection)direction
|
||||
{
|
||||
if (![(id)item conformsToProtocol:@protocol(TGMediaSelectableItem)])
|
||||
return;
|
||||
|
||||
id<TGMediaSelectableItem> targetSelectableItem = (id<TGMediaSelectableItem>)item;
|
||||
|
||||
__block NSUInteger newIndex = NSNotFound;
|
||||
[self.items enumerateObjectsUsingBlock:^(id<TGModernGalleryItem> galleryItem, NSUInteger idx, BOOL *stop)
|
||||
{
|
||||
if ([galleryItem conformsToProtocol:@protocol(TGModernGallerySelectableItem)])
|
||||
{
|
||||
id<TGMediaSelectableItem> selectableItem = ((id<TGModernGallerySelectableItem>)galleryItem).selectableMediaItem;
|
||||
|
||||
if ([selectableItem.uniqueIdentifier isEqual:targetSelectableItem.uniqueIdentifier])
|
||||
{
|
||||
newIndex = idx;
|
||||
*stop = true;
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
TGModernGalleryController *galleryController = self.controller;
|
||||
[galleryController setCurrentItemIndex:newIndex direction:direction animated:true];
|
||||
}
|
||||
|
||||
- (void)setCurrentItemWithIndex:(NSUInteger)index
|
||||
{
|
||||
if (_selectedItemsModel == nil)
|
||||
return;
|
||||
|
||||
TGModernGalleryController *galleryController = self.controller;
|
||||
|
||||
if (![galleryController.currentItem conformsToProtocol:@protocol(TGModernGallerySelectableItem)])
|
||||
return;
|
||||
|
||||
id<TGModernGallerySelectableItem> currentGalleryItem = (id<TGModernGallerySelectableItem>)galleryController.currentItem;
|
||||
|
||||
__block NSUInteger currentSelectedItemIndex = NSNotFound;
|
||||
[_selectedItemsModel.items enumerateObjectsUsingBlock:^(id<TGMediaSelectableItem> item, NSUInteger index, BOOL *stop)
|
||||
{
|
||||
if ([item.uniqueIdentifier isEqualToString:currentGalleryItem.selectableMediaItem.uniqueIdentifier])
|
||||
{
|
||||
currentSelectedItemIndex = index;
|
||||
*stop = true;
|
||||
}
|
||||
}];
|
||||
|
||||
id<TGMediaSelectableItem> item = _selectedItemsModel.items[index];
|
||||
|
||||
TGModernGalleryScrollAnimationDirection direction = TGModernGalleryScrollAnimationDirectionLeft;
|
||||
if (currentSelectedItemIndex < index)
|
||||
direction = TGModernGalleryScrollAnimationDirectionRight;
|
||||
|
||||
[self setCurrentItem:item direction:direction];
|
||||
}
|
||||
|
||||
- (UIView <TGModernGalleryInterfaceView> *)createInterfaceView
|
||||
{
|
||||
return _interfaceView;
|
||||
}
|
||||
|
||||
- (UIView *)referenceViewForItem:(id<TGModernGalleryItem>)item frame:(CGRect *)frame
|
||||
{
|
||||
TGModernGalleryController *galleryController = self.controller;
|
||||
TGModernGalleryItemView *galleryItemView = [galleryController itemViewForItem:item];
|
||||
|
||||
if ([galleryItemView isKindOfClass:[TGModernGalleryZoomableItemView class]])
|
||||
{
|
||||
TGModernGalleryZoomableItemView *zoomableItemView = (TGModernGalleryZoomableItemView *)galleryItemView;
|
||||
|
||||
if (zoomableItemView.contentView != nil)
|
||||
{
|
||||
if (frame != NULL)
|
||||
*frame = [zoomableItemView transitionViewContentRect];
|
||||
|
||||
return (UIImageView *)zoomableItemView.transitionContentView;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)updateHiddenItem
|
||||
{
|
||||
TGModernGalleryController *galleryController = self.controller;
|
||||
|
||||
for (TGModernGalleryItemView *itemView in galleryController.visibleItemViews)
|
||||
{
|
||||
if ([itemView conformsToProtocol:@protocol(TGModernGalleryEditableItemView)])
|
||||
[(TGModernGalleryItemView <TGModernGalleryEditableItemView> *)itemView setHiddenAsBeingEdited:[itemView.item isEqual:_itemBeingEdited]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateEditedItemView
|
||||
{
|
||||
TGModernGalleryController *galleryController = self.controller;
|
||||
|
||||
for (TGModernGalleryItemView *itemView in galleryController.visibleItemViews)
|
||||
{
|
||||
if ([itemView conformsToProtocol:@protocol(TGModernGalleryEditableItemView)])
|
||||
{
|
||||
if ([itemView.item isEqual:_itemBeingEdited])
|
||||
{
|
||||
[(TGModernGalleryItemView <TGModernGalleryEditableItemView> *)itemView setItem:_itemBeingEdited synchronously:true];
|
||||
if (self.itemsUpdated != nil)
|
||||
self.itemsUpdated(_itemBeingEdited);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)presentPhotoEditorForItem:(id<TGModernGalleryEditableItem>)item tab:(TGPhotoEditorTab)tab
|
||||
{
|
||||
__weak TGClipboardGalleryModel *weakSelf = self;
|
||||
|
||||
if (_itemBeingEdited != nil)
|
||||
return;
|
||||
|
||||
_itemBeingEdited = item;
|
||||
|
||||
PGPhotoEditorValues *editorValues = (PGPhotoEditorValues *)[item.editingContext adjustmentsForItem:item.editableMediaItem];
|
||||
|
||||
NSAttributedString *caption = [item.editingContext captionForItem:item.editableMediaItem];
|
||||
|
||||
CGRect refFrame = CGRectZero;
|
||||
UIView *editorReferenceView = [self referenceViewForItem:item frame:&refFrame];
|
||||
UIView *referenceView = nil;
|
||||
UIImage *screenImage = nil;
|
||||
UIView *referenceParentView = nil;
|
||||
UIImage *image = nil;
|
||||
|
||||
bool isVideo = false;
|
||||
if ([editorReferenceView isKindOfClass:[UIImageView class]])
|
||||
{
|
||||
screenImage = [(UIImageView *)editorReferenceView image];
|
||||
referenceView = editorReferenceView;
|
||||
}
|
||||
|
||||
TGPhotoEditorControllerIntent intent = isVideo ? TGPhotoEditorControllerVideoIntent : TGPhotoEditorControllerGenericIntent;
|
||||
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:_context item:item.editableMediaItem intent:intent adjustments:editorValues caption:caption screenImage:screenImage availableTabs:_interfaceView.currentTabs selectedTab:tab];
|
||||
controller.editingContext = _editingContext;
|
||||
controller.stickersContext = _stickersContext;
|
||||
self.editorController = controller;
|
||||
controller.willFinishEditing = ^(id<TGMediaEditAdjustments> adjustments, id temporaryRep, bool hasChanges)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_itemBeingEdited = nil;
|
||||
|
||||
if (strongSelf.willFinishEditingItem != nil)
|
||||
strongSelf.willFinishEditingItem(item.editableMediaItem, adjustments, temporaryRep, hasChanges);
|
||||
};
|
||||
|
||||
void (^didFinishEditingItem)(id<TGMediaEditableItem>item, id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, UIImage *thumbnailImage) = self.didFinishEditingItem;
|
||||
controller.didFinishEditing = ^(id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, UIImage *thumbnailImage, bool hasChanges)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil) {
|
||||
TGLegacyLog(@"controller.didFinishEditing strongSelf == nil");
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (adjustments != nil && hasChanges && !isVideo)
|
||||
NSAssert(resultImage != nil, @"resultImage should not be nil");
|
||||
#endif
|
||||
|
||||
if (hasChanges)
|
||||
{
|
||||
if (didFinishEditingItem != nil) {
|
||||
didFinishEditingItem(item.editableMediaItem, adjustments, resultImage, thumbnailImage);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
controller.didFinishRenderingFullSizeImage = ^(UIImage *image)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
if (strongSelf.didFinishRenderingFullSizeImage != nil)
|
||||
strongSelf.didFinishRenderingFullSizeImage(item.editableMediaItem, image);
|
||||
};
|
||||
|
||||
controller.captionSet = ^(NSAttributedString *caption)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
if (strongSelf.saveItemCaption != nil)
|
||||
strongSelf.saveItemCaption(item.editableMediaItem, caption);
|
||||
};
|
||||
|
||||
controller.requestToolbarsHidden = ^(bool hidden, bool animated)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
[strongSelf.interfaceView setToolbarsHidden:hidden animated:animated];
|
||||
};
|
||||
|
||||
controller.beginTransitionIn = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return nil;
|
||||
|
||||
if (strongSelf.editorOpened != nil)
|
||||
strongSelf.editorOpened();
|
||||
|
||||
[strongSelf updateHiddenItem];
|
||||
[strongSelf.interfaceView editorTransitionIn];
|
||||
|
||||
*referenceFrame = refFrame;
|
||||
|
||||
if (referenceView.superview == nil)
|
||||
*parentView = referenceParentView;
|
||||
|
||||
if (iosMajorVersion() >= 7)
|
||||
[strongSelf.controller setNeedsStatusBarAppearanceUpdate];
|
||||
else
|
||||
[_context setStatusBarHidden:true withAnimation:UIStatusBarAnimationNone];
|
||||
|
||||
return referenceView;
|
||||
};
|
||||
|
||||
controller.finishedTransitionIn = ^
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
TGModernGalleryController *galleryController = strongSelf.controller;
|
||||
TGModernGalleryItemView *galleryItemView = [galleryController itemViewForItem:strongSelf->_itemBeingEdited];
|
||||
if (![galleryItemView isKindOfClass:[TGModernGalleryZoomableItemView class]])
|
||||
return;
|
||||
|
||||
TGModernGalleryZoomableItemView *zoomableItemView = (TGModernGalleryZoomableItemView *)galleryItemView;
|
||||
[zoomableItemView reset];
|
||||
};
|
||||
|
||||
controller.beginTransitionOut = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return nil;
|
||||
|
||||
[strongSelf.interfaceView editorTransitionOut];
|
||||
|
||||
CGRect refFrame;
|
||||
UIView *referenceView = [strongSelf referenceViewForItem:item frame:&refFrame];
|
||||
|
||||
*referenceFrame = refFrame;
|
||||
|
||||
return referenceView;
|
||||
};
|
||||
|
||||
controller.finishedTransitionOut = ^(__unused bool saved)
|
||||
{
|
||||
__strong TGClipboardGalleryModel *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
if (strongSelf.editorClosed != nil)
|
||||
strongSelf.editorClosed();
|
||||
|
||||
[strongSelf updateHiddenItem];
|
||||
|
||||
if (iosMajorVersion() >= 7)
|
||||
[strongSelf.controller setNeedsStatusBarAppearanceUpdate];
|
||||
else {
|
||||
[_context setStatusBarHidden:false withAnimation:UIStatusBarAnimationNone];
|
||||
}
|
||||
};
|
||||
|
||||
controller.requestThumbnailImage = ^SSignal *(id<TGMediaEditableItem> editableItem)
|
||||
{
|
||||
return [editableItem thumbnailImageSignal];
|
||||
};
|
||||
|
||||
controller.requestOriginalScreenSizeImage = ^SSignal *(id<TGMediaEditableItem> editableItem, NSTimeInterval position)
|
||||
{
|
||||
return [editableItem screenImageSignal:position];
|
||||
};
|
||||
|
||||
controller.requestOriginalFullSizeImage = ^SSignal *(id<TGMediaEditableItem> editableItem, NSTimeInterval position)
|
||||
{
|
||||
return [editableItem originalImageSignal:position];
|
||||
};
|
||||
|
||||
controller.requestImage = ^
|
||||
{
|
||||
return image;
|
||||
};
|
||||
|
||||
[self.controller addChildViewController:controller];
|
||||
[self.controller.view addSubview:controller.view];
|
||||
}
|
||||
|
||||
- (void)_replaceItems:(NSArray *)items focusingOnItem:(id<TGModernGalleryItem>)item
|
||||
{
|
||||
[super _replaceItems:items focusingOnItem:item];
|
||||
|
||||
TGModernGalleryController *controller = self.controller;
|
||||
|
||||
NSArray *itemViews = [controller.visibleItemViews copy];
|
||||
for (TGModernGalleryItemView *itemView in itemViews)
|
||||
[itemView setItem:itemView.item synchronously:false];
|
||||
}
|
||||
|
||||
- (bool)_shouldAutorotate
|
||||
{
|
||||
TGPhotoEditorController *editorController = self.editorController;
|
||||
return (!editorController || [editorController shouldAutorotate]);
|
||||
}
|
||||
|
||||
@end
|
@ -1,56 +0,0 @@
|
||||
#import "TGClipboardGalleryPhotoItem.h"
|
||||
#import "TGClipboardGalleryPhotoItemView.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
|
||||
#import "UIImage+TGMediaEditableItem.h"
|
||||
|
||||
@implementation TGClipboardGalleryPhotoItem
|
||||
|
||||
@synthesize selectionContext;
|
||||
@synthesize editingContext;
|
||||
@synthesize stickersContext;
|
||||
|
||||
- (instancetype)initWithImage:(UIImage *)image
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
_image = image;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)uniqueId
|
||||
{
|
||||
return self.image.uniqueIdentifier;
|
||||
}
|
||||
|
||||
- (id<TGMediaSelectableItem>)selectableMediaItem
|
||||
{
|
||||
return self.image;
|
||||
}
|
||||
|
||||
- (id<TGMediaEditableItem>)editableMediaItem
|
||||
{
|
||||
return self.image;
|
||||
}
|
||||
|
||||
- (TGPhotoEditorTab)toolbarTabs
|
||||
{
|
||||
return TGPhotoEditorCropTab | TGPhotoEditorToolsTab | TGPhotoEditorPaintTab;
|
||||
}
|
||||
|
||||
|
||||
- (Class)viewClass
|
||||
{
|
||||
return [TGClipboardGalleryPhotoItemView class];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
return [object isKindOfClass:[TGClipboardGalleryPhotoItem class]] && TGObjectCompare(_image, ((TGClipboardGalleryPhotoItem *)object)->_image);
|
||||
}
|
||||
|
||||
|
||||
@end
|
@ -1,11 +0,0 @@
|
||||
#import <LegacyComponents/LegacyComponents.h>
|
||||
#import "TGModernGalleryEditableItemView.h"
|
||||
#import "TGModernGalleryImageItemImageView.h"
|
||||
|
||||
@interface TGClipboardGalleryPhotoItemView : TGModernGalleryZoomableItemView <TGModernGalleryEditableItemView>
|
||||
|
||||
@property (nonatomic) CGSize imageSize;
|
||||
|
||||
@property (nonatomic, strong) TGModernGalleryImageItemImageView *imageView;
|
||||
|
||||
@end
|
@ -1,208 +0,0 @@
|
||||
#import "TGClipboardGalleryPhotoItemView.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
#import "TGFont.h"
|
||||
#import "TGStringUtils.h"
|
||||
|
||||
#import <LegacyComponents/TGMediaAssetImageSignals.h>
|
||||
|
||||
#import <LegacyComponents/TGPhotoEditorUtils.h>
|
||||
|
||||
#import <LegacyComponents/TGModernGalleryZoomableScrollView.h>
|
||||
#import <LegacyComponents/TGMessageImageViewOverlayView.h>
|
||||
#import <LegacyComponents/TGImageView.h>
|
||||
|
||||
#import <LegacyComponents/TGMediaSelectionContext.h>
|
||||
|
||||
#import "TGClipboardGalleryPhotoItem.h"
|
||||
|
||||
@interface TGClipboardGalleryPhotoItemView ()
|
||||
{
|
||||
UIView *_temporaryRepView;
|
||||
|
||||
SMetaDisposable *_attributesDisposable;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TGClipboardGalleryPhotoItemView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
{
|
||||
_imageView = [[TGModernGalleryImageItemImageView alloc] init];
|
||||
[self.scrollView addSubview:_imageView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_attributesDisposable dispose];
|
||||
}
|
||||
|
||||
- (void)setHiddenAsBeingEdited:(bool)hidden
|
||||
{
|
||||
self.imageView.hidden = hidden;
|
||||
_temporaryRepView.hidden = hidden;
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
_imageView.hidden = false;
|
||||
[_imageView reset];
|
||||
}
|
||||
|
||||
- (void)setItem:(TGClipboardGalleryPhotoItem *)item synchronously:(bool)synchronously
|
||||
{
|
||||
[super setItem:item synchronously:synchronously];
|
||||
|
||||
_imageSize = item.image.size;
|
||||
[self reset];
|
||||
|
||||
if (item.image == nil)
|
||||
{
|
||||
[self.imageView reset];
|
||||
}
|
||||
else
|
||||
{
|
||||
__weak TGClipboardGalleryPhotoItemView *weakSelf = self;
|
||||
void (^fadeOutRepView)(void) = ^
|
||||
{
|
||||
__strong TGClipboardGalleryPhotoItemView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
if (strongSelf->_temporaryRepView == nil)
|
||||
return;
|
||||
|
||||
UIView *repView = strongSelf->_temporaryRepView;
|
||||
strongSelf->_temporaryRepView = nil;
|
||||
[UIView animateWithDuration:0.2f animations:^
|
||||
{
|
||||
repView.alpha = 0.0f;
|
||||
} completion:^(__unused BOOL finished)
|
||||
{
|
||||
[repView removeFromSuperview];
|
||||
}];
|
||||
};
|
||||
|
||||
SSignal *assetSignal = [SSignal single:item.image];
|
||||
|
||||
SSignal *imageSignal = assetSignal;
|
||||
if (item.editingContext != nil)
|
||||
{
|
||||
imageSignal = [[[item.editingContext imageSignalForItem:item.editableMediaItem] deliverOn:[SQueue mainQueue]] mapToSignal:^SSignal *(id result)
|
||||
{
|
||||
__strong TGClipboardGalleryPhotoItemView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return [SSignal complete];
|
||||
|
||||
if (result == nil)
|
||||
{
|
||||
return [[assetSignal deliverOn:[SQueue mainQueue]] afterNext:^(__unused id next)
|
||||
{
|
||||
fadeOutRepView();
|
||||
}];
|
||||
}
|
||||
else if ([result isKindOfClass:[UIView class]])
|
||||
{
|
||||
[strongSelf _setTemporaryRepView:result];
|
||||
return [[SSignal single:nil] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
else
|
||||
{
|
||||
return [[[SSignal single:result] deliverOn:[SQueue mainQueue]] afterNext:^(__unused id next)
|
||||
{
|
||||
fadeOutRepView();
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
[self.imageView setSignal:[[imageSignal deliverOn:[SQueue mainQueue]] afterNext:^(id next)
|
||||
{
|
||||
__strong TGClipboardGalleryPhotoItemView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
if ([next isKindOfClass:[UIImage class]])
|
||||
strongSelf->_imageSize = ((UIImage *)next).size;
|
||||
|
||||
[strongSelf reset];
|
||||
}]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_setTemporaryRepView:(UIView *)view
|
||||
{
|
||||
[_temporaryRepView removeFromSuperview];
|
||||
_temporaryRepView = view;
|
||||
|
||||
_imageSize = TGScaleToSize(view.frame.size, self.containerView.frame.size);
|
||||
|
||||
view.hidden = self.imageView.hidden;
|
||||
view.frame = CGRectMake((self.containerView.frame.size.width - _imageSize.width) / 2.0f, (self.containerView.frame.size.height - _imageSize.height) / 2.0f, _imageSize.width, _imageSize.height);
|
||||
|
||||
[self.containerView addSubview:view];
|
||||
}
|
||||
|
||||
- (void)singleTap
|
||||
{
|
||||
if ([self.item conformsToProtocol:@protocol(TGModernGallerySelectableItem)])
|
||||
{
|
||||
TGMediaSelectionContext *selectionContext = ((id<TGModernGallerySelectableItem>)self.item).selectionContext;
|
||||
id<TGMediaSelectableItem> item = ((id<TGModernGallerySelectableItem>)self.item).selectableMediaItem;
|
||||
|
||||
[selectionContext toggleItemSelection:item animated:true sender:nil success:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
id<TGModernGalleryItemViewDelegate> delegate = self.delegate;
|
||||
if ([delegate respondsToSelector:@selector(itemViewDidRequestInterfaceShowHide:)])
|
||||
[delegate itemViewDidRequestInterfaceShowHide:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIView *)footerView
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (SSignal *)contentAvailabilityStateSignal
|
||||
{
|
||||
return [SSignal single:@true];
|
||||
}
|
||||
|
||||
- (CGSize)contentSize
|
||||
{
|
||||
return _imageSize;
|
||||
}
|
||||
|
||||
- (UIView *)contentView
|
||||
{
|
||||
return _imageView;
|
||||
}
|
||||
|
||||
- (UIView *)transitionContentView
|
||||
{
|
||||
if (_temporaryRepView != nil)
|
||||
return _temporaryRepView;
|
||||
|
||||
return [self contentView];
|
||||
}
|
||||
|
||||
- (UIView *)transitionView
|
||||
{
|
||||
return self.containerView;
|
||||
}
|
||||
|
||||
- (CGRect)transitionViewContentRect
|
||||
{
|
||||
UIView *contentView = [self transitionContentView];
|
||||
return [contentView convertRect:contentView.bounds toView:[self transitionView]];
|
||||
}
|
||||
|
||||
@end
|
@ -1,226 +0,0 @@
|
||||
#import "TGClipboardMenu.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
|
||||
#import <LegacyComponents/TGStringUtils.h>
|
||||
#import <LegacyComponents/TGMenuSheetController.h>
|
||||
|
||||
#import "TGClipboardPreviewItemView.h"
|
||||
|
||||
@implementation TGClipboardMenu
|
||||
|
||||
+ (TGMenuSheetController *)presentInParentController:(TGViewController *)parentController context:(id<LegacyComponentsContext>)context images:(NSArray *)images allowGrouping:(bool)allowGrouping hasCaption:(bool)hasCaption hasTimer:(bool)hasTimer hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext presentScheduleController:(void (^)(void(^)(int32_t)))presentScheduleController presentTimerController:(void (^)(void(^)(int32_t)))presentTimerController completed:(void (^)(TGMediaSelectionContext *selectionContext, TGMediaEditingContext *editingContext, id<TGMediaSelectableItem> currentItem, bool silentPosting, int32_t scheduleTime))completed dismissed:(void (^)(void))dismissed sourceView:(UIView *)sourceView sourceRect:(CGRect (^)(void))sourceRect
|
||||
{
|
||||
bool centered = false;
|
||||
if (sourceRect == nil)
|
||||
{
|
||||
centered = true;
|
||||
sourceRect = ^CGRect
|
||||
{
|
||||
return CGRectMake(CGRectGetMidX(sourceView.frame), CGRectGetMidY(sourceView.frame), 0, 0);
|
||||
};
|
||||
}
|
||||
|
||||
TGMenuSheetController *controller = [[TGMenuSheetController alloc] initWithContext:context dark:false];
|
||||
__weak TGMenuSheetController *weakController = controller;
|
||||
controller.dismissesByOutsideTap = true;
|
||||
controller.forceFullScreen = true;
|
||||
controller.hasSwipeGesture = true;
|
||||
controller.narrowInLandscape = true;
|
||||
controller.sourceRect = sourceRect;
|
||||
controller.permittedArrowDirections = centered ? 0 : (UIPopoverArrowDirectionUp | UIPopoverArrowDirectionDown);
|
||||
controller.willDismiss = ^(__unused bool manual)
|
||||
{
|
||||
};
|
||||
controller.didDismiss = ^(__unused bool manual) {
|
||||
if (dismissed != nil)
|
||||
dismissed();
|
||||
};
|
||||
|
||||
NSMutableArray *itemViews = [[NSMutableArray alloc] init];
|
||||
|
||||
TGClipboardPreviewItemView *previewItem = [[TGClipboardPreviewItemView alloc] initWithContext:context images:images allowGrouping:allowGrouping];
|
||||
__weak TGClipboardPreviewItemView *weakPreviewItem = previewItem;
|
||||
previewItem.stickersContext = stickersContext;
|
||||
previewItem.parentController = parentController;
|
||||
previewItem.allowCaptions = hasCaption;
|
||||
previewItem.hasTimer = hasTimer;
|
||||
previewItem.hasSilentPosting = hasSilentPosting;
|
||||
previewItem.hasSchedule = hasSchedule;
|
||||
previewItem.reminder = reminder;
|
||||
previewItem.recipientName = recipientName;
|
||||
previewItem.presentScheduleController = presentScheduleController;
|
||||
previewItem.presentTimerController = presentTimerController;
|
||||
previewItem.sendPressed = ^(UIImage *currentItem, bool silentPosting, int32_t scheduleTime)
|
||||
{
|
||||
__strong TGClipboardPreviewItemView *strongPreviewItem = weakPreviewItem;
|
||||
completed(strongPreviewItem.selectionContext, strongPreviewItem.editingContext, currentItem, silentPosting, scheduleTime);
|
||||
|
||||
__strong TGMenuSheetController *strongController = weakController;
|
||||
[strongController dismissAnimated:true];
|
||||
};
|
||||
[itemViews addObject:previewItem];
|
||||
|
||||
NSString *sendTitle = TGLocalized(@"Clipboard.SendPhoto");
|
||||
NSUInteger photosCount = images.count;
|
||||
if (photosCount > 1)
|
||||
{
|
||||
NSString *format = TGLocalized([TGStringUtils integerValueFormat:@"AttachmentMenu.SendPhoto_" value:photosCount]);
|
||||
sendTitle = [NSString stringWithFormat:format, [NSString stringWithFormat:@"%ld", photosCount]];
|
||||
}
|
||||
|
||||
TGMenuSheetButtonItemView *sendItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:sendTitle type:TGMenuSheetButtonTypeSend fontSize:20.0 action:^
|
||||
{
|
||||
__strong TGClipboardPreviewItemView *strongPreviewItem = weakPreviewItem;
|
||||
completed(strongPreviewItem.selectionContext, strongPreviewItem.editingContext, nil, false, 0);
|
||||
|
||||
__strong TGMenuSheetController *strongController = weakController;
|
||||
[strongController dismissAnimated:true];
|
||||
}];
|
||||
[itemViews addObject:sendItem];
|
||||
|
||||
__weak TGMenuSheetButtonItemView *weakSendItem = sendItem;
|
||||
previewItem.selectionChanged = ^(NSUInteger count)
|
||||
{
|
||||
__strong TGMenuSheetButtonItemView *strongSendItem = weakSendItem;
|
||||
__strong TGClipboardPreviewItemView *strongPreviewItem = weakPreviewItem;
|
||||
if (count > 0)
|
||||
{
|
||||
NSString *format = TGLocalized([TGStringUtils integerValueFormat:@"AttachmentMenu.SendPhoto_" value:count]);
|
||||
NSString *sendTitle = [NSString stringWithFormat:format, [NSString stringWithFormat:@"%ld", count]];
|
||||
[strongSendItem setTitle:sendTitle];
|
||||
}
|
||||
|
||||
strongSendItem.userInteractionEnabled = count > 0;
|
||||
[strongPreviewItem setCollapsed:count == 0 animated:true];
|
||||
};
|
||||
|
||||
TGMenuSheetButtonItemView *cancelItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel fontSize:20.0 action:^
|
||||
{
|
||||
__strong TGMenuSheetController *strongController = weakController;
|
||||
[strongController dismissAnimated:true];
|
||||
}];
|
||||
[itemViews addObject:cancelItem];
|
||||
|
||||
[controller setItemViews:itemViews animated:false];
|
||||
|
||||
return controller;
|
||||
|
||||
}
|
||||
|
||||
+ (int64_t)generateGroupedId
|
||||
{
|
||||
int64_t value;
|
||||
arc4random_buf(&value, sizeof(int64_t));
|
||||
return value;
|
||||
}
|
||||
|
||||
+ (NSArray *)resultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext currentItem:(id<TGMediaSelectableItem>)currentItem descriptionGenerator:(id (^)(id, NSAttributedString *, NSString *))descriptionGenerator
|
||||
{
|
||||
NSMutableArray *signals = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *selectedItems = [selectionContext.selectedItems mutableCopy];
|
||||
if (selectedItems.count == 0 && currentItem != nil)
|
||||
[selectedItems addObject:currentItem];
|
||||
|
||||
NSNumber *groupedId;
|
||||
NSInteger i = 0;
|
||||
bool grouping = selectionContext.grouping;
|
||||
|
||||
bool hasAnyTimers = false;
|
||||
if (editingContext != nil || grouping)
|
||||
{
|
||||
for (UIImage *asset in selectedItems)
|
||||
{
|
||||
if ([editingContext timerForItem:asset] != nil) {
|
||||
hasAnyTimers = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (grouping && selectedItems.count > 1)
|
||||
groupedId = @([self generateGroupedId]);
|
||||
|
||||
for (UIImage *asset in selectedItems)
|
||||
{
|
||||
NSAttributedString *caption = [editingContext captionForItem:asset];
|
||||
id<TGMediaEditAdjustments> adjustments = [editingContext adjustmentsForItem:asset];
|
||||
NSNumber *timer = [editingContext timerForItem:asset];
|
||||
|
||||
SSignal *inlineSignal = [[SSignal single:asset] map:^id(UIImage *image)
|
||||
{
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
dict[@"type"] = @"editedPhoto";
|
||||
dict[@"image"] = image;
|
||||
|
||||
if (timer != nil)
|
||||
dict[@"timer"] = timer;
|
||||
|
||||
if (groupedId != nil)
|
||||
dict[@"groupedId"] = groupedId;
|
||||
|
||||
id generatedItem = descriptionGenerator(dict, caption, nil);
|
||||
return generatedItem;
|
||||
}];
|
||||
|
||||
SSignal *assetSignal = inlineSignal;
|
||||
SSignal *imageSignal = assetSignal;
|
||||
if (editingContext != nil)
|
||||
{
|
||||
imageSignal = [[[[[editingContext imageSignalForItem:asset withUpdates:true] filter:^bool(id result)
|
||||
{
|
||||
return result == nil || ([result isKindOfClass:[UIImage class]] && !((UIImage *)result).degraded);
|
||||
}] take:1] mapToSignal:^SSignal *(id result)
|
||||
{
|
||||
if (result == nil)
|
||||
{
|
||||
return [SSignal fail:nil];
|
||||
}
|
||||
else if ([result isKindOfClass:[UIImage class]])
|
||||
{
|
||||
UIImage *image = (UIImage *)result;
|
||||
image.edited = true;
|
||||
return [SSignal single:image];
|
||||
}
|
||||
|
||||
return [SSignal complete];
|
||||
}] onCompletion:^
|
||||
{
|
||||
__strong TGMediaEditingContext *strongEditingContext = editingContext;
|
||||
[strongEditingContext description];
|
||||
}];
|
||||
}
|
||||
|
||||
[signals addObject:[[imageSignal map:^NSDictionary *(UIImage *image)
|
||||
{
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
dict[@"type"] = @"editedPhoto";
|
||||
dict[@"image"] = image;
|
||||
|
||||
if (adjustments.paintingData.stickers.count > 0)
|
||||
dict[@"stickers"] = adjustments.paintingData.stickers;
|
||||
|
||||
if (timer != nil)
|
||||
dict[@"timer"] = timer;
|
||||
|
||||
if (groupedId != nil)
|
||||
dict[@"groupedId"] = groupedId;
|
||||
|
||||
id generatedItem = descriptionGenerator(dict, caption, nil);
|
||||
return generatedItem;
|
||||
}] catch:^SSignal *(__unused id error)
|
||||
{
|
||||
return inlineSignal;
|
||||
}]];
|
||||
|
||||
i++;
|
||||
|
||||
if (groupedId != nil && i == 10)
|
||||
{
|
||||
i = 0;
|
||||
groupedId = @([self generateGroupedId]);
|
||||
}
|
||||
}
|
||||
return signals;
|
||||
}
|
||||
|
||||
@end
|
@ -1,23 +0,0 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <SSignalKit/SSignalKit.h>
|
||||
#import <LegacyComponents/TGImageView.h>
|
||||
|
||||
@class TGMediaSelectionContext;
|
||||
@class TGMediaEditingContext;
|
||||
|
||||
@interface TGClipboardPreviewCell : UICollectionViewCell
|
||||
|
||||
@property (nonatomic, readonly) UIImage *image;
|
||||
@property (nonatomic, readonly) TGImageView *imageView;
|
||||
|
||||
@property (nonatomic, strong) TGMediaSelectionContext *selectionContext;
|
||||
@property (nonatomic, strong) TGMediaEditingContext *editingContext;
|
||||
|
||||
- (void)setCornersImage:(UIImage *)cornersImage;
|
||||
|
||||
- (void)setImage:(UIImage *)image signal:(SSignal *)signal hasCheck:(bool)hasCheck;
|
||||
- (void)setHidden:(bool)hidden animated:(bool)animated;
|
||||
|
||||
@end
|
||||
|
||||
extern NSString *const TGClipboardPreviewCellIdentifier;
|
@ -1,228 +0,0 @@
|
||||
#import "TGClipboardPreviewCell.h"
|
||||
|
||||
#import <LegacyComponents/TGMediaEditingContext.h>
|
||||
#import <LegacyComponents/TGMediaSelectionContext.h>
|
||||
|
||||
#import <LegacyComponents/TGCheckButtonView.h>
|
||||
|
||||
#import <LegacyComponents/TGModernGalleryTransitionView.h>
|
||||
|
||||
NSString *const TGClipboardPreviewCellIdentifier = @"TGClipboardPreviewCell";
|
||||
const CGFloat TGClipboardCellCornerRadius = 5.5f;
|
||||
|
||||
@interface TGClipboardPreviewCell () <TGModernGalleryTransitionView>
|
||||
{
|
||||
TGCheckButtonView *_checkButton;
|
||||
UIImageView *_cornersView;
|
||||
|
||||
SMetaDisposable *_itemSelectedDisposable;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TGClipboardPreviewCell
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
{
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
self.clipsToBounds = true;
|
||||
|
||||
_imageView = [[TGImageView alloc] initWithFrame:self.bounds];
|
||||
_imageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
[self addSubview:_imageView];
|
||||
|
||||
_cornersView = [[UIImageView alloc] init];
|
||||
_cornersView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_cornersView.frame = self.bounds;
|
||||
[self addSubview:_cornersView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setCornersImage:(UIImage *)cornersImage
|
||||
{
|
||||
_cornersView.image = cornersImage;
|
||||
}
|
||||
|
||||
- (void)setImage:(UIImage *)image signal:(SSignal *)signal hasCheck:(bool)hasCheck
|
||||
{
|
||||
_image = image;
|
||||
|
||||
if (self.selectionContext != nil)
|
||||
{
|
||||
if (_checkButton == nil && hasCheck)
|
||||
{
|
||||
_checkButton = [[TGCheckButtonView alloc] initWithStyle:TGCheckButtonStyleMedia];
|
||||
[_checkButton addTarget:self action:@selector(checkButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self addSubview:_checkButton];
|
||||
}
|
||||
|
||||
if (_itemSelectedDisposable == nil)
|
||||
_itemSelectedDisposable = [[SMetaDisposable alloc] init];
|
||||
|
||||
[self setChecked:[self.selectionContext isItemSelected:(id<TGMediaSelectableItem>)_image] animated:false];
|
||||
__weak TGClipboardPreviewCell *weakSelf = self;
|
||||
[_itemSelectedDisposable setDisposable:[[self.selectionContext itemInformativeSelectedSignal:(id<TGMediaSelectableItem>)_image] startWithNext:^(TGMediaSelectionChange *next)
|
||||
{
|
||||
__strong TGClipboardPreviewCell *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
if (next.sender != strongSelf->_checkButton)
|
||||
[strongSelf setChecked:next.selected animated:next.animated];
|
||||
}]];
|
||||
|
||||
_checkButton.hidden = !hasCheck;
|
||||
}
|
||||
|
||||
if (_image == nil)
|
||||
{
|
||||
[_imageView reset];
|
||||
return;
|
||||
}
|
||||
|
||||
[self setSignal:signal];
|
||||
}
|
||||
|
||||
|
||||
- (void)setSignal:(SSignal *)signal
|
||||
{
|
||||
if (signal != nil)
|
||||
[_imageView setSignal:signal];
|
||||
else
|
||||
[_imageView reset];
|
||||
}
|
||||
|
||||
- (void)checkButtonPressed
|
||||
{
|
||||
[_checkButton setSelected:!_checkButton.selected animated:true];
|
||||
|
||||
[self.selectionContext setItem:(id<TGMediaSelectableItem>)_image selected:_checkButton.selected animated:false sender:_checkButton];
|
||||
}
|
||||
|
||||
- (void)setChecked:(bool)checked animated:(bool)animated
|
||||
{
|
||||
[_checkButton setSelected:checked animated:animated];
|
||||
}
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
[_imageView reset];
|
||||
}
|
||||
|
||||
- (void)setHidden:(bool)hidden animated:(bool)animated
|
||||
{
|
||||
if (hidden != _imageView.hidden)
|
||||
{
|
||||
_imageView.hidden = hidden;
|
||||
|
||||
if (animated)
|
||||
{
|
||||
if (!hidden)
|
||||
{
|
||||
for (UIView *view in self.subviews)
|
||||
{
|
||||
if (view != _imageView && view != _cornersView)
|
||||
view.alpha = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
[UIView animateWithDuration:0.2 animations:^
|
||||
{
|
||||
if (!hidden)
|
||||
{
|
||||
for (UIView *view in self.subviews)
|
||||
{
|
||||
if (view != _imageView && view != _cornersView)
|
||||
view.alpha = 1.0f;
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (UIView *view in self.subviews)
|
||||
{
|
||||
if (view != _imageView && view != _cornersView)
|
||||
view.alpha = hidden ? 0.0f : 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
_imageView.frame = self.bounds;
|
||||
|
||||
if (_checkButton != nil)
|
||||
{
|
||||
CGFloat offset = 0.0f;
|
||||
if (self.superview != nil)
|
||||
{
|
||||
CGRect rect = [self.superview convertRect:self.frame toView:self.superview.superview];
|
||||
if (rect.origin.x < 0)
|
||||
offset = rect.origin.x * -1;
|
||||
else if (CGRectGetMaxX(rect) > self.superview.frame.size.width)
|
||||
offset = self.superview.frame.size.width - CGRectGetMaxX(rect);
|
||||
}
|
||||
|
||||
CGFloat x = MAX(0, MIN(self.bounds.size.width - _checkButton.frame.size.width, self.bounds.size.width - _checkButton.frame.size.width + offset));
|
||||
_checkButton.frame = CGRectMake(x, 0, _checkButton.frame.size.width, _checkButton.frame.size.height);
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)transitionImage
|
||||
{
|
||||
CGFloat scale = 1.0f;
|
||||
CGSize scaledBoundsSize = CGSizeZero;
|
||||
CGSize scaledImageSize = CGSizeZero;
|
||||
|
||||
if (self.imageView.image.size.width > self.imageView.image.size.height)
|
||||
{
|
||||
scale = self.frame.size.height / self.imageView.image.size.height;
|
||||
scaledBoundsSize = CGSizeMake(self.frame.size.width / scale, self.imageView.image.size.height);
|
||||
|
||||
scaledImageSize = CGSizeMake(self.imageView.image.size.width * scale, self.imageView.image.size.height * scale);
|
||||
|
||||
if (scaledImageSize.width < self.frame.size.width)
|
||||
{
|
||||
scale = self.frame.size.width / self.imageView.image.size.width;
|
||||
scaledBoundsSize = CGSizeMake(self.imageView.image.size.width, self.frame.size.height / scale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scale = self.frame.size.width / self.imageView.image.size.width;
|
||||
scaledBoundsSize = CGSizeMake(self.imageView.image.size.width, self.frame.size.height / scale);
|
||||
|
||||
scaledImageSize = CGSizeMake(self.imageView.image.size.width * scale, self.imageView.image.size.height * scale);
|
||||
|
||||
if (scaledImageSize.width < self.frame.size.width)
|
||||
{
|
||||
scale = self.frame.size.height / self.imageView.image.size.height;
|
||||
scaledBoundsSize = CGSizeMake(self.frame.size.width / scale, self.imageView.image.size.height);
|
||||
}
|
||||
}
|
||||
|
||||
CGRect rect = self.bounds;
|
||||
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0f);
|
||||
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
[[UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height) cornerRadius:TGClipboardCellCornerRadius] addClip];
|
||||
|
||||
CGContextScaleCTM(context, scale, scale);
|
||||
[self.imageView.image drawInRect:CGRectMake((scaledBoundsSize.width - self.imageView.image.size.width) / 2,
|
||||
(scaledBoundsSize.height - self.imageView.image.size.height) / 2,
|
||||
self.imageView.image.size.width,
|
||||
self.imageView.image.size.height)];
|
||||
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
@ -1,34 +0,0 @@
|
||||
#import <LegacyComponents/LegacyComponents.h>
|
||||
|
||||
@class TGMediaSelectionContext;
|
||||
@class TGMediaEditingContext;
|
||||
|
||||
@protocol TGPhotoPaintStickersContext;
|
||||
|
||||
@interface TGClipboardPreviewItemView : TGMenuSheetItemView
|
||||
|
||||
@property (nonatomic, weak) TGViewController *parentController;
|
||||
|
||||
@property (nonatomic, assign) bool allowCaptions;
|
||||
@property (nonatomic, assign) bool hasTimer;
|
||||
@property (nonatomic, strong) NSString *recipientName;
|
||||
|
||||
@property (nonatomic, assign) bool hasSchedule;
|
||||
@property (nonatomic, assign) bool hasSilentPosting;
|
||||
@property (nonatomic, assign) bool reminder;
|
||||
|
||||
@property (nonatomic, readonly) TGMediaSelectionContext *selectionContext;
|
||||
@property (nonatomic, readonly) TGMediaEditingContext *editingContext;
|
||||
@property (nonatomic, strong) id<TGPhotoPaintStickersContext> stickersContext;
|
||||
|
||||
@property (nonatomic, copy) void (^selectionChanged)(NSUInteger);
|
||||
@property (nonatomic, copy) void (^sendPressed)(UIImage *currentItem, bool silentPosting, int32_t scheduleTime);
|
||||
|
||||
@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t));
|
||||
@property (nonatomic, copy) void (^presentTimerController)(void (^)(int32_t));
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context images:(NSArray *)images allowGrouping:(bool)allowGrouping;
|
||||
|
||||
- (void)setCollapsed:(bool)collapsed animated:(bool)animated;
|
||||
|
||||
@end
|
@ -1,299 +0,0 @@
|
||||
#import "TGClipboardPreviewItemView.h"
|
||||
|
||||
#import "LegacyComponentsInternal.h"
|
||||
|
||||
#import <LegacyComponents/TGMediaEditingContext.h>
|
||||
#import <LegacyComponents/TGMediaSelectionContext.h>
|
||||
|
||||
#import <LegacyComponents/TGClipboardGalleryMixin.h>
|
||||
|
||||
#import "TGClipboardPreviewCell.h"
|
||||
|
||||
const CGFloat TGClipboardPreviewMaxWidth = 250.0f;
|
||||
const CGFloat TGClipboardPreviewCellHeight = 198.0f;
|
||||
const CGFloat TGClipboardPreviewEdgeInset = 8.0f;
|
||||
|
||||
@interface TGClipboardPreviewItemView () <UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
||||
{
|
||||
id<LegacyComponentsContext> _context;
|
||||
|
||||
NSArray *_images;
|
||||
|
||||
UICollectionView *_collectionView;
|
||||
UICollectionViewFlowLayout *_collectionLayout;
|
||||
|
||||
TGClipboardGalleryMixin *_galleryMixin;
|
||||
UIImage *_hiddenItem;
|
||||
|
||||
SMetaDisposable *_selectionChangedDisposable;
|
||||
|
||||
bool _collapsed;
|
||||
|
||||
UIImage *_cornersImage;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TGClipboardPreviewItemView
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context images:(NSArray *)images allowGrouping:(bool)allowGrouping
|
||||
{
|
||||
self = [super initWithType:TGMenuSheetItemTypeDefault];
|
||||
if (self != nil)
|
||||
{
|
||||
_context = context;
|
||||
_images = images;
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
_collectionLayout = [[UICollectionViewFlowLayout alloc] init];
|
||||
_collectionLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
_collectionLayout.minimumLineSpacing = 8.0f;
|
||||
|
||||
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, TGClipboardPreviewCellHeight + TGClipboardPreviewEdgeInset * 2) collectionViewLayout:_collectionLayout];
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
_collectionView.backgroundColor = [UIColor whiteColor];
|
||||
_collectionView.dataSource = self;
|
||||
_collectionView.delegate = self;
|
||||
_collectionView.showsHorizontalScrollIndicator = false;
|
||||
_collectionView.showsVerticalScrollIndicator = false;
|
||||
[_collectionView registerClass:[TGClipboardPreviewCell class] forCellWithReuseIdentifier:TGClipboardPreviewCellIdentifier];
|
||||
[self addSubview:_collectionView];
|
||||
|
||||
_selectionContext = [[TGMediaSelectionContext alloc] initWithGroupingAllowed:allowGrouping selectionLimit:100];
|
||||
if (allowGrouping)
|
||||
_selectionContext.grouping = true;
|
||||
|
||||
for (UIImage *image in _images)
|
||||
{
|
||||
[_selectionContext setItem:(id<TGMediaSelectableItem>)image selected:true];
|
||||
}
|
||||
|
||||
__weak TGClipboardPreviewItemView *weakSelf = self;
|
||||
_selectionChangedDisposable = [[SMetaDisposable alloc] init];
|
||||
[_selectionChangedDisposable setDisposable:[[_selectionContext selectionChangedSignal] startWithNext:^(__unused TGMediaSelectionChange *change)
|
||||
{
|
||||
__strong TGClipboardPreviewItemView *strongSelf = weakSelf;
|
||||
if (strongSelf != nil && strongSelf.selectionChanged != nil)
|
||||
strongSelf.selectionChanged(strongSelf->_selectionContext.count);
|
||||
}]];
|
||||
|
||||
_editingContext = [[TGMediaEditingContext alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setPallete:(TGMenuSheetPallete *)pallete
|
||||
{
|
||||
[super setPallete:pallete];
|
||||
|
||||
self.backgroundColor = pallete.backgroundColor;
|
||||
_collectionView.backgroundColor = pallete.backgroundColor;
|
||||
|
||||
CGFloat radius = 5.5f;
|
||||
CGRect rect = CGRectMake(0, 0, 12.0f, 12.0f);
|
||||
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
CGContextSetFillColorWithColor(context, pallete.backgroundColor.CGColor);
|
||||
CGContextFillRect(context, rect);
|
||||
|
||||
CGContextSetBlendMode(context, kCGBlendModeClear);
|
||||
|
||||
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
|
||||
CGContextFillEllipseInRect(context, rect);
|
||||
|
||||
_cornersImage = [UIGraphicsGetImageFromCurrentImageContext() resizableImageWithCapInsets:UIEdgeInsetsMake(radius, radius, radius, radius)];
|
||||
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
||||
- (void)setCollapsed:(bool)collapsed animated:(bool)animated
|
||||
{
|
||||
_collapsed = collapsed;
|
||||
|
||||
[self _updateHeightAnimated:animated];
|
||||
}
|
||||
|
||||
- (CGFloat)contentHeightCorrection
|
||||
{
|
||||
return _collapsed ? -TGMenuSheetButtonItemViewHeight : 0.0f;
|
||||
}
|
||||
|
||||
- (SSignal *)_signalForImage:(UIImage *)image
|
||||
{
|
||||
SSignal *originalSignal = [SSignal single:image];
|
||||
if (_editingContext == nil)
|
||||
return originalSignal;
|
||||
|
||||
SSignal *editedSignal = [_editingContext fastImageSignalForItem:image withUpdates:true];
|
||||
return [editedSignal mapToSignal:^SSignal *(id result)
|
||||
{
|
||||
if (result != nil)
|
||||
return [SSignal single:result];
|
||||
else
|
||||
return originalSignal;
|
||||
}];
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TGClipboardPreviewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:TGClipboardPreviewCellIdentifier forIndexPath:indexPath];
|
||||
cell.selectionContext = _selectionContext;
|
||||
cell.editingContext = _editingContext;
|
||||
[cell setCornersImage:_cornersImage];
|
||||
|
||||
UIImage *image = _images[indexPath.row];
|
||||
[cell setImage:image signal:[self _signalForImage:image] hasCheck:_images.count > 1];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)__unused collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
_galleryMixin = [self galleryMixinForIndexPath:indexPath];
|
||||
[_galleryMixin present];
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)__unused collectionView numberOfItemsInSection:(NSInteger)__unused section
|
||||
{
|
||||
return _images.count;
|
||||
}
|
||||
|
||||
- (UIEdgeInsets)collectionView:(UICollectionView *)__unused collectionView layout:(UICollectionViewLayout *)__unused collectionViewLayout insetForSectionAtIndex:(NSInteger)__unused section
|
||||
{
|
||||
return UIEdgeInsetsMake(TGClipboardPreviewEdgeInset, TGClipboardPreviewEdgeInset, TGClipboardPreviewEdgeInset, TGClipboardPreviewEdgeInset);
|
||||
}
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)__unused collectionView layout:(UICollectionViewLayout *)__unused collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
CGSize dimensions = [(UIImage *)_images[indexPath.row] size];
|
||||
|
||||
CGSize maxPhotoSize = CGSizeMake(TGClipboardPreviewMaxWidth, TGClipboardPreviewCellHeight);
|
||||
CGFloat width = MIN(maxPhotoSize.width, ceil(dimensions.width * maxPhotoSize.height / dimensions.height));
|
||||
return CGSizeMake(width, maxPhotoSize.height);
|
||||
}
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)__unused scrollView
|
||||
{
|
||||
for (UICollectionViewCell *cell in _collectionView.visibleCells)
|
||||
{
|
||||
if ([cell isKindOfClass:[TGClipboardPreviewCell class]])
|
||||
[(TGClipboardPreviewCell *)cell setNeedsLayout];
|
||||
}
|
||||
}
|
||||
|
||||
- (bool)requiresDivider
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
- (CGFloat)preferredHeightForWidth:(CGFloat)__unused width screenHeight:(CGFloat)__unused screenHeight
|
||||
{
|
||||
return TGClipboardPreviewCellHeight + TGClipboardPreviewEdgeInset * 2.0f;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
_collectionView.frame = self.bounds;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)_setupGalleryMixin:(TGClipboardGalleryMixin *)mixin
|
||||
{
|
||||
__weak TGClipboardPreviewItemView *weakSelf = self;
|
||||
mixin.referenceViewForItem = ^UIView *(TGClipboardGalleryPhotoItem *item)
|
||||
{
|
||||
__strong TGClipboardPreviewItemView *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
return [strongSelf referenceViewForAsset:item.image];
|
||||
|
||||
return nil;
|
||||
};
|
||||
|
||||
mixin.itemFocused = ^(TGClipboardGalleryPhotoItem *item)
|
||||
{
|
||||
__strong TGClipboardPreviewItemView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_hiddenItem = item.image;
|
||||
[strongSelf updateHiddenCellAnimated:false];
|
||||
};
|
||||
|
||||
mixin.willTransitionIn = ^
|
||||
{
|
||||
__strong TGClipboardPreviewItemView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
[strongSelf.superview bringSubviewToFront:strongSelf];
|
||||
};
|
||||
|
||||
mixin.willTransitionOut = ^
|
||||
{
|
||||
__strong TGClipboardPreviewItemView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
};
|
||||
|
||||
mixin.didTransitionOut = ^
|
||||
{
|
||||
__strong TGClipboardPreviewItemView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_hiddenItem = nil;
|
||||
[strongSelf updateHiddenCellAnimated:true];
|
||||
|
||||
strongSelf->_galleryMixin = nil;
|
||||
};
|
||||
|
||||
|
||||
mixin.completeWithItem = ^(TGClipboardGalleryPhotoItem *item, bool silentPosting, int32_t scheduleTime)
|
||||
{
|
||||
__strong TGClipboardPreviewItemView *strongSelf = weakSelf;
|
||||
if (strongSelf != nil && strongSelf.sendPressed != nil)
|
||||
strongSelf.sendPressed(item.image, silentPosting, scheduleTime);
|
||||
};
|
||||
}
|
||||
|
||||
- (TGClipboardGalleryMixin *)galleryMixinForIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
UIImage *image = _images[indexPath.row];
|
||||
UIImage *thumbnailImage = nil;
|
||||
|
||||
TGClipboardPreviewCell *cell = (TGClipboardPreviewCell *)[_collectionView cellForItemAtIndexPath:indexPath];
|
||||
if ([cell isKindOfClass:[TGClipboardPreviewCell class]])
|
||||
thumbnailImage = cell.imageView.image;
|
||||
|
||||
TGClipboardGalleryMixin *mixin = [[TGClipboardGalleryMixin alloc] initWithContext:_context image:image images:_images parentController:self.parentController thumbnailImage:thumbnailImage selectionContext:_selectionContext editingContext:_editingContext stickersContext:self.stickersContext hasCaptions:self.allowCaptions hasTimer:self.hasTimer hasSilentPosting:self.hasSilentPosting hasSchedule:self.hasSchedule reminder:self.reminder recipientName:self.recipientName];
|
||||
mixin.presentScheduleController = self.presentScheduleController;
|
||||
mixin.presentTimerController = self.presentTimerController;
|
||||
|
||||
[self _setupGalleryMixin:mixin];
|
||||
|
||||
return mixin;
|
||||
}
|
||||
|
||||
- (UIView *)referenceViewForAsset:(UIImage *)image
|
||||
{
|
||||
for (TGClipboardPreviewCell *cell in [_collectionView visibleCells])
|
||||
{
|
||||
if ([cell.image isEqual:image])
|
||||
return cell;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)updateHiddenCellAnimated:(bool)animated
|
||||
{
|
||||
for (TGClipboardPreviewCell *cell in [_collectionView visibleCells])
|
||||
[cell setHidden:([cell.image isEqual:_hiddenItem]) animated:animated];
|
||||
}
|
||||
|
||||
@end
|
@ -674,8 +674,10 @@
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
[strongController dismissAnimated:true manual:false completion:nil];
|
||||
[[[LegacyComponentsGlobals provider] applicationInstance] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
|
||||
[strongController dismissAnimated:true manual:false completion:nil];
|
||||
[[[LegacyComponentsGlobals provider] applicationInstance] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:^(BOOL success) {
|
||||
|
||||
}];
|
||||
}],
|
||||
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel fontSize:20.0 action:^
|
||||
{
|
||||
@ -1394,6 +1396,159 @@
|
||||
return signals;
|
||||
}
|
||||
|
||||
+ (NSArray *)pasteboardResultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext intent:(TGMediaAssetsControllerIntent)intent currentItem:(id<TGMediaSelectableItem>)currentItem descriptionGenerator:(id (^)(id, NSAttributedString *, NSString *, NSString *))descriptionGenerator
|
||||
{
|
||||
NSMutableArray *signals = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *selectedItems = [selectionContext.selectedItems mutableCopy];
|
||||
if (selectedItems.count == 0 && currentItem != nil)
|
||||
[selectedItems addObject:currentItem];
|
||||
|
||||
NSNumber *groupedId;
|
||||
NSInteger i = 0;
|
||||
NSInteger num = 0;
|
||||
bool grouping = selectionContext.grouping;
|
||||
|
||||
bool hasAnyTimers = false;
|
||||
if (editingContext != nil || grouping)
|
||||
{
|
||||
for (UIImage *asset in selectedItems)
|
||||
{
|
||||
if ([editingContext timerForItem:asset] != nil) {
|
||||
hasAnyTimers = true;
|
||||
}
|
||||
id<TGMediaEditAdjustments> adjustments = [editingContext adjustmentsForItem:asset];
|
||||
if ([adjustments isKindOfClass:[TGVideoEditAdjustments class]]) {
|
||||
TGVideoEditAdjustments *videoAdjustments = (TGVideoEditAdjustments *)adjustments;
|
||||
if (videoAdjustments.sendAsGif) {
|
||||
grouping = false;
|
||||
}
|
||||
}
|
||||
for (TGPhotoPaintEntity *entity in adjustments.paintingData.entities) {
|
||||
if (entity.animated) {
|
||||
grouping = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (grouping && selectedItems.count > 1)
|
||||
groupedId = @([self generateGroupedId]);
|
||||
|
||||
for (UIImage *asset in selectedItems)
|
||||
{
|
||||
NSAttributedString *caption = [editingContext captionForItem:asset];
|
||||
if (editingContext.isForcedCaption) {
|
||||
if (grouping && num > 0) {
|
||||
caption = nil;
|
||||
} else if (!grouping && num < selectedItems.count - 1) {
|
||||
caption = nil;
|
||||
}
|
||||
}
|
||||
|
||||
if (intent == TGMediaAssetsControllerSendFileIntent)
|
||||
{
|
||||
NSString *tempFileName = TGTemporaryFileName(nil);
|
||||
NSData *imageData = UIImageJPEGRepresentation(asset, 1.0);
|
||||
[imageData writeToURL:[NSURL fileURLWithPath:tempFileName] atomically:true];
|
||||
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
dict[@"type"] = @"file";
|
||||
dict[@"tempFileUrl"] = [NSURL fileURLWithPath:tempFileName];
|
||||
dict[@"fileName"] = [NSString stringWithFormat:@"IMG%03ld.jpg", i];
|
||||
dict[@"mimeType"] = TGMimeTypeForFileUTI(@"image/jpeg");
|
||||
dict[@"previewImage"] = asset;
|
||||
|
||||
if (groupedId != nil)
|
||||
dict[@"groupedId"] = groupedId;
|
||||
|
||||
id generatedItem = descriptionGenerator(dict, caption, nil, nil);
|
||||
[signals addObject:[SSignal single:generatedItem]];
|
||||
|
||||
i++;
|
||||
num++;
|
||||
} else {
|
||||
id<TGMediaEditAdjustments> adjustments = [editingContext adjustmentsForItem:asset];
|
||||
NSNumber *timer = [editingContext timerForItem:asset];
|
||||
|
||||
SSignal *inlineSignal = [[SSignal single:asset] map:^id(UIImage *image)
|
||||
{
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
dict[@"type"] = @"editedPhoto";
|
||||
dict[@"image"] = image;
|
||||
|
||||
if (timer != nil)
|
||||
dict[@"timer"] = timer;
|
||||
|
||||
if (groupedId != nil && !hasAnyTimers)
|
||||
dict[@"groupedId"] = groupedId;
|
||||
|
||||
id generatedItem = descriptionGenerator(dict, caption, nil, nil);
|
||||
return generatedItem;
|
||||
}];
|
||||
|
||||
SSignal *assetSignal = inlineSignal;
|
||||
SSignal *imageSignal = assetSignal;
|
||||
if (editingContext != nil)
|
||||
{
|
||||
imageSignal = [[[[[editingContext imageSignalForItem:asset withUpdates:true] filter:^bool(id result)
|
||||
{
|
||||
return result == nil || ([result isKindOfClass:[UIImage class]] && !((UIImage *)result).degraded);
|
||||
}] take:1] mapToSignal:^SSignal *(id result)
|
||||
{
|
||||
if (result == nil)
|
||||
{
|
||||
return [SSignal fail:nil];
|
||||
}
|
||||
else if ([result isKindOfClass:[UIImage class]])
|
||||
{
|
||||
UIImage *image = (UIImage *)result;
|
||||
image.edited = true;
|
||||
return [SSignal single:image];
|
||||
}
|
||||
|
||||
return [SSignal complete];
|
||||
}] onCompletion:^
|
||||
{
|
||||
__strong TGMediaEditingContext *strongEditingContext = editingContext;
|
||||
[strongEditingContext description];
|
||||
}];
|
||||
}
|
||||
|
||||
[signals addObject:[[imageSignal map:^NSDictionary *(UIImage *image)
|
||||
{
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
dict[@"type"] = @"editedPhoto";
|
||||
dict[@"image"] = image;
|
||||
|
||||
if (adjustments.paintingData.stickers.count > 0)
|
||||
dict[@"stickers"] = adjustments.paintingData.stickers;
|
||||
|
||||
if (timer != nil)
|
||||
dict[@"timer"] = timer;
|
||||
|
||||
if (groupedId != nil && !hasAnyTimers)
|
||||
dict[@"groupedId"] = groupedId;
|
||||
|
||||
id generatedItem = descriptionGenerator(dict, caption, nil, nil);
|
||||
return generatedItem;
|
||||
}] catch:^SSignal *(__unused id error)
|
||||
{
|
||||
return inlineSignal;
|
||||
}]];
|
||||
|
||||
i++;
|
||||
num++;
|
||||
}
|
||||
|
||||
if (groupedId != nil && i == 10)
|
||||
{
|
||||
i = 0;
|
||||
groupedId = @([self generateGroupedId]);
|
||||
}
|
||||
}
|
||||
return signals;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (UIBarButtonItem *)leftBarButtonItem
|
||||
|
@ -445,77 +445,3 @@ public func legacyMenuPaletteFromTheme(_ theme: PresentationTheme, forceDark: Bo
|
||||
}
|
||||
return TGMenuSheetPallete(dark: forceDark || theme.overallDarkAppearance, backgroundColor: sheetTheme.opaqueItemBackgroundColor, selectionColor: sheetTheme.opaqueItemHighlightedBackgroundColor, separatorColor: sheetTheme.opaqueItemSeparatorColor, accentColor: sheetTheme.controlAccentColor, destructiveColor: sheetTheme.destructiveActionTextColor, textColor: sheetTheme.primaryTextColor, secondaryTextColor: sheetTheme.secondaryTextColor, spinnerColor: sheetTheme.secondaryTextColor, badgeTextColor: sheetTheme.controlAccentColor, badgeImage: nil, cornersImage: generateStretchableFilledCircleImage(diameter: 11.0, color: nil, strokeColor: nil, strokeWidth: nil, backgroundColor: sheetTheme.opaqueItemBackgroundColor))
|
||||
}
|
||||
|
||||
public func presentLegacyPasteMenu(context: AccountContext, peer: Peer, chatLocation: ChatLocation, saveEditedPhotos: Bool, allowGrouping: Bool, hasSchedule: Bool, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>), images: [UIImage], presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, present: (ViewController, Any?) -> Void, initialLayout: ContainerViewLayout? = nil) -> ViewController {
|
||||
let defaultVideoPreset = defaultVideoPresetForContext(context)
|
||||
UserDefaults.standard.set(defaultVideoPreset.rawValue as NSNumber, forKey: "TG_preferredVideoPreset_v0")
|
||||
|
||||
let presentationData = updatedPresentationData.initial
|
||||
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: initialLayout)
|
||||
legacyController.statusBar.statusBarStyle = .Ignore
|
||||
legacyController.controllerLoaded = { [weak legacyController] in
|
||||
legacyController?.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
}
|
||||
|
||||
let emptyController = LegacyEmptyController(context: legacyController.context)!
|
||||
let navigationController = makeLegacyNavigationController(rootController: emptyController)
|
||||
navigationController.setNavigationBarHidden(true, animated: false)
|
||||
legacyController.bind(controller: navigationController)
|
||||
|
||||
var hasTimer = false
|
||||
var hasSilentPosting = false
|
||||
if peer.id != context.account.peerId {
|
||||
if peer is TelegramUser {
|
||||
hasTimer = true
|
||||
}
|
||||
hasSilentPosting = true
|
||||
}
|
||||
let recipientName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
|
||||
legacyController.enableSizeClassSignal = true
|
||||
|
||||
let paintStickersContext = LegacyPaintStickersContext(context: context)
|
||||
paintStickersContext.captionPanelView = {
|
||||
return getCaptionPanelView()
|
||||
}
|
||||
paintStickersContext.presentStickersController = { completion in
|
||||
return presentStickers({ file, animated, view, rect in
|
||||
let coder = PostboxEncoder()
|
||||
coder.encodeRootObject(file)
|
||||
completion?(coder.makeData(), animated, view, rect)
|
||||
})
|
||||
}
|
||||
|
||||
let controller = TGClipboardMenu.present(inParentController: emptyController, context: legacyController.context, images: images, allowGrouping: allowGrouping, hasCaption: true, hasTimer: hasTimer, hasSilentPosting: hasSilentPosting, hasSchedule: hasSchedule, reminder: peer.id == context.account.peerId, recipientName: recipientName, stickersContext: paintStickersContext, presentScheduleController: { done in
|
||||
presentSchedulePicker { time in
|
||||
done?(time)
|
||||
}
|
||||
}, presentTimerController: { done in
|
||||
presentTimerPicker { time in
|
||||
done?(time)
|
||||
}
|
||||
}, completed: { selectionContext, editingContext, currentItem, silentPosting, scheduleTime in
|
||||
let nativeGenerator = legacyAssetPickerItemGenerator()
|
||||
let signals = TGClipboardMenu.resultSignals(for: selectionContext, editingContext: editingContext, currentItem: currentItem, descriptionGenerator: { _1, _2, _3 in
|
||||
nativeGenerator(_1, _2, _3, nil)
|
||||
})
|
||||
sendMessagesWithSignals(signals, silentPosting, scheduleTime)
|
||||
}, dismissed: { [weak legacyController] in
|
||||
legacyController?.dismiss()
|
||||
}, sourceView: emptyController.view, sourceRect: nil)!
|
||||
controller.customRemoveFromParentViewController = { [weak legacyController] in
|
||||
legacyController?.dismiss()
|
||||
}
|
||||
|
||||
let presentationDisposable = updatedPresentationData.signal.start(next: { [weak legacyController] presentationData in
|
||||
if let legacyController = legacyController, let controller = legacyController.legacyController as? TGMenuSheetController {
|
||||
controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme, forceDark: false)
|
||||
}
|
||||
})
|
||||
legacyController.disposables.add(presentationDisposable)
|
||||
|
||||
present(legacyController, nil)
|
||||
controller.present(in: emptyController, sourceView: nil, animated: true)
|
||||
|
||||
return legacyController
|
||||
}
|
||||
|
26
submodules/MediaPasteboardUI/BUILD
Normal file
26
submodules/MediaPasteboardUI/BUILD
Normal file
@ -0,0 +1,26 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "MediaPasteboardUI",
|
||||
module_name = "MediaPasteboardUI",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/AttachmentUI:AttachmentUI",
|
||||
"//submodules/MediaPickerUI:MediaPickerUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,29 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import TelegramCore
|
||||
import AttachmentUI
|
||||
import MediaPickerUI
|
||||
import AccountContext
|
||||
import LegacyComponents
|
||||
|
||||
public func mediaPasteboardScreen(
|
||||
context: AccountContext,
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||
peer: EnginePeer,
|
||||
subjects: [UIImage],
|
||||
presentMediaPicker: @escaping (_ subject: MediaPickerScreen.Subject, _ saveEditedPhotos: Bool, _ bannedSendMedia: (Int32, Bool)?, _ present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void) -> Void,
|
||||
getSourceRect: (() -> CGRect?)? = nil
|
||||
) -> ViewController {
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: peer.id), buttons: [.standalone], initialButton: .standalone)
|
||||
controller.requestController = { _, present in
|
||||
presentMediaPicker(.media(subjects.map { .image($0) }), false, nil, { mediaPicker, mediaPickerContext in
|
||||
present(mediaPicker, mediaPickerContext)
|
||||
})
|
||||
}
|
||||
controller.updateSelectionCount(subjects.count)
|
||||
controller.getSourceRect = getSourceRect
|
||||
return controller
|
||||
}
|
@ -56,6 +56,32 @@ private func gallerySelectionItems(item: TGMediaSelectableItem, selectionContext
|
||||
galleryItem.stickersContext = stickersContext
|
||||
galleryItems.append(galleryItem)
|
||||
|
||||
if selectedItem.uniqueIdentifier == item.uniqueIdentifier {
|
||||
if let galleryItem = galleryItem as? TGMediaPickerGalleryItem {
|
||||
galleryItem.immediateThumbnailImage = immediateThumbnail
|
||||
}
|
||||
focusItem = galleryItem
|
||||
}
|
||||
} else if let asset = selectedItem as? UIImage {
|
||||
let galleryItem: (TGModernGallerySelectableItem & TGModernGalleryEditableItem) = TGMediaPickerGalleryPhotoItem(asset: asset)
|
||||
galleryItem.selectionContext = selectionContext
|
||||
galleryItem.editingContext = editingContext
|
||||
galleryItem.stickersContext = stickersContext
|
||||
galleryItems.append(galleryItem)
|
||||
|
||||
if selectedItem.uniqueIdentifier == item.uniqueIdentifier {
|
||||
if let galleryItem = galleryItem as? TGMediaPickerGalleryItem {
|
||||
galleryItem.immediateThumbnailImage = immediateThumbnail
|
||||
}
|
||||
focusItem = galleryItem
|
||||
}
|
||||
} else if let asset = selectedItem as? TGCameraCapturedVideo {
|
||||
let galleryItem: (TGModernGallerySelectableItem & TGModernGalleryEditableItem) = TGMediaPickerGalleryVideoItem(asset: asset)
|
||||
galleryItem.selectionContext = selectionContext
|
||||
galleryItem.editingContext = editingContext
|
||||
galleryItem.stickersContext = stickersContext
|
||||
galleryItems.append(galleryItem)
|
||||
|
||||
if selectedItem.uniqueIdentifier == item.uniqueIdentifier {
|
||||
if let galleryItem = galleryItem as? TGMediaPickerGalleryItem {
|
||||
galleryItem.immediateThumbnailImage = immediateThumbnail
|
||||
|
@ -15,6 +15,7 @@ import PhotoResources
|
||||
|
||||
enum MediaPickerGridItemContent: Equatable {
|
||||
case asset(PHFetchResult<PHAsset>, Int)
|
||||
case media(MediaPickerScreen.Subject.Media, Int)
|
||||
}
|
||||
|
||||
final class MediaPickerGridItem: GridItem {
|
||||
@ -32,21 +33,31 @@ final class MediaPickerGridItem: GridItem {
|
||||
|
||||
func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode {
|
||||
switch self.content {
|
||||
case let .asset(fetchResult, index):
|
||||
let node = MediaPickerGridItemNode()
|
||||
node.setup(interaction: self.interaction, fetchResult: fetchResult, index: index, theme: self.theme)
|
||||
return node
|
||||
case let .asset(fetchResult, index):
|
||||
let node = MediaPickerGridItemNode()
|
||||
node.setup(interaction: self.interaction, fetchResult: fetchResult, index: index, theme: self.theme)
|
||||
return node
|
||||
case let .media(media, index):
|
||||
let node = MediaPickerGridItemNode()
|
||||
node.setup(interaction: self.interaction, media: media, index: index, theme: self.theme)
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
func update(node: GridItemNode) {
|
||||
switch self.content {
|
||||
case let .asset(fetchResult, index):
|
||||
guard let node = node as? MediaPickerGridItemNode else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
node.setup(interaction: self.interaction, fetchResult: fetchResult, index: index, theme: self.theme)
|
||||
case let .asset(fetchResult, index):
|
||||
guard let node = node as? MediaPickerGridItemNode else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
node.setup(interaction: self.interaction, fetchResult: fetchResult, index: index, theme: self.theme)
|
||||
case let .media(media, index):
|
||||
guard let node = node as? MediaPickerGridItemNode else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
node.setup(interaction: self.interaction, media: media, index: index, theme: self.theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,6 +76,7 @@ private let maskImage = generateImage(CGSize(width: 1.0, height: 24.0), opaque:
|
||||
})
|
||||
|
||||
final class MediaPickerGridItemNode: GridItemNode {
|
||||
var currentMediaState: (TGMediaSelectableItem, Int)?
|
||||
var currentState: (PHFetchResult<PHAsset>, Int)?
|
||||
private let imageNode: ImageNode
|
||||
private var checkNode: InteractiveCheckNode?
|
||||
@ -103,12 +115,14 @@ final class MediaPickerGridItemNode: GridItemNode {
|
||||
}
|
||||
|
||||
var identifier: String {
|
||||
return self.asset?.localIdentifier ?? ""
|
||||
return self.selectableItem?.uniqueIdentifier ?? ""
|
||||
}
|
||||
|
||||
var asset: PHAsset? {
|
||||
if let (fetchResult, index) = self.currentState {
|
||||
return fetchResult[index]
|
||||
var selectableItem: TGMediaSelectableItem? {
|
||||
if let (media, _) = self.currentMediaState {
|
||||
return media
|
||||
} else if let (fetchResult, index) = self.currentState {
|
||||
return TGMediaAsset(phAsset: fetchResult[index])
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
@ -116,25 +130,23 @@ final class MediaPickerGridItemNode: GridItemNode {
|
||||
|
||||
var _cachedTag: Int32?
|
||||
var tag: Int32? {
|
||||
if let tag = self._cachedTag {
|
||||
return tag
|
||||
} else if let asset = self.asset, let localTimestamp = asset.creationDate?.timeIntervalSince1970 {
|
||||
let tag = Month(localTimestamp: Int32(localTimestamp)).packedValue
|
||||
self._cachedTag = tag
|
||||
return tag
|
||||
} else {
|
||||
// if let tag = self._cachedTag {
|
||||
// return tag
|
||||
// } else if let asset = self.asset, let localTimestamp = asset.creationDate?.timeIntervalSince1970 {
|
||||
// let tag = Month(localTimestamp: Int32(localTimestamp)).packedValue
|
||||
// self._cachedTag = tag
|
||||
// return tag
|
||||
// } else {
|
||||
return nil
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
func updateSelectionState(animated: Bool = false) {
|
||||
if self.checkNode == nil, let _ = self.interaction?.selectionState, let theme = self.theme {
|
||||
let checkNode = InteractiveCheckNode(theme: CheckNodeTheme(theme: theme, style: .overlay))
|
||||
checkNode.valueChanged = { [weak self] value in
|
||||
if let strongSelf = self, let asset = strongSelf.asset, let interaction = strongSelf.interaction {
|
||||
if let legacyAsset = TGMediaAsset(phAsset: asset) {
|
||||
interaction.toggleSelection(legacyAsset, value, false)
|
||||
}
|
||||
if let strongSelf = self, let interaction = strongSelf.interaction, let selectableItem = strongSelf.selectableItem {
|
||||
interaction.toggleSelection(selectableItem, value, false)
|
||||
}
|
||||
}
|
||||
self.addSubnode(checkNode)
|
||||
@ -142,10 +154,10 @@ final class MediaPickerGridItemNode: GridItemNode {
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
|
||||
if let asset = self.asset, let interaction = self.interaction, let selectionState = interaction.selectionState {
|
||||
let selected = selectionState.isIdentifierSelected(asset.localIdentifier)
|
||||
if let legacyAsset = TGMediaAsset(phAsset: asset) {
|
||||
let index = selectionState.index(of: legacyAsset)
|
||||
if let interaction = self.interaction, let selectionState = interaction.selectionState {
|
||||
let selected = selectionState.isIdentifierSelected(self.identifier)
|
||||
if let selectableItem = self.selectableItem {
|
||||
let index = selectionState.index(of: selectableItem)
|
||||
if index != NSNotFound {
|
||||
self.checkNode?.content = .counter(Int(index))
|
||||
}
|
||||
@ -155,12 +167,10 @@ final class MediaPickerGridItemNode: GridItemNode {
|
||||
}
|
||||
|
||||
func updateHiddenMedia() {
|
||||
if let asset = self.asset {
|
||||
let wasHidden = self.isHidden
|
||||
self.isHidden = self.interaction?.hiddenMediaId == asset.localIdentifier
|
||||
if !self.isHidden && wasHidden {
|
||||
self.animateFadeIn(animateCheckNode: true)
|
||||
}
|
||||
let wasHidden = self.isHidden
|
||||
self.isHidden = self.interaction?.hiddenMediaId == self.identifier
|
||||
if !self.isHidden && wasHidden {
|
||||
self.animateFadeIn(animateCheckNode: true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,6 +188,73 @@ final class MediaPickerGridItemNode: GridItemNode {
|
||||
|
||||
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
|
||||
}
|
||||
|
||||
func setup(interaction: MediaPickerInteraction, media: MediaPickerScreen.Subject.Media, index: Int, theme: PresentationTheme) {
|
||||
self.interaction = interaction
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundColor = theme.list.mediaPlaceholderColor
|
||||
|
||||
if self.currentMediaState == nil || self.currentMediaState!.0.uniqueIdentifier != media.identifier || self.currentState!.1 != index {
|
||||
// let editingContext = interaction.editingState
|
||||
// let asset = media.asset as? TGMediaEditableItem
|
||||
//
|
||||
// let editedSignal = Signal<UIImage?, NoError> { subscriber in
|
||||
// if let signal = editingContext.thumbnailImageSignal(forIdentifier: media.identifier) {
|
||||
// let disposable = signal.start(next: { next in
|
||||
// if let image = next as? UIImage {
|
||||
// subscriber.putNext(image)
|
||||
// } else {
|
||||
// subscriber.putNext(nil)
|
||||
// }
|
||||
// }, error: { _ in
|
||||
// }, completed: nil)!
|
||||
//
|
||||
// return ActionDisposable {
|
||||
// disposable.dispose()
|
||||
// }
|
||||
// } else {
|
||||
// return EmptyDisposable
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// let originalImageSignal = Signal<UIImage?, NoError> { subscriber in
|
||||
// if let signal = asset?.thumbnailImageSignal?()
|
||||
// }
|
||||
//
|
||||
// let scale = min(2.0, UIScreenScale)
|
||||
// let targetSize = CGSize(width: 128.0 * scale, height: 128.0 * scale)
|
||||
// let originalSignal: Signal<UIImage, NoError> = assetImage(fetchResult: fetchResult, index: index, targetSize: targetSize, exact: false)
|
||||
// let imageSignal: Signal<UIImage?, NoError> = editedSignal
|
||||
// |> mapToSignal { result in
|
||||
// if let result = result {
|
||||
// return .single(result)
|
||||
// } else {
|
||||
// return originalSignal
|
||||
// }
|
||||
// }
|
||||
// self.imageNode.setSignal(imageSignal)
|
||||
//
|
||||
// if case .video = media, let asset = media.asset as? TGCameraCapturedVideo {
|
||||
// self.typeIconNode.image = UIImage(bundleImageName: "Media Editor/MediaVideo")
|
||||
//
|
||||
// if self.typeIconNode.supernode == nil {
|
||||
// self.durationNode.attributedText = NSAttributedString(string: stringForDuration(Int32(asset.videoDuration)), font: Font.semibold(12.0), textColor: .white)
|
||||
//
|
||||
// self.addSubnode(self.gradientNode)
|
||||
// self.addSubnode(self.typeIconNode)
|
||||
// self.addSubnode(self.durationNode)
|
||||
// self.setNeedsLayout()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
self.currentMediaState = (media.asset, index)
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
|
||||
self.updateSelectionState()
|
||||
self.updateHiddenMedia()
|
||||
}
|
||||
|
||||
func setup(interaction: MediaPickerInteraction, fetchResult: PHFetchResult<PHAsset>, index: Int, theme: PresentationTheme) {
|
||||
self.interaction = interaction
|
||||
|
@ -103,6 +103,34 @@ struct Month: Equatable {
|
||||
}
|
||||
|
||||
public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
public enum Subject {
|
||||
public enum Media: Equatable {
|
||||
case image(UIImage)
|
||||
case video(URL)
|
||||
|
||||
var asset: TGMediaSelectableItem {
|
||||
switch self {
|
||||
case let .image(image):
|
||||
return image
|
||||
case let .video(url):
|
||||
return TGCameraCapturedVideo(url: url)
|
||||
}
|
||||
}
|
||||
|
||||
var identifier: String {
|
||||
switch self {
|
||||
case let .image(image):
|
||||
return image.uniqueIdentifier
|
||||
case let .video(url):
|
||||
return url.absoluteString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case assets(PHAssetCollection?)
|
||||
case media([Media])
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
@ -113,7 +141,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
private let peer: EnginePeer?
|
||||
private let chatLocation: ChatLocation?
|
||||
private let bannedSendMedia: (Int32, Bool)?
|
||||
private let collection: PHAssetCollection?
|
||||
private let subject: Subject
|
||||
private let saveEditedPhotos: Bool
|
||||
|
||||
private let titleView: MediaPickerTitleView
|
||||
@ -149,6 +177,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
enum State {
|
||||
case noAccess(cameraAccess: AVAuthorizationStatus?)
|
||||
case assets(fetchResult: PHFetchResult<PHAsset>?, preload: Bool, mediaAccess: PHAuthorizationStatus, cameraAccess: AVAuthorizationStatus?)
|
||||
case media([Subject.Media])
|
||||
}
|
||||
|
||||
private weak var controller: MediaPickerScreen?
|
||||
@ -205,7 +234,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
|
||||
super.init()
|
||||
|
||||
if controller.collection != nil {
|
||||
if case .assets(nil) = controller.subject {
|
||||
} else {
|
||||
self.preloadPromise.set(false)
|
||||
}
|
||||
|
||||
@ -214,27 +244,32 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.containerNode.addSubnode(self.gridNode)
|
||||
// self.containerNode.addSubnode(self.scrollingArea)
|
||||
|
||||
let collection = controller.collection
|
||||
let preloadPromise = self.preloadPromise
|
||||
let updatedState = combineLatest(mediaAssetsContext.mediaAccess(), mediaAssetsContext.cameraAccess())
|
||||
|> mapToSignal { mediaAccess, cameraAccess -> Signal<State, NoError> in
|
||||
if case .notDetermined = mediaAccess {
|
||||
return .single(.assets(fetchResult: nil, preload: false, mediaAccess: mediaAccess, cameraAccess: cameraAccess))
|
||||
} else if [.restricted, .denied].contains(mediaAccess) {
|
||||
return .single(.noAccess(cameraAccess: cameraAccess))
|
||||
} else {
|
||||
if let collection = collection {
|
||||
return combineLatest(mediaAssetsContext.fetchAssets(collection), preloadPromise.get())
|
||||
|> map { fetchResult, preload in
|
||||
return .assets(fetchResult: fetchResult, preload: preload, mediaAccess: mediaAccess, cameraAccess: cameraAccess)
|
||||
}
|
||||
let updatedState: Signal<State, NoError>
|
||||
switch controller.subject {
|
||||
case let .assets(collection):
|
||||
updatedState = combineLatest(mediaAssetsContext.mediaAccess(), mediaAssetsContext.cameraAccess())
|
||||
|> mapToSignal { mediaAccess, cameraAccess -> Signal<State, NoError> in
|
||||
if case .notDetermined = mediaAccess {
|
||||
return .single(.assets(fetchResult: nil, preload: false, mediaAccess: mediaAccess, cameraAccess: cameraAccess))
|
||||
} else if [.restricted, .denied].contains(mediaAccess) {
|
||||
return .single(.noAccess(cameraAccess: cameraAccess))
|
||||
} else {
|
||||
return combineLatest(mediaAssetsContext.recentAssets(), preloadPromise.get())
|
||||
|> map { fetchResult, preload in
|
||||
return .assets(fetchResult: fetchResult, preload: preload, mediaAccess: mediaAccess, cameraAccess: cameraAccess)
|
||||
if let collection = collection {
|
||||
return combineLatest(mediaAssetsContext.fetchAssets(collection), preloadPromise.get())
|
||||
|> map { fetchResult, preload in
|
||||
return .assets(fetchResult: fetchResult, preload: preload, mediaAccess: mediaAccess, cameraAccess: cameraAccess)
|
||||
}
|
||||
} else {
|
||||
return combineLatest(mediaAssetsContext.recentAssets(), preloadPromise.get())
|
||||
|> map { fetchResult, preload in
|
||||
return .assets(fetchResult: fetchResult, preload: preload, mediaAccess: mediaAccess, cameraAccess: cameraAccess)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .media(media):
|
||||
updatedState = .single(.media(media))
|
||||
}
|
||||
|
||||
self.itemsDisposable = (updatedState
|
||||
@ -325,10 +360,30 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.gridNode.scrollView.alwaysBounceVertical = true
|
||||
self.gridNode.scrollView.showsVerticalScrollIndicator = false
|
||||
|
||||
if self.controller?.collection != nil {
|
||||
self.selectionGesture = MediaPickerGridSelectionGesture(target: nil, action: nil, gridNode: self.gridNode)
|
||||
self.selectionGesture?.delegate = self
|
||||
self.selectionGesture?.began = { [weak self] in
|
||||
self?.controller?.cancelPanGesture()
|
||||
}
|
||||
self.selectionGesture?.itemAt = { [weak self] point in
|
||||
if let strongSelf = self, let itemNode = strongSelf.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let selectableItem = itemNode.selectableItem {
|
||||
return (selectableItem, strongSelf.controller?.interaction?.selectionState?.isIdentifierSelected(selectableItem.uniqueIdentifier) ?? false)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
self.selectionGesture?.updateSelection = { [weak self] asset, selected in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil)
|
||||
}
|
||||
}
|
||||
|
||||
if let controller = self.controller, case let .assets(collection) = controller.subject, collection != nil {
|
||||
self.gridNode.view.interactiveTransitionGestureRecognizerTest = { point -> Bool in
|
||||
return point.x > 44.0
|
||||
}
|
||||
|
||||
self.selectionGesture?.sideInset = 44.0
|
||||
}
|
||||
|
||||
self.scrollingArea.beginScrolling = { [weak self] in
|
||||
@ -346,7 +401,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
// strongSelf.isFastScrolling = false
|
||||
}
|
||||
|
||||
if self.controller?.collection == nil {
|
||||
if let controller = self.controller, case .assets(nil) = controller.subject {
|
||||
let cameraView = TGAttachmentCameraView(forSelfPortrait: false)!
|
||||
cameraView.clipsToBounds = true
|
||||
cameraView.removeCorners()
|
||||
@ -363,27 +418,6 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
} else {
|
||||
self.containerNode.clipsToBounds = true
|
||||
}
|
||||
|
||||
self.selectionGesture = MediaPickerGridSelectionGesture(target: nil, action: nil, gridNode: self.gridNode)
|
||||
self.selectionGesture?.delegate = self
|
||||
self.selectionGesture?.began = { [weak self] in
|
||||
self?.controller?.cancelPanGesture()
|
||||
}
|
||||
self.selectionGesture?.itemAt = { [weak self] point in
|
||||
if let strongSelf = self, let itemNode = strongSelf.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let asset = itemNode.asset.flatMap({ TGMediaAsset(phAsset: $0) }) {
|
||||
return (asset, strongSelf.controller?.interaction?.selectionState?.isItemSelected(asset) ?? false)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
self.selectionGesture?.updateSelection = { [weak self] asset, selected in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil)
|
||||
}
|
||||
}
|
||||
if self.controller?.collection != nil {
|
||||
self.selectionGesture?.sideInset = 44.0
|
||||
}
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
@ -450,51 +484,57 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
var updateLayout = false
|
||||
|
||||
switch state {
|
||||
case let .noAccess(cameraAccess):
|
||||
if case .assets = previousState {
|
||||
updateLayout = true
|
||||
} else if case let .noAccess(previousCameraAccess) = previousState, previousCameraAccess != cameraAccess {
|
||||
case let .noAccess(cameraAccess):
|
||||
if case .assets = previousState {
|
||||
updateLayout = true
|
||||
} else if case let .noAccess(previousCameraAccess) = previousState, previousCameraAccess != cameraAccess {
|
||||
updateLayout = true
|
||||
}
|
||||
if case .notDetermined = cameraAccess, !self.requestedCameraAccess {
|
||||
self.requestedCameraAccess = true
|
||||
self.mediaAssetsContext.requestCameraAccess()
|
||||
}
|
||||
case let .assets(fetchResult, preload, mediaAccess, cameraAccess):
|
||||
if let fetchResult = fetchResult {
|
||||
let totalCount = fetchResult.count
|
||||
let count = preload ? min(13, totalCount) : totalCount
|
||||
|
||||
for i in 0 ..< count {
|
||||
let index: Int
|
||||
if case let .assets(collection) = controller.subject, let _ = collection {
|
||||
index = i
|
||||
} else {
|
||||
index = totalCount - i - 1
|
||||
}
|
||||
entries.append(MediaPickerGridEntry(stableId: stableId, content: .asset(fetchResult, index)))
|
||||
stableId += 1
|
||||
}
|
||||
|
||||
if case let .assets(previousFetchResult, _, _, previousCameraAccess) = previousState, previousFetchResult == nil || previousCameraAccess != cameraAccess {
|
||||
updateLayout = true
|
||||
}
|
||||
|
||||
if case .notDetermined = cameraAccess, !self.requestedCameraAccess {
|
||||
self.requestedCameraAccess = true
|
||||
self.mediaAssetsContext.requestCameraAccess()
|
||||
}
|
||||
case let .assets(fetchResult, preload, mediaAccess, cameraAccess):
|
||||
if let fetchResult = fetchResult {
|
||||
let totalCount = fetchResult.count
|
||||
let count = preload ? min(13, totalCount) : totalCount
|
||||
|
||||
for i in 0 ..< count {
|
||||
let index: Int
|
||||
if self.controller?.collection != nil {
|
||||
index = i
|
||||
} else {
|
||||
index = totalCount - i - 1
|
||||
}
|
||||
entries.append(MediaPickerGridEntry(stableId: stableId, content: .asset(fetchResult, index)))
|
||||
stableId += 1
|
||||
}
|
||||
|
||||
if case let .assets(previousFetchResult, _, _, previousCameraAccess) = previousState, previousFetchResult == nil || previousCameraAccess != cameraAccess {
|
||||
updateLayout = true
|
||||
}
|
||||
|
||||
if case .notDetermined = cameraAccess, !self.requestedCameraAccess {
|
||||
self.requestedCameraAccess = true
|
||||
self.mediaAssetsContext.requestCameraAccess()
|
||||
}
|
||||
} else if case .notDetermined = mediaAccess, !self.requestedMediaAccess {
|
||||
self.requestedMediaAccess = true
|
||||
self.mediaAssetsContext.requestMediaAccess()
|
||||
}
|
||||
} else if case .notDetermined = mediaAccess, !self.requestedMediaAccess {
|
||||
self.requestedMediaAccess = true
|
||||
self.mediaAssetsContext.requestMediaAccess()
|
||||
}
|
||||
case let .media(media):
|
||||
let count = media.count
|
||||
for i in 0 ..< count {
|
||||
entries.append(MediaPickerGridEntry(stableId: stableId, content: .media(media[i], i)))
|
||||
stableId += 1
|
||||
}
|
||||
}
|
||||
|
||||
let previousEntries = self.currentEntries
|
||||
self.currentEntries = entries
|
||||
|
||||
var scrollToItem: GridNodeScrollToItem?
|
||||
if self.controller?.collection != nil && previousEntries.isEmpty && !entries.isEmpty {
|
||||
if case let .assets(collection) = controller.subject, let _ = collection, previousEntries.isEmpty && !entries.isEmpty {
|
||||
scrollToItem = GridNodeScrollToItem(index: entries.count - 1, position: .bottom(0.0), transition: .immediate, directionHint: .down, adjustForSection: false)
|
||||
}
|
||||
|
||||
@ -531,7 +571,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
|
||||
private var currentDisplayMode: DisplayMode = .all
|
||||
func updateDisplayMode(_ displayMode: DisplayMode) {
|
||||
func updateDisplayMode(_ displayMode: DisplayMode, animated: Bool = true) {
|
||||
let updated = self.currentDisplayMode != displayMode
|
||||
self.currentDisplayMode = displayMode
|
||||
|
||||
@ -539,8 +579,13 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.controller?.dismissAllTooltips()
|
||||
|
||||
if case .selected = displayMode, self.selectionNode == nil, let controller = self.controller {
|
||||
let selectionNode = MediaPickerSelectedListNode(context: controller.context)
|
||||
selectionNode.alpha = 0.0
|
||||
var persistentItems = false
|
||||
if case .media = controller.subject {
|
||||
persistentItems = true
|
||||
}
|
||||
|
||||
let selectionNode = MediaPickerSelectedListNode(context: controller.context, persistentItems: persistentItems)
|
||||
selectionNode.alpha = animated ? 0.0 : 1.0
|
||||
selectionNode.layer.allowsGroupOpacity = true
|
||||
selectionNode.isUserInteractionEnabled = false
|
||||
selectionNode.interaction = self.controller?.interaction
|
||||
@ -548,7 +593,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
if let strongSelf = self {
|
||||
var node: MediaPickerGridItemNode?
|
||||
strongSelf.gridNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? MediaPickerGridItemNode, itemNode.asset?.localIdentifier == identifier {
|
||||
if let itemNode = itemNode as? MediaPickerGridItemNode, itemNode.identifier == identifier {
|
||||
node = itemNode
|
||||
}
|
||||
}
|
||||
@ -583,13 +628,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
|
||||
if updated {
|
||||
switch displayMode {
|
||||
if animated {
|
||||
switch displayMode {
|
||||
case .selected:
|
||||
self.selectionNode?.animateIn(initiated: { [weak self] in
|
||||
self?.updateNavigation(transition: .immediate)
|
||||
}, completion: completion)
|
||||
case .all:
|
||||
self.selectionNode?.animateOut(completion: completion)
|
||||
}
|
||||
} else {
|
||||
self.updateNavigation(transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -612,7 +661,12 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
|
||||
self.openingMedia = true
|
||||
|
||||
let reversed = controller.collection == nil
|
||||
let reversed: Bool
|
||||
if case .assets(nil) = controller.subject {
|
||||
reversed = true
|
||||
} else {
|
||||
reversed = false
|
||||
}
|
||||
let index = reversed ? fetchResult.count - index - 1 : index
|
||||
self.currentGalleryController = presentLegacyMediaPickerGallery(context: controller.context, peer: controller.peer, chatLocation: controller.chatLocation, presentationData: self.presentationData, source: .fetchResult(fetchResult: fetchResult, index: index, reversed: reversed), immediateThumbnail: immediateThumbnail, selectionContext: interaction.selectionState, editingContext: interaction.editingState, hasSilentPosting: true, hasSchedule: true, hasTimer: hasTimer, updateHiddenMedia: { [weak self] id in
|
||||
self?.hiddenMediaId.set(.single(id))
|
||||
@ -689,7 +743,14 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
|
||||
let proceed: (Bool) -> Void = { convertToJpeg in
|
||||
guard let signals = TGMediaAssetsController.resultSignals(for: controller.interaction?.selectionState, editingContext: controller.interaction?.editingState, intent: asFile ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent, currentItem: nil, storeAssets: true, convertToJpeg: convertToJpeg, descriptionGenerator: legacyAssetPickerItemGenerator(), saveEditedPhotos: controller.saveEditedPhotos) else {
|
||||
let signals: [Any]!
|
||||
switch controller.subject {
|
||||
case .assets:
|
||||
signals = TGMediaAssetsController.resultSignals(for: controller.interaction?.selectionState, editingContext: controller.interaction?.editingState, intent: asFile ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent, currentItem: nil, storeAssets: true, convertToJpeg: convertToJpeg, descriptionGenerator: legacyAssetPickerItemGenerator(), saveEditedPhotos: controller.saveEditedPhotos)
|
||||
case .media:
|
||||
signals = TGMediaAssetsController.pasteboardResultSignals(for: controller.interaction?.selectionState, editingContext: controller.interaction?.editingState, intent: asFile ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent, currentItem: nil, descriptionGenerator: legacyAssetPickerItemGenerator())
|
||||
}
|
||||
guard let signals = signals else {
|
||||
return
|
||||
}
|
||||
controller.completed = true
|
||||
@ -980,8 +1041,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
})
|
||||
|
||||
if let selectionNode = self.selectionNode {
|
||||
let selectedItems = self.controller?.interaction?.selectionState?.selectedItems() as? [TGMediaSelectableItem] ?? []
|
||||
if let selectionNode = self.selectionNode, let controller = self.controller {
|
||||
let selectedItems = controller.interaction?.selectionState?.selectedItems() as? [TGMediaSelectableItem] ?? []
|
||||
let updateSelectionNode = {
|
||||
selectionNode.updateLayout(size: bounds.size, insets: cleanGridInsets, items: selectedItems, grouped: self.controller?.groupedValue ?? true, theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper, bubbleCorners: self.presentationData.chatBubbleCorners, transition: transition)
|
||||
}
|
||||
@ -1068,20 +1129,27 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
|
||||
private var isDismissing = false
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer?, chatLocation: ChatLocation?, bannedSendMedia: (Int32, Bool)?, collection: PHAssetCollection? = nil, editingContext: TGMediaEditingContext? = nil, selectionContext: TGMediaSelectionContext? = nil, saveEditedPhotos: Bool = false) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer?, chatLocation: ChatLocation?, bannedSendMedia: (Int32, Bool)?, subject: Subject, editingContext: TGMediaEditingContext? = nil, selectionContext: TGMediaSelectionContext? = nil, saveEditedPhotos: Bool = false) {
|
||||
self.context = context
|
||||
|
||||
|
||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.presentationData = presentationData
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.peer = peer
|
||||
self.chatLocation = chatLocation
|
||||
self.bannedSendMedia = bannedSendMedia
|
||||
self.collection = collection
|
||||
self.subject = subject
|
||||
self.saveEditedPhotos = saveEditedPhotos
|
||||
|
||||
let selectionContext = selectionContext ?? TGMediaSelectionContext()
|
||||
|
||||
self.titleView = MediaPickerTitleView(theme: self.presentationData.theme, segments: [self.presentationData.strings.Attachment_AllMedia, self.presentationData.strings.Attachment_SelectedMedia(1)], selectedIndex: 0)
|
||||
self.titleView.title = collection?.localizedTitle ?? presentationData.strings.Attachment_Gallery
|
||||
|
||||
if case let .assets(collection) = subject, let collection = collection {
|
||||
self.titleView.title = collection.localizedTitle ?? presentationData.strings.Attachment_Gallery
|
||||
} else {
|
||||
self.titleView.title = presentationData.strings.Attachment_Gallery
|
||||
}
|
||||
|
||||
self.moreButtonNode = MoreButtonNode(theme: self.presentationData.theme)
|
||||
|
||||
@ -1108,16 +1176,16 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
strongSelf.controllerNode.updateDisplayMode(index == 0 ? .all : .selected)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
self.navigationItem.titleView = self.titleView
|
||||
|
||||
if collection == nil {
|
||||
if case let .assets(collection) = self.subject, collection != nil {
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(backButtonAppearanceWithTitle: self.presentationData.strings.Common_Back, target: self, action: #selector(self.backPressed))
|
||||
} else {
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: self.moreButtonNode)
|
||||
self.navigationItem.rightBarButtonItem?.action = #selector(self.rightButtonPressed)
|
||||
self.navigationItem.rightBarButtonItem?.target = self
|
||||
} else {
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(backButtonAppearanceWithTitle: self.presentationData.strings.Common_Back, target: self, action: #selector(self.backPressed))
|
||||
}
|
||||
|
||||
self.moreButtonNode.action = { [weak self] _, gesture in
|
||||
@ -1186,10 +1254,16 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerNode.dismissInput()
|
||||
}
|
||||
}, selectionState: selectionContext ?? TGMediaSelectionContext(), editingState: editingContext ?? TGMediaEditingContext())
|
||||
}, selectionState: selectionContext, editingState: editingContext ?? TGMediaEditingContext())
|
||||
self.interaction?.selectionState?.grouping = true
|
||||
|
||||
self.updateSelectionState(count: Int32(selectionContext?.count() ?? 0))
|
||||
if case let .media(media) = self.subject {
|
||||
for item in media {
|
||||
selectionContext.setItem(item.asset, selected: true)
|
||||
}
|
||||
}
|
||||
|
||||
self.updateSelectionState(count: Int32(selectionContext.count()))
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
@ -1205,25 +1279,43 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
|
||||
self._ready.set(self.controllerNode.ready.get())
|
||||
|
||||
if case .media = self.subject {
|
||||
self.controllerNode.updateDisplayMode(.selected, animated: false)
|
||||
}
|
||||
|
||||
super.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
private weak var undoOverlayController: UndoOverlayController?
|
||||
private func showSelectionUndo(item: TGMediaSelectableItem) {
|
||||
var asset: PHAsset?
|
||||
if let item = item as? TGMediaAsset {
|
||||
asset = item.backingAsset
|
||||
} else if let item = item as? TGCameraCapturedVideo {
|
||||
asset = item.originalAsset.backingAsset
|
||||
}
|
||||
|
||||
guard let asset = asset else {
|
||||
return
|
||||
}
|
||||
|
||||
let scale = min(2.0, UIScreenScale)
|
||||
let targetSize = CGSize(width: 64.0 * scale, height: 64.0 * scale)
|
||||
let _ = (assetImage(asset: asset, targetSize: targetSize, exact: false)
|
||||
|
||||
let image: Signal<UIImage?, NoError>
|
||||
if let item = item as? TGMediaAsset {
|
||||
image = assetImage(asset: item.backingAsset, targetSize: targetSize, exact: false)
|
||||
} else if let item = item as? TGCameraCapturedVideo {
|
||||
image = assetImage(asset: item.originalAsset.backingAsset, targetSize: targetSize, exact: false)
|
||||
} else if let item = item as? TGMediaEditableItem {
|
||||
image = Signal<UIImage?, NoError> { subscriber in
|
||||
let disposable = item.thumbnailImageSignal?().start(next: { next in
|
||||
if let next = next as? UIImage {
|
||||
subscriber.putNext(next)
|
||||
}
|
||||
}, error: { _ in
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
|
||||
return ActionDisposable {
|
||||
disposable?.dispose()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (image
|
||||
|> deliverOnMainQueue).start(next: { [weak self] image in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -1240,6 +1332,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
} else if let _ = item as? TGCameraCapturedVideo {
|
||||
videosCount += 1
|
||||
} else if let _ = item as? UIImage {
|
||||
photosCount += 1
|
||||
}
|
||||
})
|
||||
let totalCount = Int32(photosCount + videosCount)
|
||||
@ -1288,17 +1382,24 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
private var selectionCount: Int32 = 0
|
||||
fileprivate func updateSelectionState(count: Int32) {
|
||||
self.selectionCount = count
|
||||
if count > 0 {
|
||||
self.titleView.segments = [self.presentationData.strings.Attachment_AllMedia, self.presentationData.strings.Attachment_SelectedMedia(count)]
|
||||
self.titleView.segmentsHidden = false
|
||||
self.moreButtonNode.iconNode.enqueueState(.more, animated: true)
|
||||
} else {
|
||||
|
||||
if case let .media(media) = self.subject {
|
||||
self.titleView.title = media.count == 1 ? self.presentationData.strings.Attachment_Pasteboard : self.presentationData.strings.Attachment_SelectedMedia(count)
|
||||
self.titleView.segmentsHidden = true
|
||||
self.moreButtonNode.iconNode.enqueueState(.search, animated: true)
|
||||
|
||||
if self.titleView.index != 0 {
|
||||
Queue.mainQueue().after(0.3) {
|
||||
self.titleView.index = 0
|
||||
self.moreButtonNode.iconNode.enqueueState(.more, animated: false)
|
||||
} else {
|
||||
if count > 0 {
|
||||
self.titleView.segments = [self.presentationData.strings.Attachment_AllMedia, self.presentationData.strings.Attachment_SelectedMedia(count)]
|
||||
self.titleView.segmentsHidden = false
|
||||
self.moreButtonNode.iconNode.enqueueState(.more, animated: true)
|
||||
} else {
|
||||
self.titleView.segmentsHidden = true
|
||||
self.moreButtonNode.iconNode.enqueueState(.search, animated: true)
|
||||
|
||||
if self.titleView.index != 0 {
|
||||
Queue.mainQueue().after(0.3) {
|
||||
self.titleView.index = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1328,7 +1429,15 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
public func requestDismiss(completion: @escaping () -> Void) {
|
||||
if let selectionState = self.interaction?.selectionState, selectionState.count() > 0 {
|
||||
self.isDismissing = true
|
||||
let controller = textAlertController(context: self.context, title: nil, text: self.presentationData.strings.Attachment_CancelSelectionAlertText, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertNo, action: {
|
||||
|
||||
let text: String
|
||||
if case .media = self.subject {
|
||||
text = self.presentationData.strings.Attachment_DiscardPasteboardAlertText
|
||||
} else {
|
||||
text = self.presentationData.strings.Attachment_CancelSelectionAlertText
|
||||
}
|
||||
|
||||
let controller = textAlertController(context: self.context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertNo, action: {
|
||||
|
||||
}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertYes, action: { [weak self] in
|
||||
self?.dismissAllTooltips()
|
||||
@ -1383,7 +1492,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.requestAttachmentMenuExpansion()
|
||||
self.presentWebSearch(MediaGroupsScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mediaAssetsContext: self.controllerNode.mediaAssetsContext, openGroup: { [weak self] collection in
|
||||
if let strongSelf = self {
|
||||
let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, chatLocation: strongSelf.chatLocation, bannedSendMedia: strongSelf.bannedSendMedia, collection: collection, editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState)
|
||||
let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, chatLocation: strongSelf.chatLocation, bannedSendMedia: strongSelf.bannedSendMedia, subject: .assets(collection), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState)
|
||||
|
||||
mediaPicker.presentStickers = strongSelf.presentStickers
|
||||
mediaPicker.presentSchedulePicker = strongSelf.presentSchedulePicker
|
||||
|
@ -15,7 +15,7 @@ import AccountContext
|
||||
import ChatMessageBackground
|
||||
|
||||
private class MediaPickerSelectedItemNode: ASDisplayNode {
|
||||
let asset: TGMediaAsset
|
||||
let asset: TGMediaEditableItem
|
||||
private let interaction: MediaPickerInteraction?
|
||||
|
||||
private let imageNode: ImageNode
|
||||
@ -53,7 +53,7 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
|
||||
|
||||
private var videoDuration: Double?
|
||||
|
||||
init(asset: TGMediaAsset, interaction: MediaPickerInteraction?) {
|
||||
init(asset: TGMediaEditableItem, interaction: MediaPickerInteraction?) {
|
||||
self.imageNode = ImageNode()
|
||||
self.imageNode.contentMode = .scaleAspectFill
|
||||
self.imageNode.clipsToBounds = true
|
||||
@ -91,7 +91,7 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
|
||||
if let adjustments = adjustments as? TGVideoEditAdjustments, adjustments.trimApplied() {
|
||||
duration = adjustments.trimEndValue - adjustments.trimStartValue
|
||||
} else {
|
||||
duration = asset.videoDuration
|
||||
duration = asset.originalDuration ?? 0.0
|
||||
}
|
||||
strongSelf.videoDuration = duration
|
||||
|
||||
@ -114,6 +114,9 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
@objc private func tap() {
|
||||
guard let asset = self.asset as? TGMediaSelectableItem else {
|
||||
return
|
||||
}
|
||||
self.interaction?.openSelectedMedia(asset, self.imageNode.image)
|
||||
}
|
||||
|
||||
@ -142,14 +145,33 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
|
||||
if let adjustments = self.interaction?.editingState.adjustments(for: self.asset), adjustments.cropApplied(forAvatar: false) {
|
||||
dimensions = adjustments.cropRect.size
|
||||
} else {
|
||||
dimensions = self.asset.dimensions
|
||||
dimensions = self.asset.originalSize ?? CGSize()
|
||||
}
|
||||
|
||||
let scale = min(2.0, UIScreenScale)
|
||||
let scaledDimensions = dimensions.aspectFilled(CGSize(width: 320.0, height: 320.0))
|
||||
let targetSize = CGSize(width: scaledDimensions.width * scale, height: scaledDimensions.height * scale)
|
||||
|
||||
let originalSignal = assetImage(asset: self.asset.backingAsset, targetSize: targetSize, exact: false)
|
||||
let originalSignal: Signal<UIImage?, NoError>
|
||||
if let asset = self.asset as? TGMediaAsset {
|
||||
originalSignal = assetImage(asset: asset.backingAsset, targetSize: targetSize, exact: false)
|
||||
} else {
|
||||
let asset = self.asset
|
||||
originalSignal = Signal<UIImage?, NoError> { subscriber in
|
||||
let disposable = asset.screenImageSignal?(0.0).start(next: { next in
|
||||
if let next = next as? UIImage {
|
||||
subscriber.putNext(next)
|
||||
}
|
||||
}, error: { _ in
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
|
||||
return ActionDisposable {
|
||||
disposable?.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
let imageSignal: Signal<UIImage?, NoError> = editedSignal
|
||||
|> mapToSignal { result in
|
||||
if let result = result {
|
||||
@ -166,8 +188,8 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
|
||||
if self.checkNode == nil, let _ = self.interaction?.selectionState, let theme = self.theme {
|
||||
let checkNode = InteractiveCheckNode(theme: CheckNodeTheme(theme: theme, style: .overlay))
|
||||
checkNode.valueChanged = { [weak self] value in
|
||||
if let strongSelf = self, let interaction = strongSelf.interaction {
|
||||
interaction.toggleSelection(strongSelf.asset, value, true)
|
||||
if let strongSelf = self, let interaction = strongSelf.interaction, let selectableItem = strongSelf.asset as? TGMediaSelectableItem {
|
||||
interaction.toggleSelection(selectableItem, value, true)
|
||||
}
|
||||
}
|
||||
self.addSubnode(checkNode)
|
||||
@ -178,9 +200,9 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
if let interaction = self.interaction, let selectionState = interaction.selectionState, let identifier = self.asset.uniqueIdentifier {
|
||||
let selected = selectionState.isIdentifierSelected(identifier)
|
||||
let index = selectionState.index(of: self.asset)
|
||||
if let interaction = self.interaction, let selectionState = interaction.selectionState, let selectableItem = self.asset as? TGMediaSelectableItem {
|
||||
let selected = selectionState.isIdentifierSelected(selectableItem.uniqueIdentifier)
|
||||
let index = selectionState.index(of: selectableItem)
|
||||
if index != NSNotFound {
|
||||
self.checkNode?.content = .counter(Int(index))
|
||||
}
|
||||
@ -379,6 +401,7 @@ private class MessageBackgroundNode: ASDisplayNode {
|
||||
|
||||
final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDelegate {
|
||||
private let context: AccountContext
|
||||
private let persistentItems: Bool
|
||||
|
||||
fileprivate let wallpaperBackgroundNode: WallpaperBackgroundNode
|
||||
private let scrollNode: ASScrollNode
|
||||
@ -398,8 +421,10 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
||||
private var didSetReady = false
|
||||
private var ready = Promise<Bool>()
|
||||
|
||||
init(context: AccountContext) {
|
||||
init(context: AccountContext, persistentItems: Bool) {
|
||||
self.context = context
|
||||
self.persistentItems = persistentItems
|
||||
|
||||
self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false, useExperimentalImplementation: context.sharedContext.immediateExperimentalUISettings.experimentalBackground)
|
||||
self.wallpaperBackgroundNode.backgroundColor = .black
|
||||
self.scrollNode = ASScrollNode()
|
||||
@ -421,7 +446,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
||||
self.scrollNode.view.panGestureRecognizer.cancelsTouchesInView = true
|
||||
self.scrollNode.view.showsVerticalScrollIndicator = false
|
||||
|
||||
self.view.addGestureRecognizer(ReorderingGestureRecognizer(shouldBegin: { [weak self] point in
|
||||
self.view.addGestureRecognizer(ReorderingGestureRecognizer(animateOnTouch: !self.persistentItems, shouldBegin: { [weak self] point in
|
||||
if let strongSelf = self, !strongSelf.scrollNode.view.isDragging && strongSelf.itemNodes.count > 1 {
|
||||
let point = strongSelf.view.convert(point, to: strongSelf.scrollNode.view)
|
||||
for (_, itemNode) in strongSelf.itemNodes {
|
||||
@ -611,8 +636,8 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
||||
}
|
||||
}
|
||||
|
||||
if let targetNode = targetNode, let targetIndex = self.interaction?.selectionState?.index(of: targetNode.asset) {
|
||||
self.interaction?.selectionState?.move(itemNode.asset, to: targetIndex)
|
||||
if let targetNode = targetNode, let sourceItem = itemNode.asset as? TGMediaSelectableItem, let targetItem = targetNode.asset as? TGMediaSelectableItem, let targetIndex = self.interaction?.selectionState?.index(of: targetItem) {
|
||||
self.interaction?.selectionState?.move(sourceItem, to: targetIndex)
|
||||
}
|
||||
reorderNode.animateCompletion(completion: { [weak reorderNode] in
|
||||
reorderNode?.removeFromSupernode()
|
||||
@ -647,7 +672,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
||||
|
||||
var validIds: [String] = []
|
||||
for item in items {
|
||||
guard let asset = item as? TGMediaAsset, let identifier = asset.uniqueIdentifier else {
|
||||
guard let asset = item as? TGMediaEditableItem, let identifier = asset.uniqueIdentifier else {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -671,7 +696,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
||||
if let adjustments = self.interaction?.editingState.adjustments(for: asset), adjustments.cropApplied(forAvatar: false) {
|
||||
itemSizes.append(adjustments.cropRect.size)
|
||||
} else {
|
||||
itemSizes.append(asset.dimensions)
|
||||
itemSizes.append(asset.originalSize ?? CGSize())
|
||||
}
|
||||
}
|
||||
|
||||
@ -981,9 +1006,12 @@ private class ReorderingGestureRecognizer: UIGestureRecognizer {
|
||||
private var initialLocation: CGPoint?
|
||||
private var longPressTimer: SwiftSignalKit.Timer?
|
||||
|
||||
var animateOnTouch = true
|
||||
|
||||
private var itemNode: MediaPickerSelectedItemNode?
|
||||
|
||||
public init(shouldBegin: @escaping (CGPoint) -> (allowed: Bool, requiresLongPress: Bool, itemNode: MediaPickerSelectedItemNode?), willBegin: @escaping (CGPoint) -> Void, began: @escaping (MediaPickerSelectedItemNode) -> Void, ended: @escaping (CGPoint?) -> Void, moved: @escaping (CGPoint) -> Void) {
|
||||
public init(animateOnTouch: Bool, shouldBegin: @escaping (CGPoint) -> (allowed: Bool, requiresLongPress: Bool, itemNode: MediaPickerSelectedItemNode?), willBegin: @escaping (CGPoint) -> Void, began: @escaping (MediaPickerSelectedItemNode) -> Void, ended: @escaping (CGPoint?) -> Void, moved: @escaping (CGPoint) -> Void) {
|
||||
self.animateOnTouch = animateOnTouch
|
||||
self.shouldBegin = shouldBegin
|
||||
self.willBegin = willBegin
|
||||
self.began = began
|
||||
@ -1048,7 +1076,7 @@ private class ReorderingGestureRecognizer: UIGestureRecognizer {
|
||||
if let location = touches.first?.location(in: self.view) {
|
||||
let (allowed, requiresLongPress, itemNode) = self.shouldBegin(location)
|
||||
if allowed {
|
||||
if let itemNode = itemNode {
|
||||
if let itemNode = itemNode, self.animateOnTouch {
|
||||
itemNode.layer.animateScale(from: 1.0, to: 0.98, duration: 0.2, delay: 0.1)
|
||||
}
|
||||
self.itemNode = itemNode
|
||||
|
@ -302,6 +302,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/ChatTitleView",
|
||||
"//submodules/InviteLinksUI:InviteLinksUI",
|
||||
"//submodules/TelegramUI/Components/NotificationPeerExceptionController",
|
||||
"//submodules/MediaPasteboardUI:MediaPasteboardUI",
|
||||
] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||
|
@ -84,6 +84,7 @@ import EntityKeyboard
|
||||
import ChatTitleView
|
||||
import EmojiStatusComponent
|
||||
import ChatTimerScreen
|
||||
import MediaPasteboardUI
|
||||
|
||||
#if DEBUG
|
||||
import os.signpost
|
||||
@ -12609,11 +12610,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
|
||||
private func presentMediaPicker(saveEditedPhotos: Bool, bannedSendMedia: (Int32, Bool)?, present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) {
|
||||
private func presentMediaPicker(subject: MediaPickerScreen.Subject = .assets(nil), saveEditedPhotos: Bool, bannedSendMedia: (Int32, Bool)?, present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) {
|
||||
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
|
||||
return
|
||||
}
|
||||
let controller = MediaPickerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: EnginePeer(peer), chatLocation: self.chatLocation, bannedSendMedia: bannedSendMedia, saveEditedPhotos: saveEditedPhotos)
|
||||
let controller = MediaPickerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: EnginePeer(peer), chatLocation: self.chatLocation, bannedSendMedia: bannedSendMedia, subject: subject, saveEditedPhotos: saveEditedPhotos)
|
||||
let mediaPickerContext = controller.mediaPickerContext
|
||||
controller.openCamera = { [weak self] cameraView in
|
||||
self?.openCamera(cameraView: cameraView)
|
||||
@ -13699,43 +13700,23 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|> deliverOnMainQueue).start(next: { [weak self] settings in
|
||||
if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
let _ = presentLegacyPasteMenu(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, updatedPresentationData: strongSelf.updatedPresentationData, images: images, presentSchedulePicker: { [weak self] done in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentScheduleTimePicker(style: .media, completion: { [weak self] time in
|
||||
if let strongSelf = self {
|
||||
done(time)
|
||||
if strongSelf.presentationInterfaceState.subject != .scheduledMessages && time != scheduleWhenOnlineTimestamp {
|
||||
strongSelf.openScheduledMessages()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}, presentTimerPicker: { [weak self] done in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentTimerPicker(style: .media, completion: { time in
|
||||
done(time)
|
||||
})
|
||||
}
|
||||
}, sendMessagesWithSignals: { signals, silentPosting, scheduleTime in
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil)
|
||||
}, presentStickers: { [weak self] completion in
|
||||
if let strongSelf = self {
|
||||
let controller = DrawingStickersScreen(context: strongSelf.context, selectSticker: { fileReference, view, rect in
|
||||
completion(fileReference.media, fileReference.media.isAnimatedSticker || fileReference.media.isVideoSticker, view, rect)
|
||||
return true
|
||||
})
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
return controller
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}, getCaptionPanelView: { [weak self] in
|
||||
return self?.getCaptionPanelView()
|
||||
}, present: { [weak self] controller, arguments in
|
||||
if let strongSelf = self {
|
||||
strongSelf.present(controller, in: .window(.root), with: arguments)
|
||||
}
|
||||
}, initialLayout: strongSelf.validLayout)
|
||||
|
||||
let controller = mediaPasteboardScreen(
|
||||
context: strongSelf.context,
|
||||
updatedPresentationData: strongSelf.updatedPresentationData,
|
||||
peer: EnginePeer(peer),
|
||||
subjects: images,
|
||||
presentMediaPicker: { [weak self] subject, saveEditedPhotos, bannedSendMedia, present in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentMediaPicker(subject: subject, saveEditedPhotos: saveEditedPhotos, bannedSendMedia: bannedSendMedia, present: present, updateMediaPickerContext: { _ in }, completion: { [weak self] signals, silentPosting, scheduleTime, getAnimatedTransitionSource, completion in
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||
})
|
||||
}
|
||||
},
|
||||
getSourceRect: nil
|
||||
)
|
||||
controller.navigationPresentation = .flatModal
|
||||
strongSelf.push(controller)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1286,7 +1286,7 @@ private final class WebAppContextReferenceContentSource: ContextReferenceContent
|
||||
}
|
||||
|
||||
public func standaloneWebAppController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, params: WebAppParameters, openUrl: @escaping (String) -> Void, getInputContainerNode: @escaping () -> (CGFloat, ASDisplayNode, () -> AttachmentController.InputPanelTransition?)? = { return nil }, completion: @escaping () -> Void = {}, willDismiss: @escaping () -> Void = {}, didDismiss: @escaping () -> Void = {}, getNavigationController: @escaping () -> NavigationController? = { return nil }, getSourceRect: (() -> CGRect?)? = nil) -> ViewController {
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.fromMenu, makeEntityInputView: {
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.fromMenu, hasTextInput: false, makeEntityInputView: {
|
||||
return nil
|
||||
})
|
||||
controller.getInputContainerNode = getInputContainerNode
|
||||
|
Loading…
x
Reference in New Issue
Block a user