mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-06 17:00:13 +00:00
Various improvements
This commit is contained in:
parent
4bf5df909c
commit
c0bfe87449
@ -13659,10 +13659,13 @@ Sorry for the inconvenience.";
|
||||
"Gift.Wear.GetBenefits" = "and get these benefits:";
|
||||
"Gift.Wear.Badge.Title" = "Radiant Badge";
|
||||
"Gift.Wear.Badge.Text" = "The glittering icon of this item will be displayed next to your name.";
|
||||
"Gift.Wear.Badge.ChannelText" = "The glittering icon of this item will be displayed next to your channel's name.";
|
||||
"Gift.Wear.Design.Title" = "Unqiue Profile Design";
|
||||
"Gift.Wear.Design.Text" = "Your profile page will get the color and the symbol of this item.";
|
||||
"Gift.Wear.Design.ChannelText" = "Your channel's page will get the color and the symbol of this item.";
|
||||
"Gift.Wear.Proof.Title" = "Proof of Ownership";
|
||||
"Gift.Wear.Proof.Text" = "Tapping the icon of this item next to your name will show its info and owner.";
|
||||
"Gift.Wear.Proof.ChannelText" = "Tapping the icon of this item next to your channel's name will show its info and owner.";
|
||||
"Gift.Wear.Start" = "Start Wearing";
|
||||
|
||||
"Gift.View.Header.Transfer" = "transfer";
|
||||
@ -13766,7 +13769,7 @@ Sorry for the inconvenience.";
|
||||
"PeerInfo.Gifts.NoResults.ViewAll" = "View All Gifts";
|
||||
|
||||
|
||||
"Gift.Displayed.ChannelText" = "The gift is now shown on channel's Page.";
|
||||
"Gift.Hidden.ChannelText" = "The gift is removed from channel's Page.";
|
||||
"Gift.Displayed.ChannelText" = "The gift is now shown on the channel's Page.";
|
||||
"Gift.Hidden.ChannelText" = "The gift is removed from the channel's Page.";
|
||||
|
||||
"Gift.Upgrade.AddChannelName" = "Add channel name to the gift";
|
||||
|
||||
@ -109,11 +109,12 @@ public final class ContactSelectionControllerParams {
|
||||
public let displayCallIcons: Bool
|
||||
public let multipleSelection: Bool
|
||||
public let requirePhoneNumbers: Bool
|
||||
public let allowChannelsInSearch: Bool
|
||||
public let confirmation: (ContactListPeer) -> Signal<Bool, NoError>
|
||||
public let openProfile: ((EnginePeer) -> Void)?
|
||||
public let sendMessage: ((EnginePeer) -> Void)?
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: ContactSelectionControllerMode = .generic, autoDismiss: Bool = true, title: @escaping (PresentationStrings) -> String, options: Signal<[ContactListAdditionalOption], NoError> = .single([]), displayDeviceContacts: Bool = false, displayCallIcons: Bool = false, multipleSelection: Bool = false, requirePhoneNumbers: Bool = false, confirmation: @escaping (ContactListPeer) -> Signal<Bool, NoError> = { _ in .single(true) }, openProfile: ((EnginePeer) -> Void)? = nil, sendMessage: ((EnginePeer) -> Void)? = nil) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: ContactSelectionControllerMode = .generic, autoDismiss: Bool = true, title: @escaping (PresentationStrings) -> String, options: Signal<[ContactListAdditionalOption], NoError> = .single([]), displayDeviceContacts: Bool = false, displayCallIcons: Bool = false, multipleSelection: Bool = false, requirePhoneNumbers: Bool = false, allowChannelsInSearch: Bool = false, confirmation: @escaping (ContactListPeer) -> Signal<Bool, NoError> = { _ in .single(true) }, openProfile: ((EnginePeer) -> Void)? = nil, sendMessage: ((EnginePeer) -> Void)? = nil) {
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.mode = mode
|
||||
@ -124,6 +125,7 @@ public final class ContactSelectionControllerParams {
|
||||
self.displayCallIcons = displayCallIcons
|
||||
self.multipleSelection = multipleSelection
|
||||
self.requirePhoneNumbers = requirePhoneNumbers
|
||||
self.allowChannelsInSearch = allowChannelsInSearch
|
||||
self.confirmation = confirmation
|
||||
self.openProfile = openProfile
|
||||
self.sendMessage = sendMessage
|
||||
|
||||
@ -277,7 +277,7 @@ public struct PremiumConfiguration {
|
||||
minChannelWallpaperLevel: get(data["channel_wallpaper_level_min"]) ?? defaultValue.minChannelWallpaperLevel,
|
||||
minChannelCustomWallpaperLevel: get(data["channel_custom_wallpaper_level_min"]) ?? defaultValue.minChannelCustomWallpaperLevel,
|
||||
minChannelRestrictAdsLevel: get(data["channel_restrict_sponsored_level_min"]) ?? defaultValue.minChannelRestrictAdsLevel,
|
||||
minChannelWearGiftLevel: get(data["channel_wear_collectible_level_min"]) ?? defaultValue.minChannelWearGiftLevel,
|
||||
minChannelWearGiftLevel: get(data["channel_emoji_status_level_min"]) ?? defaultValue.minChannelWearGiftLevel,
|
||||
minGroupProfileIconLevel: get(data["group_profile_bg_icon_level_min"]) ?? defaultValue.minGroupProfileIconLevel,
|
||||
minGroupEmojiStatusLevel: get(data["group_emoji_status_level_min"]) ?? defaultValue.minGroupEmojiStatusLevel,
|
||||
minGroupWallpaperLevel: get(data["group_wallpaper_level_min"]) ?? defaultValue.minGroupWallpaperLevel,
|
||||
|
||||
@ -207,6 +207,7 @@ public struct ContactsSearchCategories: OptionSet {
|
||||
public static let cloudContacts = ContactsSearchCategories(rawValue: 1 << 0)
|
||||
public static let global = ContactsSearchCategories(rawValue: 1 << 1)
|
||||
public static let deviceContacts = ContactsSearchCategories(rawValue: 1 << 2)
|
||||
public static let channels = ContactsSearchCategories(rawValue: 1 << 3)
|
||||
}
|
||||
|
||||
public final class ContactsSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
@ -449,7 +450,10 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
if let remotePeers = remotePeers {
|
||||
for peer in remotePeers.0 {
|
||||
if !(peer.peer is TelegramUser) {
|
||||
continue
|
||||
if let channel = peer.peer as? TelegramChannel, case .broadcast = channel.info, categories.contains(.channels) {
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if let user = peer.peer as? TelegramUser {
|
||||
@ -488,7 +492,10 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
}
|
||||
for peer in remotePeers.1 {
|
||||
if !(peer.peer is TelegramUser) {
|
||||
continue
|
||||
if let channel = peer.peer as? TelegramChannel, case .broadcast = channel.info, categories.contains(.channels) {
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if let user = peer.peer as? TelegramUser, requirePhoneNumbers {
|
||||
|
||||
@ -65,6 +65,7 @@ typedef enum
|
||||
@property (nonatomic, assign) bool hasSilentPosting;
|
||||
@property (nonatomic, assign) bool hasSchedule;
|
||||
@property (nonatomic, assign) bool reminder;
|
||||
@property (nonatomic, assign) bool hasCoverButton;
|
||||
|
||||
@property (nonatomic, assign) bool forum;
|
||||
@property (nonatomic, assign) bool isSuggesting;
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
@property (nonatomic, assign) bool hasSilentPosting;
|
||||
@property (nonatomic, assign) bool hasSchedule;
|
||||
@property (nonatomic, assign) bool reminder;
|
||||
@property (nonatomic, assign) bool hasCoverButton;
|
||||
|
||||
@property (nonatomic, assign) bool forum;
|
||||
@property (nonatomic, assign) bool isSuggesting;
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
|
||||
@property (nonatomic, readonly) UIView *timerButton;
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context focusItem:(id<TGModernGalleryItem>)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext hasSelectionPanel:(bool)hasSelectionPanel hasCameraButton:(bool)hasCameraButton recipientName:(NSString *)recipientName isScheduledMessages:(bool)isScheduledMessages;
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context focusItem:(id<TGModernGalleryItem>)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext hasSelectionPanel:(bool)hasSelectionPanel hasCameraButton:(bool)hasCameraButton recipientName:(NSString *)recipientName isScheduledMessages:(bool)isScheduledMessages hasCoverButton:(bool)hasCoverButton;
|
||||
|
||||
- (void)setSelectedItemsModel:(TGMediaPickerGallerySelectedItemsModel *)selectedItemsModel;
|
||||
- (void)setEditorTabPressed:(void (^)(TGPhotoEditorTab tab))editorTabPressed;
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
@property (nonatomic, readonly) TGMediaSelectionContext *selectionContext;
|
||||
@property (nonatomic, strong) id<TGPhotoPaintStickersContext> stickersContext;
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context items:(NSArray *)items focusItem:(id<TGModernGalleryItem>)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions hasSelectionPanel:(bool)hasSelectionPanel hasCamera:(bool)hasCamera recipientName:(NSString *)recipientName isScheduledMessages:(bool)isScheduledMessages;
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context items:(NSArray *)items focusItem:(id<TGModernGalleryItem>)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions hasSelectionPanel:(bool)hasSelectionPanel hasCamera:(bool)hasCamera recipientName:(NSString *)recipientName isScheduledMessages:(bool)isScheduledMessages hasCoverButton:(bool)hasCoverButton;
|
||||
|
||||
- (void)presentPhotoEditorForItem:(id<TGModernGalleryEditableItem>)item tab:(TGPhotoEditorTab)tab;
|
||||
- (void)presentPhotoEditorForItem:(id<TGModernGalleryEditableItem>)item tab:(TGPhotoEditorTab)tab snapshots:(NSArray *)snapshots fromRect:(CGRect)fromRect;
|
||||
|
||||
@ -31,9 +31,9 @@
|
||||
@property (nonatomic, copy) void (^presentScheduleController)(bool, void (^)(int32_t));
|
||||
@property (nonatomic, copy) void (^presentTimerController)(void (^)(int32_t));
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext;
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder hasCoverButton:(bool)hasCoverButton stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext;
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext;
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder hasCoverButton:(bool)hasCoverButton stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext;
|
||||
|
||||
- (void)present;
|
||||
- (void)updateWithFetchResult:(TGMediaAssetFetchResult *)fetchResult;
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
- (void)setController:(UIViewController *(^)(void))closePressed;
|
||||
- (void)setClosePressed:(void (^)())closePressed;
|
||||
- (void)setScrollViewOffsetRequested:(void (^)(CGFloat offset))scrollViewOffsetRequested;
|
||||
- (void)setGesturesEnabled:(void (^)(bool enabled))setGesturesEnabled;
|
||||
|
||||
- (void)itemFocused:(id<TGModernGalleryItem>)item itemView:(TGModernGalleryItemView *)itemView;
|
||||
|
||||
|
||||
@ -38,6 +38,8 @@
|
||||
@property (nonatomic, strong) UIView<TGModernGalleryDefaultFooterAccessoryView> *defaultFooterAccessoryLeftView;
|
||||
@property (nonatomic, strong) UIView<TGModernGalleryDefaultFooterAccessoryView> *defaultFooterAccessoryRightView;
|
||||
|
||||
@property (nonatomic, assign) bool gesturesEnabled;
|
||||
|
||||
- (void)_setItem:(id<TGModernGalleryItem>)item;
|
||||
- (void)setItem:(id<TGModernGalleryItem>)item synchronously:(bool)synchronously;
|
||||
|
||||
|
||||
@ -839,7 +839,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
|
||||
if ([cell isKindOfClass:[TGAttachmentAssetCell class]])
|
||||
thumbnailImage = cell.imageView.image;
|
||||
|
||||
TGMediaPickerModernGalleryMixin *mixin = [[TGMediaPickerModernGalleryMixin alloc] initWithContext:_context item:asset fetchResult:_fetchResult parentController:self.parentController thumbnailImage:thumbnailImage selectionContext:_selectionContext editingContext:_editingContext hasCaptions:(_allowCaptions && !_forProfilePhoto) allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:self.onlyCrop inhibitDocumentCaptions:_inhibitDocumentCaptions inhibitMute:self.inhibitMute asFile:self.asFile itemsLimit:TGAttachmentDisplayedAssetLimit recipientName:self.recipientName hasSilentPosting:self.hasSilentPosting hasSchedule:self.hasSchedule reminder:self.reminder stickersContext:self.stickersContext];
|
||||
TGMediaPickerModernGalleryMixin *mixin = [[TGMediaPickerModernGalleryMixin alloc] initWithContext:_context item:asset fetchResult:_fetchResult parentController:self.parentController thumbnailImage:thumbnailImage selectionContext:_selectionContext editingContext:_editingContext hasCaptions:(_allowCaptions && !_forProfilePhoto) allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:self.onlyCrop inhibitDocumentCaptions:_inhibitDocumentCaptions inhibitMute:self.inhibitMute asFile:self.asFile itemsLimit:TGAttachmentDisplayedAssetLimit recipientName:self.recipientName hasSilentPosting:self.hasSilentPosting hasSchedule:self.hasSchedule reminder:self.reminder hasCoverButton:false stickersContext:self.stickersContext];
|
||||
mixin.presentScheduleController = self.presentScheduleController;
|
||||
mixin.presentTimerController = self.presentTimerController;
|
||||
__weak TGAttachmentCarouselItemView *weakSelf = self;
|
||||
|
||||
@ -1536,7 +1536,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
|
||||
}];
|
||||
|
||||
bool hasCamera = !self.inhibitMultipleCapture && (((_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerGenericPhotoOnlyIntent || _intent == TGCameraControllerGenericVideoOnlyIntent) && !_shortcut) || (_intent == TGCameraControllerPassportMultipleIntent));
|
||||
TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:windowContext items:galleryItems focusItem:focusItem selectionContext:_items.count > 1 ? selectionContext : nil editingContext:editingContext hasCaptions:self.allowCaptions allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:_intent == TGCameraControllerPassportIntent || _intent == TGCameraControllerPassportIdIntent || _intent == TGCameraControllerPassportMultipleIntent inhibitDocumentCaptions:self.inhibitDocumentCaptions hasSelectionPanel:true hasCamera:hasCamera recipientName:self.recipientName isScheduledMessages:false];
|
||||
TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:windowContext items:galleryItems focusItem:focusItem selectionContext:_items.count > 1 ? selectionContext : nil editingContext:editingContext hasCaptions:self.allowCaptions allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:_intent == TGCameraControllerPassportIntent || _intent == TGCameraControllerPassportIdIntent || _intent == TGCameraControllerPassportMultipleIntent inhibitDocumentCaptions:self.inhibitDocumentCaptions hasSelectionPanel:true hasCamera:hasCamera recipientName:self.recipientName isScheduledMessages:false hasCoverButton:false];
|
||||
model.inhibitMute = self.inhibitMute;
|
||||
model.controller = galleryController;
|
||||
model.stickersContext = self.stickersContext;
|
||||
|
||||
@ -260,6 +260,7 @@
|
||||
pickerController.hasSilentPosting = strongController.hasSilentPosting;
|
||||
pickerController.hasSchedule = strongController.hasSchedule;
|
||||
pickerController.reminder = strongController.reminder;
|
||||
pickerController.hasCoverButton = strongController.hasCoverButton;
|
||||
pickerController.forum = strongController.forum;
|
||||
pickerController.isSuggesting = strongController.isSuggesting;
|
||||
pickerController.presentScheduleController = strongController.presentScheduleController;
|
||||
@ -365,6 +366,12 @@
|
||||
self.pickerController.reminder = reminder;
|
||||
}
|
||||
|
||||
- (void)setHasCoverButton:(bool)hasCoverButton
|
||||
{
|
||||
_hasCoverButton = hasCoverButton;
|
||||
self.pickerController.hasCoverButton = hasCoverButton;
|
||||
}
|
||||
|
||||
- (void)setForum:(bool)forum {
|
||||
_forum = forum;
|
||||
self.pickerController.forum = forum;
|
||||
|
||||
@ -359,7 +359,7 @@
|
||||
|
||||
- (TGMediaPickerModernGalleryMixin *)_galleryMixinForContext:(id<LegacyComponentsContext>)context item:(id)item thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities inhibitDocumentCaptions:(bool)inhibitDocumentCaptions asFile:(bool)asFile
|
||||
{
|
||||
return [[TGMediaPickerModernGalleryMixin alloc] initWithContext:context item:item fetchResult:_fetchResult parentController:self thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:self.hasTimer onlyCrop:self.onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:self.inhibitMute asFile:asFile itemsLimit:0 recipientName:self.recipientName hasSilentPosting:self.hasSilentPosting hasSchedule:self.hasSchedule reminder:self.reminder stickersContext:self.stickersContext];
|
||||
return [[TGMediaPickerModernGalleryMixin alloc] initWithContext:context item:item fetchResult:_fetchResult parentController:self thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:self.hasTimer onlyCrop:self.onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:self.inhibitMute asFile:asFile itemsLimit:0 recipientName:self.recipientName hasSilentPosting:self.hasSilentPosting hasSchedule:self.hasSchedule reminder:self.reminder hasCoverButton:self.hasCoverButton stickersContext:self.stickersContext];
|
||||
}
|
||||
|
||||
- (TGMediaPickerModernGalleryMixin *)galleryMixinForIndexPath:(NSIndexPath *)indexPath previewMode:(bool)previewMode outAsset:(TGMediaAsset **)outAsset
|
||||
|
||||
@ -117,6 +117,7 @@
|
||||
|
||||
void (^_closePressed)();
|
||||
void (^_scrollViewOffsetRequested)(CGFloat offset);
|
||||
void (^_setGesturesEnabled)(bool offset);
|
||||
|
||||
id<LegacyComponentsContext> _context;
|
||||
|
||||
@ -132,7 +133,7 @@
|
||||
|
||||
@synthesize safeAreaInset = _safeAreaInset;
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context focusItem:(id<TGModernGalleryItem>)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext hasSelectionPanel:(bool)hasSelectionPanel hasCameraButton:(bool)hasCameraButton recipientName:(NSString *)recipientName isScheduledMessages:(bool)isScheduledMessages
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context focusItem:(id<TGModernGalleryItem>)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext hasSelectionPanel:(bool)hasSelectionPanel hasCameraButton:(bool)hasCameraButton recipientName:(NSString *)recipientName isScheduledMessages:(bool)isScheduledMessages hasCoverButton:(bool)hasCoverButton
|
||||
{
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
if (self != nil)
|
||||
@ -159,10 +160,7 @@
|
||||
|
||||
_wrapperView = [[TGMediaPickerGalleryWrapperView alloc] initWithFrame:CGRectZero];
|
||||
[self addSubview:_wrapperView];
|
||||
|
||||
_headerWrapperView = [[UIView alloc] init];
|
||||
[_wrapperView addSubview:_headerWrapperView];
|
||||
|
||||
|
||||
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
|
||||
void(^toolbarCancelPressed)(void) = ^
|
||||
{
|
||||
@ -247,16 +245,18 @@
|
||||
// [_cameraButton setHidden:true animated:false];
|
||||
}
|
||||
|
||||
_coverButton = [[TGMediaPickerCoverButton alloc] initWithFrame:CGRectMake(0, 0, 120, 26) gallery:false];
|
||||
_coverButton.hidden = true;
|
||||
[_coverButton addTarget:self action:@selector(coverButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_wrapperView addSubview:_coverButton];
|
||||
|
||||
_coverGalleryButton = [[TGMediaPickerCoverButton alloc] initWithFrame:CGRectMake(0, 0, 120, 26) gallery:true];
|
||||
_coverGalleryButton.hidden = true;
|
||||
[_coverGalleryButton addTarget:self action:@selector(coverGalleryButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_wrapperView addSubview:_coverGalleryButton];
|
||||
|
||||
if (hasCoverButton) {
|
||||
_coverButton = [[TGMediaPickerCoverButton alloc] initWithFrame:CGRectMake(0, 0, 180, 26) gallery:false];
|
||||
_coverButton.hidden = true;
|
||||
[_coverButton addTarget:self action:@selector(coverButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_wrapperView addSubview:_coverButton];
|
||||
|
||||
_coverGalleryButton = [[TGMediaPickerCoverButton alloc] initWithFrame:CGRectMake(0, 0, 180, 26) gallery:true];
|
||||
_coverGalleryButton.hidden = true;
|
||||
[_coverGalleryButton addTarget:self action:@selector(coverGalleryButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_wrapperView addSubview:_coverGalleryButton];
|
||||
}
|
||||
|
||||
if (_selectionContext != nil)
|
||||
{
|
||||
_checkButton = [[TGCheckButtonView alloc] initWithStyle:TGCheckButtonStyleGallery];
|
||||
@ -426,6 +426,9 @@
|
||||
_captionMixin.stickersContext = stickersContext;
|
||||
[_captionMixin createInputPanelIfNeeded];
|
||||
|
||||
_headerWrapperView = [[UIView alloc] init];
|
||||
[_wrapperView addSubview:_headerWrapperView];
|
||||
|
||||
TGPhotoEditorDoneButton doneButton = isScheduledMessages ? TGPhotoEditorDoneButtonSchedule : TGPhotoEditorDoneButtonSend;
|
||||
|
||||
_portraitToolbarView = [[TGPhotoToolbarView alloc] initWithContext:_context backButton:TGPhotoEditorBackButtonBack doneButton:doneButton solidBackground:false];
|
||||
@ -442,33 +445,35 @@
|
||||
if ([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad)
|
||||
[_wrapperView addSubview:_landscapeToolbarView];
|
||||
|
||||
_cancelCoverButton = [[TGModernButton alloc] init];
|
||||
_cancelCoverButton.hidden = true;
|
||||
_cancelCoverButton.titleLabel.font = TGSystemFontOfSize(17.0);
|
||||
[_cancelCoverButton setTitle:TGLocalized(@"Common.Cancel") forState:UIControlStateNormal];
|
||||
[_cancelCoverButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
[_cancelCoverButton addTarget:self action:@selector(cancelCoverButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_cancelCoverButton sizeToFit];
|
||||
[_wrapperView addSubview:_cancelCoverButton];
|
||||
|
||||
_coverTitleLabel = [[UILabel alloc] init];
|
||||
_coverTitleLabel.hidden = true;
|
||||
_coverTitleLabel.textColor = [UIColor whiteColor];
|
||||
_coverTitleLabel.font = TGBoldSystemFontOfSize(17.0);
|
||||
_coverTitleLabel.text = TGLocalized(@"Media.SelectFrame");
|
||||
[_coverTitleLabel sizeToFit];
|
||||
[_wrapperView addSubview:_coverTitleLabel];
|
||||
|
||||
_saveCoverButton = [[TGModernButton alloc] init];
|
||||
_saveCoverButton.clipsToBounds = true;
|
||||
_saveCoverButton.layer.cornerRadius = 10.0;
|
||||
_saveCoverButton.hidden = true;
|
||||
[_saveCoverButton setBackgroundColor:UIColorRGB(0x007aff)];
|
||||
_saveCoverButton.titleLabel.font = TGBoldSystemFontOfSize(17.0);
|
||||
[_saveCoverButton setTitle:TGLocalized(@"Media.SaveCover") forState:UIControlStateNormal];
|
||||
[_saveCoverButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
[_saveCoverButton addTarget:self action:@selector(saveCoverButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_wrapperView addSubview:_saveCoverButton];
|
||||
if (hasCoverButton) {
|
||||
_cancelCoverButton = [[TGModernButton alloc] init];
|
||||
_cancelCoverButton.hidden = true;
|
||||
_cancelCoverButton.titleLabel.font = TGSystemFontOfSize(17.0);
|
||||
[_cancelCoverButton setTitle:TGLocalized(@"Common.Cancel") forState:UIControlStateNormal];
|
||||
[_cancelCoverButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
[_cancelCoverButton addTarget:self action:@selector(cancelCoverButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_cancelCoverButton sizeToFit];
|
||||
[_wrapperView addSubview:_cancelCoverButton];
|
||||
|
||||
_coverTitleLabel = [[UILabel alloc] init];
|
||||
_coverTitleLabel.hidden = true;
|
||||
_coverTitleLabel.textColor = [UIColor whiteColor];
|
||||
_coverTitleLabel.font = TGBoldSystemFontOfSize(17.0);
|
||||
_coverTitleLabel.text = TGLocalized(@"Media.SelectFrame");
|
||||
[_coverTitleLabel sizeToFit];
|
||||
[_wrapperView addSubview:_coverTitleLabel];
|
||||
|
||||
_saveCoverButton = [[TGModernButton alloc] init];
|
||||
_saveCoverButton.clipsToBounds = true;
|
||||
_saveCoverButton.layer.cornerRadius = 10.0;
|
||||
_saveCoverButton.hidden = true;
|
||||
[_saveCoverButton setBackgroundColor:UIColorRGB(0x007aff)];
|
||||
_saveCoverButton.titleLabel.font = TGBoldSystemFontOfSize(17.0);
|
||||
[_saveCoverButton setTitle:TGLocalized(@"Media.SaveCover") forState:UIControlStateNormal];
|
||||
[_saveCoverButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
[_saveCoverButton addTarget:self action:@selector(saveCoverButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_wrapperView addSubview:_saveCoverButton];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -554,6 +559,11 @@
|
||||
_scrollViewOffsetRequested = [scrollViewOffsetRequested copy];
|
||||
}
|
||||
|
||||
- (void)setGesturesEnabled:(void (^)(bool))setGesturesEnabled
|
||||
{
|
||||
_setGesturesEnabled = [setGesturesEnabled copy];
|
||||
}
|
||||
|
||||
- (void)setEditorTabPressed:(void (^)(TGPhotoEditorTab tab))editorTabPressed
|
||||
{
|
||||
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
|
||||
@ -634,7 +644,7 @@
|
||||
[_checkButton setNumber:[_selectionContext indexOfItem:selectableItem]];
|
||||
signal = [_selectionContext itemInformativeSelectedSignal:selectableItem];
|
||||
[_itemSelectedDisposable setDisposable:[signal startStrictWithNext:^(TGMediaSelectionChange *next)
|
||||
{
|
||||
{
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
@ -648,7 +658,7 @@
|
||||
|
||||
__weak TGModernGalleryItemView *weakItemView = itemView;
|
||||
[_itemAvailabilityDisposable setDisposable:[[[itemView contentAvailabilityStateSignal] deliverOn:[SQueue mainQueue]] startStrictWithNext:^(id next)
|
||||
{
|
||||
{
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
__strong TGModernGalleryItemView *strongItemView = weakItemView;
|
||||
if (strongSelf == nil || strongItemView == nil)
|
||||
@ -678,7 +688,13 @@
|
||||
}
|
||||
strongSelf->_muteButton.hidden = !sendableAsGif;
|
||||
|
||||
bool canHaveCover = [strongItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]];
|
||||
bool canHaveCover = false;
|
||||
if ([strongItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]]) {
|
||||
TGMediaPickerGalleryVideoItemView *itemView = (TGMediaPickerGalleryVideoItemView *)strongItemView;
|
||||
if (itemView.editableMediaItem.originalDuration >= 60.0) {
|
||||
canHaveCover = true;
|
||||
}
|
||||
}
|
||||
strongSelf->_coverButton.hidden = !canHaveCover;
|
||||
}
|
||||
} file:__FILE_NAME__ line:__LINE__]];
|
||||
@ -863,6 +879,9 @@
|
||||
if ([currentItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]]) {
|
||||
[(TGMediaPickerGalleryVideoItemView *)currentItemView prepareForCoverEditing];
|
||||
}
|
||||
|
||||
_setGesturesEnabled(false);
|
||||
_currentItemView.gesturesEnabled = false;
|
||||
}
|
||||
|
||||
- (void)coverEditorTransitionOut {
|
||||
@ -894,6 +913,9 @@
|
||||
if ([currentItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]]) {
|
||||
[(TGMediaPickerGalleryVideoItemView *)currentItemView returnFromCoverEditing];
|
||||
}
|
||||
|
||||
_setGesturesEnabled(true);
|
||||
_currentItemView.gesturesEnabled = true;
|
||||
}
|
||||
|
||||
- (void)cancelCoverButtonPressed
|
||||
@ -1609,25 +1631,35 @@
|
||||
{
|
||||
UIView *view = [super hitTest:point withEvent:event];
|
||||
|
||||
if (view == _photoCounterButton
|
||||
|| view == _checkButton
|
||||
|| view == _muteButton
|
||||
|| view == _groupButton
|
||||
|| view == _cameraButton
|
||||
|| view == _coverButton
|
||||
|| view == _cancelCoverButton
|
||||
|| view == _saveCoverButton
|
||||
|| view == _coverGalleryButton
|
||||
|| [view isDescendantOfView:_headerWrapperView]
|
||||
|| [view isDescendantOfView:_portraitToolbarView]
|
||||
|| [view isDescendantOfView:_landscapeToolbarView]
|
||||
|| [view isDescendantOfView:_selectedPhotosView]
|
||||
|| [view isDescendantOfView:_captionMixin.inputPanelView]
|
||||
|| ([view isDescendantOfView:_captionMixin.dismissView] && _captionMixin.dismissView.alpha > 0.0)
|
||||
|| [view isKindOfClass:[TGMenuButtonView class]])
|
||||
|
||||
{
|
||||
return view;
|
||||
if (_coverTitleLabel.hidden) {
|
||||
if (view == _photoCounterButton
|
||||
|| view == _checkButton
|
||||
|| view == _muteButton
|
||||
|| view == _groupButton
|
||||
|| view == _cameraButton
|
||||
|| view == _coverButton
|
||||
|| view == _cancelCoverButton
|
||||
|| view == _saveCoverButton
|
||||
|| view == _coverGalleryButton
|
||||
|| [view isDescendantOfView:_headerWrapperView]
|
||||
|| [view isDescendantOfView:_portraitToolbarView]
|
||||
|| [view isDescendantOfView:_landscapeToolbarView]
|
||||
|| [view isDescendantOfView:_selectedPhotosView]
|
||||
|| [view isDescendantOfView:_captionMixin.inputPanelView]
|
||||
|| ([view isDescendantOfView:_captionMixin.dismissView] && _captionMixin.dismissView.alpha > 0.0)
|
||||
|| [view isKindOfClass:[TGMenuButtonView class]])
|
||||
|
||||
{
|
||||
return view;
|
||||
}
|
||||
} else {
|
||||
if (view == _cancelCoverButton
|
||||
|| view == _saveCoverButton
|
||||
|| view == _coverGalleryButton
|
||||
|| [view isDescendantOfView:_headerWrapperView])
|
||||
{
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
@ -1982,7 +2014,7 @@
|
||||
|
||||
_landscapeToolbarView.frame = CGRectMake(_landscapeToolbarView.frame.origin.x, screenEdges.top, TGPhotoEditorToolbarSize, self.frame.size.height);
|
||||
|
||||
_headerWrapperView.frame = CGRectMake(screenEdges.left, _portraitToolbarView.frame.origin.y - 64.0 - [_captionMixin.inputPanel baseHeight], self.frame.size.width, 64.0);
|
||||
_headerWrapperView.frame = CGRectMake(screenEdges.left, _portraitToolbarView.frame.origin.y - 64.0 - [_captionMixin.inputPanel baseHeight], self.frame.size.width, 72.0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
NSString *_recipientName;
|
||||
bool _hasCamera;
|
||||
bool _isScheduledMessages;
|
||||
bool _hasCoverButton;
|
||||
}
|
||||
|
||||
@property (nonatomic, weak) TGPhotoEditorController *editorController;
|
||||
@ -48,7 +49,7 @@
|
||||
|
||||
@implementation TGMediaPickerGalleryModel
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context items:(NSArray *)items focusItem:(id<TGModernGalleryItem>)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions hasSelectionPanel:(bool)hasSelectionPanel hasCamera:(bool)hasCamera recipientName:(NSString *)recipientName isScheduledMessages:(bool)isScheduledMessages
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context items:(NSArray *)items focusItem:(id<TGModernGalleryItem>)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions hasSelectionPanel:(bool)hasSelectionPanel hasCamera:(bool)hasCamera recipientName:(NSString *)recipientName isScheduledMessages:(bool)isScheduledMessages hasCoverButton:(bool)hasCoverButton
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
@ -70,6 +71,7 @@
|
||||
_recipientName = recipientName;
|
||||
_hasCamera = hasCamera;
|
||||
_isScheduledMessages = isScheduledMessages;
|
||||
_hasCoverButton = hasCoverButton;
|
||||
|
||||
__weak TGMediaPickerGalleryModel *weakSelf = self;
|
||||
if (selectionContext != nil)
|
||||
@ -179,7 +181,7 @@
|
||||
if (_interfaceView == nil)
|
||||
{
|
||||
__weak TGMediaPickerGalleryModel *weakSelf = self;
|
||||
_interfaceView = [[TGMediaPickerGalleryInterfaceView alloc] initWithContext:_context focusItem:_initialFocusItem selectionContext:_selectionContext editingContext:_editingContext stickersContext:_stickersContext hasSelectionPanel:_hasSelectionPanel hasCameraButton:_hasCamera recipientName:_recipientName isScheduledMessages:_isScheduledMessages];
|
||||
_interfaceView = [[TGMediaPickerGalleryInterfaceView alloc] initWithContext:_context focusItem:_initialFocusItem selectionContext:_selectionContext editingContext:_editingContext stickersContext:_stickersContext hasSelectionPanel:_hasSelectionPanel hasCameraButton:_hasCamera recipientName:_recipientName isScheduledMessages:_isScheduledMessages hasCoverButton:_hasCoverButton];
|
||||
_interfaceView.hasCaptions = _hasCaptions;
|
||||
_interfaceView.allowCaptionEntities = _allowCaptionEntities;
|
||||
_interfaceView.hasTimer = _hasTimer;
|
||||
|
||||
@ -120,6 +120,8 @@
|
||||
|
||||
CMTime _chaseTime;
|
||||
bool _chasingTime;
|
||||
|
||||
bool _isCoverEditing;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) TGMediaPickerGalleryVideoItem *item;
|
||||
@ -763,12 +765,14 @@
|
||||
|
||||
- (void)prepareForCoverEditing
|
||||
{
|
||||
_isCoverEditing = true;
|
||||
[self setPlayButtonHidden:true animated:true];
|
||||
[self stop];
|
||||
}
|
||||
|
||||
- (void)returnFromCoverEditing
|
||||
{
|
||||
_isCoverEditing = false;
|
||||
if (![self usePhotoBehavior])
|
||||
[self setPlayButtonHidden:false animated:true];
|
||||
}
|
||||
@ -1272,6 +1276,9 @@
|
||||
|
||||
- (void)playPressed
|
||||
{
|
||||
if (!self.gesturesEnabled)
|
||||
return;
|
||||
|
||||
if (_downloadRequired)
|
||||
[self _download];
|
||||
else
|
||||
@ -1437,7 +1444,12 @@
|
||||
if (_wasPlayingBeforeScrubbing) {
|
||||
[self play];
|
||||
} else {
|
||||
[self setPlayButtonHidden:false animated:true];
|
||||
if (!_isCoverEditing)
|
||||
[self setPlayButtonHidden:false animated:true];
|
||||
}
|
||||
|
||||
if (videoScrubber == _coverScrubberView) {
|
||||
[_coverScrubberView setValue:_scrubberView.value resetPosition:true];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -138,8 +138,8 @@ typedef enum
|
||||
static UIImage *leftCurtain;
|
||||
static UIImage *rightCurtain;
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(24.0f, 40.0f), false, 0.0f);
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(11.0f, 40.0f), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.8).CGColor);
|
||||
@ -150,11 +150,11 @@ typedef enum
|
||||
leftCurtain = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(24.0f, 40.0f), false, 0.0f);
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(11.0f, 40.0f), false, 0.0f);
|
||||
context = UIGraphicsGetCurrentContext();
|
||||
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.8).CGColor);
|
||||
path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(-16.0, 0, 40, 40) cornerRadius:9.0];
|
||||
path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(-29.0, 0, 40, 40) cornerRadius:9.0];
|
||||
CGContextAddPath(context, path.CGPath);
|
||||
CGContextFillPath(context);
|
||||
|
||||
@ -163,12 +163,12 @@ typedef enum
|
||||
});
|
||||
|
||||
_leftCurtainView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
||||
_leftCurtainView.image = [leftCurtain stretchableImageWithLeftCapWidth:11 topCapHeight:20];
|
||||
_leftCurtainView.image = [leftCurtain resizableImageWithCapInsets:UIEdgeInsetsMake(0, 11, 0, 0)];
|
||||
_leftCurtainView.clipsToBounds = true;
|
||||
[_wrapperView addSubview:_leftCurtainView];
|
||||
|
||||
_rightCurtainView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
||||
_rightCurtainView.image = [rightCurtain stretchableImageWithLeftCapWidth:11 topCapHeight:20];
|
||||
_rightCurtainView.image = [rightCurtain resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 11)];
|
||||
_rightCurtainView.clipsToBounds = true;
|
||||
[_wrapperView addSubview:_rightCurtainView];
|
||||
|
||||
@ -409,7 +409,7 @@ typedef enum
|
||||
[_dotHandle addGestureRecognizer:_dotPanGestureRecognizer];
|
||||
|
||||
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
|
||||
_tapGestureRecognizer.enabled = false;
|
||||
_tapGestureRecognizer.enabled = cover;
|
||||
[_trimView addGestureRecognizer:_tapGestureRecognizer];
|
||||
}
|
||||
return self;
|
||||
|
||||
@ -40,17 +40,17 @@
|
||||
|
||||
@implementation TGMediaPickerModernGalleryMixin
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder hasCoverButton:(bool)hasCoverButton stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext
|
||||
{
|
||||
return [self initWithContext:context item:item fetchResult:fetchResult momentList:nil parentController:parentController thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:hasTimer onlyCrop:onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:inhibitMute asFile:asFile itemsLimit:itemsLimit recipientName:recipientName hasSilentPosting:hasSilentPosting hasSchedule:hasSchedule reminder:reminder stickersContext:stickersContext];
|
||||
return [self initWithContext:context item:item fetchResult:fetchResult momentList:nil parentController:parentController thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:hasTimer onlyCrop:onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:inhibitMute asFile:asFile itemsLimit:itemsLimit recipientName:recipientName hasSilentPosting:hasSilentPosting hasSchedule:hasSchedule reminder:reminder hasCoverButton:hasCoverButton stickersContext:stickersContext];
|
||||
}
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder hasCoverButton:(bool)hasCoverButton stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext
|
||||
{
|
||||
return [self initWithContext:context item:item fetchResult:nil momentList:momentList parentController:parentController thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:hasTimer onlyCrop:onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:inhibitMute asFile:asFile itemsLimit:itemsLimit recipientName:nil hasSilentPosting:hasSilentPosting hasSchedule:hasSchedule reminder:reminder stickersContext:stickersContext];
|
||||
return [self initWithContext:context item:item fetchResult:nil momentList:momentList parentController:parentController thumbnailImage:thumbnailImage selectionContext:selectionContext editingContext:editingContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:hasTimer onlyCrop:onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions inhibitMute:inhibitMute asFile:asFile itemsLimit:itemsLimit recipientName:nil hasSilentPosting:hasSilentPosting hasSchedule:hasSchedule reminder:reminder hasCoverButton:hasCoverButton stickersContext:stickersContext];
|
||||
}
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder hasCoverButton:(bool)hasCoverButton stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
@ -84,7 +84,7 @@
|
||||
|
||||
NSArray *galleryItems = [self prepareGalleryItemsForFetchResult:fetchResult selectionContext:selectionContext editingContext:editingContext stickersContext:stickersContext asFile:asFile enumerationBlock:enumerationBlock];
|
||||
|
||||
TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:[_windowManager context] items:galleryItems focusItem:focusItem selectionContext:selectionContext editingContext:editingContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:hasTimer onlyCrop:onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions hasSelectionPanel:true hasCamera:false recipientName:recipientName isScheduledMessages:false];
|
||||
TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:[_windowManager context] items:galleryItems focusItem:focusItem selectionContext:selectionContext editingContext:editingContext hasCaptions:hasCaptions allowCaptionEntities:allowCaptionEntities hasTimer:hasTimer onlyCrop:onlyCrop inhibitDocumentCaptions:inhibitDocumentCaptions hasSelectionPanel:true hasCamera:false recipientName:recipientName isScheduledMessages:false hasCoverButton:hasCoverButton];
|
||||
_galleryModel = model;
|
||||
model.stickersContext = stickersContext;
|
||||
model.inhibitMute = inhibitMute;
|
||||
|
||||
@ -790,6 +790,7 @@ const CGFloat TGPhotoCounterButtonMaskFade = 18;
|
||||
UIImageView *_thumbnailView;
|
||||
UILabel *_label;
|
||||
|
||||
CGFloat _labelWidth;
|
||||
bool _gallery;
|
||||
}
|
||||
|
||||
@ -805,9 +806,15 @@ const CGFloat TGPhotoCounterButtonMaskFade = 18;
|
||||
self.exclusiveTouch = true;
|
||||
_gallery = gallery;
|
||||
|
||||
CGFloat width = _gallery ? 168.0 : 98.0;
|
||||
UIFont *font = [TGFont boldSystemFontOfSize:14];
|
||||
NSString *title = _gallery ? TGLocalized(@"Media.ChooseFromGallery") : TGLocalized(@"Media.EditCover");
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
CGFloat width = floor([title sizeWithFont:font].width) + 28.0f;
|
||||
#pragma clang diagnostic pop
|
||||
_labelWidth = width;
|
||||
|
||||
_wrapperView = [[UIView alloc] initWithFrame:CGRectMake((120 - width) / 2.0, 0, width, 26)];
|
||||
_wrapperView = [[UIView alloc] initWithFrame:CGRectMake(TGScreenPixelFloor((180 - width) / 2.0), 0, width, 26)];
|
||||
_wrapperView.userInteractionEnabled = false;
|
||||
[self addSubview:_wrapperView];
|
||||
|
||||
@ -825,9 +832,9 @@ const CGFloat TGPhotoCounterButtonMaskFade = 18;
|
||||
|
||||
_label = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
_label.backgroundColor = [UIColor clearColor];
|
||||
_label.font = [TGFont boldSystemFontOfSize:14];
|
||||
_label.font = font;
|
||||
_label.textColor = [UIColor whiteColor];
|
||||
_label.text = _gallery ? TGLocalized(@"Media.ChooseFromGallery") : TGLocalized(@"Media.EditCover");
|
||||
_label.text = title;
|
||||
[_label sizeToFit];
|
||||
_label.frame = CGRectMake(10.0, (26.0 - _label.frame.size.height) / 2.0, _label.frame.size.width, _label.frame.size.height);
|
||||
[_wrapperView addSubview:_label];
|
||||
@ -846,15 +853,15 @@ const CGFloat TGPhotoCounterButtonMaskFade = 18;
|
||||
_thumbnailView.hidden = false;
|
||||
_thumbnailView.image = image;
|
||||
|
||||
_wrapperView.frame = CGRectMake(0, 0, 120, 26);
|
||||
_backgroundView.frame = CGRectMake(0.0f, 0, 120, 26);
|
||||
CGFloat width = _labelWidth + 22.0;
|
||||
_wrapperView.frame = CGRectMake(TGScreenPixelFloor((180 - width) / 2.0), 0, width, 26);
|
||||
_backgroundView.frame = CGRectMake(0.0f, 0, width, 26);
|
||||
_label.frame = CGRectMake(10.0 + 22.0, (26.0 - _label.frame.size.height) / 2.0, _label.frame.size.width, _label.frame.size.height);
|
||||
} else {
|
||||
_thumbnailView.hidden = true;
|
||||
|
||||
CGFloat width = _gallery ? 168.0 : 98.0;
|
||||
|
||||
_wrapperView.frame = CGRectMake(11, 0, width, 26);
|
||||
CGFloat width = _labelWidth;
|
||||
_wrapperView.frame = CGRectMake(TGScreenPixelFloor((180 - width) / 2.0), 0, width, 26);
|
||||
_backgroundView.frame = CGRectMake(0.0f, 0, width, 26);
|
||||
_label.frame = CGRectMake(10.0, (26.0 - _label.frame.size.height) / 2.0, _label.frame.size.width, _label.frame.size.height);
|
||||
}
|
||||
|
||||
@ -15,4 +15,9 @@
|
||||
_coverScrubberView.frame = CGRectMake(_safeAreaInset.left, _scrubberView.frame.origin.y + 16.0, self.frame.size.width - _safeAreaInset.left - _safeAreaInset.right, _scrubberView.frame.size.height);
|
||||
}
|
||||
|
||||
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
|
||||
CGRect bounds = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height + 16.0);
|
||||
return CGRectContainsPoint(bounds, point);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -355,6 +355,9 @@
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setGesturesEnabled:(void (^)(bool))setGesturesEnabled {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
_gesturesEnabled = true;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -95,7 +96,7 @@
|
||||
|
||||
- (bool)allowsScrollingAtPoint:(CGPoint)__unused point
|
||||
{
|
||||
return true;
|
||||
return _gesturesEnabled;
|
||||
}
|
||||
|
||||
- (SSignal *)contentAvailabilityStateSignal
|
||||
|
||||
@ -84,6 +84,12 @@ static const CGFloat swipeDistanceThreshold = 128.0f;
|
||||
if (strongSelf != nil)
|
||||
[strongSelf setScrollViewVerticalOffset:offset];
|
||||
};
|
||||
_interfaceView.gesturesEnabled = ^(bool enabled)
|
||||
{
|
||||
__strong TGModernGalleryView *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
[strongSelf setGesturesEnabled:enabled];
|
||||
};
|
||||
[self addSubview:_interfaceView];
|
||||
|
||||
bool hasSwipeGesture = true;
|
||||
@ -111,6 +117,10 @@ static const CGFloat swipeDistanceThreshold = 128.0f;
|
||||
return (_dismissProgress < FLT_EPSILON && (_interfaceView == nil || ![_interfaceView respondsToSelector:@selector(shouldAutorotate)] || [_interfaceView shouldAutorotate]));
|
||||
}
|
||||
|
||||
- (void)setGesturesEnabled:(bool)gesturesEnabled {
|
||||
_scrollViewContainer.gestureRecognizers.firstObject.enabled = gesturesEnabled;
|
||||
}
|
||||
|
||||
- (CGRect)_boundsFrame
|
||||
{
|
||||
CGRect bounds = (CGRect){CGPointZero, self.frame.size};
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
galleryItem.editingContext = editingContext;
|
||||
galleryItem.stickersContext = stickersContext;
|
||||
|
||||
TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:windowContext items:@[galleryItem] focusItem:galleryItem selectionContext:nil editingContext:editingContext hasCaptions:true allowCaptionEntities:true hasTimer:false onlyCrop:false inhibitDocumentCaptions:false hasSelectionPanel:false hasCamera:false recipientName:recipientName isScheduledMessages:false];
|
||||
TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:windowContext items:@[galleryItem] focusItem:galleryItem selectionContext:nil editingContext:editingContext hasCaptions:true allowCaptionEntities:true hasTimer:false onlyCrop:false inhibitDocumentCaptions:false hasSelectionPanel:false hasCamera:false recipientName:recipientName isScheduledMessages:false hasCoverButton:false];
|
||||
model.controller = galleryController;
|
||||
model.stickersContext = stickersContext;
|
||||
|
||||
|
||||
@ -36,6 +36,9 @@ public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, co
|
||||
}
|
||||
controller.hasSchedule = hasSchedule
|
||||
controller.reminder = peer.id == context.account.peerId
|
||||
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
controller.hasCoverButton = true
|
||||
}
|
||||
controller.presentScheduleController = { media, done in
|
||||
presentSchedulePicker(media, { time in
|
||||
done?(time)
|
||||
|
||||
@ -104,6 +104,10 @@ enum LegacyMediaPickerGallerySource {
|
||||
func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?, threadTitle: String?, chatLocation: ChatLocation?, isScheduledMessages: Bool, presentationData: PresentationData, source: LegacyMediaPickerGallerySource, immediateThumbnail: UIImage?, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, hasSilentPosting: Bool, hasSchedule: Bool, hasTimer: Bool, updateHiddenMedia: @escaping (String?) -> Void, initialLayout: ContainerViewLayout?, transitionHostView: @escaping () -> UIView?, transitionView: @escaping (String) -> UIView?, completed: @escaping (TGMediaSelectableItem & TGMediaEditableItem, Bool, Int32?, @escaping () -> Void) -> Void, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, present: @escaping (ViewController, Any?) -> Void, finishedTransitionIn: @escaping () -> Void, willTransitionOut: @escaping () -> Void, dismissAll: @escaping () -> Void, editCover: @escaping (CGSize, @escaping (UIImage) -> Void) -> Void = { _, _ in }) -> TGModernGalleryController {
|
||||
let reminder = peer?.id == context.account.peerId
|
||||
let hasSilentPosting = hasSilentPosting && peer?.id != context.account.peerId
|
||||
var hasCoverButton = false
|
||||
if case let .channel(channel) = peer, case .broadcast = channel.info {
|
||||
hasCoverButton = true
|
||||
}
|
||||
|
||||
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
|
||||
legacyController.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
|
||||
@ -139,7 +143,7 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?,
|
||||
}
|
||||
}
|
||||
|
||||
let model = TGMediaPickerGalleryModel(context: legacyController.context, items: items, focus: focusItem, selectionContext: selectionContext, editingContext: editingContext, hasCaptions: true, allowCaptionEntities: true, hasTimer: hasTimer, onlyCrop: false, inhibitDocumentCaptions: false, hasSelectionPanel: true, hasCamera: false, recipientName: recipientName, isScheduledMessages: isScheduledMessages)!
|
||||
let model = TGMediaPickerGalleryModel(context: legacyController.context, items: items, focus: focusItem, selectionContext: selectionContext, editingContext: editingContext, hasCaptions: true, allowCaptionEntities: true, hasTimer: hasTimer, onlyCrop: false, inhibitDocumentCaptions: false, hasSelectionPanel: true, hasCamera: false, recipientName: recipientName, isScheduledMessages: isScheduledMessages, hasCoverButton: hasCoverButton)!
|
||||
model.stickersContext = paintStickersContext
|
||||
controller.model = model
|
||||
model.controller = controller
|
||||
|
||||
@ -646,7 +646,7 @@ private final class SheetContent: CombinedComponent {
|
||||
case .noAds:
|
||||
textString = strings.ChannelBoost_EnableNoAdsLevelText("\(requiredLevel)").string
|
||||
case .wearGift:
|
||||
textString = strings.ChannelBoost_EnableNoAdsLevelText("\(requiredLevel)").string
|
||||
textString = strings.ChannelBoost_WearGiftLevelText("\(requiredLevel)").string
|
||||
}
|
||||
} else {
|
||||
let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining))
|
||||
|
||||
@ -914,7 +914,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1301522832] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
|
||||
dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) }
|
||||
dict[46953416] = { return Api.StarGift.parse_starGift($0) }
|
||||
dict[-1145732050] = { return Api.StarGift.parse_starGiftUnique($0) }
|
||||
dict[-218202550] = { return Api.StarGift.parse_starGiftUnique($0) }
|
||||
dict[-1809377438] = { return Api.StarGiftAttribute.parse_starGiftAttributeBackdrop($0) }
|
||||
dict[970559507] = { return Api.StarGiftAttribute.parse_starGiftAttributeModel($0) }
|
||||
dict[-524291476] = { return Api.StarGiftAttribute.parse_starGiftAttributeOriginalDetails($0) }
|
||||
|
||||
@ -575,7 +575,7 @@ public extension Api {
|
||||
public extension Api {
|
||||
enum StarGift: TypeConstructorDescription {
|
||||
case starGift(flags: Int32, id: Int64, sticker: Api.Document, stars: Int64, availabilityRemains: Int32?, availabilityTotal: Int32?, convertStars: Int64, firstSaleDate: Int32?, lastSaleDate: Int32?, upgradeStars: Int64?)
|
||||
case starGiftUnique(flags: Int32, id: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32)
|
||||
case starGiftUnique(flags: Int32, id: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -594,9 +594,9 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(lastSaleDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt64(upgradeStars!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let attributes, let availabilityIssued, let availabilityTotal):
|
||||
case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1145732050)
|
||||
buffer.appendInt32(-218202550)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
@ -605,6 +605,7 @@ public extension Api {
|
||||
serializeInt32(num, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {ownerId!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(ownerName!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeString(ownerAddress!, buffer: buffer, boxed: false)}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(attributes.count))
|
||||
for item in attributes {
|
||||
@ -620,8 +621,8 @@ public extension Api {
|
||||
switch self {
|
||||
case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars):
|
||||
return ("starGift", [("flags", flags as Any), ("id", id as Any), ("sticker", sticker as Any), ("stars", stars as Any), ("availabilityRemains", availabilityRemains as Any), ("availabilityTotal", availabilityTotal as Any), ("convertStars", convertStars as Any), ("firstSaleDate", firstSaleDate as Any), ("lastSaleDate", lastSaleDate as Any), ("upgradeStars", upgradeStars as Any)])
|
||||
case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let attributes, let availabilityIssued, let availabilityTotal):
|
||||
return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any)])
|
||||
case .starGiftUnique(let flags, let id, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal):
|
||||
return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -682,14 +683,16 @@ public extension Api {
|
||||
} }
|
||||
var _7: String?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) }
|
||||
var _8: [Api.StarGiftAttribute]?
|
||||
var _8: String?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_8 = parseString(reader) }
|
||||
var _9: [Api.StarGiftAttribute]?
|
||||
if let _ = reader.readInt32() {
|
||||
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftAttribute.self)
|
||||
_9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftAttribute.self)
|
||||
}
|
||||
var _9: Int32?
|
||||
_9 = reader.readInt32()
|
||||
var _10: Int32?
|
||||
_10 = reader.readInt32()
|
||||
var _11: Int32?
|
||||
_11 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
@ -697,11 +700,12 @@ public extension Api {
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = _10 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
|
||||
return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, title: _3!, slug: _4!, num: _5!, ownerId: _6, ownerName: _7, attributes: _8!, availabilityIssued: _9!, availabilityTotal: _10!)
|
||||
let _c11 = _11 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
|
||||
return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, title: _3!, slug: _4!, num: _5!, ownerId: _6, ownerName: _7, ownerAddress: _8, attributes: _9!, availabilityIssued: _10!, availabilityTotal: _11!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
||||
@ -5560,9 +5560,9 @@ public extension Api.functions.messages {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, topMsgId: Int32?, scheduleDate: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, topMsgId: Int32?, scheduleDate: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?, videoTimestamp: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-721186296)
|
||||
buffer.appendInt32(1836374536)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
fromPeer.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
@ -5580,7 +5580,8 @@ public extension Api.functions.messages {
|
||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {quickReplyShortcut!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", String(describing: flags)), ("fromPeer", String(describing: fromPeer)), ("id", String(describing: id)), ("randomId", String(describing: randomId)), ("toPeer", String(describing: toPeer)), ("topMsgId", String(describing: topMsgId)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
if Int(flags) & Int(1 << 20) != 0 {serializeInt32(videoTimestamp!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", String(describing: flags)), ("fromPeer", String(describing: fromPeer)), ("id", String(describing: id)), ("randomId", String(describing: randomId)), ("toPeer", String(describing: toPeer)), ("topMsgId", String(describing: topMsgId)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut)), ("videoTimestamp", String(describing: videoTimestamp))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
|
||||
@ -851,23 +851,54 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
switch result {
|
||||
case let .media(media, key):
|
||||
if !forceReupload, let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference {
|
||||
var flags: Int32 = 0
|
||||
var ttlSeconds: Int32?
|
||||
if let autoclearMessageAttribute = autoclearMessageAttribute {
|
||||
flags |= 1 << 0
|
||||
ttlSeconds = autoclearMessageAttribute.timeout
|
||||
}
|
||||
|
||||
for attribute in attributes {
|
||||
if let _ = attribute as? MediaSpoilerMessageAttribute {
|
||||
flags |= 1 << 2
|
||||
var videoCoverSignal: Signal<UploadedMediaThumbnailResult, PendingMessageUploadError> = .single(.none)
|
||||
if let cover = file.videoCover, let resource = cover.representations.first?.resource {
|
||||
let fileReference: AnyMediaReference
|
||||
if let partialReference = file.partialReference {
|
||||
fileReference = partialReference.mediaReference(media)
|
||||
} else {
|
||||
fileReference = .standalone(media: media)
|
||||
}
|
||||
videoCoverSignal = uploadedVideoCover(network: network, postbox: postbox, resourceReference: fileReference.resourceReference(resource), peerId: peerId)
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> map { result in
|
||||
if let result = result {
|
||||
return .photo(result)
|
||||
} else {
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return .single(.progress(PendingMessageUploadedContentProgress(progress: 1.0)))
|
||||
|> then(
|
||||
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), videoCover: nil, videoTimestamp: nil, ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
)
|
||||
return videoCoverSignal
|
||||
|> mapToSignal { videoCover -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
var flags: Int32 = 0
|
||||
var ttlSeconds: Int32?
|
||||
if let autoclearMessageAttribute = autoclearMessageAttribute {
|
||||
flags |= 1 << 0
|
||||
ttlSeconds = autoclearMessageAttribute.timeout
|
||||
}
|
||||
|
||||
for attribute in attributes {
|
||||
if let _ = attribute as? MediaSpoilerMessageAttribute {
|
||||
flags |= 1 << 2
|
||||
}
|
||||
}
|
||||
|
||||
var videoCoverPhoto: Api.InputPhoto?
|
||||
if case let .photo(photo) = videoCover {
|
||||
videoCoverPhoto = photo
|
||||
}
|
||||
if let _ = videoCoverPhoto {
|
||||
flags |= 1 << 3
|
||||
}
|
||||
|
||||
return .single(.progress(PendingMessageUploadedContentProgress(progress: 1.0)))
|
||||
|> then(
|
||||
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), videoCover: videoCoverPhoto, videoTimestamp: nil, ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
)
|
||||
}
|
||||
}
|
||||
referenceKey = key
|
||||
case let .localReference(key):
|
||||
|
||||
@ -442,7 +442,7 @@ private func sendUploadedMessageContent(
|
||||
}
|
||||
|
||||
if let forwardSourceInfoAttribute = forwardSourceInfoAttribute, let sourcePeer = transaction.getPeer(forwardSourceInfoAttribute.messageId.peerId), let sourceInputPeer = apiInputPeer(sourcePeer) {
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer, topMsgId: topMsgId, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil), tag: dependencyTag)
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer, topMsgId: topMsgId, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, videoTimestamp: nil), tag: dependencyTag)
|
||||
|> map(NetworkRequestResult.result)
|
||||
} else {
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
||||
|
||||
@ -926,7 +926,7 @@ public final class PendingMessageManager {
|
||||
} else if let inputSourcePeerId = forwardPeerIds.first, let inputSourcePeer = transaction.getPeer(inputSourcePeerId).flatMap(apiInputPeer) {
|
||||
let dependencyTag = PendingMessageRequestDependencyTag(messageId: messages[0].0.id)
|
||||
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: inputSourcePeer, id: forwardIds.map { $0.0.id }, randomId: forwardIds.map { $0.1 }, toPeer: inputPeer, topMsgId: topMsgId, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut), tag: dependencyTag)
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: inputSourcePeer, id: forwardIds.map { $0.0.id }, randomId: forwardIds.map { $0.1 }, toPeer: inputPeer, topMsgId: topMsgId, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, videoTimestamp: nil), tag: dependencyTag)
|
||||
} else {
|
||||
assertionFailure()
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "Invalid forward source"))
|
||||
@ -1521,7 +1521,7 @@ public final class PendingMessageManager {
|
||||
}
|
||||
|
||||
if let forwardSourceInfoAttribute = forwardSourceInfoAttribute, let sourcePeer = transaction.getPeer(forwardSourceInfoAttribute.messageId.peerId), let sourceInputPeer = apiInputPeer(sourcePeer) {
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer, topMsgId: topMsgId, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut), tag: dependencyTag)
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer, topMsgId: topMsgId, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, videoTimestamp: nil), tag: dependencyTag)
|
||||
|> map(NetworkRequestResult.result)
|
||||
} else {
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
||||
|
||||
@ -662,6 +662,15 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
peerIds.append(senderId)
|
||||
}
|
||||
return peerIds
|
||||
case let .starGiftUnique(_, _, _, _, _, _, _, peerId, senderId, _):
|
||||
var peerIds: [PeerId] = []
|
||||
if let peerId {
|
||||
peerIds.append(peerId)
|
||||
}
|
||||
if let senderId {
|
||||
peerIds.append(senderId)
|
||||
}
|
||||
return peerIds
|
||||
default:
|
||||
return []
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ func _internal_forwardGameWithScore(account: Account, messageId: MessageId, to p
|
||||
flags |= (1 << 13)
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: fromInputPeer, id: [messageId.id], randomId: [Int64.random(in: Int64.min ... Int64.max)], toPeer: toInputPeer, topMsgId: threadId.flatMap { Int32(clamping: $0) }, scheduleDate: nil, sendAs: sendAsInputPeer, quickReplyShortcut: nil))
|
||||
return account.network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: fromInputPeer, id: [messageId.id], randomId: [Int64.random(in: Int64.min ... Int64.max)], toPeer: toInputPeer, topMsgId: threadId.flatMap { Int32(clamping: $0) }, scheduleDate: nil, sendAs: sendAsInputPeer, quickReplyShortcut: nil, videoTimestamp: nil))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
return .single(nil)
|
||||
|
||||
@ -207,6 +207,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
||||
case slug
|
||||
case ownerPeerId
|
||||
case ownerName
|
||||
case ownerAddress
|
||||
case attributes
|
||||
case availability
|
||||
}
|
||||
@ -281,8 +282,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
||||
)
|
||||
case 3:
|
||||
self = .originalInfo(
|
||||
senderPeerId: try container.decodeIfPresent(Int64.self, forKey: .sendPeerId).flatMap { EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value($0)) },
|
||||
recipientPeerId: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(try container.decode(Int64.self, forKey: .recipientPeerId))),
|
||||
senderPeerId: try container.decodeIfPresent(Int64.self, forKey: .sendPeerId).flatMap { EnginePeer.Id($0) },
|
||||
recipientPeerId: EnginePeer.Id(try container.decode(Int64.self, forKey: .recipientPeerId)),
|
||||
date: try container.decode(Int32.self, forKey: .date),
|
||||
text: try container.decodeIfPresent(String.self, forKey: .text),
|
||||
entities: try container.decodeIfPresent([MessageTextEntity].self, forKey: .entities)
|
||||
@ -319,8 +320,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
||||
)
|
||||
case 3:
|
||||
self = .originalInfo(
|
||||
senderPeerId: decoder.decodeOptionalInt64ForKey(CodingKeys.sendPeerId.rawValue).flatMap { EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value($0)) },
|
||||
recipientPeerId: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(decoder.decodeInt64ForKey(CodingKeys.recipientPeerId.rawValue, orElse: 0))),
|
||||
senderPeerId: decoder.decodeOptionalInt64ForKey(CodingKeys.sendPeerId.rawValue).flatMap { EnginePeer.Id($0) },
|
||||
recipientPeerId: EnginePeer.Id(decoder.decodeInt64ForKey(CodingKeys.recipientPeerId.rawValue, orElse: 0)),
|
||||
date: decoder.decodeInt32ForKey(CodingKeys.date.rawValue, orElse: 0),
|
||||
text: decoder.decodeOptionalStringForKey(CodingKeys.text.rawValue),
|
||||
entities: decoder.decodeObjectArrayWithDecoderForKey(CodingKeys.entities.rawValue)
|
||||
@ -354,8 +355,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
||||
try container.encode(rarity, forKey: .rarity)
|
||||
case let .originalInfo(senderPeerId, recipientPeerId, date, text, entities):
|
||||
try container.encode(Int32(3), forKey: .type)
|
||||
try container.encodeIfPresent(senderPeerId?.id._internalGetInt64Value(), forKey: .sendPeerId)
|
||||
try container.encode(recipientPeerId.id._internalGetInt64Value(), forKey: .recipientPeerId)
|
||||
try container.encodeIfPresent(senderPeerId?.toInt64(), forKey: .sendPeerId)
|
||||
try container.encode(recipientPeerId.toInt64(), forKey: .recipientPeerId)
|
||||
try container.encode(date, forKey: .date)
|
||||
try container.encodeIfPresent(text, forKey: .text)
|
||||
try container.encodeIfPresent(entities, forKey: .entities)
|
||||
@ -385,11 +386,11 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
||||
case let .originalInfo(senderPeerId, recipientPeerId, date, text, entities):
|
||||
encoder.encodeInt32(3, forKey: CodingKeys.type.rawValue)
|
||||
if let senderPeerId {
|
||||
encoder.encodeInt64(senderPeerId.id._internalGetInt64Value(), forKey: CodingKeys.sendPeerId.rawValue)
|
||||
encoder.encodeInt64(senderPeerId.toInt64(), forKey: CodingKeys.sendPeerId.rawValue)
|
||||
} else {
|
||||
encoder.encodeNil(forKey: CodingKeys.sendPeerId.rawValue)
|
||||
}
|
||||
encoder.encodeInt64(recipientPeerId.id._internalGetInt64Value(), forKey: CodingKeys.recipientPeerId.rawValue)
|
||||
encoder.encodeInt64(recipientPeerId.toInt64(), forKey: CodingKeys.recipientPeerId.rawValue)
|
||||
encoder.encodeInt32(date, forKey: CodingKeys.date.rawValue)
|
||||
if let text {
|
||||
encoder.encodeString(text, forKey: CodingKeys.text.rawValue)
|
||||
@ -434,6 +435,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
||||
public enum Owner: Equatable {
|
||||
case peerId(EnginePeer.Id)
|
||||
case name(String)
|
||||
case address(String)
|
||||
}
|
||||
|
||||
public enum DecodingError: Error {
|
||||
@ -465,7 +467,9 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
||||
self.number = try container.decode(Int32.self, forKey: .number)
|
||||
self.slug = try container.decodeIfPresent(String.self, forKey: .slug) ?? ""
|
||||
if let ownerId = try container.decodeIfPresent(Int64.self, forKey: .ownerPeerId) {
|
||||
self.owner = .peerId(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(ownerId)))
|
||||
self.owner = .peerId(EnginePeer.Id(ownerId))
|
||||
} else if let ownerAddress = try container.decodeIfPresent(String.self, forKey: .ownerAddress) {
|
||||
self.owner = .address(ownerAddress)
|
||||
} else if let ownerName = try container.decodeIfPresent(String.self, forKey: .ownerName) {
|
||||
self.owner = .name(ownerName)
|
||||
} else {
|
||||
@ -481,7 +485,9 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
||||
self.number = decoder.decodeInt32ForKey(CodingKeys.number.rawValue, orElse: 0)
|
||||
self.slug = decoder.decodeStringForKey(CodingKeys.slug.rawValue, orElse: "")
|
||||
if let ownerId = decoder.decodeOptionalInt64ForKey(CodingKeys.ownerPeerId.rawValue) {
|
||||
self.owner = .peerId(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(ownerId)))
|
||||
self.owner = .peerId(EnginePeer.Id(ownerId))
|
||||
} else if let ownerAddress = decoder.decodeOptionalStringForKey(CodingKeys.ownerAddress.rawValue) {
|
||||
self.owner = .address(ownerAddress)
|
||||
} else if let ownerName = decoder.decodeOptionalStringForKey(CodingKeys.ownerName.rawValue) {
|
||||
self.owner = .name(ownerName)
|
||||
} else {
|
||||
@ -499,9 +505,11 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
||||
try container.encode(self.slug, forKey: .slug)
|
||||
switch self.owner {
|
||||
case let .peerId(peerId):
|
||||
try container.encode(peerId.id._internalGetInt64Value(), forKey: .ownerPeerId)
|
||||
try container.encode(peerId.toInt64(), forKey: .ownerPeerId)
|
||||
case let .name(name):
|
||||
try container.encode(name, forKey: .ownerName)
|
||||
case let .address(address):
|
||||
try container.encode(address, forKey: .ownerAddress)
|
||||
}
|
||||
try container.encode(self.attributes, forKey: .attributes)
|
||||
try container.encode(self.availability, forKey: .availability)
|
||||
@ -514,9 +522,11 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
||||
encoder.encodeString(self.slug, forKey: CodingKeys.slug.rawValue)
|
||||
switch self.owner {
|
||||
case let .peerId(peerId):
|
||||
encoder.encodeInt64(peerId.id._internalGetInt64Value(), forKey: CodingKeys.ownerPeerId.rawValue)
|
||||
encoder.encodeInt64(peerId.toInt64(), forKey: CodingKeys.ownerPeerId.rawValue)
|
||||
case let .name(name):
|
||||
encoder.encodeString(name, forKey: CodingKeys.ownerName.rawValue)
|
||||
case let .address(address):
|
||||
encoder.encodeString(address, forKey: CodingKeys.ownerAddress.rawValue)
|
||||
}
|
||||
encoder.encodeObjectArray(self.attributes, forKey: CodingKeys.attributes.rawValue)
|
||||
encoder.encodeObject(self.availability, forKey: CodingKeys.availability.rawValue)
|
||||
@ -605,9 +615,11 @@ extension StarGift {
|
||||
return nil
|
||||
}
|
||||
self = .generic(StarGift.Gift(id: id, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars))
|
||||
case let .starGiftUnique(_, id, title, slug, num, ownerPeerId, ownerName, attributes, availabilityIssued, availabilityTotal):
|
||||
case let .starGiftUnique(_, id, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal):
|
||||
let owner: StarGift.UniqueGift.Owner
|
||||
if let ownerId = ownerPeerId?.peerId {
|
||||
if let ownerAddress {
|
||||
owner = .address(ownerAddress)
|
||||
} else if let ownerId = ownerPeerId?.peerId {
|
||||
owner = .peerId(ownerId)
|
||||
} else if let ownerName {
|
||||
owner = .name(ownerName)
|
||||
|
||||
@ -835,6 +835,10 @@ public extension TelegramEngine {
|
||||
return _internal_updatePeerEmojiStatus(account: self.account, peerId: peerId, fileId: fileId, expirationDate: expirationDate)
|
||||
}
|
||||
|
||||
public func updatePeerStarGiftStatus(peerId: EnginePeer.Id, starGift: StarGift.UniqueGift, expirationDate: Int32?) -> Signal<Never, UpdatePeerEmojiStatusError> {
|
||||
return _internal_updatePeerStarGiftStatus(account: self.account, peerId: peerId, starGift: starGift, expirationDate: expirationDate)
|
||||
}
|
||||
|
||||
public func checkChannelRevenueWithdrawalAvailability() -> Signal<Never, RequestRevenueWithdrawalError> {
|
||||
return _internal_checkChannelRevenueWithdrawalAvailability(account: self.account)
|
||||
}
|
||||
|
||||
@ -308,3 +308,61 @@ func _internal_updatePeerEmojiStatus(account: Account, peerId: PeerId, fileId: I
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_updatePeerStarGiftStatus(account: Account, peerId: PeerId, starGift: StarGift.UniqueGift, expirationDate: Int32?) -> Signal<Never, UpdatePeerEmojiStatusError> {
|
||||
var flags: Int32 = 0
|
||||
if let _ = expirationDate {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
var file: TelegramMediaFile?
|
||||
var patternFile: TelegramMediaFile?
|
||||
var innerColor: Int32?
|
||||
var outerColor: Int32?
|
||||
var patternColor: Int32?
|
||||
var textColor: Int32?
|
||||
for attribute in starGift.attributes {
|
||||
switch attribute {
|
||||
case let .model(_, fileValue, _):
|
||||
file = fileValue
|
||||
case let .pattern(_, patternFileValue, _):
|
||||
patternFile = patternFileValue
|
||||
case let .backdrop(_, innerColorValue, outerColorValue, patternColorValue, textColorValue, _):
|
||||
innerColor = innerColorValue
|
||||
outerColor = outerColorValue
|
||||
patternColor = patternColorValue
|
||||
textColor = textColorValue
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
let apiEmojiStatus: Api.EmojiStatus
|
||||
var emojiStatus: PeerEmojiStatus?
|
||||
if let file, let patternFile, let innerColor, let outerColor, let patternColor, let textColor {
|
||||
apiEmojiStatus = .inputEmojiStatusCollectible(flags: flags, collectibleId: starGift.id, until: expirationDate)
|
||||
emojiStatus = PeerEmojiStatus(content: .starGift(id: starGift.id, fileId: file.fileId.id, title: starGift.title, slug: starGift.slug, patternFileId: patternFile.fileId.id, innerColor: innerColor, outerColor: outerColor, patternColor: patternColor, textColor: textColor), expirationDate: expirationDate)
|
||||
} else {
|
||||
apiEmojiStatus = .emojiStatusEmpty
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> Api.InputChannel? in
|
||||
if let peer = transaction.getPeer(peerId) as? TelegramChannel {
|
||||
updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedEmojiStatus(emojiStatus)], update: { _, updated in updated })
|
||||
}
|
||||
return transaction.getPeer(peerId).flatMap(apiInputChannel)
|
||||
}
|
||||
|> castError(UpdatePeerEmojiStatusError.self)
|
||||
|> mapToSignal { inputChannel -> Signal<Never, UpdatePeerEmojiStatusError> in
|
||||
guard let inputChannel = inputChannel else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
return account.network.request(Api.functions.channels.updateEmojiStatus(channel: inputChannel, emojiStatus: apiEmojiStatus))
|
||||
|> ignoreValues
|
||||
|> `catch` { error -> Signal<Never, UpdatePeerEmojiStatusError> in
|
||||
if error.errorDescription == "CHAT_NOT_MODIFIED" {
|
||||
return .complete()
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -464,6 +464,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
private var fetchStatus: MediaResourceStatus?
|
||||
private var actualFetchStatus: MediaResourceStatus?
|
||||
private let fetchDisposable = MetaDisposable()
|
||||
private let coverFetchDisposable = MetaDisposable()
|
||||
|
||||
private let videoNodeReadyDisposable = MetaDisposable()
|
||||
private let playerStatusDisposable = MetaDisposable()
|
||||
@ -626,6 +627,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
self.videoNodeReadyDisposable.dispose()
|
||||
self.playerStatusDisposable.dispose()
|
||||
self.fetchDisposable.dispose()
|
||||
self.coverFetchDisposable.dispose()
|
||||
self.secretTimer?.invalidate()
|
||||
self.hlsInlinePlaybackRangeDisposable?.dispose()
|
||||
}
|
||||
@ -2125,6 +2127,10 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
||||
strongSelf.fetchDisposable.set(visibilityAwareFetchSignal.startStrict())
|
||||
}
|
||||
}
|
||||
|
||||
if let file = media as? TelegramMediaFile, let image = file.videoCover, let representation = largestRepresentationForPhoto(image) {
|
||||
strongSelf.coverFetchDisposable.set(messageMediaImageInteractiveFetched(context: context, message: message, image: image, resource: representation.resource, range: representationFetchRangeForDisplayAtSize(representation: representation, dimension: nil), userInitiated: false, storeToDownloadsPeerId: nil).startStrict())
|
||||
}
|
||||
} else if currentAutomaticDownload != automaticDownload, case .full = automaticDownload {
|
||||
strongSelf.fetchControls.with({ $0 })?.fetch(false)
|
||||
}
|
||||
|
||||
@ -47,6 +47,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
"//submodules/PasswordSetupUI",
|
||||
"//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController",
|
||||
"//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@ -32,6 +32,7 @@ import GiftAnimationComponent
|
||||
import LottieComponent
|
||||
import ContextUI
|
||||
import TelegramNotices
|
||||
import PremiumLockButtonSubtitleComponent
|
||||
|
||||
private let modelButtonTag = GenericComponentViewTag()
|
||||
private let backdropButtonTag = GenericComponentViewTag()
|
||||
@ -128,6 +129,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
var upgradeForm: BotPaymentForm?
|
||||
var upgradeFormDisposable: Disposable?
|
||||
var upgradeDisposable: Disposable?
|
||||
let levelsDisposable = MetaDisposable()
|
||||
|
||||
var inWearPreview = false
|
||||
var pendingWear = false
|
||||
@ -171,6 +173,11 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
if let fromPeerId = arguments.fromPeerId, !peerIds.contains(fromPeerId) {
|
||||
peerIds.append(fromPeerId)
|
||||
}
|
||||
if case let .message(message) = subject {
|
||||
for media in message.media {
|
||||
peerIds.append(contentsOf: media.peerIds)
|
||||
}
|
||||
}
|
||||
if case let .unique(gift) = arguments.gift {
|
||||
if case let .peerId(peerId) = gift.owner {
|
||||
peerIds.append(peerId)
|
||||
@ -219,6 +226,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.disposable = combineLatest(queue: Queue.mainQueue(),
|
||||
context.engine.data.get(EngineDataMap(
|
||||
peerIds.map { peerId -> TelegramEngine.EngineData.Item.Peer.Peer in
|
||||
@ -270,6 +278,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
self.sampleDisposable.dispose()
|
||||
self.upgradeFormDisposable?.dispose()
|
||||
self.upgradeDisposable?.dispose()
|
||||
self.levelsDisposable.dispose()
|
||||
}
|
||||
|
||||
func requestUpgradePreview() {
|
||||
@ -293,7 +302,11 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
self.inWearPreview = false
|
||||
self.updated(transition: .spring(duration: 0.4))
|
||||
|
||||
let _ = self.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).startStandalone()
|
||||
if let arguments = self.subject.arguments, let peerId = arguments.peerId, peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
let _ = self.context.engine.peers.updatePeerStarGiftStatus(peerId: peerId, starGift: uniqueGift, expirationDate: nil).startStandalone()
|
||||
} else {
|
||||
let _ = self.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).startStandalone()
|
||||
}
|
||||
|
||||
let _ = ApplicationSpecificNotice.incrementStarGiftWearTips(accountManager: self.context.sharedContext.accountManager).startStandalone()
|
||||
}
|
||||
@ -303,7 +316,11 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
self.pendingWear = false
|
||||
self.updated(transition: .spring(duration: 0.4))
|
||||
|
||||
let _ = self.context.engine.accountData.setEmojiStatus(file: nil, expirationDate: nil).startStandalone()
|
||||
if let arguments = self.subject.arguments, let peerId = arguments.peerId, peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
let _ = self.context.engine.peers.updatePeerEmojiStatus(peerId: peerId, fileId: nil, expirationDate: nil)
|
||||
} else {
|
||||
let _ = self.context.engine.accountData.setEmojiStatus(file: nil, expirationDate: nil).startStandalone()
|
||||
}
|
||||
}
|
||||
|
||||
func commitUpgrade() {
|
||||
@ -543,7 +560,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
if let uniqueGift {
|
||||
if showWearPreview {
|
||||
headerHeight = 200.0
|
||||
} else if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId {
|
||||
} else if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId || isChannelGift {
|
||||
headerHeight = 314.0
|
||||
} else {
|
||||
headerHeight = 240.0
|
||||
@ -563,11 +580,18 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
headerSubject = nil
|
||||
}
|
||||
|
||||
var ownerPeerId: EnginePeer.Id
|
||||
if let uniqueGift, case let .peerId(peerId) = uniqueGift.owner {
|
||||
ownerPeerId = peerId
|
||||
} else {
|
||||
ownerPeerId = component.context.account.peerId
|
||||
}
|
||||
|
||||
var wearPeerNameChild: _UpdatedChildComponent?
|
||||
if showWearPreview, let uniqueGift {
|
||||
var peerName = ""
|
||||
if let accountPeer = state.peerMap[component.context.account.peerId] {
|
||||
peerName = accountPeer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||
if let ownerPeer = state.peerMap[ownerPeerId] {
|
||||
peerName = ownerPeer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||
}
|
||||
wearPeerNameChild = wearPeerName.update(
|
||||
component: MultilineTextComponent(
|
||||
@ -675,12 +699,12 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
|
||||
if let wearPeerNameChild {
|
||||
if let accountPeer = state.peerMap[component.context.account.peerId] {
|
||||
if let ownerPeer = state.peerMap[ownerPeerId] {
|
||||
let wearAvatar = wearAvatar.update(
|
||||
component: AvatarComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
peer: accountPeer
|
||||
peer: ownerPeer
|
||||
),
|
||||
environment: {},
|
||||
availableSize: CGSize(width: 100.0, height: 100.0),
|
||||
@ -696,7 +720,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
let wearPeerStatus = wearPeerStatus.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: strings.Presence_online,
|
||||
string: isChannelGift ? strings.Channel_Status : strings.Presence_online,
|
||||
font: Font.regular(17.0),
|
||||
textColor: vibrantColor,
|
||||
paragraphAlignment: .center
|
||||
@ -736,7 +760,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: strings.Gift_Wear_Badge_Title,
|
||||
titleColor: textColor,
|
||||
text: strings.Gift_Wear_Badge_Text,
|
||||
text: isChannelGift ? strings.Gift_Wear_Badge_ChannelText : strings.Gift_Wear_Badge_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/Collectible/Badge",
|
||||
@ -750,7 +774,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: strings.Gift_Wear_Design_Title,
|
||||
titleColor: textColor,
|
||||
text: strings.Gift_Wear_Design_Text,
|
||||
text: isChannelGift ? strings.Gift_Wear_Design_ChannelText : strings.Gift_Wear_Design_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/BoostPerk/CoverColor",
|
||||
@ -764,7 +788,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: strings.Gift_Wear_Proof_Title,
|
||||
titleColor: textColor,
|
||||
text: strings.Gift_Wear_Proof_Text,
|
||||
text: isChannelGift ? strings.Gift_Wear_Proof_ChannelText : strings.Gift_Wear_Proof_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/Collectible/Proof",
|
||||
@ -1249,12 +1273,20 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
case let .name(name):
|
||||
tableItems.append(.init(
|
||||
id: "anon_owner",
|
||||
id: "name_owner",
|
||||
title: strings.Gift_Unique_Owner,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: name, font: tableFont, textColor: tableTextColor)))
|
||||
)
|
||||
))
|
||||
case let .address(address):
|
||||
tableItems.append(.init(
|
||||
id: "address_owner",
|
||||
title: strings.Gift_Unique_Owner,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: address, font: tableMonospaceFont, textColor: tableTextColor)))
|
||||
)
|
||||
))
|
||||
}
|
||||
} else if let peerId = subject.arguments?.fromPeerId, let peer = state.peerMap[peerId] {
|
||||
var isBot = false
|
||||
@ -1346,7 +1378,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
|
||||
if let uniqueGift {
|
||||
if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId {
|
||||
if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId || isChannelGift {
|
||||
let buttonSpacing: CGFloat = 10.0
|
||||
let buttonWidth = floor(context.availableSize.width - sideInset * 2.0 - buttonSpacing * 2.0) / 3.0
|
||||
let buttonHeight: CGFloat = 58.0
|
||||
@ -1397,12 +1429,24 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
controller.dismissAllTooltips()
|
||||
}
|
||||
|
||||
let canWear: Bool
|
||||
if isChannelGift, case let .channel(channel) = state.peerMap[ownerPeerId] {
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 })
|
||||
let requiredLevel = Int(BoostSubject.wearGift.requiredLevel(group: false, context: component.context, configuration: premiumConfiguration))
|
||||
if let boostLevel = channel.approximateBoostLevel {
|
||||
canWear = boostLevel >= requiredLevel
|
||||
} else {
|
||||
canWear = false
|
||||
}
|
||||
} else {
|
||||
canWear = component.context.isPremium
|
||||
}
|
||||
let _ = (ApplicationSpecificNotice.getStarGiftWearTips(accountManager: component.context.sharedContext.accountManager)
|
||||
|> deliverOnMainQueue).start(next: { [weak state] count in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
if !component.context.isPremium || count < 3 {
|
||||
if !canWear || count < 3 {
|
||||
state.requestWearPreview()
|
||||
} else {
|
||||
state.commitWear(uniqueGift)
|
||||
@ -1856,9 +1900,36 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
let buttonChild: _UpdatedChildComponent
|
||||
if showWearPreview, let uniqueGift {
|
||||
let buttonContent: AnyComponentWithIdentity<Empty>
|
||||
if !component.context.isPremium {
|
||||
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 })
|
||||
let requiredLevel = Int(BoostSubject.wearGift.requiredLevel(group: false, context: component.context, configuration: premiumConfiguration))
|
||||
|
||||
var canWear = true
|
||||
if isChannelGift, case let .channel(channel) = state.peerMap[ownerPeerId], (channel.approximateBoostLevel ?? 0) < requiredLevel {
|
||||
canWear = false
|
||||
buttonContent = AnyComponentWithIdentity(
|
||||
id: AnyHashable("wear_locked"),
|
||||
id: AnyHashable("wear_channel"),
|
||||
component: AnyComponent(
|
||||
VStack([
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable("label"),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Wear_Start, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
),
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable("level"),
|
||||
component: AnyComponent(PremiumLockButtonSubtitleComponent(
|
||||
count: requiredLevel,
|
||||
theme: theme,
|
||||
strings: strings
|
||||
))
|
||||
)
|
||||
], spacing: 3.0)
|
||||
)
|
||||
)
|
||||
} else if !isChannelGift && !component.context.isPremium {
|
||||
canWear = false
|
||||
buttonContent = AnyComponentWithIdentity(
|
||||
id: AnyHashable("wear_premium"),
|
||||
component: AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(
|
||||
@ -1888,30 +1959,48 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
action: { [weak state] in
|
||||
if let state {
|
||||
let context = component.context
|
||||
if !context.isPremium, let controller = controller() as? GiftViewScreen {
|
||||
if !canWear, let controller = controller() as? GiftViewScreen {
|
||||
controller.dismissAllTooltips()
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let text = strings.Gift_View_TooltipPremiumWearing
|
||||
let tooltipController = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .premiumPaywall(title: nil, text: text, customUndoText: nil, timeout: nil, linkAction: nil),
|
||||
position: .bottom,
|
||||
animateInAsReplacement: false,
|
||||
appearance: UndoOverlayController.Appearance(sideInset: 16.0, bottomInset: 62.0),
|
||||
action: { [weak controller] action in
|
||||
if case .info = action {
|
||||
controller?.dismissAllTooltips()
|
||||
let premiumController = context.sharedContext.makePremiumIntroController(context: context, source: .messageEffects, forceDark: false, dismissed: nil)
|
||||
controller?.push(premiumController)
|
||||
Queue.mainQueue().after(0.6, {
|
||||
component.cancel(false)
|
||||
})
|
||||
if isChannelGift {
|
||||
state.levelsDisposable.set(combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
context.engine.peers.getChannelBoostStatus(peerId: ownerPeerId),
|
||||
context.engine.peers.getMyBoostStatus()
|
||||
).startStandalone(next: { [weak controller] boostStatus, myBoostStatus in
|
||||
guard let controller, let boostStatus, let myBoostStatus else {
|
||||
return
|
||||
}
|
||||
return false
|
||||
}
|
||||
)
|
||||
controller.present(tooltipController, in: .window(.root))
|
||||
component.cancel(true)
|
||||
|
||||
let levelsController = context.sharedContext.makePremiumBoostLevelsController(context: context, peerId: ownerPeerId, subject: .wearGift, boostStatus: boostStatus, myBoostStatus: myBoostStatus, forceDark: false, openStats: nil)
|
||||
controller.push(levelsController)
|
||||
|
||||
HapticFeedback().impact(.light)
|
||||
}))
|
||||
} else {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let text = strings.Gift_View_TooltipPremiumWearing
|
||||
let tooltipController = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .premiumPaywall(title: nil, text: text, customUndoText: nil, timeout: nil, linkAction: nil),
|
||||
position: .bottom,
|
||||
animateInAsReplacement: false,
|
||||
appearance: UndoOverlayController.Appearance(sideInset: 16.0, bottomInset: 62.0),
|
||||
action: { [weak controller] action in
|
||||
if case .info = action {
|
||||
controller?.dismissAllTooltips()
|
||||
let premiumController = context.sharedContext.makePremiumIntroController(context: context, source: .messageEffects, forceDark: false, dismissed: nil)
|
||||
controller?.push(premiumController)
|
||||
Queue.mainQueue().after(0.6, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
)
|
||||
controller.present(tooltipController, in: .window(.root))
|
||||
}
|
||||
} else {
|
||||
state.commitWear(uniqueGift)
|
||||
if case .wearPreview = component.subject {
|
||||
@ -3655,7 +3744,7 @@ private final class AvatarComponent: Component {
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 42.0))
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
|
||||
@ -1751,7 +1751,7 @@ func targetSize(cropSize: CGSize, rotateSideward: Bool = false) -> CGSize {
|
||||
return CGSize(width: renderWidth, height: renderHeight)
|
||||
}
|
||||
|
||||
public func recommendedVideoExportConfiguration(values: MediaEditorValues, duration: Double, image: Bool = false, forceFullHd: Bool = false, frameRate: Float, isSticker: Bool = false) -> MediaEditorVideoExport.Configuration {
|
||||
public func recommendedVideoExportConfiguration(values: MediaEditorValues, duration: Double, image: Bool = false, forceFullHd: Bool = false, frameRate: Float, isSticker: Bool = false, isAvatar: Bool = false) -> MediaEditorVideoExport.Configuration {
|
||||
let compressionProperties: [String: Any]
|
||||
let codecType: Any
|
||||
|
||||
@ -1763,7 +1763,17 @@ public func recommendedVideoExportConfiguration(values: MediaEditorValues, durat
|
||||
if image {
|
||||
videoBitrate = 5000
|
||||
} else {
|
||||
if duration < 10 {
|
||||
if isAvatar {
|
||||
if duration <= 2.0 {
|
||||
videoBitrate = 2400
|
||||
} else if duration <= 5.0 {
|
||||
videoBitrate = 2000
|
||||
} else if duration <= 8.0 {
|
||||
videoBitrate = 1500
|
||||
} else {
|
||||
videoBitrate = 1100
|
||||
}
|
||||
} else if duration < 10 {
|
||||
videoBitrate = 5800
|
||||
} else if duration < 20 {
|
||||
videoBitrate = 5500
|
||||
@ -1797,7 +1807,12 @@ public func recommendedVideoExportConfiguration(values: MediaEditorValues, durat
|
||||
|
||||
useHEVC = false
|
||||
} else {
|
||||
if isSticker {
|
||||
if isAvatar {
|
||||
width = 640
|
||||
height = 640
|
||||
frameRate = 30
|
||||
useHEVC = false
|
||||
} else if isSticker {
|
||||
width = 512
|
||||
height = 512
|
||||
useVP9 = true
|
||||
|
||||
@ -147,7 +147,7 @@ extension PeerInfoScreenImpl {
|
||||
}
|
||||
controller.videoCompletion = { [weak self] image, url, values, markup, commit in
|
||||
resultImage = image
|
||||
self?.updateProfileVideo(image, asset: url, values: values, markup: markup, mode: mode, uploadStatus: uploadStatusPromise)
|
||||
self?.updateProfileVideo(image, video: nil, values: nil, markup: markup, mode: mode, uploadStatus: uploadStatusPromise)
|
||||
commit()
|
||||
}
|
||||
parentController?.push(controller)
|
||||
@ -207,7 +207,7 @@ extension PeerInfoScreenImpl {
|
||||
case let .video(video, coverImage, values, _, _):
|
||||
if let coverImage {
|
||||
resultImage = coverImage
|
||||
self?.updateProfileVideo(coverImage, asset: video, values: values, markup: nil, mode: mode, uploadStatus: uploadStatusPromise)
|
||||
self?.updateProfileVideo(coverImage, video: video, values: values, markup: nil, mode: mode, uploadStatus: uploadStatusPromise)
|
||||
}
|
||||
commit({})
|
||||
default:
|
||||
@ -459,7 +459,7 @@ extension PeerInfoScreenImpl {
|
||||
}))
|
||||
}
|
||||
|
||||
public func updateProfileVideo(_ image: UIImage, asset: Any?, values: MediaEditorValues?, markup: UploadPeerPhotoMarkup?, mode: PeerInfoAvatarEditingMode, uploadStatus: Promise<PeerInfoAvatarUploadStatus>?) {
|
||||
public func updateProfileVideo(_ image: UIImage, video: MediaEditorScreenImpl.MediaResult.VideoResult?, values: MediaEditorValues?, markup: UploadPeerPhotoMarkup?, mode: PeerInfoAvatarEditingMode, uploadStatus: Promise<PeerInfoAvatarUploadStatus>?) {
|
||||
var uploadVideo = true
|
||||
if let _ = markup {
|
||||
if let data = self.context.currentAppConfiguration.with({ $0 }).data, let uploadVideoValue = data["upload_markup_video"] as? Bool, uploadVideoValue {
|
||||
@ -482,9 +482,82 @@ extension PeerInfoScreenImpl {
|
||||
let context = self.context
|
||||
|
||||
let videoResource: Signal<TelegramMediaResource?, UploadPeerPhotoError>
|
||||
if uploadVideo {
|
||||
videoResource = Signal { subscriber in
|
||||
return EmptyDisposable
|
||||
if uploadVideo, let video, let values {
|
||||
var exportSubject: Signal<(MediaEditorVideoExport.Subject, Double), NoError>?
|
||||
switch video {
|
||||
case let .imageFile(path):
|
||||
if let image = UIImage(contentsOfFile: path) {
|
||||
exportSubject = .single((.image(image: image), 3.0))
|
||||
}
|
||||
case let .videoFile(path):
|
||||
let asset = AVURLAsset(url: NSURL(fileURLWithPath: path) as URL)
|
||||
exportSubject = .single((.video(asset: asset, isStory: false), asset.duration.seconds))
|
||||
case let .asset(localIdentifier):
|
||||
exportSubject = Signal { subscriber in
|
||||
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil)
|
||||
if fetchResult.count != 0 {
|
||||
let asset = fetchResult.object(at: 0)
|
||||
if asset.mediaType == .video {
|
||||
PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in
|
||||
if let avAsset {
|
||||
subscriber.putNext((.video(asset: avAsset, isStory: true), avAsset.duration.seconds))
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let options = PHImageRequestOptions()
|
||||
options.deliveryMode = .highQualityFormat
|
||||
PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .default, options: options) { image, _ in
|
||||
if let image {
|
||||
subscriber.putNext((.image(image: image), 3.0))
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return EmptyDisposable
|
||||
}
|
||||
}
|
||||
|
||||
if let exportSubject {
|
||||
videoResource = exportSubject
|
||||
|> castError(UploadPeerPhotoError.self)
|
||||
|> mapToSignal { exportSubject, duration in
|
||||
return Signal<TelegramMediaResource?, UploadPeerPhotoError> { subscriber in
|
||||
let configuration = recommendedVideoExportConfiguration(values: values, duration: duration, forceFullHd: true, frameRate: 60.0, isAvatar: true)
|
||||
//let outputPath = NSTemporaryDirectory() + "\(Int64.random(in: 0 ..< .max)).mp4"
|
||||
let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4")
|
||||
let videoExport = MediaEditorVideoExport(postbox: context.account.postbox, subject: exportSubject, configuration: configuration, outputPath: tempFile.path, textScale: 2.0)
|
||||
|
||||
let _ = (videoExport.status
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] status in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
switch status {
|
||||
case .completed:
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: tempFile.path), options: .mappedIfSafe) {
|
||||
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
||||
subscriber.putNext(resource)
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
EngineTempBox.shared.dispose(tempFile)
|
||||
case let .progress(progress):
|
||||
Queue.mainQueue().async {
|
||||
self.controllerNode.state = self.controllerNode.state.withAvatarUploadProgress(.value(CGFloat(progress * 0.45)))
|
||||
self.requestLayout(transition: .immediate)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
return EmptyDisposable
|
||||
}
|
||||
}
|
||||
} else {
|
||||
videoResource = .single(nil)
|
||||
}
|
||||
} else {
|
||||
videoResource = .single(nil)
|
||||
|
||||
@ -1236,7 +1236,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
controller.videoCompletion = { [weak self] image, url, values, markup, commit in
|
||||
if let strongSelf = self {
|
||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
settingsController.updateProfileVideo(image, asset: AVURLAsset(url: url), values: values, markup: markup, mode: .accept, uploadStatus: nil)
|
||||
settingsController.updateProfileVideo(image, video: nil, values: nil, markup: markup, mode: .accept, uploadStatus: nil)
|
||||
commit()
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
private let displayCallIcons: Bool
|
||||
private let multipleSelection: Bool
|
||||
private let requirePhoneNumbers: Bool
|
||||
private let allowChannelsInSearch: Bool
|
||||
|
||||
private let openProfile: ((EnginePeer) -> Void)?
|
||||
private let sendMessage: ((EnginePeer) -> Void)?
|
||||
@ -108,6 +109,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
self.confirmation = params.confirmation
|
||||
self.multipleSelection = params.multipleSelection
|
||||
self.requirePhoneNumbers = params.requirePhoneNumbers
|
||||
self.allowChannelsInSearch = params.allowChannelsInSearch
|
||||
|
||||
self.openProfile = params.openProfile
|
||||
self.sendMessage = params.sendMessage
|
||||
@ -216,7 +218,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
}
|
||||
|
||||
override func loadDisplayNode() {
|
||||
self.displayNode = ContactSelectionControllerNode(context: self.context, mode: self.mode, presentationData: self.presentationData, options: self.options, displayDeviceContacts: self.displayDeviceContacts, displayCallIcons: self.displayCallIcons, multipleSelection: self.multipleSelection, requirePhoneNumbers: self.requirePhoneNumbers)
|
||||
self.displayNode = ContactSelectionControllerNode(context: self.context, mode: self.mode, presentationData: self.presentationData, options: self.options, displayDeviceContacts: self.displayDeviceContacts, displayCallIcons: self.displayCallIcons, multipleSelection: self.multipleSelection, requirePhoneNumbers: self.requirePhoneNumbers, allowChannelsInSearch: self.allowChannelsInSearch)
|
||||
self._ready.set(self.contactsNode.contactListNode.ready)
|
||||
|
||||
self.contactsNode.navigationBar = self.navigationBar
|
||||
|
||||
@ -23,6 +23,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
|
||||
private let displayDeviceContacts: Bool
|
||||
private let displayCallIcons: Bool
|
||||
private let allowChannelsInSearch: Bool
|
||||
private let filters: [ContactListFilter]
|
||||
|
||||
let contactListNode: ContactListNode
|
||||
@ -56,11 +57,12 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
|
||||
var searchContainerNode: ContactsSearchContainerNode?
|
||||
|
||||
init(context: AccountContext, mode: ContactSelectionControllerMode, presentationData: PresentationData, options: Signal<[ContactListAdditionalOption], NoError>, displayDeviceContacts: Bool, displayCallIcons: Bool, multipleSelection: Bool, requirePhoneNumbers: Bool) {
|
||||
init(context: AccountContext, mode: ContactSelectionControllerMode, presentationData: PresentationData, options: Signal<[ContactListAdditionalOption], NoError>, displayDeviceContacts: Bool, displayCallIcons: Bool, multipleSelection: Bool, requirePhoneNumbers: Bool, allowChannelsInSearch: Bool) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.displayDeviceContacts = displayDeviceContacts
|
||||
self.displayCallIcons = displayCallIcons
|
||||
self.allowChannelsInSearch = allowChannelsInSearch
|
||||
|
||||
var excludeSelf = true
|
||||
|
||||
@ -247,6 +249,9 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
} else {
|
||||
categories.insert(.global)
|
||||
}
|
||||
if self.allowChannelsInSearch {
|
||||
categories.insert(.channels)
|
||||
}
|
||||
|
||||
let searchContainerNode = ContactsSearchContainerNode(context: self.context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), onlyWriteable: false, categories: categories, filters: self.filters, addContact: nil, openPeer: { [weak self] peer in
|
||||
if let strongSelf = self {
|
||||
@ -316,6 +321,10 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
} else {
|
||||
categories.insert(.global)
|
||||
}
|
||||
if self.allowChannelsInSearch {
|
||||
categories.insert(.channels)
|
||||
}
|
||||
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), onlyWriteable: false, categories: categories, filters: self.filters, addContact: nil, openPeer: { [weak self] peer in
|
||||
if let strongSelf = self {
|
||||
var updated = false
|
||||
|
||||
@ -2302,8 +2302,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
mode = .starsGifting(birthdays: nil, hasActions: true, showSelf: false)
|
||||
}
|
||||
|
||||
var allowChannelsInSearch = false
|
||||
let contactOptions: Signal<[ContactListAdditionalOption], NoError>
|
||||
if case let .starGiftTransfer(_, _, _, _, canExportDate) = source {
|
||||
allowChannelsInSearch = true
|
||||
var subtitle: String?
|
||||
if let canExportDate {
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
@ -2369,6 +2371,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
autoDismiss: false,
|
||||
title: { _ in return title },
|
||||
options: contactOptions,
|
||||
allowChannelsInSearch: allowChannelsInSearch,
|
||||
openProfile: { peer in
|
||||
openProfileImpl?(peer)
|
||||
},
|
||||
@ -2454,7 +2457,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return
|
||||
}
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
if currentTime > canExportDate || "".isEmpty {
|
||||
if currentTime > canExportDate {
|
||||
let alertController = giftWithdrawAlertController(context: context, gift: gift, commit: {
|
||||
let _ = (context.engine.payments.checkStarGiftWithdrawalAvailability(reference: reference)
|
||||
|> deliverOnMainQueue).start(error: { [weak controller] error in
|
||||
|
||||
@ -338,7 +338,7 @@ func presentLegacyWebSearchGallery(context: AccountContext, peer: EnginePeer?, t
|
||||
|
||||
let (items, focusItem) = galleryItems(account: context.account, results: results, current: current, selectionContext: selectionContext, editingContext: editingContext)
|
||||
|
||||
let model = TGMediaPickerGalleryModel(context: legacyController.context, items: items, focus: focusItem, selectionContext: selectionContext, editingContext: editingContext, hasCaptions: false, allowCaptionEntities: true, hasTimer: false, onlyCrop: false, inhibitDocumentCaptions: false, hasSelectionPanel: false, hasCamera: false, recipientName: recipientName, isScheduledMessages: false)!
|
||||
let model = TGMediaPickerGalleryModel(context: legacyController.context, items: items, focus: focusItem, selectionContext: selectionContext, editingContext: editingContext, hasCaptions: false, allowCaptionEntities: true, hasTimer: false, onlyCrop: false, inhibitDocumentCaptions: false, hasSelectionPanel: false, hasCamera: false, recipientName: recipientName, isScheduledMessages: false, hasCoverButton: false)!
|
||||
model.stickersContext = paintStickersContext
|
||||
controller.model = model
|
||||
model.controller = controller
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user