diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 166ad92b4b..ebf196ee09 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -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"; diff --git a/submodules/AccountContext/Sources/ContactSelectionController.swift b/submodules/AccountContext/Sources/ContactSelectionController.swift index 1ebf41f53d..d74999152d 100644 --- a/submodules/AccountContext/Sources/ContactSelectionController.swift +++ b/submodules/AccountContext/Sources/ContactSelectionController.swift @@ -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 public let openProfile: ((EnginePeer) -> Void)? public let sendMessage: ((EnginePeer) -> Void)? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = 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 = { _ in .single(true) }, openProfile: ((EnginePeer) -> Void)? = nil, sendMessage: ((EnginePeer) -> Void)? = nil) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = 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 = { _ 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 diff --git a/submodules/AccountContext/Sources/Premium.swift b/submodules/AccountContext/Sources/Premium.swift index 8f335d4812..0062f30e40 100644 --- a/submodules/AccountContext/Sources/Premium.swift +++ b/submodules/AccountContext/Sources/Premium.swift @@ -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, diff --git a/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift b/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift index 9056670c02..7a9a595cd6 100644 --- a/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift @@ -207,6 +207,7 @@ public struct ContactsSearchCategories: OptionSet { public static let cloudContacts = ContactsSearchCategories(rawValue: 1 << 0) public static let global = ContactsSearchCategories(rawValue: 1 << 1) public static let deviceContacts = ContactsSearchCategories(rawValue: 1 << 2) + public static let channels = ContactsSearchCategories(rawValue: 1 << 3) } public final class ContactsSearchContainerNode: SearchDisplayControllerContentNode { @@ -449,7 +450,10 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo if let remotePeers = remotePeers { for peer in remotePeers.0 { if !(peer.peer is TelegramUser) { - continue + if let channel = peer.peer as? TelegramChannel, case .broadcast = channel.info, categories.contains(.channels) { + } else { + continue + } } if let user = peer.peer as? TelegramUser { @@ -488,7 +492,10 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo } for peer in remotePeers.1 { if !(peer.peer is TelegramUser) { - continue + if let channel = peer.peer as? TelegramChannel, case .broadcast = channel.info, categories.contains(.channels) { + } else { + continue + } } if let user = peer.peer as? TelegramUser, requirePhoneNumbers { diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h index 01f465c4a7..178f043270 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h @@ -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; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerController.h index 6e48ddfac5..c256f0832c 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerController.h @@ -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; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryInterfaceView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryInterfaceView.h index b354ce642b..075896120d 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryInterfaceView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryInterfaceView.h @@ -37,7 +37,7 @@ @property (nonatomic, readonly) UIView *timerButton; -- (instancetype)initWithContext:(id)context focusItem:(id)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id)stickersContext hasSelectionPanel:(bool)hasSelectionPanel hasCameraButton:(bool)hasCameraButton recipientName:(NSString *)recipientName isScheduledMessages:(bool)isScheduledMessages; +- (instancetype)initWithContext:(id)context focusItem:(id)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id)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; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryModel.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryModel.h index 26d30bdef6..e276b9c85a 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryModel.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerGalleryModel.h @@ -46,7 +46,7 @@ @property (nonatomic, readonly) TGMediaSelectionContext *selectionContext; @property (nonatomic, strong) id stickersContext; -- (instancetype)initWithContext:(id)context items:(NSArray *)items focusItem:(id)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)context items:(NSArray *)items focusItem:(id)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)item tab:(TGPhotoEditorTab)tab; - (void)presentPhotoEditorForItem:(id)item tab:(TGPhotoEditorTab)tab snapshots:(NSArray *)snapshots fromRect:(CGRect)fromRect; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerModernGalleryMixin.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerModernGalleryMixin.h index 818cbe4dd2..24f4876030 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerModernGalleryMixin.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerModernGalleryMixin.h @@ -31,9 +31,9 @@ @property (nonatomic, copy) void (^presentScheduleController)(bool, void (^)(int32_t)); @property (nonatomic, copy) void (^presentTimerController)(void (^)(int32_t)); -- (instancetype)initWithContext:(id)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)stickersContext; +- (instancetype)initWithContext:(id)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)stickersContext; -- (instancetype)initWithContext:(id)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)stickersContext; +- (instancetype)initWithContext:(id)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)stickersContext; - (void)present; - (void)updateWithFetchResult:(TGMediaAssetFetchResult *)fetchResult; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryInterfaceView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryInterfaceView.h index 09aeff8345..a21f36027d 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryInterfaceView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryInterfaceView.h @@ -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)item itemView:(TGModernGalleryItemView *)itemView; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryItemView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryItemView.h index 5746499959..c60410dd24 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryItemView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernGalleryItemView.h @@ -38,6 +38,8 @@ @property (nonatomic, strong) UIView *defaultFooterAccessoryLeftView; @property (nonatomic, strong) UIView *defaultFooterAccessoryRightView; +@property (nonatomic, assign) bool gesturesEnabled; + - (void)_setItem:(id)item; - (void)setItem:(id)item synchronously:(bool)synchronously; diff --git a/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m b/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m index d64d69ffa1..727164f8dc 100644 --- a/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m +++ b/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m @@ -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; diff --git a/submodules/LegacyComponents/Sources/TGCameraController.m b/submodules/LegacyComponents/Sources/TGCameraController.m index 49e7bf16ae..468e45bd69 100644 --- a/submodules/LegacyComponents/Sources/TGCameraController.m +++ b/submodules/LegacyComponents/Sources/TGCameraController.m @@ -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; diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m index 39afde67f6..bf0ca5e06f 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m @@ -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; diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m index a71c324917..bae3594fdd 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m @@ -359,7 +359,7 @@ - (TGMediaPickerModernGalleryMixin *)_galleryMixinForContext:(id)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 diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m index 702fb68d00..7a7a4a70b3 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m @@ -117,6 +117,7 @@ void (^_closePressed)(); void (^_scrollViewOffsetRequested)(CGFloat offset); + void (^_setGesturesEnabled)(bool offset); id _context; @@ -132,7 +133,7 @@ @synthesize safeAreaInset = _safeAreaInset; -- (instancetype)initWithContext:(id)context focusItem:(id)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id)stickersContext hasSelectionPanel:(bool)hasSelectionPanel hasCameraButton:(bool)hasCameraButton recipientName:(NSString *)recipientName isScheduledMessages:(bool)isScheduledMessages +- (instancetype)initWithContext:(id)context focusItem:(id)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext stickersContext:(id)stickersContext hasSelectionPanel:(bool)hasSelectionPanel hasCameraButton:(bool)hasCameraButton recipientName:(NSString *)recipientName isScheduledMessages:(bool)isScheduledMessages hasCoverButton:(bool)hasCoverButton { self = [super initWithFrame:CGRectZero]; if (self != nil) @@ -159,10 +160,7 @@ _wrapperView = [[TGMediaPickerGalleryWrapperView alloc] initWithFrame:CGRectZero]; [self addSubview:_wrapperView]; - - _headerWrapperView = [[UIView alloc] init]; - [_wrapperView addSubview:_headerWrapperView]; - + __weak TGMediaPickerGalleryInterfaceView *weakSelf = self; void(^toolbarCancelPressed)(void) = ^ { @@ -247,16 +245,18 @@ // [_cameraButton setHidden:true animated:false]; } - _coverButton = [[TGMediaPickerCoverButton alloc] initWithFrame:CGRectMake(0, 0, 120, 26) gallery:false]; - _coverButton.hidden = true; - [_coverButton addTarget:self action:@selector(coverButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - [_wrapperView addSubview:_coverButton]; - - _coverGalleryButton = [[TGMediaPickerCoverButton alloc] initWithFrame:CGRectMake(0, 0, 120, 26) gallery:true]; - _coverGalleryButton.hidden = true; - [_coverGalleryButton addTarget:self action:@selector(coverGalleryButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - [_wrapperView addSubview:_coverGalleryButton]; - + if (hasCoverButton) { + _coverButton = [[TGMediaPickerCoverButton alloc] initWithFrame:CGRectMake(0, 0, 180, 26) gallery:false]; + _coverButton.hidden = true; + [_coverButton addTarget:self action:@selector(coverButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [_wrapperView addSubview:_coverButton]; + + _coverGalleryButton = [[TGMediaPickerCoverButton alloc] initWithFrame:CGRectMake(0, 0, 180, 26) gallery:true]; + _coverGalleryButton.hidden = true; + [_coverGalleryButton addTarget:self action:@selector(coverGalleryButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [_wrapperView addSubview:_coverGalleryButton]; + } + if (_selectionContext != nil) { _checkButton = [[TGCheckButtonView alloc] initWithStyle:TGCheckButtonStyleGallery]; @@ -426,6 +426,9 @@ _captionMixin.stickersContext = stickersContext; [_captionMixin createInputPanelIfNeeded]; + _headerWrapperView = [[UIView alloc] init]; + [_wrapperView addSubview:_headerWrapperView]; + TGPhotoEditorDoneButton doneButton = isScheduledMessages ? TGPhotoEditorDoneButtonSchedule : TGPhotoEditorDoneButtonSend; _portraitToolbarView = [[TGPhotoToolbarView alloc] initWithContext:_context backButton:TGPhotoEditorBackButtonBack doneButton:doneButton solidBackground:false]; @@ -442,33 +445,35 @@ if ([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad) [_wrapperView addSubview:_landscapeToolbarView]; - _cancelCoverButton = [[TGModernButton alloc] init]; - _cancelCoverButton.hidden = true; - _cancelCoverButton.titleLabel.font = TGSystemFontOfSize(17.0); - [_cancelCoverButton setTitle:TGLocalized(@"Common.Cancel") forState:UIControlStateNormal]; - [_cancelCoverButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; - [_cancelCoverButton addTarget:self action:@selector(cancelCoverButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - [_cancelCoverButton sizeToFit]; - [_wrapperView addSubview:_cancelCoverButton]; - - _coverTitleLabel = [[UILabel alloc] init]; - _coverTitleLabel.hidden = true; - _coverTitleLabel.textColor = [UIColor whiteColor]; - _coverTitleLabel.font = TGBoldSystemFontOfSize(17.0); - _coverTitleLabel.text = TGLocalized(@"Media.SelectFrame"); - [_coverTitleLabel sizeToFit]; - [_wrapperView addSubview:_coverTitleLabel]; - - _saveCoverButton = [[TGModernButton alloc] init]; - _saveCoverButton.clipsToBounds = true; - _saveCoverButton.layer.cornerRadius = 10.0; - _saveCoverButton.hidden = true; - [_saveCoverButton setBackgroundColor:UIColorRGB(0x007aff)]; - _saveCoverButton.titleLabel.font = TGBoldSystemFontOfSize(17.0); - [_saveCoverButton setTitle:TGLocalized(@"Media.SaveCover") forState:UIControlStateNormal]; - [_saveCoverButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; - [_saveCoverButton addTarget:self action:@selector(saveCoverButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - [_wrapperView addSubview:_saveCoverButton]; + if (hasCoverButton) { + _cancelCoverButton = [[TGModernButton alloc] init]; + _cancelCoverButton.hidden = true; + _cancelCoverButton.titleLabel.font = TGSystemFontOfSize(17.0); + [_cancelCoverButton setTitle:TGLocalized(@"Common.Cancel") forState:UIControlStateNormal]; + [_cancelCoverButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [_cancelCoverButton addTarget:self action:@selector(cancelCoverButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [_cancelCoverButton sizeToFit]; + [_wrapperView addSubview:_cancelCoverButton]; + + _coverTitleLabel = [[UILabel alloc] init]; + _coverTitleLabel.hidden = true; + _coverTitleLabel.textColor = [UIColor whiteColor]; + _coverTitleLabel.font = TGBoldSystemFontOfSize(17.0); + _coverTitleLabel.text = TGLocalized(@"Media.SelectFrame"); + [_coverTitleLabel sizeToFit]; + [_wrapperView addSubview:_coverTitleLabel]; + + _saveCoverButton = [[TGModernButton alloc] init]; + _saveCoverButton.clipsToBounds = true; + _saveCoverButton.layer.cornerRadius = 10.0; + _saveCoverButton.hidden = true; + [_saveCoverButton setBackgroundColor:UIColorRGB(0x007aff)]; + _saveCoverButton.titleLabel.font = TGBoldSystemFontOfSize(17.0); + [_saveCoverButton setTitle:TGLocalized(@"Media.SaveCover") forState:UIControlStateNormal]; + [_saveCoverButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [_saveCoverButton addTarget:self action:@selector(saveCoverButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [_wrapperView addSubview:_saveCoverButton]; + } } return self; } @@ -554,6 +559,11 @@ _scrollViewOffsetRequested = [scrollViewOffsetRequested copy]; } +- (void)setGesturesEnabled:(void (^)(bool))setGesturesEnabled +{ + _setGesturesEnabled = [setGesturesEnabled copy]; +} + - (void)setEditorTabPressed:(void (^)(TGPhotoEditorTab tab))editorTabPressed { __weak TGMediaPickerGalleryInterfaceView *weakSelf = self; @@ -634,7 +644,7 @@ [_checkButton setNumber:[_selectionContext indexOfItem:selectableItem]]; signal = [_selectionContext itemInformativeSelectedSignal:selectableItem]; [_itemSelectedDisposable setDisposable:[signal startStrictWithNext:^(TGMediaSelectionChange *next) - { + { __strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf; if (strongSelf == nil) return; @@ -648,7 +658,7 @@ __weak TGModernGalleryItemView *weakItemView = itemView; [_itemAvailabilityDisposable setDisposable:[[[itemView contentAvailabilityStateSignal] deliverOn:[SQueue mainQueue]] startStrictWithNext:^(id next) - { + { __strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf; __strong TGModernGalleryItemView *strongItemView = weakItemView; if (strongSelf == nil || strongItemView == nil) @@ -678,7 +688,13 @@ } strongSelf->_muteButton.hidden = !sendableAsGif; - bool canHaveCover = [strongItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]]; + bool canHaveCover = false; + if ([strongItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]]) { + TGMediaPickerGalleryVideoItemView *itemView = (TGMediaPickerGalleryVideoItemView *)strongItemView; + if (itemView.editableMediaItem.originalDuration >= 60.0) { + canHaveCover = true; + } + } strongSelf->_coverButton.hidden = !canHaveCover; } } file:__FILE_NAME__ line:__LINE__]]; @@ -863,6 +879,9 @@ if ([currentItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]]) { [(TGMediaPickerGalleryVideoItemView *)currentItemView prepareForCoverEditing]; } + + _setGesturesEnabled(false); + _currentItemView.gesturesEnabled = false; } - (void)coverEditorTransitionOut { @@ -894,6 +913,9 @@ if ([currentItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]]) { [(TGMediaPickerGalleryVideoItemView *)currentItemView returnFromCoverEditing]; } + + _setGesturesEnabled(true); + _currentItemView.gesturesEnabled = true; } - (void)cancelCoverButtonPressed @@ -1609,25 +1631,35 @@ { UIView *view = [super hitTest:point withEvent:event]; - if (view == _photoCounterButton - || view == _checkButton - || view == _muteButton - || view == _groupButton - || view == _cameraButton - || view == _coverButton - || view == _cancelCoverButton - || view == _saveCoverButton - || view == _coverGalleryButton - || [view isDescendantOfView:_headerWrapperView] - || [view isDescendantOfView:_portraitToolbarView] - || [view isDescendantOfView:_landscapeToolbarView] - || [view isDescendantOfView:_selectedPhotosView] - || [view isDescendantOfView:_captionMixin.inputPanelView] - || ([view isDescendantOfView:_captionMixin.dismissView] && _captionMixin.dismissView.alpha > 0.0) - || [view isKindOfClass:[TGMenuButtonView class]]) - - { - return view; + if (_coverTitleLabel.hidden) { + if (view == _photoCounterButton + || view == _checkButton + || view == _muteButton + || view == _groupButton + || view == _cameraButton + || view == _coverButton + || view == _cancelCoverButton + || view == _saveCoverButton + || view == _coverGalleryButton + || [view isDescendantOfView:_headerWrapperView] + || [view isDescendantOfView:_portraitToolbarView] + || [view isDescendantOfView:_landscapeToolbarView] + || [view isDescendantOfView:_selectedPhotosView] + || [view isDescendantOfView:_captionMixin.inputPanelView] + || ([view isDescendantOfView:_captionMixin.dismissView] && _captionMixin.dismissView.alpha > 0.0) + || [view isKindOfClass:[TGMenuButtonView class]]) + + { + return view; + } + } else { + if (view == _cancelCoverButton + || view == _saveCoverButton + || view == _coverGalleryButton + || [view isDescendantOfView:_headerWrapperView]) + { + return view; + } } return nil; @@ -1982,7 +2014,7 @@ _landscapeToolbarView.frame = CGRectMake(_landscapeToolbarView.frame.origin.x, screenEdges.top, TGPhotoEditorToolbarSize, self.frame.size.height); - _headerWrapperView.frame = CGRectMake(screenEdges.left, _portraitToolbarView.frame.origin.y - 64.0 - [_captionMixin.inputPanel baseHeight], self.frame.size.width, 64.0); + _headerWrapperView.frame = CGRectMake(screenEdges.left, _portraitToolbarView.frame.origin.y - 64.0 - [_captionMixin.inputPanel baseHeight], self.frame.size.width, 72.0); } break; } diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m index f4c3addf66..a65545ddab 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m @@ -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)context items:(NSArray *)items focusItem:(id)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)context items:(NSArray *)items focusItem:(id)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; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m index 82fd3f1628..6cb1da5c02 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m @@ -120,6 +120,8 @@ CMTime _chaseTime; bool _chasingTime; + + bool _isCoverEditing; } @property (nonatomic, strong) TGMediaPickerGalleryVideoItem *item; @@ -763,12 +765,14 @@ - (void)prepareForCoverEditing { + _isCoverEditing = true; [self setPlayButtonHidden:true animated:true]; [self stop]; } - (void)returnFromCoverEditing { + _isCoverEditing = false; if (![self usePhotoBehavior]) [self setPlayButtonHidden:false animated:true]; } @@ -1272,6 +1276,9 @@ - (void)playPressed { + if (!self.gesturesEnabled) + return; + if (_downloadRequired) [self _download]; else @@ -1437,7 +1444,12 @@ if (_wasPlayingBeforeScrubbing) { [self play]; } else { - [self setPlayButtonHidden:false animated:true]; + if (!_isCoverEditing) + [self setPlayButtonHidden:false animated:true]; + } + + if (videoScrubber == _coverScrubberView) { + [_coverScrubberView setValue:_scrubberView.value resetPosition:true]; } } diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m index ef50069cc0..097e0cc147 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m @@ -138,8 +138,8 @@ typedef enum static UIImage *leftCurtain; static UIImage *rightCurtain; dispatch_once(&onceToken, ^ - { - UIGraphicsBeginImageContextWithOptions(CGSizeMake(24.0f, 40.0f), false, 0.0f); + { + UIGraphicsBeginImageContextWithOptions(CGSizeMake(11.0f, 40.0f), false, 0.0f); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.8).CGColor); @@ -150,11 +150,11 @@ typedef enum leftCurtain = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); - UIGraphicsBeginImageContextWithOptions(CGSizeMake(24.0f, 40.0f), false, 0.0f); + UIGraphicsBeginImageContextWithOptions(CGSizeMake(11.0f, 40.0f), false, 0.0f); context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.8).CGColor); - path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(-16.0, 0, 40, 40) cornerRadius:9.0]; + path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(-29.0, 0, 40, 40) cornerRadius:9.0]; CGContextAddPath(context, path.CGPath); CGContextFillPath(context); @@ -163,12 +163,12 @@ typedef enum }); _leftCurtainView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)]; - _leftCurtainView.image = [leftCurtain stretchableImageWithLeftCapWidth:11 topCapHeight:20]; + _leftCurtainView.image = [leftCurtain resizableImageWithCapInsets:UIEdgeInsetsMake(0, 11, 0, 0)]; _leftCurtainView.clipsToBounds = true; [_wrapperView addSubview:_leftCurtainView]; _rightCurtainView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)]; - _rightCurtainView.image = [rightCurtain stretchableImageWithLeftCapWidth:11 topCapHeight:20]; + _rightCurtainView.image = [rightCurtain resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 11)]; _rightCurtainView.clipsToBounds = true; [_wrapperView addSubview:_rightCurtainView]; @@ -409,7 +409,7 @@ typedef enum [_dotHandle addGestureRecognizer:_dotPanGestureRecognizer]; _tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; - _tapGestureRecognizer.enabled = false; + _tapGestureRecognizer.enabled = cover; [_trimView addGestureRecognizer:_tapGestureRecognizer]; } return self; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m b/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m index 40b4c39f7a..756f4deb62 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m @@ -40,17 +40,17 @@ @implementation TGMediaPickerModernGalleryMixin -- (instancetype)initWithContext:(id)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)stickersContext +- (instancetype)initWithContext:(id)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)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)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)stickersContext +- (instancetype)initWithContext:(id)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)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)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)stickersContext +- (instancetype)initWithContext:(id)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)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; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m index 17a13d7c21..cdbf67a314 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m @@ -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); } diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerScrubberHeaderView.m b/submodules/LegacyComponents/Sources/TGMediaPickerScrubberHeaderView.m index 0e833dac52..fb334177fb 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerScrubberHeaderView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerScrubberHeaderView.m @@ -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 diff --git a/submodules/LegacyComponents/Sources/TGModernGalleryDefaultInterfaceView.m b/submodules/LegacyComponents/Sources/TGModernGalleryDefaultInterfaceView.m index 7f1720a598..a05098eb6c 100644 --- a/submodules/LegacyComponents/Sources/TGModernGalleryDefaultInterfaceView.m +++ b/submodules/LegacyComponents/Sources/TGModernGalleryDefaultInterfaceView.m @@ -355,6 +355,9 @@ { } +- (void)setGesturesEnabled:(void (^)(bool))setGesturesEnabled { +} + @end diff --git a/submodules/LegacyComponents/Sources/TGModernGalleryItemView.m b/submodules/LegacyComponents/Sources/TGModernGalleryItemView.m index ed32c8fb7f..3deaa9c175 100644 --- a/submodules/LegacyComponents/Sources/TGModernGalleryItemView.m +++ b/submodules/LegacyComponents/Sources/TGModernGalleryItemView.m @@ -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 diff --git a/submodules/LegacyComponents/Sources/TGModernGalleryView.m b/submodules/LegacyComponents/Sources/TGModernGalleryView.m index 370258a176..a86e027c2a 100644 --- a/submodules/LegacyComponents/Sources/TGModernGalleryView.m +++ b/submodules/LegacyComponents/Sources/TGModernGalleryView.m @@ -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}; diff --git a/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m b/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m index 386f2749f5..14af60512a 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m +++ b/submodules/LegacyComponents/Sources/TGPhotoVideoEditor.m @@ -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; diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift index b06a830370..54ce4dfa40 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift @@ -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) diff --git a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift index a4f35fe788..fc022bc9cb 100644 --- a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift +++ b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift @@ -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 diff --git a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift index 13992bde98..aae7f44fe6 100644 --- a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift @@ -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)) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 25b6b413d5..655906e5ac 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -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) } diff --git a/submodules/TelegramApi/Sources/Api24.swift b/submodules/TelegramApi/Sources/Api24.swift index dfa6990fdf..a70e047e6b 100644 --- a/submodules/TelegramApi/Sources/Api24.swift +++ b/submodules/TelegramApi/Sources/Api24.swift @@ -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 diff --git a/submodules/TelegramApi/Sources/Api38.swift b/submodules/TelegramApi/Sources/Api38.swift index 580ab64ff2..a9b0a4f693 100644 --- a/submodules/TelegramApi/Sources/Api38.swift +++ b/submodules/TelegramApi/Sources/Api38.swift @@ -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) { + 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) { 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() { diff --git a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift index 9cb72f36af..1cf0ee08d4 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift @@ -851,23 +851,54 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili switch result { case let .media(media, key): if !forceReupload, let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference { - var flags: Int32 = 0 - var ttlSeconds: Int32? - if let autoclearMessageAttribute = autoclearMessageAttribute { - flags |= 1 << 0 - ttlSeconds = autoclearMessageAttribute.timeout - } - for attribute in attributes { - if let _ = attribute as? MediaSpoilerMessageAttribute { - flags |= 1 << 2 + var videoCoverSignal: Signal = .single(.none) + if let cover = file.videoCover, let resource = cover.representations.first?.resource { + let fileReference: AnyMediaReference + if let partialReference = file.partialReference { + fileReference = partialReference.mediaReference(media) + } else { + fileReference = .standalone(media: media) + } + videoCoverSignal = uploadedVideoCover(network: network, postbox: postbox, resourceReference: fileReference.resourceReference(resource), peerId: peerId) + |> mapError { _ -> PendingMessageUploadError in return .generic } + |> map { result in + if let result = result { + return .photo(result) + } else { + return .none + } } } - return .single(.progress(PendingMessageUploadedContentProgress(progress: 1.0))) - |> then( - .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), videoCover: nil, videoTimestamp: nil, ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil))) - ) + return videoCoverSignal + |> mapToSignal { videoCover -> Signal in + var flags: Int32 = 0 + var ttlSeconds: Int32? + if let autoclearMessageAttribute = autoclearMessageAttribute { + flags |= 1 << 0 + ttlSeconds = autoclearMessageAttribute.timeout + } + + for attribute in attributes { + if let _ = attribute as? MediaSpoilerMessageAttribute { + flags |= 1 << 2 + } + } + + var videoCoverPhoto: Api.InputPhoto? + if case let .photo(photo) = videoCover { + videoCoverPhoto = photo + } + if let _ = videoCoverPhoto { + flags |= 1 << 3 + } + + return .single(.progress(PendingMessageUploadedContentProgress(progress: 1.0))) + |> then( + .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), videoCover: videoCoverPhoto, videoTimestamp: nil, ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil))) + ) + } } referenceKey = key case let .localReference(key): diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift index 1a147217d9..0352b97142 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift @@ -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")) diff --git a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift index ab1ad09559..b15e196681 100644 --- a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift +++ b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift @@ -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")) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift index e45b7d8596..6dc1fdecb2 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift @@ -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 [] } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ForwardGame.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ForwardGame.swift index f7e5d42833..473c45e194 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ForwardGame.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ForwardGame.swift @@ -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 in return .single(nil) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index b545d74054..24b3cd52a9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -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) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 986ec158e2..873bddf159 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -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 { + return _internal_updatePeerStarGiftStatus(account: self.account, peerId: peerId, starGift: starGift, expirationDate: expirationDate) + } + public func checkChannelRevenueWithdrawalAvailability() -> Signal { return _internal_checkChannelRevenueWithdrawalAvailability(account: self.account) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift index bbb1851742..ef3ca6433d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift @@ -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 { + 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 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 in + if error.errorDescription == "CHAT_NOT_MODIFIED" { + return .complete() + } else { + return .fail(.generic) + } + } + } +} diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift index aeed9b708e..f7ccee5ae3 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD index 788dc255c7..c48c5179ad 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD @@ -47,6 +47,7 @@ swift_library( "//submodules/TelegramUI/Components/EmojiStatusComponent", "//submodules/PasswordSetupUI", "//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController", + "//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 7bbc3497ac..26ebb74d56 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -32,6 +32,7 @@ import GiftAnimationComponent import LottieComponent import ContextUI import TelegramNotices +import PremiumLockButtonSubtitleComponent private let modelButtonTag = GenericComponentViewTag() private let backdropButtonTag = GenericComponentViewTag() @@ -128,6 +129,7 @@ private final class GiftViewSheetContent: CombinedComponent { var upgradeForm: BotPaymentForm? var upgradeFormDisposable: Disposable? var upgradeDisposable: Disposable? + let levelsDisposable = MetaDisposable() var inWearPreview = false var pendingWear = false @@ -171,6 +173,11 @@ private final class GiftViewSheetContent: CombinedComponent { if let fromPeerId = arguments.fromPeerId, !peerIds.contains(fromPeerId) { peerIds.append(fromPeerId) } + if case let .message(message) = subject { + for media in message.media { + peerIds.append(contentsOf: media.peerIds) + } + } if case let .unique(gift) = arguments.gift { if case let .peerId(peerId) = gift.owner { peerIds.append(peerId) @@ -219,6 +226,7 @@ private final class GiftViewSheetContent: CombinedComponent { } } } + self.disposable = combineLatest(queue: Queue.mainQueue(), context.engine.data.get(EngineDataMap( peerIds.map { peerId -> TelegramEngine.EngineData.Item.Peer.Peer in @@ -270,6 +278,7 @@ private final class GiftViewSheetContent: CombinedComponent { self.sampleDisposable.dispose() self.upgradeFormDisposable?.dispose() self.upgradeDisposable?.dispose() + self.levelsDisposable.dispose() } func requestUpgradePreview() { @@ -293,7 +302,11 @@ private final class GiftViewSheetContent: CombinedComponent { self.inWearPreview = false self.updated(transition: .spring(duration: 0.4)) - let _ = self.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).startStandalone() + if let arguments = self.subject.arguments, let peerId = arguments.peerId, peerId.namespace == Namespaces.Peer.CloudChannel { + let _ = self.context.engine.peers.updatePeerStarGiftStatus(peerId: peerId, starGift: uniqueGift, expirationDate: nil).startStandalone() + } else { + let _ = self.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).startStandalone() + } let _ = ApplicationSpecificNotice.incrementStarGiftWearTips(accountManager: self.context.sharedContext.accountManager).startStandalone() } @@ -303,7 +316,11 @@ private final class GiftViewSheetContent: CombinedComponent { self.pendingWear = false self.updated(transition: .spring(duration: 0.4)) - let _ = self.context.engine.accountData.setEmojiStatus(file: nil, expirationDate: nil).startStandalone() + if let arguments = self.subject.arguments, let peerId = arguments.peerId, peerId.namespace == Namespaces.Peer.CloudChannel { + let _ = self.context.engine.peers.updatePeerEmojiStatus(peerId: peerId, fileId: nil, expirationDate: nil) + } else { + let _ = self.context.engine.accountData.setEmojiStatus(file: nil, expirationDate: nil).startStandalone() + } } func commitUpgrade() { @@ -543,7 +560,7 @@ private final class GiftViewSheetContent: CombinedComponent { if let uniqueGift { if showWearPreview { headerHeight = 200.0 - } else if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId { + } else if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId || isChannelGift { headerHeight = 314.0 } else { headerHeight = 240.0 @@ -563,11 +580,18 @@ private final class GiftViewSheetContent: CombinedComponent { headerSubject = nil } + var ownerPeerId: EnginePeer.Id + if let uniqueGift, case let .peerId(peerId) = uniqueGift.owner { + ownerPeerId = peerId + } else { + ownerPeerId = component.context.account.peerId + } + var wearPeerNameChild: _UpdatedChildComponent? if showWearPreview, let uniqueGift { var peerName = "" - if let accountPeer = state.peerMap[component.context.account.peerId] { - peerName = accountPeer.displayTitle(strings: strings, displayOrder: nameDisplayOrder) + if let ownerPeer = state.peerMap[ownerPeerId] { + peerName = ownerPeer.displayTitle(strings: strings, displayOrder: nameDisplayOrder) } wearPeerNameChild = wearPeerName.update( component: MultilineTextComponent( @@ -675,12 +699,12 @@ private final class GiftViewSheetContent: CombinedComponent { } if let wearPeerNameChild { - if let accountPeer = state.peerMap[component.context.account.peerId] { + if let ownerPeer = state.peerMap[ownerPeerId] { let wearAvatar = wearAvatar.update( component: AvatarComponent( context: component.context, theme: theme, - peer: accountPeer + peer: ownerPeer ), environment: {}, availableSize: CGSize(width: 100.0, height: 100.0), @@ -696,7 +720,7 @@ private final class GiftViewSheetContent: CombinedComponent { let wearPeerStatus = wearPeerStatus.update( component: MultilineTextComponent( text: .plain(NSAttributedString( - string: strings.Presence_online, + string: isChannelGift ? strings.Channel_Status : strings.Presence_online, font: Font.regular(17.0), textColor: vibrantColor, paragraphAlignment: .center @@ -736,7 +760,7 @@ private final class GiftViewSheetContent: CombinedComponent { component: AnyComponent(ParagraphComponent( title: strings.Gift_Wear_Badge_Title, titleColor: textColor, - text: strings.Gift_Wear_Badge_Text, + text: isChannelGift ? strings.Gift_Wear_Badge_ChannelText : strings.Gift_Wear_Badge_Text, textColor: secondaryTextColor, accentColor: linkColor, iconName: "Premium/Collectible/Badge", @@ -750,7 +774,7 @@ private final class GiftViewSheetContent: CombinedComponent { component: AnyComponent(ParagraphComponent( title: strings.Gift_Wear_Design_Title, titleColor: textColor, - text: strings.Gift_Wear_Design_Text, + text: isChannelGift ? strings.Gift_Wear_Design_ChannelText : strings.Gift_Wear_Design_Text, textColor: secondaryTextColor, accentColor: linkColor, iconName: "Premium/BoostPerk/CoverColor", @@ -764,7 +788,7 @@ private final class GiftViewSheetContent: CombinedComponent { component: AnyComponent(ParagraphComponent( title: strings.Gift_Wear_Proof_Title, titleColor: textColor, - text: strings.Gift_Wear_Proof_Text, + text: isChannelGift ? strings.Gift_Wear_Proof_ChannelText : strings.Gift_Wear_Proof_Text, textColor: secondaryTextColor, accentColor: linkColor, iconName: "Premium/Collectible/Proof", @@ -1249,12 +1273,20 @@ private final class GiftViewSheetContent: CombinedComponent { } case let .name(name): tableItems.append(.init( - id: "anon_owner", + id: "name_owner", title: strings.Gift_Unique_Owner, component: AnyComponent( MultilineTextComponent(text: .plain(NSAttributedString(string: name, font: tableFont, textColor: tableTextColor))) ) )) + case let .address(address): + tableItems.append(.init( + id: "address_owner", + title: strings.Gift_Unique_Owner, + component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: address, font: tableMonospaceFont, textColor: tableTextColor))) + ) + )) } } else if let peerId = subject.arguments?.fromPeerId, let peer = state.peerMap[peerId] { var isBot = false @@ -1346,7 +1378,7 @@ private final class GiftViewSheetContent: CombinedComponent { } if let uniqueGift { - if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId { + if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId || isChannelGift { let buttonSpacing: CGFloat = 10.0 let buttonWidth = floor(context.availableSize.width - sideInset * 2.0 - buttonSpacing * 2.0) / 3.0 let buttonHeight: CGFloat = 58.0 @@ -1397,12 +1429,24 @@ private final class GiftViewSheetContent: CombinedComponent { controller.dismissAllTooltips() } + let canWear: Bool + if isChannelGift, case let .channel(channel) = state.peerMap[ownerPeerId] { + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) + let requiredLevel = Int(BoostSubject.wearGift.requiredLevel(group: false, context: component.context, configuration: premiumConfiguration)) + if let boostLevel = channel.approximateBoostLevel { + canWear = boostLevel >= requiredLevel + } else { + canWear = false + } + } else { + canWear = component.context.isPremium + } let _ = (ApplicationSpecificNotice.getStarGiftWearTips(accountManager: component.context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { [weak state] count in guard let state else { return } - if !component.context.isPremium || count < 3 { + if !canWear || count < 3 { state.requestWearPreview() } else { state.commitWear(uniqueGift) @@ -1856,9 +1900,36 @@ private final class GiftViewSheetContent: CombinedComponent { let buttonChild: _UpdatedChildComponent if showWearPreview, let uniqueGift { let buttonContent: AnyComponentWithIdentity - if !component.context.isPremium { + + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) + let requiredLevel = Int(BoostSubject.wearGift.requiredLevel(group: false, context: component.context, configuration: premiumConfiguration)) + + var canWear = true + if isChannelGift, case let .channel(channel) = state.peerMap[ownerPeerId], (channel.approximateBoostLevel ?? 0) < requiredLevel { + canWear = false buttonContent = AnyComponentWithIdentity( - id: AnyHashable("wear_locked"), + id: AnyHashable("wear_channel"), + component: AnyComponent( + VStack([ + AnyComponentWithIdentity( + id: AnyHashable("label"), + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Wear_Start, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)))) + ), + AnyComponentWithIdentity( + id: AnyHashable("level"), + component: AnyComponent(PremiumLockButtonSubtitleComponent( + count: requiredLevel, + theme: theme, + strings: strings + )) + ) + ], spacing: 3.0) + ) + ) + } else if !isChannelGift && !component.context.isPremium { + canWear = false + buttonContent = AnyComponentWithIdentity( + id: AnyHashable("wear_premium"), component: AnyComponent( HStack([ AnyComponentWithIdentity( @@ -1888,30 +1959,48 @@ private final class GiftViewSheetContent: CombinedComponent { action: { [weak state] in if let state { let context = component.context - if !context.isPremium, let controller = controller() as? GiftViewScreen { + if !canWear, let controller = controller() as? GiftViewScreen { controller.dismissAllTooltips() - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let text = strings.Gift_View_TooltipPremiumWearing - let tooltipController = UndoOverlayController( - presentationData: presentationData, - content: .premiumPaywall(title: nil, text: text, customUndoText: nil, timeout: nil, linkAction: nil), - position: .bottom, - animateInAsReplacement: false, - appearance: UndoOverlayController.Appearance(sideInset: 16.0, bottomInset: 62.0), - action: { [weak controller] action in - if case .info = action { - controller?.dismissAllTooltips() - let premiumController = context.sharedContext.makePremiumIntroController(context: context, source: .messageEffects, forceDark: false, dismissed: nil) - controller?.push(premiumController) - Queue.mainQueue().after(0.6, { - component.cancel(false) - }) + if isChannelGift { + state.levelsDisposable.set(combineLatest( + queue: Queue.mainQueue(), + context.engine.peers.getChannelBoostStatus(peerId: ownerPeerId), + context.engine.peers.getMyBoostStatus() + ).startStandalone(next: { [weak controller] boostStatus, myBoostStatus in + guard let controller, let boostStatus, let myBoostStatus else { + return } - return false - } - ) - controller.present(tooltipController, in: .window(.root)) + component.cancel(true) + + let levelsController = context.sharedContext.makePremiumBoostLevelsController(context: context, peerId: ownerPeerId, subject: .wearGift, boostStatus: boostStatus, myBoostStatus: myBoostStatus, forceDark: false, openStats: nil) + controller.push(levelsController) + + HapticFeedback().impact(.light) + })) + } else { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let text = strings.Gift_View_TooltipPremiumWearing + let tooltipController = UndoOverlayController( + presentationData: presentationData, + content: .premiumPaywall(title: nil, text: text, customUndoText: nil, timeout: nil, linkAction: nil), + position: .bottom, + animateInAsReplacement: false, + appearance: UndoOverlayController.Appearance(sideInset: 16.0, bottomInset: 62.0), + action: { [weak controller] action in + if case .info = action { + controller?.dismissAllTooltips() + let premiumController = context.sharedContext.makePremiumIntroController(context: context, source: .messageEffects, forceDark: false, dismissed: nil) + controller?.push(premiumController) + Queue.mainQueue().after(0.6, { + component.cancel(false) + }) + } + return false + } + ) + controller.present(tooltipController, in: .window(.root)) + } } else { state.commitWear(uniqueGift) if case .wearPreview = component.subject { @@ -3655,7 +3744,7 @@ private final class AvatarComponent: Component { private weak var state: EmptyComponentState? override init(frame: CGRect) { - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) + self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 42.0)) super.init(frame: frame) diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift index b287ed2d45..d7f632092c 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift @@ -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 diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift index 6de7f0a6d8..bb52d763b6 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift @@ -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?) { + public func updateProfileVideo(_ image: UIImage, video: MediaEditorScreenImpl.MediaResult.VideoResult?, values: MediaEditorValues?, markup: UploadPeerPhotoMarkup?, mode: PeerInfoAvatarEditingMode, uploadStatus: Promise?) { var uploadVideo = true if let _ = markup { if let data = self.context.currentAppConfiguration.with({ $0 }).data, let uploadVideoValue = data["upload_markup_video"] as? Bool, uploadVideoValue { @@ -482,9 +482,82 @@ extension PeerInfoScreenImpl { let context = self.context let videoResource: Signal - if uploadVideo { - videoResource = Signal { subscriber in - return EmptyDisposable + if uploadVideo, let video, let values { + var exportSubject: Signal<(MediaEditorVideoExport.Subject, Double), NoError>? + switch video { + case let .imageFile(path): + if let image = UIImage(contentsOfFile: path) { + exportSubject = .single((.image(image: image), 3.0)) + } + case let .videoFile(path): + let asset = AVURLAsset(url: NSURL(fileURLWithPath: path) as URL) + exportSubject = .single((.video(asset: asset, isStory: false), asset.duration.seconds)) + case let .asset(localIdentifier): + exportSubject = Signal { subscriber in + let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil) + if fetchResult.count != 0 { + let asset = fetchResult.object(at: 0) + if asset.mediaType == .video { + PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in + if let avAsset { + subscriber.putNext((.video(asset: avAsset, isStory: true), avAsset.duration.seconds)) + subscriber.putCompletion() + } + } + } else { + let options = PHImageRequestOptions() + options.deliveryMode = .highQualityFormat + PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .default, options: options) { image, _ in + if let image { + subscriber.putNext((.image(image: image), 3.0)) + subscriber.putCompletion() + } + } + } + } + return EmptyDisposable + } + } + + if let exportSubject { + videoResource = exportSubject + |> castError(UploadPeerPhotoError.self) + |> mapToSignal { exportSubject, duration in + return Signal { 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) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index dcdcd5f011..0796f64cf1 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -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() } } diff --git a/submodules/TelegramUI/Sources/ContactSelectionController.swift b/submodules/TelegramUI/Sources/ContactSelectionController.swift index fdd0d2397d..31e7b99618 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionController.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionController.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift index e83f215593..23e2c818ae 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 6379d6ebce..3100b5c984 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -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 diff --git a/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift b/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift index 751c1bdeb6..29f65144f9 100644 --- a/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift +++ b/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift @@ -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