Various improvements

This commit is contained in:
Ilya Laktyushin 2025-01-22 13:24:13 +04:00
parent 4bf5df909c
commit c0bfe87449
50 changed files with 626 additions and 206 deletions

View File

@ -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";

View File

@ -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

View File

@ -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,

View File

@ -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,8 +450,11 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
if let remotePeers = remotePeers {
for peer in remotePeers.0 {
if !(peer.peer is TelegramUser) {
if let channel = peer.peer as? TelegramChannel, case .broadcast = channel.info, categories.contains(.channels) {
} else {
continue
}
}
if let user = peer.peer as? TelegramUser {
if requirePhoneNumbers {
@ -488,8 +492,11 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
}
for peer in remotePeers.1 {
if !(peer.peer is TelegramUser) {
if let channel = peer.peer as? TelegramChannel, case .broadcast = channel.info, categories.contains(.channels) {
} else {
continue
}
}
if let user = peer.peer as? TelegramUser, requirePhoneNumbers {
let phone = user.phone ?? ""

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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)
@ -160,9 +161,6 @@
_wrapperView = [[TGMediaPickerGalleryWrapperView alloc] initWithFrame:CGRectZero];
[self addSubview:_wrapperView];
_headerWrapperView = [[UIView alloc] init];
[_wrapperView addSubview:_headerWrapperView];
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
void(^toolbarCancelPressed)(void) = ^
{
@ -247,15 +245,17 @@
// [_cameraButton setHidden:true animated:false];
}
_coverButton = [[TGMediaPickerCoverButton alloc] initWithFrame:CGRectMake(0, 0, 120, 26) gallery:false];
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, 120, 26) gallery:true];
_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)
{
@ -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,6 +445,7 @@
if ([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad)
[_wrapperView addSubview:_landscapeToolbarView];
if (hasCoverButton) {
_cancelCoverButton = [[TGModernButton alloc] init];
_cancelCoverButton.hidden = true;
_cancelCoverButton.titleLabel.font = TGSystemFontOfSize(17.0);
@ -470,6 +474,7 @@
[_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;
@ -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,6 +1631,7 @@
{
UIView *view = [super hitTest:point withEvent:event];
if (_coverTitleLabel.hidden) {
if (view == _photoCounterButton
|| view == _checkButton
|| view == _muteButton
@ -1629,6 +1652,15 @@
{
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;
}

View File

@ -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;

View File

@ -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,8 +1444,13 @@
if (_wasPlayingBeforeScrubbing) {
[self play];
} else {
if (!_isCoverEditing)
[self setPlayButtonHidden:false animated:true];
}
if (videoScrubber == _coverScrubberView) {
[_coverScrubberView setValue:_scrubberView.value resetPosition:true];
}
}
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber valueDidChange:(NSTimeInterval)position

View File

@ -139,7 +139,7 @@ typedef enum
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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -355,6 +355,9 @@
{
}
- (void)setGesturesEnabled:(void (^)(bool))setGesturesEnabled {
}
@end

View File

@ -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

View File

@ -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};

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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) }

View File

@ -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

View File

@ -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() {

View File

@ -851,6 +851,28 @@ 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 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 videoCoverSignal
|> mapToSignal { videoCover -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
var flags: Int32 = 0
var ttlSeconds: Int32?
if let autoclearMessageAttribute = autoclearMessageAttribute {
@ -864,11 +886,20 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
}
}
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: nil, videoTimestamp: nil, ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
.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):
referenceKey = key

View File

@ -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"))

View File

@ -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"))

View File

@ -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 []
}

View File

@ -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)

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}
}
}
}

View File

@ -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)
}

View File

@ -47,6 +47,7 @@ swift_library(
"//submodules/TelegramUI/Components/EmojiStatusComponent",
"//submodules/PasswordSetupUI",
"//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController",
"//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent",
],
visibility = [
"//visibility:public",

View File

@ -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))
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,8 +316,12 @@ private final class GiftViewSheetContent: CombinedComponent {
self.pendingWear = false
self.updated(transition: .spring(duration: 0.4))
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() {
guard let arguments = self.subject.arguments, let peerId = arguments.peerId, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else {
@ -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,9 +1959,26 @@ 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()
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
}
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(
@ -1912,6 +2000,7 @@ private final class GiftViewSheetContent: CombinedComponent {
}
)
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)

View File

@ -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

View File

@ -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,10 +482,83 @@ extension PeerInfoScreenImpl {
let context = self.context
let videoResource: Signal<TelegramMediaResource?, UploadPeerPhotoError>
if uploadVideo {
videoResource = Signal { subscriber in
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)
}

View File

@ -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()
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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