mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
a8fd8c6085
commit
ef6c097f6f
@ -13095,6 +13095,9 @@ Sorry for the inconvenience.";
|
||||
"Gift.Send.Remains_any" = "%@ left";
|
||||
"Gift.Send.Sold_1" = "%@ sold";
|
||||
"Gift.Send.Sold_any" = "%@ sold";
|
||||
"Gift.Send.Success" = "You sent a gift to **%1$@** for **%2$@**.";
|
||||
"Gift.Send.Success.Stars_1" = "%@ Star";
|
||||
"Gift.Send.Success.Stars_any" = "%@ Stars";
|
||||
|
||||
"Gift.Send.ErrorUnknown" = "An error occurred. Please try again.";
|
||||
"Gift.Send.ErrorOutOfStock" = "Sorry, this gift is sold out. Please choose another gift.";
|
||||
@ -13667,6 +13670,11 @@ Sorry for the inconvenience.";
|
||||
"Gift.View.Header.TakeOff" = "take off";
|
||||
"Gift.View.Header.Share" = "share";
|
||||
|
||||
"Gift.View.PutOn" = "You put on %@";
|
||||
"Gift.View.TookOff" = "You took off %@";
|
||||
|
||||
"Gift.View.TooltipPremiumWearing" = "Subscribe to [Telegram Premium]() to wear collectibles.";
|
||||
|
||||
"Conversation.AddToContactsLong" = "Add to Contacts";
|
||||
|
||||
"PeerInfo.PaneRecommendedBots" = "Similar Bots";
|
||||
@ -13696,3 +13704,42 @@ Sorry for the inconvenience.";
|
||||
|
||||
"ChatListFilter.NameEnableAnimations" = "Enable Animations";
|
||||
"ChatListFilter.NameDisableAnimations" = "Disable Animations";
|
||||
|
||||
"Chat.SendGiftTooltip" = "Tap here to send a gift";
|
||||
|
||||
"ChannelBoost.Table.WearGift" = "Wear Unique Collectibles";
|
||||
|
||||
"ChannelBoost.WearGift" = "Wear Item";
|
||||
"ChannelBoost.WearGiftLevelText" = "Your channel needs **Level %1$@** to wear collectibles.";
|
||||
|
||||
"PeerInfo.Gifts.SendGift" = "Send Gift";
|
||||
"PeerInfo.Gifts.ChannelNotify" = "Notify About New Gifts";
|
||||
"PeerInfo.Gifts.ChannelNotifyTooltip" = "You will receive a message from Telegram when your channel receives a gift.";
|
||||
|
||||
"Notification.StarsGift.Channel.Sent" = "%1$@ sent a gift to %2$@ for %3$@";
|
||||
|
||||
"Gift.View.Display.Channel" = "Display in Gifts";
|
||||
"Gift.View.DisplayedInfoHide.Channel" = "The item is visible in your channel's Gifts. [Hide >]()";
|
||||
"Gift.View.HiddenInfo.Channel" = "This item is hidden from visitors of your channel.";
|
||||
"Gift.View.HiddenInfoShow.Channel" = "This item is hidden from visitors of your channel. [Show >]()";
|
||||
|
||||
"Gift.View.KeepOrConvertDescription.Channel" = "Your channel can keep this gift in your Profile or convert it to %@. [More About Stars >]()";
|
||||
"Gift.View.KeepUpgradeOrConvertDescription.Channel" = "Your channel can keep this gift, upgrade it, or sell it for %@. [More About Stars >]()";
|
||||
|
||||
"MediaPicker.UseAnEmoji" = "Use an Emoji";
|
||||
"MediaPicker.SetNewPhoto" = "Set new profile photo";
|
||||
"MediaPicker.ChooseCover" = "Choose Cover";
|
||||
"MediaPicker.RemovePhoto" = "Remove Photo";
|
||||
|
||||
"EmojiInput.SectionTitleCollectibles" = "COLLECTIBLES";
|
||||
|
||||
"Gift.Options.GiftChannel.Title" = "Send a Gift";
|
||||
"Gift.Options.GiftChannel.Text" = "Select a gift to show appreciation for **%@**.";
|
||||
|
||||
"Gift.SendChannel.Title" = "Gift Preview";
|
||||
"Gift.SendChannel.HideMyName.Info" = "Hide my name and message from visitors of this channel. The channel admins will still see them.";
|
||||
|
||||
"Media.EditCover" = "Edit Cover";
|
||||
"Media.ChooseFromGallery" = "Choose From Gallery";
|
||||
"Media.SelectFrame" = "Select Frame";
|
||||
"Media.SaveCover" = "Save Cover";
|
||||
|
@ -121,6 +121,7 @@ public enum BoostSubject: Equatable {
|
||||
case audioTranscription
|
||||
case emojiPack
|
||||
case noAds
|
||||
case wearGift
|
||||
}
|
||||
|
||||
public enum StarsPurchasePurpose: Equatable {
|
||||
@ -157,6 +158,7 @@ public struct PremiumConfiguration {
|
||||
minChannelWallpaperLevel: 9,
|
||||
minChannelCustomWallpaperLevel: 10,
|
||||
minChannelRestrictAdsLevel: 50,
|
||||
minChannelWearGiftLevel: 8,
|
||||
minGroupProfileIconLevel: 7,
|
||||
minGroupEmojiStatusLevel: 8,
|
||||
minGroupWallpaperLevel: 9,
|
||||
@ -185,6 +187,7 @@ public struct PremiumConfiguration {
|
||||
public let minChannelWallpaperLevel: Int32
|
||||
public let minChannelCustomWallpaperLevel: Int32
|
||||
public let minChannelRestrictAdsLevel: Int32
|
||||
public let minChannelWearGiftLevel: Int32
|
||||
public let minGroupProfileIconLevel: Int32
|
||||
public let minGroupEmojiStatusLevel: Int32
|
||||
public let minGroupWallpaperLevel: Int32
|
||||
@ -212,6 +215,7 @@ public struct PremiumConfiguration {
|
||||
minChannelWallpaperLevel: Int32,
|
||||
minChannelCustomWallpaperLevel: Int32,
|
||||
minChannelRestrictAdsLevel: Int32,
|
||||
minChannelWearGiftLevel: Int32,
|
||||
minGroupProfileIconLevel: Int32,
|
||||
minGroupEmojiStatusLevel: Int32,
|
||||
minGroupWallpaperLevel: Int32,
|
||||
@ -238,6 +242,7 @@ public struct PremiumConfiguration {
|
||||
self.minChannelWallpaperLevel = minChannelWallpaperLevel
|
||||
self.minChannelCustomWallpaperLevel = minChannelCustomWallpaperLevel
|
||||
self.minChannelRestrictAdsLevel = minChannelRestrictAdsLevel
|
||||
self.minChannelWearGiftLevel = minChannelWearGiftLevel
|
||||
self.minGroupProfileIconLevel = minGroupProfileIconLevel
|
||||
self.minGroupEmojiStatusLevel = minGroupEmojiStatusLevel
|
||||
self.minGroupWallpaperLevel = minGroupWallpaperLevel
|
||||
@ -272,6 +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,
|
||||
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,
|
||||
|
@ -1875,9 +1875,8 @@ public final class ChatListNode: ListView {
|
||||
}))
|
||||
case .premiumGrace:
|
||||
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .gracePremium).startStandalone()
|
||||
// self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_BirthdayInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in
|
||||
// return true
|
||||
// }))
|
||||
case .setupPhoto:
|
||||
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .setupPhoto).startStandalone()
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -1993,12 +1992,7 @@ public final class ChatListNode: ListView {
|
||||
context.account.stateManager.contactBirthdays,
|
||||
starsSubscriptionsContextPromise.get()
|
||||
)
|
||||
|> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, data, birthdays, starsSubscriptionsContext -> Signal<ChatListNotice?, NoError> in
|
||||
#if DEBUG
|
||||
var suggestions = suggestions
|
||||
suggestions.insert(.setupPhoto, at: 0)
|
||||
#endif
|
||||
|
||||
|> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, data, birthdays, starsSubscriptionsContext -> Signal<ChatListNotice?, NoError> in
|
||||
let (accountPeer, birthday) = data
|
||||
|
||||
if let newSessionReview = newSessionReviews.first {
|
||||
|
@ -294,7 +294,7 @@ public class DrawingReactionEntityView: DrawingStickerEntityView {
|
||||
let context = self.context
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: presentationData.strings.Story_Editor_TooltipPremiumReaction, undoText: nil, customAction: nil), elevatedLayout: true, animateInAsReplacement: false, blurred: true, action: { [weak self] action in
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: presentationData.strings.Story_Editor_TooltipPremiumReaction, undoText: nil, customAction: nil), elevatedLayout: true, animateInAsReplacement: false, appearance: UndoOverlayController.Appearance(isBlurred: true), action: { [weak self] action in
|
||||
if case .info = action, let self {
|
||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .storiesExpirationDurations, forceDark: true, dismissed: nil)
|
||||
self.containerView?.push(controller)
|
||||
|
@ -464,7 +464,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
||||
storeAttributedTextInPasteboard(text)
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let undoController = UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, blurred: true, action: { _ in true })
|
||||
let undoController = UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, appearance: UndoOverlayController.Appearance(isBlurred: true), action: { _ in true })
|
||||
|
||||
self.controllerInteraction?.presentController(undoController, nil)
|
||||
case .share:
|
||||
|
@ -62,6 +62,10 @@
|
||||
- (void)setImage:(UIImage *)image thumbnailImage:(UIImage *)thumbnailImage forItem:(id<TGMediaEditableItem>)item synchronous:(bool)synchronous;
|
||||
- (void)setFullSizeImage:(UIImage *)image forItem:(id<TGMediaEditableItem>)item;
|
||||
|
||||
- (SSignal *)coverImageSignalForItem:(NSObject<TGMediaEditableItem> *)item;
|
||||
- (void)setCoverImage:(UIImage *)image forItem:(id<TGMediaEditableItem>)item;
|
||||
- (UIImage *)coverImageForItem:(NSObject<TGMediaEditableItem> *)item;
|
||||
|
||||
- (void)setTemporaryRep:(id)rep forItem:(id<TGMediaEditableItem>)item;
|
||||
|
||||
- (SSignal *)fullSizeImageUrlForItem:(id<TGMediaEditableItem>)item;
|
||||
|
@ -31,6 +31,9 @@
|
||||
- (void)prepareForEditing;
|
||||
- (void)returnFromEditing;
|
||||
|
||||
- (void)prepareForCoverEditing;
|
||||
- (void)returnFromCoverEditing;
|
||||
|
||||
- (UIImage *)screenImage;
|
||||
- (UIImage *)transitionImage;
|
||||
- (CGRect)editorTransitionViewRect;
|
||||
|
@ -120,6 +120,8 @@
|
||||
|
||||
@property (nonatomic, copy) id<TGCaptionPanelView> _Nullable(^ _Nullable captionPanelView)(void);
|
||||
|
||||
@property (nonatomic, copy) void (^ _Nullable editCover)(CGSize dimensions, void(^_Nonnull completion)(UIImage * _Nonnull));
|
||||
|
||||
|
||||
- (UIView<TGPhotoSolidRoundedButtonView> *_Nonnull)solidRoundedButton:(NSString *_Nonnull)title action:(void(^_Nonnull)(void))action;
|
||||
- (id<TGPhotoDrawingAdapter> _Nonnull)drawingAdapter:(CGSize)size originalSize:(CGSize)originalSize isVideo:(bool)isVideo isAvatar:(bool)isAvatar entitiesView:(UIView<TGPhotoDrawingEntitiesView> * _Nullable)entitiesView;
|
||||
|
@ -1331,6 +1331,8 @@
|
||||
CGSize dimensions = [TGMediaVideoConverter dimensionsFor:asset.originalSize adjustments:adjustments preset:preset];
|
||||
NSTimeInterval duration = adjustments.trimApplied ? (adjustments.trimEndValue - adjustments.trimStartValue) : asset.videoDuration;
|
||||
|
||||
UIImage *coverImage = [editingContext coverImageForItem:asset];
|
||||
|
||||
[signals addObject:[thumbnailSignal map:^id(UIImage *image)
|
||||
{
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
@ -1341,6 +1343,7 @@
|
||||
dict[@"adjustments"] = adjustments;
|
||||
dict[@"dimensions"] = [NSValue valueWithCGSize:dimensions];
|
||||
dict[@"duration"] = @(duration);
|
||||
dict[@"coverImage"] = coverImage;
|
||||
|
||||
if (adjustments.paintingData.stickers.count > 0)
|
||||
dict[@"stickers"] = adjustments.paintingData.stickers;
|
||||
|
@ -106,6 +106,8 @@
|
||||
TGMemoryImageCache *_originalImageCache;
|
||||
TGMemoryImageCache *_originalThumbnailImageCache;
|
||||
|
||||
TGMemoryImageCache *_coverImageCache;
|
||||
|
||||
TGModernCache *_diskCache;
|
||||
NSURL *_fullSizeResultsUrl;
|
||||
NSURL *_paintingDatasUrl;
|
||||
@ -119,6 +121,7 @@
|
||||
|
||||
SPipe *_representationPipe;
|
||||
SPipe *_thumbnailImagePipe;
|
||||
SPipe *_coverImagePipe;
|
||||
SPipe *_adjustmentsPipe;
|
||||
SPipe *_captionPipe;
|
||||
SPipe *_timerPipe;
|
||||
@ -166,6 +169,9 @@
|
||||
_originalThumbnailImageCache = [[TGMemoryImageCache alloc] initWithSoftMemoryLimit:[[self class] thumbnailImageSoftMemoryLimit]
|
||||
hardMemoryLimit:[[self class] thumbnailImageHardMemoryLimit]];
|
||||
|
||||
_coverImageCache = [[TGMemoryImageCache alloc] initWithSoftMemoryLimit:[[self class] thumbnailImageSoftMemoryLimit] * 10
|
||||
hardMemoryLimit:[[self class] thumbnailImageHardMemoryLimit] * 10];
|
||||
|
||||
NSString *diskCachePath = [[[LegacyComponentsGlobals provider] dataStoragePath] stringByAppendingPathComponent:[[self class] diskCachePath]];
|
||||
_diskCache = [[TGModernCache alloc] initWithPath:diskCachePath size:[[self class] diskMemoryLimit]];
|
||||
|
||||
@ -194,6 +200,7 @@
|
||||
_thumbnailImagePipe = [[SPipe alloc] init];
|
||||
_adjustmentsPipe = [[SPipe alloc] init];
|
||||
_captionPipe = [[SPipe alloc] init];
|
||||
_coverImagePipe = [[SPipe alloc] init];
|
||||
_timerPipe = [[SPipe alloc] init];
|
||||
_spoilerPipe = [[SPipe alloc] init];
|
||||
_pricePipe = [[SPipe alloc] init];
|
||||
@ -908,6 +915,46 @@
|
||||
[_faces removeObjectForKey:itemId];
|
||||
}
|
||||
|
||||
|
||||
- (SSignal *)coverImageSignalForIdentifier:(NSString *)identifier
|
||||
{
|
||||
NSString *itemId = [TGMediaEditingContext _coverImageUriForItemId:identifier];
|
||||
if (itemId == nil)
|
||||
return [SSignal fail:nil];
|
||||
|
||||
SSignal *updateSignal = [[_coverImagePipe.signalProducer() filter:^bool(TGMediaImageUpdate *update)
|
||||
{
|
||||
return [update.item.uniqueIdentifier isEqualToString:identifier];
|
||||
}] map:^id(TGMediaImageUpdate *update)
|
||||
{
|
||||
return update.representation;
|
||||
}];
|
||||
|
||||
return [[SSignal single:[_coverImageCache imageForKey:itemId attributes:NULL]]
|
||||
then:updateSignal];
|
||||
}
|
||||
|
||||
- (SSignal *)coverImageSignalForItem:(NSObject<TGMediaEditableItem> *)item {
|
||||
return [self coverImageSignalForIdentifier:item.uniqueIdentifier];
|
||||
}
|
||||
|
||||
- (UIImage *)coverImageForItem:(NSObject<TGMediaEditableItem> *)item {
|
||||
NSString *itemId = [TGMediaEditingContext _coverImageUriForItemId:item.uniqueIdentifier];
|
||||
if (itemId == nil)
|
||||
return nil;
|
||||
return [_coverImageCache imageForKey:itemId attributes:NULL];
|
||||
}
|
||||
|
||||
- (void)setCoverImage:(UIImage *)image forItem:(id<TGMediaEditableItem>)item
|
||||
{
|
||||
NSString *itemId = [TGMediaEditingContext _coverImageUriForItemId:item.uniqueIdentifier];
|
||||
if (itemId == nil)
|
||||
return;
|
||||
|
||||
[_coverImageCache setImage:image forKey:itemId attributes:NULL];
|
||||
_coverImagePipe.sink([TGMediaImageUpdate imageUpdateWithItem:item representation:image]);
|
||||
}
|
||||
|
||||
- (void)setFullSizeImage:(UIImage *)image forItem:(id<TGMediaEditableItem>)item
|
||||
{
|
||||
NSString *itemId = [self _contextualIdForItemId:item.uniqueIdentifier];
|
||||
@ -1167,6 +1214,11 @@
|
||||
return [NSString stringWithFormat:@"%@://%@", [self thumbnailImageUriScheme], itemId];
|
||||
}
|
||||
|
||||
+ (NSString *)_coverImageUriForItemId:(NSString *)itemId
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@://%@", @"photo-editor-cover", itemId];
|
||||
}
|
||||
|
||||
#pragma mark - Constants
|
||||
|
||||
+ (NSString *)imageUriScheme
|
||||
|
@ -92,10 +92,17 @@
|
||||
TGMediaPickerGroupButton *_groupButton;
|
||||
TGMediaPickerCameraButton *_cameraButton;
|
||||
|
||||
TGMediaPickerCoverButton *_coverButton;
|
||||
TGModernButton *_cancelCoverButton;
|
||||
TGModernButton *_saveCoverButton;
|
||||
TGMediaPickerCoverButton *_coverGalleryButton;
|
||||
UILabel *_coverTitleLabel;
|
||||
|
||||
TGMediaPickerPhotoStripView *_selectedPhotosView;
|
||||
|
||||
SMetaDisposable *_adjustmentsDisposable;
|
||||
SMetaDisposable *_captionDisposable;
|
||||
SMetaDisposable *_coverDisposable;
|
||||
SMetaDisposable *_itemAvailabilityDisposable;
|
||||
SMetaDisposable *_itemSelectedDisposable;
|
||||
id<SDisposable> _selectionChangedDisposable;
|
||||
@ -136,6 +143,7 @@
|
||||
|
||||
_adjustmentsDisposable = [[SMetaDisposable alloc] init];
|
||||
_captionDisposable = [[SMetaDisposable alloc] init];
|
||||
_coverDisposable = [[SMetaDisposable alloc] init];
|
||||
_itemSelectedDisposable = [[SMetaDisposable alloc] init];
|
||||
_itemAvailabilityDisposable = [[SMetaDisposable alloc] init];
|
||||
_tooltipDismissDisposable = [[SMetaDisposable alloc] init];
|
||||
@ -200,7 +208,7 @@
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@(3) forKey:@"TG_displayedMediaTimerTooltip_v3"];
|
||||
};
|
||||
|
||||
_muteButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 39.0f, 39.0f)];
|
||||
_muteButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 40.0f, 40.0f)];
|
||||
_muteButton.hidden = true;
|
||||
_muteButton.adjustsImageWhenHighlighted = false;
|
||||
[_muteButton setBackgroundImage:[TGPhotoEditorInterfaceAssets gifBackgroundImage] forState:UIControlStateNormal];
|
||||
@ -239,13 +247,23 @@
|
||||
// [_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 (_selectionContext != nil)
|
||||
{
|
||||
_checkButton = [[TGCheckButtonView alloc] initWithStyle:TGCheckButtonStyleGallery];
|
||||
_checkButton.frame = CGRectMake(self.frame.size.width - 53, 11, _checkButton.frame.size.width, _checkButton.frame.size.height);
|
||||
[_checkButton addTarget:self action:@selector(checkButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_wrapperView addSubview:_checkButton];
|
||||
|
||||
|
||||
if (hasSelectionPanel)
|
||||
{
|
||||
_selectedPhotosView = [[TGMediaPickerPhotoStripView alloc] initWithFrame:CGRectZero];
|
||||
@ -263,14 +281,14 @@
|
||||
_selectedPhotosView.hidden = true;
|
||||
[_wrapperView addSubview:_selectedPhotosView];
|
||||
}
|
||||
|
||||
|
||||
_photoCounterButton = [[TGMediaPickerPhotoCounterButton alloc] initWithFrame:CGRectMake(0, 0, 64, 38)];
|
||||
[_photoCounterButton addTarget:self action:@selector(photoCounterButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
_photoCounterButton.userInteractionEnabled = false;
|
||||
[_wrapperView addSubview:_photoCounterButton];
|
||||
|
||||
_selectionChangedDisposable = [[_selectionContext selectionChangedSignal] startStrictWithNext:^(id next)
|
||||
{
|
||||
{
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
@ -290,7 +308,7 @@
|
||||
if (_editingContext != nil)
|
||||
{
|
||||
_timersChangedDisposable = [_editingContext.timersUpdatedSignal startStrictWithNext:^(__unused NSNumber *next)
|
||||
{
|
||||
{
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
@ -299,7 +317,7 @@
|
||||
} file:__FILE_NAME__ line:__LINE__];
|
||||
|
||||
_adjustmentsChangedDisposable = [_editingContext.adjustmentsUpdatedSignal startStrictWithNext:^(__unused NSNumber *next)
|
||||
{
|
||||
{
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
@ -377,7 +395,7 @@
|
||||
offset = -keyboardHeight / 2.0f;
|
||||
|
||||
[UIView animateWithDuration:duration delay:0.0f options:animationCurve animations:^
|
||||
{
|
||||
{
|
||||
if (strongSelf->_scrollViewOffsetRequested != nil)
|
||||
strongSelf->_scrollViewOffsetRequested(offset);
|
||||
} completion:nil];
|
||||
@ -423,6 +441,34 @@
|
||||
|
||||
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];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -430,9 +476,10 @@
|
||||
- (void)dealloc
|
||||
{
|
||||
[_actionHandle reset];
|
||||
|
||||
|
||||
[_adjustmentsDisposable dispose];
|
||||
[_captionDisposable dispose];
|
||||
[_coverDisposable dispose];
|
||||
[_itemSelectedDisposable dispose];
|
||||
[_itemAvailabilityDisposable dispose];
|
||||
[_selectionChangedDisposable dispose];
|
||||
@ -472,7 +519,7 @@
|
||||
|
||||
bool groupingButtonVisible = _groupButton != nil && onlyGroupableMedia && _selectionContext.count > 1;
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
{
|
||||
[_groupButton setInternalHidden:!groupingButtonVisible animated:true];
|
||||
});
|
||||
|
||||
@ -529,7 +576,7 @@
|
||||
{
|
||||
if (self.timerRequested != nil)
|
||||
self.timerRequested();
|
||||
|
||||
|
||||
if (!TGIsPad())
|
||||
[self setAllInterfaceHidden:true delay:0.0f animated:true];
|
||||
}
|
||||
@ -566,9 +613,9 @@
|
||||
[_currentItemView setSafeAreaInset:[self localSafeAreaInset]];
|
||||
|
||||
UIEdgeInsets screenEdges = [self screenEdges];
|
||||
|
||||
|
||||
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
|
||||
|
||||
|
||||
[self _layoutRecipientLabelForOrientation:[self interfaceOrientation] screenEdges:screenEdges hasHeaderView:(itemView.headerView != nil)];
|
||||
|
||||
if (_selectionContext != nil)
|
||||
@ -587,7 +634,7 @@
|
||||
[_checkButton setNumber:[_selectionContext indexOfItem:selectableItem]];
|
||||
signal = [_selectionContext itemInformativeSelectedSignal:selectableItem];
|
||||
[_itemSelectedDisposable setDisposable:[signal startStrictWithNext:^(TGMediaSelectionChange *next)
|
||||
{
|
||||
{
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
@ -601,18 +648,18 @@
|
||||
|
||||
__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)
|
||||
return;
|
||||
|
||||
|
||||
bool available = [next boolValue];
|
||||
|
||||
NSString *itemId = nil;
|
||||
if ([strongItemView.item respondsToSelector:@selector(uniqueId)])
|
||||
itemId = [itemView.item performSelector:@selector(uniqueId)];
|
||||
|
||||
|
||||
NSString *currentId = nil;
|
||||
if ([strongSelf->_currentItem respondsToSelector:@selector(uniqueId)])
|
||||
currentId = [strongSelf->_currentItem performSelector:@selector(uniqueId)];
|
||||
@ -630,6 +677,9 @@
|
||||
}
|
||||
}
|
||||
strongSelf->_muteButton.hidden = !sendableAsGif;
|
||||
|
||||
bool canHaveCover = [strongItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]];
|
||||
strongSelf->_coverButton.hidden = !canHaveCover;
|
||||
}
|
||||
} file:__FILE_NAME__ line:__LINE__]];
|
||||
|
||||
@ -682,28 +732,28 @@
|
||||
|
||||
NSArray *items = @
|
||||
[
|
||||
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Camera.Discard") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
||||
{
|
||||
__strong TGMenuSheetController *strongController = weakController;
|
||||
if (strongController == nil)
|
||||
return;
|
||||
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_capturing = false;
|
||||
strongSelf->_closePressed();
|
||||
|
||||
[strongController dismissAnimated:true manual:false completion:nil];
|
||||
}],
|
||||
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel fontSize:20.0 action:^
|
||||
{
|
||||
__strong TGMenuSheetController *strongController = weakController;
|
||||
if (strongController != nil)
|
||||
[strongController dismissAnimated:true];
|
||||
}]
|
||||
];
|
||||
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Camera.Discard") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
||||
{
|
||||
__strong TGMenuSheetController *strongController = weakController;
|
||||
if (strongController == nil)
|
||||
return;
|
||||
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_capturing = false;
|
||||
strongSelf->_closePressed();
|
||||
|
||||
[strongController dismissAnimated:true manual:false completion:nil];
|
||||
}],
|
||||
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel fontSize:20.0 action:^
|
||||
{
|
||||
__strong TGMenuSheetController *strongController = weakController;
|
||||
if (strongController != nil)
|
||||
[strongController dismissAnimated:true];
|
||||
}]
|
||||
];
|
||||
|
||||
[controller setItemViews:items];
|
||||
controller.sourceRect = ^
|
||||
@ -740,7 +790,7 @@
|
||||
bool animated = false;
|
||||
if (!_selectedPhotosView.isAnimating)
|
||||
animated = true;
|
||||
|
||||
|
||||
id<TGMediaSelectableItem>selectableItem = nil;
|
||||
if ([_currentItem conformsToProtocol:@protocol(TGModernGallerySelectableItem)])
|
||||
selectableItem = ((id<TGModernGallerySelectableItem>)_currentItem).selectableMediaItem;
|
||||
@ -777,6 +827,119 @@
|
||||
[self updateGroupingButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)coverButtonPressed
|
||||
{
|
||||
[self coverEditorTransitionIn];
|
||||
}
|
||||
|
||||
- (void)coverEditorTransitionIn {
|
||||
[self setAllInterfaceHidden:true keepHeader:true delay:0.0 animated:true];
|
||||
|
||||
_coverTitleLabel.hidden = false;
|
||||
_cancelCoverButton.hidden = false;
|
||||
_saveCoverButton.hidden = false;
|
||||
_coverGalleryButton.hidden = false;
|
||||
|
||||
_coverTitleLabel.alpha = 0.0;
|
||||
_cancelCoverButton.alpha = 0.0;
|
||||
_saveCoverButton.alpha = 0.0;
|
||||
_coverGalleryButton.alpha = 0.0;
|
||||
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
_coverTitleLabel.alpha = 1.0;
|
||||
_cancelCoverButton.alpha = 1.0;
|
||||
_saveCoverButton.alpha = 1.0;
|
||||
_coverGalleryButton.alpha = 1.0;
|
||||
|
||||
for (UIView *headerView in _itemHeaderViews) {
|
||||
if ([headerView isKindOfClass:[TGMediaPickerScrubberHeaderView class]]) {
|
||||
((TGMediaPickerScrubberHeaderView *)headerView).scrubberView.alpha = 0.0f;
|
||||
((TGMediaPickerScrubberHeaderView *)headerView).coverScrubberView.alpha = 1.0f;
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
TGModernGalleryItemView *currentItemView = _currentItemView;
|
||||
if ([currentItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]]) {
|
||||
[(TGMediaPickerGalleryVideoItemView *)currentItemView prepareForCoverEditing];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)coverEditorTransitionOut {
|
||||
[self setAllInterfaceHidden:false keepHeader:true delay:0.0 animated:true];
|
||||
|
||||
[_cancelCoverButton.layer removeAllAnimations];
|
||||
[_saveCoverButton.layer removeAllAnimations];
|
||||
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
_coverTitleLabel.alpha = 0.0;
|
||||
_cancelCoverButton.alpha = 0.0;
|
||||
_saveCoverButton.alpha = 0.0;
|
||||
_coverGalleryButton.alpha = 0.0;
|
||||
|
||||
for (UIView *headerView in _itemHeaderViews) {
|
||||
if ([headerView isKindOfClass:[TGMediaPickerScrubberHeaderView class]]) {
|
||||
((TGMediaPickerScrubberHeaderView *)headerView).scrubberView.alpha = 1.0f;
|
||||
((TGMediaPickerScrubberHeaderView *)headerView).coverScrubberView.alpha = 0.0f;
|
||||
}
|
||||
}
|
||||
} completion:^(BOOL finished) {
|
||||
_coverTitleLabel.hidden = true;
|
||||
_cancelCoverButton.hidden = true;
|
||||
_saveCoverButton.hidden = true;
|
||||
_coverGalleryButton.hidden = true;
|
||||
}];
|
||||
|
||||
TGModernGalleryItemView *currentItemView = _currentItemView;
|
||||
if ([currentItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]]) {
|
||||
[(TGMediaPickerGalleryVideoItemView *)currentItemView returnFromCoverEditing];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancelCoverButtonPressed
|
||||
{
|
||||
TGDispatchAfter(0.01, dispatch_get_main_queue(), ^{
|
||||
[self coverEditorTransitionOut];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)saveCoverButtonPressed
|
||||
{
|
||||
id<TGModernGalleryEditableItem> galleryEditableItem = (id<TGModernGalleryEditableItem>)_currentItem;
|
||||
TGModernGalleryItemView *currentItemView = _currentItemView;
|
||||
if ([currentItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]]) {
|
||||
id<TGMediaEditableItem> editableMediaItem = [galleryEditableItem editableMediaItem];
|
||||
[_editingContext setCoverImage:[(TGMediaPickerGalleryVideoItemView *)currentItemView screenImage] forItem:editableMediaItem];
|
||||
}
|
||||
|
||||
TGDispatchAfter(0.01, dispatch_get_main_queue(), ^{
|
||||
[self coverEditorTransitionOut];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)coverGalleryButtonPressed {
|
||||
if (_currentItem == nil)
|
||||
return;
|
||||
|
||||
id<TGModernGalleryEditableItem> galleryEditableItem = (id<TGModernGalleryEditableItem>)_currentItem;
|
||||
if ([_currentItem conformsToProtocol:@protocol(TGModernGalleryEditableItem)])
|
||||
{
|
||||
id<TGMediaEditableItem> editableMediaItem = [galleryEditableItem editableMediaItem];
|
||||
CGSize originalSize = CGSizeZero;
|
||||
if ([editableMediaItem respondsToSelector:@selector(originalSize)])
|
||||
originalSize = editableMediaItem.originalSize;
|
||||
|
||||
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
|
||||
_captionMixin.stickersContext.editCover(originalSize, ^(UIImage *cover){
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
[strongSelf->_editingContext setCoverImage:cover forItem:editableMediaItem];
|
||||
[strongSelf coverEditorTransitionOut];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateEditorButtonsForItem:(id<TGModernGalleryItem>)item animated:(bool)animated
|
||||
{
|
||||
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
|
||||
@ -791,6 +954,14 @@
|
||||
return;
|
||||
[strongSelf->_captionMixin setCaption:caption animated:animated];
|
||||
} file:__FILE_NAME__ line:__LINE__]];
|
||||
|
||||
[_coverDisposable setDisposable:[[galleryEditableItem.editingContext coverImageSignalForItem:editableMediaItem] startStrictWithNext:^(UIImage *cover)
|
||||
{
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
[strongSelf->_coverButton setImage:cover];
|
||||
} file:__FILE_NAME__ line:__LINE__]];
|
||||
}
|
||||
|
||||
if (_editingContext == nil || _editingContext.inhibitEditing)
|
||||
@ -938,7 +1109,6 @@
|
||||
qualityButton.iconImage = icon;
|
||||
}
|
||||
|
||||
bool willShowTimerTooltip = false;
|
||||
TGPhotoEditorButton *timerButton = [_portraitToolbarView buttonForTab:TGPhotoEditorTimerTab];
|
||||
if (timerButton != nil)
|
||||
{
|
||||
@ -959,7 +1129,6 @@
|
||||
|
||||
if ([self shouldDisplayTooltip])
|
||||
{
|
||||
willShowTimerTooltip = true;
|
||||
TGDispatchAfter(0.5, dispatch_get_main_queue(), ^
|
||||
{
|
||||
if (!TGIsPad() && self.frame.size.width > self.frame.size.height)
|
||||
@ -1098,6 +1267,7 @@
|
||||
{
|
||||
_checkButton.alpha = alpha;
|
||||
_muteButton.alpha = alpha;
|
||||
_coverButton.alpha = alpha;
|
||||
_arrowView.alpha = alpha * 0.6f;
|
||||
_recipientLabel.alpha = alpha * 0.6;
|
||||
} completion:^(BOOL finished)
|
||||
@ -1106,6 +1276,7 @@
|
||||
{
|
||||
_checkButton.userInteractionEnabled = !hidden;
|
||||
_muteButton.userInteractionEnabled = !hidden;
|
||||
_coverButton.userInteractionEnabled = !hidden;
|
||||
}
|
||||
}];
|
||||
|
||||
@ -1129,6 +1300,9 @@
|
||||
_muteButton.alpha = alpha;
|
||||
_muteButton.userInteractionEnabled = !hidden;
|
||||
|
||||
_coverButton.alpha = alpha;
|
||||
_coverButton.userInteractionEnabled = !hidden;
|
||||
|
||||
_arrowView.alpha = alpha * 0.6f;
|
||||
_recipientLabel.alpha = alpha * 0.6;
|
||||
}
|
||||
@ -1145,7 +1319,11 @@
|
||||
[_groupButton setHidden:true animated:animated];
|
||||
}
|
||||
|
||||
- (void)setAllInterfaceHidden:(bool)hidden delay:(NSTimeInterval)__unused delay animated:(bool)animated
|
||||
- (void)setAllInterfaceHidden:(bool)hidden delay:(NSTimeInterval)delay animated:(bool)animated {
|
||||
[self setAllInterfaceHidden:hidden keepHeader:false delay:delay animated:animated];
|
||||
}
|
||||
|
||||
- (void)setAllInterfaceHidden:(bool)hidden keepHeader:(bool)keepHeader delay:(NSTimeInterval)__unused delay animated:(bool)animated
|
||||
{
|
||||
CGFloat alpha = (hidden ? 0.0f : 1.0f);
|
||||
if (animated)
|
||||
@ -1154,8 +1332,9 @@
|
||||
{
|
||||
_checkButton.alpha = alpha;
|
||||
_muteButton.alpha = alpha;
|
||||
_coverButton.alpha = alpha;
|
||||
_arrowView.alpha = alpha * 0.6;
|
||||
_recipientLabel.alpha = alpha;
|
||||
_recipientLabel.alpha = alpha * 0.6;
|
||||
_portraitToolbarView.alpha = alpha;
|
||||
_landscapeToolbarView.alpha = alpha;
|
||||
_captionMixin.inputPanelView.alpha = alpha;
|
||||
@ -1166,6 +1345,7 @@
|
||||
{
|
||||
_checkButton.userInteractionEnabled = !hidden;
|
||||
_muteButton.userInteractionEnabled = !hidden;
|
||||
_coverButton.userInteractionEnabled = !hidden;
|
||||
_portraitToolbarView.userInteractionEnabled = !hidden;
|
||||
_landscapeToolbarView.userInteractionEnabled = !hidden;
|
||||
_captionMixin.inputPanelView.userInteractionEnabled = !hidden;
|
||||
@ -1193,6 +1373,9 @@
|
||||
_muteButton.alpha = alpha;
|
||||
_muteButton.userInteractionEnabled = !hidden;
|
||||
|
||||
_coverButton.alpha = alpha;
|
||||
_coverButton.userInteractionEnabled = !hidden;
|
||||
|
||||
_arrowView.alpha = alpha * 0.6;
|
||||
_recipientLabel.alpha = alpha;
|
||||
|
||||
@ -1219,7 +1402,7 @@
|
||||
if (!_groupButton.hidden)
|
||||
[_groupButton setHidden:true animated:animated];
|
||||
|
||||
[self setItemHeaderViewHidden:hidden animated:animated];
|
||||
[self setItemHeaderViewHidden:!keepHeader && hidden animated:animated];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@ -1431,6 +1614,10 @@
|
||||
|| view == _muteButton
|
||||
|| view == _groupButton
|
||||
|| view == _cameraButton
|
||||
|| view == _coverButton
|
||||
|| view == _cancelCoverButton
|
||||
|| view == _saveCoverButton
|
||||
|| view == _coverGalleryButton
|
||||
|| [view isDescendantOfView:_headerWrapperView]
|
||||
|| [view isDescendantOfView:_portraitToolbarView]
|
||||
|| [view isDescendantOfView:_landscapeToolbarView]
|
||||
@ -1492,7 +1679,7 @@
|
||||
break;
|
||||
|
||||
default:
|
||||
frame = CGRectMake(screenEdges.left + 5, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 45 - _safeAreaInset.bottom - panelInset - (hasHeaderView ? 64.0 : 0.0), _muteButton.frame.size.width, _muteButton.frame.size.height);
|
||||
frame = CGRectMake(screenEdges.left + 5, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 26 - _safeAreaInset.bottom - panelInset - (hasHeaderView ? 64.0 : 0.0), _muteButton.frame.size.width, _muteButton.frame.size.height);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1526,6 +1713,34 @@
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (CGRect)_coverButtonFrameForOrientation:(UIInterfaceOrientation)orientation screenEdges:(UIEdgeInsets)screenEdges hasHeaderView:(bool)hasHeaderView
|
||||
{
|
||||
CGRect frame = CGRectZero;
|
||||
if (_safeAreaInset.top > 20.0f)
|
||||
screenEdges.top += _safeAreaInset.top;
|
||||
screenEdges.left += _safeAreaInset.left;
|
||||
screenEdges.right -= _safeAreaInset.right;
|
||||
screenEdges.bottom -= _safeAreaInset.bottom;
|
||||
|
||||
CGFloat panelInset = 0.0f;
|
||||
frame = CGRectMake(screenEdges.left + ((screenEdges.right - screenEdges.left) - _coverButton.frame.size.width) / 2.0, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 49 - _safeAreaInset.bottom - panelInset, _coverButton.frame.size.width, _coverButton.frame.size.height);
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (CGRect)_coverGalleryButtonFrameForOrientation:(UIInterfaceOrientation)orientation screenEdges:(UIEdgeInsets)screenEdges hasHeaderView:(bool)hasHeaderView
|
||||
{
|
||||
CGRect frame = CGRectZero;
|
||||
if (_safeAreaInset.top > 20.0f)
|
||||
screenEdges.top += _safeAreaInset.top;
|
||||
screenEdges.left += _safeAreaInset.left;
|
||||
screenEdges.right -= _safeAreaInset.right;
|
||||
screenEdges.bottom -= _safeAreaInset.bottom;
|
||||
|
||||
CGFloat panelInset = 0.0f;
|
||||
frame = CGRectMake(screenEdges.left + ((screenEdges.right - screenEdges.left) - _coverButton.frame.size.width) / 2.0, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 33 - _safeAreaInset.bottom - panelInset, _coverButton.frame.size.width, _coverButton.frame.size.height);
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (CGRect)_checkButtonFrameForOrientation:(UIInterfaceOrientation)orientation screenEdges:(UIEdgeInsets)screenEdges hasHeaderView:(bool)hasHeaderView
|
||||
{
|
||||
CGRect frame = CGRectZero;
|
||||
@ -1712,6 +1927,14 @@
|
||||
portraitToolbarViewBottomEdge = screenEdges.bottom;
|
||||
_portraitToolbarView.frame = CGRectMake(screenEdges.left, portraitToolbarViewBottomEdge - TGPhotoEditorToolbarSize - _safeAreaInset.bottom, self.frame.size.width, TGPhotoEditorToolbarSize + _safeAreaInset.bottom);
|
||||
|
||||
CGFloat coverTitleTopY = screenEdges.top;
|
||||
if (_safeAreaInset.top > 20.0f + FLT_EPSILON)
|
||||
coverTitleTopY += _safeAreaInset.top;
|
||||
|
||||
_saveCoverButton.frame = CGRectMake(screenEdges.left + 16.0, portraitToolbarViewBottomEdge - 50.0 - 16.0 - _safeAreaInset.bottom, self.frame.size.width - 16.0 * 2.0, 50.0);
|
||||
_cancelCoverButton.frame = CGRectMake(screenEdges.left + 16.0, coverTitleTopY + 20, _cancelCoverButton.frame.size.width, _cancelCoverButton.frame.size.height);
|
||||
_coverTitleLabel.frame = CGRectMake(screenEdges.left + floor((self.frame.size.width - _coverTitleLabel.frame.size.width) / 2.0), coverTitleTopY + 26, _coverTitleLabel.frame.size.width, _coverTitleLabel.frame.size.height);
|
||||
|
||||
UIEdgeInsets captionEdgeInsets = screenEdges;
|
||||
captionEdgeInsets.bottom = _portraitToolbarView.frame.size.height;
|
||||
[_captionMixin updateLayoutWithFrame:self.bounds edgeInsets:captionEdgeInsets animated:false];
|
||||
@ -1752,9 +1975,9 @@
|
||||
{
|
||||
[UIView performWithoutAnimation:^
|
||||
{
|
||||
_photoCounterButton.frame = CGRectMake(screenEdges.right - 56 - _safeAreaInset.right, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 40 - _safeAreaInset.bottom - (hasHeaderView ? 64.0 : 0.0), 64, 38);
|
||||
_photoCounterButton.frame = CGRectMake(screenEdges.right - 56 - _safeAreaInset.right, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 22 - _safeAreaInset.bottom - (hasHeaderView ? 64.0 : 0.0), 64, 38);
|
||||
|
||||
_selectedPhotosView.frame = CGRectMake(screenEdges.left + 4, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - photosViewSize - 54 - _safeAreaInset.bottom - (hasHeaderView ? 64.0 : 0.0), self.frame.size.width - 4 * 2 - _safeAreaInset.right, photosViewSize);
|
||||
_selectedPhotosView.frame = CGRectMake(screenEdges.left + 4, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - photosViewSize - 36 - _safeAreaInset.bottom - (hasHeaderView ? 64.0 : 0.0), self.frame.size.width - 4 * 2 - _safeAreaInset.right, photosViewSize);
|
||||
}];
|
||||
|
||||
_landscapeToolbarView.frame = CGRectMake(_landscapeToolbarView.frame.origin.x, screenEdges.top, TGPhotoEditorToolbarSize, self.frame.size.height);
|
||||
@ -1767,6 +1990,9 @@
|
||||
_muteButton.frame = [self _muteButtonFrameForOrientation:orientation screenEdges:screenEdges hasHeaderView:true];
|
||||
_checkButton.frame = [self _checkButtonFrameForOrientation:orientation screenEdges:screenEdges hasHeaderView:hasHeaderView];
|
||||
_groupButton.frame = [self _groupButtonFrameForOrientation:orientation screenEdges:screenEdges hasHeaderView:hasHeaderView];
|
||||
_coverButton.frame = [self _coverButtonFrameForOrientation:orientation screenEdges:screenEdges hasHeaderView:hasHeaderView];
|
||||
_coverGalleryButton.frame = [self _coverGalleryButtonFrameForOrientation:orientation screenEdges:screenEdges hasHeaderView:hasHeaderView];
|
||||
|
||||
[UIView performWithoutAnimation:^
|
||||
{
|
||||
_cameraButton.frame = [self _cameraButtonFrameForOrientation:orientation screenEdges:screenEdges hasHeaderView:hasHeaderView panelVisible:_selectedPhotosView != nil && !_selectedPhotosView.isInternalHidden];
|
||||
|
@ -67,6 +67,9 @@
|
||||
UIView *_headerView;
|
||||
UIView *_scrubberPanelView;
|
||||
TGMediaPickerGalleryVideoScrubber *_scrubberView;
|
||||
|
||||
TGMediaPickerGalleryVideoScrubber *_coverScrubberView;
|
||||
|
||||
bool _wasPlayingBeforeScrubbing;
|
||||
bool _appeared;
|
||||
bool _scrubbingPanelPresented;
|
||||
@ -225,13 +228,21 @@
|
||||
//scrubberBackgroundView.backgroundColor = [TGPhotoEditorInterfaceAssets toolbarTransparentBackgroundColor];
|
||||
[_scrubberPanelView addSubview:scrubberBackgroundView];
|
||||
|
||||
_scrubberView = [[TGMediaPickerGalleryVideoScrubber alloc] initWithFrame:CGRectMake(0.0f, _headerView.frame.size.height - 44.0f, _headerView.frame.size.width, 68.0f)];
|
||||
_scrubberView = [[TGMediaPickerGalleryVideoScrubber alloc] initWithFrame:CGRectMake(0.0f, _headerView.frame.size.height - 44.0f, _headerView.frame.size.width, 68.0f) cover:false];
|
||||
_scrubberView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
_scrubberView.dataSource = self;
|
||||
_scrubberView.delegate = self;
|
||||
headerView.scrubberView = _scrubberView;
|
||||
[_scrubberPanelView addSubview:_scrubberView];
|
||||
|
||||
_coverScrubberView = [[TGMediaPickerGalleryVideoScrubber alloc] initWithFrame:CGRectMake(0.0f, _headerView.frame.size.height - 44.0f, _headerView.frame.size.width, 68.0f) cover:true];
|
||||
_coverScrubberView.alpha = 0.0;
|
||||
_coverScrubberView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
_coverScrubberView.dataSource = self;
|
||||
_coverScrubberView.delegate = self;
|
||||
headerView.coverScrubberView = _coverScrubberView;
|
||||
[_scrubberPanelView addSubview:_coverScrubberView];
|
||||
|
||||
_fileInfoLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 10.0f, _scrubberPanelView.frame.size.width, 21)];
|
||||
_fileInfoLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
_fileInfoLabel.backgroundColor = [UIColor clearColor];
|
||||
@ -420,6 +431,7 @@
|
||||
}
|
||||
|
||||
_scrubberView.allowsTrimming = false;
|
||||
_coverScrubberView.allowsTrimming = false;
|
||||
_videoDimensions = item.dimensions;
|
||||
|
||||
if (_entitiesView == nil) {
|
||||
@ -596,6 +608,12 @@
|
||||
strongSelf->_scrubberView.trimStartValue = adjustments.trimStartValue;
|
||||
strongSelf->_scrubberView.trimEndValue = adjustments.trimEndValue;
|
||||
strongSelf->_scrubberView.value = adjustments.trimStartValue;
|
||||
|
||||
strongSelf->_coverScrubberView.trimStartValue = adjustments.trimStartValue;
|
||||
strongSelf->_coverScrubberView.trimEndValue = adjustments.trimEndValue;
|
||||
strongSelf->_coverScrubberView.value = adjustments.trimStartValue;
|
||||
[strongSelf->_coverScrubberView _layoutTrimCurtainViews];
|
||||
|
||||
[strongSelf->_scrubberView setTrimApplied:(adjustments.trimStartValue > 0 || adjustments.trimEndValue < videoDuration)];
|
||||
strongSelf->_shouldResetScrubber = false;
|
||||
}
|
||||
@ -603,14 +621,21 @@
|
||||
{
|
||||
strongSelf->_scrubberView.trimStartValue = 0;
|
||||
strongSelf->_scrubberView.trimEndValue = videoDuration;
|
||||
|
||||
strongSelf->_coverScrubberView.trimStartValue = 0;
|
||||
strongSelf->_coverScrubberView.trimEndValue = videoDuration;
|
||||
|
||||
[strongSelf->_scrubberView setTrimApplied:false];
|
||||
strongSelf->_shouldResetScrubber = true;
|
||||
}
|
||||
|
||||
[strongSelf->_scrubberView reloadData];
|
||||
[strongSelf->_coverScrubberView reloadData];
|
||||
|
||||
if (!strongSelf->_appeared)
|
||||
{
|
||||
[strongSelf->_scrubberView resetToStart];
|
||||
[strongSelf->_coverScrubberView resetToStart];
|
||||
strongSelf->_appeared = true;
|
||||
}
|
||||
} file:__FILE_NAME__ line:__LINE__]];
|
||||
@ -629,6 +654,7 @@
|
||||
if (afterReload) {
|
||||
_cachedThumbnails = nil;
|
||||
[_scrubberView reloadData];
|
||||
[_coverScrubberView reloadData];
|
||||
}
|
||||
else {
|
||||
[self setScrubbingPanelHidden:false animated:true];
|
||||
@ -648,8 +674,10 @@
|
||||
|
||||
if (hidden)
|
||||
{
|
||||
if (!_scrubbingPanelPresented)
|
||||
if (!_scrubbingPanelPresented) {
|
||||
[_scrubberView ignoreThumbnails];
|
||||
[_coverScrubberView ignoreThumbnails];
|
||||
}
|
||||
|
||||
_scrubbingPanelPresented = false;
|
||||
|
||||
@ -680,6 +708,7 @@
|
||||
|
||||
[_scrubberPanelView layoutSubviews];
|
||||
[_scrubberView layoutSubviews];
|
||||
[_coverScrubberView layoutSubviews];
|
||||
|
||||
void (^changeBlock)(void) = ^
|
||||
{
|
||||
@ -732,6 +761,18 @@
|
||||
[self setPlayButtonHidden:false animated:true];
|
||||
}
|
||||
|
||||
- (void)prepareForCoverEditing
|
||||
{
|
||||
[self setPlayButtonHidden:true animated:true];
|
||||
[self stop];
|
||||
}
|
||||
|
||||
- (void)returnFromCoverEditing
|
||||
{
|
||||
if (![self usePhotoBehavior])
|
||||
[self setPlayButtonHidden:false animated:true];
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
bool frameChanged = !CGRectEqualToRect(frame, self.frame);
|
||||
@ -741,6 +782,7 @@
|
||||
if (_appeared && frameChanged)
|
||||
{
|
||||
[_scrubberView resetThumbnails];
|
||||
[_coverScrubberView resetThumbnails];
|
||||
|
||||
[_scrubberPanelView setNeedsLayout];
|
||||
[_scrubberPanelView layoutIfNeeded];
|
||||
@ -748,6 +790,7 @@
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[_scrubberView reloadThumbnails];
|
||||
[_coverScrubberView reloadThumbnails];
|
||||
[_scrubberPanelView layoutSubviews];
|
||||
});
|
||||
}
|
||||
@ -1116,6 +1159,7 @@
|
||||
self.isPlaying = false;
|
||||
[_scrubberView setIsPlaying:false];
|
||||
[_scrubberView resetToStart];
|
||||
[_coverScrubberView resetToStart];
|
||||
|
||||
[_positionTimer invalidate];
|
||||
_positionTimer = nil;
|
||||
@ -1249,6 +1293,7 @@
|
||||
{
|
||||
[self _seekToPosition:_scrubberView.trimStartValue manual:false];
|
||||
[_scrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
|
||||
[_coverScrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
|
||||
}
|
||||
|
||||
[_player play];
|
||||
@ -1310,10 +1355,12 @@
|
||||
_positionTimer = nil;
|
||||
|
||||
[_scrubberView resetToStart];
|
||||
[_coverScrubberView resetToStart];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_scrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
|
||||
[_coverScrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
|
||||
}
|
||||
|
||||
[self _seekToPosition:_scrubberView.trimStartValue manual:false];
|
||||
@ -1322,6 +1369,7 @@
|
||||
- (void)positionTimerEvent
|
||||
{
|
||||
[_scrubberView setValue:CMTimeGetSeconds(_player.currentItem.currentTime)];
|
||||
[_coverScrubberView setValue:CMTimeGetSeconds(_player.currentItem.currentTime)];
|
||||
}
|
||||
|
||||
- (void)_seekToPosition:(NSTimeInterval)position manual:(bool)__unused manual
|
||||
@ -1396,6 +1444,11 @@
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber valueDidChange:(NSTimeInterval)position
|
||||
{
|
||||
[self _seekToPosition:position manual:true];
|
||||
if (videoScrubber == _scrubberView) {
|
||||
[_coverScrubberView setValue:position resetPosition:true];
|
||||
} else {
|
||||
[_scrubberView setValue:position resetPosition:true];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Trimming
|
||||
@ -1426,6 +1479,10 @@
|
||||
[self updatePlayerRange:videoScrubber.trimEndValue];
|
||||
[self updateEditAdjusments];
|
||||
|
||||
_coverScrubberView.trimStartValue = videoScrubber.trimStartValue;
|
||||
_coverScrubberView.trimEndValue = videoScrubber.trimEndValue;
|
||||
[_coverScrubberView _layoutTrimCurtainViews];
|
||||
|
||||
[self setPlayButtonHidden:false animated:true];
|
||||
}
|
||||
|
||||
@ -1622,8 +1679,12 @@
|
||||
return [SSignal single:thumbnails];
|
||||
}
|
||||
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber requestThumbnailImagesForTimestamps:(NSArray *)timestamps size:(CGSize)size isSummaryThumbnails:(bool)isSummaryThumbnails
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)videoScrubber requestThumbnailImagesForTimestamps:(NSArray *)timestamps size:(CGSize)size isSummaryThumbnails:(bool)isSummaryThumbnails
|
||||
{
|
||||
if (isSummaryThumbnails && videoScrubber == _coverScrubberView) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (timestamps.count == 0)
|
||||
return;
|
||||
|
||||
@ -1692,8 +1753,10 @@
|
||||
|
||||
[images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger index, __unused BOOL *stop)
|
||||
{
|
||||
if (index < timestamps.count)
|
||||
if (index < timestamps.count) {
|
||||
[strongSelf->_scrubberView setThumbnailImage:image forTimestamp:[timestamps[index] doubleValue] index:index isSummaryThubmnail:isSummaryThumbnails last:index == (images.count - 1)];
|
||||
[strongSelf->_coverScrubberView setThumbnailImage:image forTimestamp:[timestamps[index] doubleValue] index:index isSummaryThubmnail:isSummaryThumbnails last:index == (images.count - 1)];
|
||||
}
|
||||
}];
|
||||
} completed:^
|
||||
{
|
||||
|
@ -28,6 +28,9 @@
|
||||
@property (nonatomic, readonly) bool isScrubbing;
|
||||
@property (nonatomic, assign) bool isPlaying;
|
||||
@property (nonatomic, assign) NSTimeInterval value;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame cover:(bool)cover;
|
||||
|
||||
- (void)setValue:(NSTimeInterval)value resetPosition:(bool)resetPosition;
|
||||
|
||||
- (void)setTrimApplied:(bool)trimApplied;
|
||||
@ -48,6 +51,7 @@
|
||||
- (CGPoint)scrubberPositionForPosition:(NSTimeInterval)position;
|
||||
|
||||
- (void)_updateScrubberAnimationsAndResetCurrentPosition:(bool)resetCurrentPosition;
|
||||
- (void)_layoutTrimCurtainViews;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -35,8 +35,8 @@ typedef enum
|
||||
UIView *_zoomedThumbnailWrapperView;
|
||||
UIView *_summaryThumbnailWrapperView;
|
||||
TGMediaPickerGalleryVideoTrimView *_trimView;
|
||||
UIView *_leftCurtainView;
|
||||
UIView *_rightCurtainView;
|
||||
UIImageView *_leftCurtainView;
|
||||
UIImageView *_rightCurtainView;
|
||||
UIControl *_scrubberHandle;
|
||||
|
||||
UIControl *_dotHandle;
|
||||
@ -86,7 +86,7 @@ typedef enum
|
||||
|
||||
@implementation TGMediaPickerGalleryVideoScrubber
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
- (instancetype)initWithFrame:(CGRect)frame cover:(bool)cover
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
@ -105,7 +105,7 @@ typedef enum
|
||||
_currentTimeLabel.layer.shadowOpacity = 0.6;
|
||||
_currentTimeLabel.layer.rasterizationScale = TGScreenScaling();
|
||||
_currentTimeLabel.layer.shouldRasterize = true;
|
||||
[self addSubview:_currentTimeLabel];
|
||||
//[self addSubview:_currentTimeLabel];
|
||||
|
||||
_inverseTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(frame.size.width - 108, 4, 100, 15)];
|
||||
_inverseTimeLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
|
||||
@ -120,9 +120,9 @@ typedef enum
|
||||
_inverseTimeLabel.layer.shadowOpacity = 0.6;
|
||||
_inverseTimeLabel.layer.rasterizationScale = TGScreenScaling();
|
||||
_inverseTimeLabel.layer.shouldRasterize = true;
|
||||
[self addSubview:_inverseTimeLabel];
|
||||
//[self addSubview:_inverseTimeLabel];
|
||||
|
||||
_wrapperView = [[UIControl alloc] initWithFrame:CGRectMake(8, 24, 0, 36)];
|
||||
_wrapperView = [[UIControl alloc] initWithFrame:CGRectMake(8, 24, 0, 40)];
|
||||
_wrapperView.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -10, -5, -10);
|
||||
[self addSubview:_wrapperView];
|
||||
|
||||
@ -131,21 +131,47 @@ typedef enum
|
||||
|
||||
_summaryThumbnailWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 32)];
|
||||
_summaryThumbnailWrapperView.clipsToBounds = true;
|
||||
_summaryThumbnailWrapperView.layer.cornerRadius = 5.0;
|
||||
_summaryThumbnailWrapperView.layer.cornerRadius = 9.0;
|
||||
[_wrapperView addSubview:_summaryThumbnailWrapperView];
|
||||
|
||||
_leftCurtainView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
||||
_leftCurtainView.backgroundColor = [[TGPhotoEditorInterfaceAssets toolbarBackgroundColor] colorWithAlphaComponent:0.8f];
|
||||
static dispatch_once_t onceToken;
|
||||
static UIImage *leftCurtain;
|
||||
static UIImage *rightCurtain;
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(24.0f, 40.0f), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.8).CGColor);
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 40, 40) cornerRadius:9.0];
|
||||
CGContextAddPath(context, path.CGPath);
|
||||
CGContextFillPath(context);
|
||||
|
||||
leftCurtain = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(24.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];
|
||||
CGContextAddPath(context, path.CGPath);
|
||||
CGContextFillPath(context);
|
||||
|
||||
rightCurtain = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
});
|
||||
|
||||
_leftCurtainView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
||||
_leftCurtainView.image = [leftCurtain stretchableImageWithLeftCapWidth:11 topCapHeight:20];
|
||||
_leftCurtainView.clipsToBounds = true;
|
||||
_leftCurtainView.layer.cornerRadius = 5.0;
|
||||
[_wrapperView addSubview:_leftCurtainView];
|
||||
|
||||
_rightCurtainView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
||||
_rightCurtainView.backgroundColor = [[TGPhotoEditorInterfaceAssets toolbarBackgroundColor] colorWithAlphaComponent:0.8f];
|
||||
|
||||
_rightCurtainView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
||||
_rightCurtainView.image = [rightCurtain stretchableImageWithLeftCapWidth:11 topCapHeight:20];
|
||||
_rightCurtainView.clipsToBounds = true;
|
||||
_rightCurtainView.layer.cornerRadius = 5.0;
|
||||
[_wrapperView addSubview:_rightCurtainView];
|
||||
|
||||
|
||||
__weak TGMediaPickerGalleryVideoScrubber *weakSelf = self;
|
||||
_trimView = [[TGMediaPickerGalleryVideoTrimView alloc] initWithFrame:CGRectZero];
|
||||
_trimView.exclusiveTouch = true;
|
||||
@ -308,9 +334,9 @@ typedef enum
|
||||
[_wrapperView addSubview:_dotHandle];
|
||||
|
||||
static UIImage *dotFrameImage = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
static dispatch_once_t onceToken2;
|
||||
dispatch_once(&onceToken2, ^
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(_dotHandle.frame.size.width, _dotHandle.frame.size.height), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
|
||||
@ -336,26 +362,34 @@ typedef enum
|
||||
_dotFrameView = [[UIImageView alloc] initWithFrame:_dotHandle.bounds];
|
||||
_dotFrameView.image = dotFrameImage;
|
||||
[_dotHandle addSubview:_dotFrameView];
|
||||
|
||||
_scrubberHandle = [[UIControl alloc] initWithFrame:CGRectMake(0, -4.0f, 5.0f, 44.0f)];
|
||||
|
||||
_scrubberHandle = [[UIControl alloc] initWithFrame:CGRectMake(0, -4.0f, cover ? 30.0 : 5.0f, 48.0f)];
|
||||
_scrubberHandle.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -12, -5, -12);
|
||||
[_wrapperView addSubview:_scrubberHandle];
|
||||
|
||||
static UIImage *handleViewImage = nil;
|
||||
static dispatch_once_t onceToken2;
|
||||
dispatch_once(&onceToken2, ^
|
||||
UIImage *handleViewImage = nil;
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(_scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 0.5f, [UIColor colorWithWhite:0.0f alpha:0.65f].CGColor);
|
||||
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
|
||||
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0.5f, 0.5f, _scrubberHandle.frame.size.width - 1, _scrubberHandle.frame.size.height - 1.0f) cornerRadius:2.0f];
|
||||
[path fill];
|
||||
if (cover) {
|
||||
CGFloat lineWidth = 2.0 - TGSeparatorHeight();
|
||||
CGContextSetLineWidth(context, lineWidth);
|
||||
CGContextSetStrokeColorWithColor(context, UIColor.whiteColor.CGColor);
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(CGRectMake(0, 0, 30, 48), lineWidth / 2.0, lineWidth / 2.0) cornerRadius:8];
|
||||
CGContextAddPath(context, path.CGPath);
|
||||
CGContextStrokePath(context);
|
||||
} else {
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake((_scrubberHandle.frame.size.width - 2.0) / 2.0, 1.5f, 2.0, _scrubberHandle.frame.size.height - 3.0f) cornerRadius:2.0f];
|
||||
CGContextAddPath(context, path.CGPath);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
handleViewImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
});
|
||||
}
|
||||
|
||||
UIImageView *scrubberImageView = [[UIImageView alloc] initWithFrame:_scrubberHandle.bounds];
|
||||
scrubberImageView.image = handleViewImage;
|
||||
@ -780,7 +814,6 @@ typedef enum
|
||||
|
||||
- (void)setThumbnailImage:(UIImage *)image forTimestamp:(NSTimeInterval)__unused timestamp index:(NSInteger)index isSummaryThubmnail:(bool)isSummaryThumbnail last:(bool)last
|
||||
{
|
||||
bool exists = false;
|
||||
if (isSummaryThumbnail)
|
||||
{
|
||||
if (index == 0 && _summaryThumbnailViews.count > 0 && _summaryThumbnailSnapshotView == nil) {
|
||||
@ -790,7 +823,6 @@ typedef enum
|
||||
}
|
||||
|
||||
if (_summaryThumbnailViews.count >= index + 1) {
|
||||
exists = true;
|
||||
[_summaryThumbnailViews[index] setImage:image animated:true];
|
||||
} else {
|
||||
TGMediaPickerGalleryVideoScrubberThumbnailView *thumbnailView = [[TGMediaPickerGalleryVideoScrubberThumbnailView alloc] initWithImage:image originalSize:_originalSize cropRect:_cropRect cropOrientation:_cropOrientation cropMirrored:_cropMirrored];
|
||||
@ -852,7 +884,7 @@ typedef enum
|
||||
|
||||
if (orientation == UIImageOrientationLeft || orientation == UIImageOrientationRight)
|
||||
aspectRatio = 1.0f / aspectRatio;
|
||||
return CGSizeMake(CGCeil(36.0f * aspectRatio), 36.0f);
|
||||
return CGSizeMake(CGCeil(40.0f * aspectRatio), 40.0f);
|
||||
}
|
||||
|
||||
- (void)_layoutSummaryThumbnailViewsForZoom:(bool)forZoom
|
||||
@ -1431,7 +1463,7 @@ typedef enum
|
||||
CGFloat minX = duration > FLT_EPSILON ? ((CGFloat)startPosition * trimRect.size.width / (CGFloat)duration + trimRect.origin.x - normalScrubbingRect.origin.x) : 0.0f;
|
||||
CGFloat maxX = duration > FLT_EPSILON ? ((CGFloat)endPosition * trimRect.size.width / (CGFloat)duration + trimRect.origin.x + normalScrubbingRect.origin.x) : 0.0f;
|
||||
|
||||
return CGRectMake(minX, 0, maxX - minX, 36);
|
||||
return CGRectMake(minX, 0, maxX - minX, 40);
|
||||
}
|
||||
|
||||
- (void)_layoutTrimViewZoomedIn:(bool)zoomedIn
|
||||
@ -1462,8 +1494,8 @@ typedef enum
|
||||
CGRect scrubbingRect = [self _scrubbingRect];
|
||||
CGRect normalScrubbingRect = [self _scrubbingRectZoomedIn:false];
|
||||
|
||||
_leftCurtainView.frame = CGRectMake(scrubbingRect.origin.x - 12.0f, 0.0f, _trimView.frame.origin.x - scrubbingRect.origin.x + normalScrubbingRect.origin.x + 12.0f, 36.0f);
|
||||
_rightCurtainView.frame = CGRectMake(CGRectGetMaxX(_trimView.frame) - 4.0f, 0.0, scrubbingRect.origin.x + scrubbingRect.size.width - CGRectGetMaxX(_trimView.frame) - scrubbingRect.origin.x + normalScrubbingRect.origin.x + 4.0f + 12.0f, 36.0f);
|
||||
_leftCurtainView.frame = CGRectMake(scrubbingRect.origin.x - 12.0f, 0.0f, _trimView.frame.origin.x - scrubbingRect.origin.x + normalScrubbingRect.origin.x + 11.0f, 40.0f);
|
||||
_rightCurtainView.frame = CGRectMake(CGRectGetMaxX(_trimView.frame) - 7.0f, 0.0, scrubbingRect.origin.x + scrubbingRect.size.width - CGRectGetMaxX(_trimView.frame) - scrubbingRect.origin.x + normalScrubbingRect.origin.x + 7.0f + 12.0f, 40.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1479,14 +1511,14 @@ typedef enum
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
_wrapperView.frame = CGRectMake(TGVideoScrubberPadding, 24, self.frame.size.width - TGVideoScrubberPadding * 2.0f, 36);
|
||||
_wrapperView.frame = CGRectMake(TGVideoScrubberPadding, 24, self.frame.size.width - TGVideoScrubberPadding * 2.0f, 40);
|
||||
[self _layoutTrimViewZoomedIn:_zoomedIn];
|
||||
|
||||
CGRect scrubbingRect = [self _scrubbingRect];
|
||||
if (isnan(scrubbingRect.origin.x) || isnan(scrubbingRect.origin.y))
|
||||
return;
|
||||
|
||||
_summaryThumbnailWrapperView.frame = CGRectMake(MIN(0.0, scrubbingRect.origin.x), 0.0f, MAX(_wrapperView.frame.size.width, scrubbingRect.size.width), 36.0f);
|
||||
_summaryThumbnailWrapperView.frame = CGRectMake(MIN(0.0, scrubbingRect.origin.x), 0.0f, MAX(_wrapperView.frame.size.width, scrubbingRect.size.width), 40.0f);
|
||||
_zoomedThumbnailWrapperView.frame = _summaryThumbnailWrapperView.frame;
|
||||
|
||||
[self _updateScrubberAnimationsAndResetCurrentPosition:true];
|
||||
|
@ -10,5 +10,6 @@
|
||||
@property (nonatomic, assign) bool trimmingEnabled;
|
||||
|
||||
- (void)setTrimming:(bool)trimming animated:(bool)animated;
|
||||
- (void)setTrimmingEnabled:(bool)trimmingEnabled animated:(bool)animated;
|
||||
|
||||
@end
|
||||
|
@ -11,6 +11,10 @@
|
||||
{
|
||||
UIButton *_leftSegmentView;
|
||||
UIButton *_rightSegmentView;
|
||||
UIImageView *_borderView;
|
||||
|
||||
UIImageView *_leftCapsuleView;
|
||||
UIImageView *_rightCapsuleView;
|
||||
|
||||
UILongPressGestureRecognizer *_startHandlePressGestureRecognizer;
|
||||
UILongPressGestureRecognizer *_endHandlePressGestureRecognizer;
|
||||
@ -34,28 +38,48 @@
|
||||
{
|
||||
self.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -25, -5, -25);
|
||||
|
||||
UIColor *normalColor = UIColorRGB(0x4d4d4d);
|
||||
UIColor *accentColor = [TGPhotoEditorInterfaceAssets accentColor];
|
||||
UIColor *normalColor = UIColorRGB(0xffffff);
|
||||
UIColor *accentColor = UIColorRGB(0xf8d74a);
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
static UIImage *handle;
|
||||
static UIImage *border;
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(12.0f, 36.0f), false, 0.0f);
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(12.0f, 40.0f), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
[normalColor setFill];
|
||||
[[UIBezierPath bezierPathWithRoundedRect:CGRectMake(0.0f, 0.0f, 12.0f, 36.0f) byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomLeft cornerRadii:CGSizeMake(4.0f, 4.0f)] fill];
|
||||
CGContextSetFillColorWithColor(context, normalColor.CGColor);
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 40, 40) cornerRadius:9.0];
|
||||
CGContextAddPath(context, path.CGPath);
|
||||
CGContextFillPath(context);
|
||||
|
||||
CGContextSetBlendMode(context, kCGBlendModeClear);
|
||||
|
||||
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
|
||||
path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(11.0f, 1.0f + TGSeparatorHeight(), 20, 40.0f - (1.0f + TGSeparatorHeight()) * 2.0) cornerRadius:2.0];
|
||||
CGContextAddPath(context, path.CGPath);
|
||||
CGContextFillPath(context);
|
||||
|
||||
handle = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1.0f, 40.0f), false, 0.0f);
|
||||
context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(context, normalColor.CGColor);
|
||||
CGContextFillRect(context, CGRectMake(0, 0, 1, 1.0 + TGSeparatorHeight()));
|
||||
CGContextFillRect(context, CGRectMake(0, 40.0 - 1.0 - TGSeparatorHeight(), 1, 1.0 + TGSeparatorHeight()));
|
||||
border = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
});
|
||||
|
||||
UIImage *leftImage = handle;
|
||||
UIImage *leftHighlightedImage = TGTintedImage(handle, accentColor);
|
||||
UIImage *rightImage = [UIImage imageWithCGImage:handle.CGImage scale:handle.scale orientation:UIImageOrientationUpMirrored];
|
||||
UIImage *rightHighlightedImage = [UIImage imageWithCGImage:leftHighlightedImage.CGImage scale:handle.scale orientation:UIImageOrientationUpMirrored];
|
||||
UIImage *borderHighlightedImage = TGTintedImage(border, accentColor);
|
||||
|
||||
_leftSegmentView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 12, 36)];
|
||||
_leftSegmentView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 12, 40)];
|
||||
_leftSegmentView.adjustsImageWhenHighlighted = false;
|
||||
[_leftSegmentView setBackgroundImage:leftImage forState:UIControlStateNormal];
|
||||
[_leftSegmentView setBackgroundImage:leftHighlightedImage forState:UIControlStateSelected];
|
||||
@ -63,7 +87,7 @@
|
||||
_leftSegmentView.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -25, -5, -10);
|
||||
[self addSubview:_leftSegmentView];
|
||||
|
||||
_rightSegmentView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 12, 36)];
|
||||
_rightSegmentView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 12, 40)];
|
||||
_rightSegmentView.adjustsImageWhenHighlighted = false;
|
||||
[_rightSegmentView setBackgroundImage:rightImage forState:UIControlStateNormal];
|
||||
[_rightSegmentView setBackgroundImage:rightHighlightedImage forState:UIControlStateSelected];
|
||||
@ -71,6 +95,22 @@
|
||||
_rightSegmentView.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -10, -5, -25);
|
||||
[self addSubview:_rightSegmentView];
|
||||
|
||||
_borderView = [[UIImageView alloc] initWithImage:border];
|
||||
_borderView.highlightedImage = borderHighlightedImage;
|
||||
[self addSubview:_borderView];
|
||||
|
||||
_leftCapsuleView = [[UIImageView alloc] initWithFrame:CGRectMake(5.0 - TGSeparatorHeight(), 14.0 + TGSeparatorHeight(), 2.0, 11.0)];
|
||||
_leftCapsuleView.backgroundColor = UIColorRGB(0x343436);
|
||||
_leftCapsuleView.clipsToBounds = true;
|
||||
_leftCapsuleView.layer.cornerRadius = 1.0;
|
||||
[_leftSegmentView addSubview:_leftCapsuleView];
|
||||
|
||||
_rightCapsuleView = [[UIImageView alloc] initWithFrame:CGRectMake(12.0 - 3.0 - 4.0 + TGSeparatorHeight(), 14.0 + TGSeparatorHeight(), 2.0, 11.0)];
|
||||
_rightCapsuleView.backgroundColor = UIColorRGB(0x343436);
|
||||
_rightCapsuleView.clipsToBounds = true;
|
||||
_rightCapsuleView.layer.cornerRadius = 1.0;
|
||||
[_rightSegmentView addSubview:_rightCapsuleView];
|
||||
|
||||
_startHandlePressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleHandlePress:)];
|
||||
_startHandlePressGestureRecognizer.delegate = self;
|
||||
_startHandlePressGestureRecognizer.minimumPressDuration = 0.1f;
|
||||
@ -98,24 +138,39 @@
|
||||
|
||||
_leftSegmentView.hidden = !trimmingEnabled;
|
||||
_rightSegmentView.hidden = !trimmingEnabled;
|
||||
_borderView.hidden = !trimmingEnabled;
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setTrimmingEnabled:(bool)trimmingEnabled animated:(bool)animated {
|
||||
_trimmingEnabled = trimmingEnabled;
|
||||
|
||||
CGFloat alpha = trimmingEnabled ? 1.0 : 0.0;
|
||||
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
_leftSegmentView.alpha = alpha;
|
||||
_rightSegmentView.alpha = alpha;
|
||||
_borderView.alpha = alpha;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setTrimming:(bool)trimming animated:(bool)animated
|
||||
{
|
||||
if (animated)
|
||||
{
|
||||
[UIView animateWithDuration:0.15f animations:^
|
||||
{
|
||||
[_leftSegmentView setSelected:trimming];
|
||||
[_rightSegmentView setSelected:trimming];
|
||||
}];
|
||||
[_leftSegmentView setSelected:trimming];
|
||||
[_rightSegmentView setSelected:trimming];
|
||||
[_borderView setHighlighted:trimming];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_leftSegmentView setSelected:trimming];
|
||||
[_rightSegmentView setSelected:trimming];
|
||||
[_borderView setHighlighted:trimming];
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,6 +282,7 @@
|
||||
|
||||
_leftSegmentView.frame = CGRectMake(0, 0, handleWidth, self.frame.size.height);
|
||||
_rightSegmentView.frame = CGRectMake(self.frame.size.width - handleWidth, 0, handleWidth, self.frame.size.height);
|
||||
_borderView.frame = CGRectMake(handleWidth, 0, self.frame.size.width - handleWidth * 2.0, self.frame.size.height);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -30,3 +30,12 @@
|
||||
- (void)setInternalHidden:(bool)internalHidden animated:(bool)animated;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TGMediaPickerCoverButton : UIButton
|
||||
|
||||
- (void)setImage:(UIImage *)image;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame gallery:(bool)gallery;
|
||||
|
||||
@end
|
||||
|
@ -48,7 +48,7 @@ const CGFloat TGPhotoCounterButtonMaskFade = 18;
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.3f).CGColor);
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.5f).CGColor);
|
||||
|
||||
CGContextFillEllipseInRect(context, CGRectMake(3.5f, 1.0f, 31.0f, 31.0f));
|
||||
|
||||
@ -778,3 +778,118 @@ const CGFloat TGPhotoCounterButtonMaskFade = 18;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
@interface TGMediaPickerCoverButton ()
|
||||
{
|
||||
UIView *_wrapperView;
|
||||
UIView *_backgroundView;
|
||||
UIImageView *_iconView;
|
||||
UIImageView *_thumbnailView;
|
||||
UILabel *_label;
|
||||
|
||||
bool _gallery;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TGMediaPickerCoverButton
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame gallery:(bool)gallery
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
{
|
||||
self.exclusiveTouch = true;
|
||||
_gallery = gallery;
|
||||
|
||||
CGFloat width = _gallery ? 168.0 : 98.0;
|
||||
|
||||
_wrapperView = [[UIView alloc] initWithFrame:CGRectMake((120 - width) / 2.0, 0, width, 26)];
|
||||
_wrapperView.userInteractionEnabled = false;
|
||||
[self addSubview:_wrapperView];
|
||||
|
||||
_backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0, width, 26)];
|
||||
_backgroundView.clipsToBounds = true;
|
||||
_backgroundView.layer.cornerRadius = 13.0;
|
||||
_backgroundView.backgroundColor = UIColorRGBA(0x000000, 0.5f);
|
||||
[_wrapperView addSubview:_backgroundView];
|
||||
|
||||
_thumbnailView = [[UIImageView alloc] initWithFrame:CGRectMake(1.0, 1.0, 24.0, 24.0)];
|
||||
_thumbnailView.clipsToBounds = true;
|
||||
_thumbnailView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
_thumbnailView.layer.cornerRadius = 12.0f;
|
||||
[_wrapperView addSubview:_thumbnailView];
|
||||
|
||||
_label = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
_label.backgroundColor = [UIColor clearColor];
|
||||
_label.font = [TGFont boldSystemFontOfSize:14];
|
||||
_label.textColor = [UIColor whiteColor];
|
||||
_label.text = _gallery ? TGLocalized(@"Media.ChooseFromGallery") : TGLocalized(@"Media.EditCover");
|
||||
[_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];
|
||||
|
||||
_iconView = [[UIImageView alloc] initWithFrame:CGRectMake(CGRectGetMaxX(_label.frame) + 4.0, 7, 8, 12)];
|
||||
_iconView.alpha = 0.3;
|
||||
_iconView.contentMode = UIViewContentModeCenter;
|
||||
_iconView.image = TGTintedImage([UIImage imageNamed:@"Editor/ArrowRight"], UIColorRGB(0xffffff));
|
||||
[_wrapperView addSubview:_iconView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setImage:(UIImage *)image {
|
||||
if (image != nil) {
|
||||
_thumbnailView.hidden = false;
|
||||
_thumbnailView.image = image;
|
||||
|
||||
_wrapperView.frame = CGRectMake(0, 0, 120, 26);
|
||||
_backgroundView.frame = CGRectMake(0.0f, 0, 120, 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);
|
||||
_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);
|
||||
}
|
||||
_iconView.frame = CGRectMake(CGRectGetMaxX(_label.frame) + 4.0, 7, 8, 12);
|
||||
}
|
||||
|
||||
- (void)setWrapperScale:(CGFloat)scale animated:(bool)animated
|
||||
{
|
||||
void (^change)(void) = ^
|
||||
{
|
||||
_wrapperView.transform = CGAffineTransformMakeScale(scale, scale);
|
||||
};
|
||||
|
||||
if (animated)
|
||||
[UIView animateWithDuration:0.12 delay:0 options:UIViewAnimationOptionAllowAnimatedContent animations:change completion:nil];
|
||||
else
|
||||
change();
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
[self setWrapperScale:0.85f animated:true];
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
[self setWrapperScale:1.0f animated:true];
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesCancelled:touches withEvent:event];
|
||||
[self setWrapperScale:1.0f animated:true];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -7,5 +7,6 @@
|
||||
@property (nonatomic, assign) UIEdgeInsets safeAreaInset;
|
||||
@property (nonatomic, strong) UIView *panelView;
|
||||
@property (nonatomic, strong) TGMediaPickerGalleryVideoScrubber *scrubberView;
|
||||
@property (nonatomic, strong) TGMediaPickerGalleryVideoScrubber *coverScrubberView;
|
||||
|
||||
@end
|
||||
|
@ -11,6 +11,8 @@
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
_scrubberView.frame = CGRectMake(_safeAreaInset.left, _scrubberView.frame.origin.y, self.frame.size.width - _safeAreaInset.left - _safeAreaInset.right, _scrubberView.frame.size.height);
|
||||
|
||||
_coverScrubberView.frame = CGRectMake(_safeAreaInset.left, _scrubberView.frame.origin.y + 16.0, self.frame.size.width - _safeAreaInset.left - _safeAreaInset.right, _scrubberView.frame.size.height);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -360,7 +360,7 @@
|
||||
[_dotImageView addGestureRecognizer:dotTapRecognizer];
|
||||
|
||||
if ([self presentedForAvatarCreation] && _item.isVideo) {
|
||||
_scrubberView = [[TGMediaPickerGalleryVideoScrubber alloc] initWithFrame:CGRectMake(0.0f, 0.0, _portraitToolbarView.frame.size.width, 68.0f)];
|
||||
_scrubberView = [[TGMediaPickerGalleryVideoScrubber alloc] initWithFrame:CGRectMake(0.0f, 0.0, _portraitToolbarView.frame.size.width, 68.0f) cover: false];
|
||||
_scrubberView.minimumLength = 3.0;
|
||||
_scrubberView.layer.allowsGroupOpacity = true;
|
||||
_scrubberView.hasDotPicker = true;
|
||||
|
@ -123,11 +123,11 @@
|
||||
static UIImage *muteBackground;
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
CGRect rect = CGRectMake(0, 0, 39.0f, 39.0f);
|
||||
CGRect rect = CGRectMake(0, 0, 40.0f, 40.0f);
|
||||
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.3f).CGColor);
|
||||
CGContextFillEllipseInRect(context, CGRectInset(rect, 3, 3));
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.5f).CGColor);
|
||||
CGContextFillEllipseInRect(context, rect);
|
||||
|
||||
muteBackground = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
@ -173,7 +173,7 @@
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.7f).CGColor);
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.5f).CGColor);
|
||||
|
||||
CGContextFillEllipseInRect(context, CGRectMake(3.5f, 1.0f, 31.0f, 31.0f));
|
||||
|
||||
@ -198,7 +198,7 @@
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.7f).CGColor);
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.5f).CGColor);
|
||||
|
||||
CGContextFillEllipseInRect(context, CGRectMake(3.5f, 1.0f, 31.0f, 31.0f));
|
||||
|
||||
@ -358,7 +358,7 @@
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(30.0f, 30.0f), false, 0.0f);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.7f).CGColor);
|
||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.5f).CGColor);
|
||||
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0.5f, 0.5f, 29.0f, 29.0f) cornerRadius:8.5f];
|
||||
CGContextAddPath(context, path.CGPath);
|
||||
|
@ -127,7 +127,7 @@ private enum LegacyAssetVideoData {
|
||||
private enum LegacyAssetItem {
|
||||
case image(data: LegacyAssetImageData, thumbnail: UIImage?, caption: NSAttributedString?, stickers: [FileMediaReference])
|
||||
case file(data: LegacyAssetImageData, thumbnail: UIImage?, mimeType: String, name: String, caption: NSAttributedString?)
|
||||
case video(data: LegacyAssetVideoData, thumbnail: UIImage?, adjustments: TGVideoEditAdjustments?, caption: NSAttributedString?, asFile: Bool, asAnimation: Bool, stickers: [FileMediaReference])
|
||||
case video(data: LegacyAssetVideoData, thumbnail: UIImage?, cover: UIImage?, adjustments: TGVideoEditAdjustments?, caption: NSAttributedString?, asFile: Bool, asAnimation: Bool, stickers: [FileMediaReference])
|
||||
}
|
||||
|
||||
private final class LegacyAssetItemWrapper: NSObject {
|
||||
@ -167,13 +167,14 @@ public func legacyAssetPickerItemGenerator() -> ((Any?, NSAttributedString?, Str
|
||||
if (dict["type"] as! NSString) == "editedPhoto" || (dict["type"] as! NSString) == "capturedPhoto" {
|
||||
let image = dict["image"] as! UIImage
|
||||
let thumbnail = dict["previewImage"] as? UIImage
|
||||
let cover = dict["coverImage"] as? UIImage
|
||||
|
||||
var result: [AnyHashable : Any] = [:]
|
||||
if let isAnimation = dict["isAnimation"] as? NSNumber, isAnimation.boolValue {
|
||||
let url: String? = (dict["url"] as? String) ?? (dict["url"] as? URL)?.path
|
||||
if let url = url {
|
||||
let dimensions = image.size
|
||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: 4.0), thumbnail: thumbnail, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: false, asAnimation: true, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: 4.0), thumbnail: thumbnail, cover: cover, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: false, asAnimation: true, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
||||
}
|
||||
} else {
|
||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .image(data: .image(image), thumbnail: thumbnail, caption: caption, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
||||
@ -220,7 +221,7 @@ public func legacyAssetPickerItemGenerator() -> ((Any?, NSAttributedString?, Str
|
||||
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
||||
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
||||
|
||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: tempFileUrl.path, dimensions: dimensions, duration: duration), thumbnail: thumbnail, adjustments: nil, caption: caption, asFile: false, asAnimation: true, stickers: []), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: tempFileUrl.path, dimensions: dimensions, duration: duration), thumbnail: thumbnail, cover: nil, adjustments: nil, caption: caption, asFile: false, asAnimation: true, stickers: []), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
||||
return result
|
||||
}
|
||||
|
||||
@ -230,6 +231,7 @@ public func legacyAssetPickerItemGenerator() -> ((Any?, NSAttributedString?, Str
|
||||
}
|
||||
} else if (dict["type"] as! NSString) == "video" {
|
||||
let thumbnail = dict["previewImage"] as? UIImage
|
||||
let cover = dict["coverImage"] as? UIImage
|
||||
var asFile = false
|
||||
if let document = dict["document"] as? NSNumber, document.boolValue {
|
||||
asFile = true
|
||||
@ -237,17 +239,18 @@ public func legacyAssetPickerItemGenerator() -> ((Any?, NSAttributedString?, Str
|
||||
|
||||
if let asset = dict["asset"] as? TGMediaAsset {
|
||||
var result: [AnyHashable: Any] = [:]
|
||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .asset(asset), thumbnail: thumbnail, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .asset(asset), thumbnail: thumbnail, cover: cover, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
||||
return result
|
||||
} else if let url = (dict["url"] as? String) ?? (dict["url"] as? URL)?.absoluteString {
|
||||
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
||||
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
||||
var result: [AnyHashable: Any] = [:]
|
||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), thumbnail: thumbnail, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), thumbnail: thumbnail, cover: cover, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
||||
return result
|
||||
}
|
||||
} else if (dict["type"] as! NSString) == "cameraVideo" {
|
||||
let thumbnail = dict["previewImage"] as? UIImage
|
||||
let cover = dict["coverImage"] as? UIImage
|
||||
var asFile = false
|
||||
if let document = dict["document"] as? NSNumber, document.boolValue {
|
||||
asFile = true
|
||||
@ -259,7 +262,7 @@ public func legacyAssetPickerItemGenerator() -> ((Any?, NSAttributedString?, Str
|
||||
let dimensions = previewImage.pixelSize()
|
||||
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
||||
var result: [AnyHashable: Any] = [:]
|
||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), thumbnail: thumbnail, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), thumbnail: thumbnail, cover: cover, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
||||
return result
|
||||
}
|
||||
}
|
||||
@ -756,7 +759,7 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A
|
||||
default:
|
||||
break
|
||||
}
|
||||
case let .video(data, thumbnail, adjustments, caption, asFile, asAnimation, stickers):
|
||||
case let .video(data, thumbnail, cover, adjustments, caption, asFile, asAnimation, stickers):
|
||||
var finalDimensions: CGSize
|
||||
var finalDuration: Double
|
||||
switch data {
|
||||
@ -799,6 +802,26 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A
|
||||
}
|
||||
}
|
||||
|
||||
var videoCover: TelegramMediaImage?
|
||||
if let cover {
|
||||
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
let coverSize = cover.size.aspectFitted(CGSize(width: 320.0, height: 320.0))
|
||||
let coverImage = TGScaleImageToPixelSize(cover, coverSize)!
|
||||
if let coverData = coverImage.jpegData(compressionQuality: 0.6) {
|
||||
account.postbox.mediaBox.storeResourceData(resource.id, data: coverData)
|
||||
videoCover = TelegramMediaImage(
|
||||
imageId: MediaId(namespace: 0, id: 0),
|
||||
representations: [
|
||||
TelegramMediaImageRepresentation(dimensions: PixelDimensions(coverSize), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)
|
||||
],
|
||||
immediateThumbnailData: nil,
|
||||
reference: nil,
|
||||
partialReference: nil,
|
||||
flags: []
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let defaultPreset = TGMediaVideoConversionPreset(rawValue: UInt32(UserDefaults.standard.integer(forKey: "TG_preferredVideoPreset_v0")))
|
||||
|
||||
var preset: TGMediaVideoConversionPreset = TGMediaVideoConversionPresetCompressedMedium
|
||||
@ -891,7 +914,7 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A
|
||||
fileAttributes.append(.HasLinkedStickers)
|
||||
}
|
||||
|
||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes, alternativeRepresentations: [])
|
||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], videoCover: videoCover, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes, alternativeRepresentations: [])
|
||||
|
||||
if let timer = item.timer, timer > 0 && (timer <= 60 || timer == viewOnceTimeout) {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
||||
|
@ -569,6 +569,7 @@ public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRender
|
||||
|
||||
public final class LegacyPaintStickersContext: NSObject, TGPhotoPaintStickersContext {
|
||||
public var captionPanelView: (() -> TGCaptionPanelView?)?
|
||||
public var editCover: ((CGSize, @escaping (UIImage) -> Void) -> Void)?
|
||||
|
||||
private let context: AccountContext
|
||||
|
||||
|
@ -619,14 +619,18 @@ open class LegacyController: ViewController, PresentableController {
|
||||
override open func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.view.endEditing(true)
|
||||
switch self.presentation {
|
||||
case .modal:
|
||||
self.controllerNode.animateModalOut { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: completion)
|
||||
}
|
||||
case .custom:
|
||||
case .modal:
|
||||
self.controllerNode.animateModalOut { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: completion)
|
||||
}
|
||||
case .custom:
|
||||
if let _ = self.navigationController as? NavigationController {
|
||||
super.dismiss(animated: false, completion: completion)
|
||||
} else {
|
||||
self.presentingViewController?.dismiss(animated: false, completion: completion)
|
||||
case .navigation:
|
||||
(self.navigationController as? NavigationController)?.filterController(self, animated: true)
|
||||
}
|
||||
case .navigation:
|
||||
(self.navigationController as? NavigationController)?.filterController(self, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,18 +83,18 @@ final class AvatarEditorPreviewView: UIView {
|
||||
self.currentSize = size
|
||||
self.backgroundView.frame = CGRect(origin: .zero, size: size)
|
||||
|
||||
//TODO:localize
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let labelSize = self.label.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "Use an Emoji",
|
||||
font: Font.semibold(14.0),
|
||||
string: presentationData.strings.MediaPicker_UseAnEmoji,
|
||||
font: Font.semibold(12.0),
|
||||
textColor: .white
|
||||
)),
|
||||
textShadowColor: UIColor(white: 0.0, alpha: 0.4),
|
||||
textShadowBlur: 4.0
|
||||
textShadowColor: UIColor(white: 0.0, alpha: 0.3),
|
||||
textShadowBlur: 3.0
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
@ -104,7 +104,7 @@ final class AvatarEditorPreviewView: UIView {
|
||||
if view.superview == nil {
|
||||
self.addSubview(view)
|
||||
}
|
||||
view.frame = CGRect(origin: CGPoint(x: floor((size.width - labelSize.width) / 2.0), y: size.height - labelSize.height - 20.0), size: labelSize)
|
||||
view.frame = CGRect(origin: CGPoint(x: floor((size.width - labelSize.width) / 2.0), y: size.height - labelSize.height - 22.0), size: labelSize)
|
||||
}
|
||||
|
||||
guard !self.files.isEmpty else {
|
||||
|
@ -101,7 +101,7 @@ enum LegacyMediaPickerGallerySource {
|
||||
case selection(item: TGMediaSelectableItem)
|
||||
}
|
||||
|
||||
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) -> TGModernGalleryController {
|
||||
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
|
||||
|
||||
@ -112,6 +112,9 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?,
|
||||
paintStickersContext.captionPanelView = {
|
||||
return getCaptionPanelView()
|
||||
}
|
||||
paintStickersContext.editCover = { dimensions, completion in
|
||||
editCover(dimensions, completion)
|
||||
}
|
||||
|
||||
let controller = TGModernGalleryController(context: legacyController.context)!
|
||||
controller.asyncTransitionIn = true
|
||||
|
@ -160,6 +160,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
case wallpaper
|
||||
case story
|
||||
case addImage
|
||||
case cover
|
||||
case createSticker
|
||||
case createAvatar
|
||||
}
|
||||
@ -234,6 +235,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
}
|
||||
|
||||
var dismissAll: () -> Void = { }
|
||||
public var editCover: (CGSize, @escaping (UIImage) -> Void) -> Void = { _, _ in }
|
||||
|
||||
private class Node: ViewControllerTracingNode, ASGestureRecognizerDelegate {
|
||||
enum DisplayMode {
|
||||
@ -311,7 +313,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
self.presentationData = controller.presentationData
|
||||
|
||||
var assetType: PHAssetMediaType?
|
||||
if case let .assets(_, mode) = controller.subject, [.wallpaper, .addImage, .createSticker].contains(mode) {
|
||||
if case let .assets(_, mode) = controller.subject, [.wallpaper, .addImage, .cover, .createSticker].contains(mode) {
|
||||
assetType = .image
|
||||
}
|
||||
let mediaAssetsContext = MediaAssetsContext(assetType: assetType)
|
||||
@ -522,7 +524,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
self.gridNode.scrollView.alwaysBounceVertical = true
|
||||
self.gridNode.scrollView.showsVerticalScrollIndicator = false
|
||||
|
||||
if case let .assets(_, mode) = controller.subject, [.wallpaper, .story, .addImage, .createSticker, .createAvatar].contains(mode) {
|
||||
if case let .assets(_, mode) = controller.subject, [.wallpaper, .story, .addImage, .cover, .createSticker, .createAvatar].contains(mode) {
|
||||
|
||||
} else {
|
||||
let selectionGesture = MediaPickerGridSelectionGesture<TGMediaSelectableItem>()
|
||||
@ -1217,7 +1219,9 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
}
|
||||
}, presentSchedulePicker: controller.presentSchedulePicker, presentTimerPicker: controller.presentTimerPicker, getCaptionPanelView: controller.getCaptionPanelView, present: { [weak self] c, a in
|
||||
self?.currentGalleryParentController = c
|
||||
self?.controller?.present(c, in: .window(.root), with: a)
|
||||
c.navigationPresentation = .flatModal
|
||||
self?.controller?.parentController()?.push(c)
|
||||
//self?.controller?.present(c, in: .window(.root), with: a)
|
||||
}, finishedTransitionIn: { [weak self] in
|
||||
self?.openingMedia = false
|
||||
self?.hasGallery = true
|
||||
@ -1227,6 +1231,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
self?.updateIsCameraActive()
|
||||
}, dismissAll: { [weak self] in
|
||||
self?.controller?.dismissAll()
|
||||
}, editCover: { [weak self] dimensions, completion in
|
||||
self?.controller?.editCover(dimensions, completion)
|
||||
})
|
||||
}
|
||||
|
||||
@ -1266,6 +1272,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
self?.updateIsCameraActive()
|
||||
}, dismissAll: { [weak self] in
|
||||
self?.controller?.dismissAll()
|
||||
}, editCover: { _, _ in
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -1540,9 +1548,6 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
|
||||
var cutoutRect: CGRect?
|
||||
var cameraRect: CGRect? = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: 0.0), size: CGSize(width: itemWidth, height: itemWidth * 2.0 + 1.0))
|
||||
if case .assets(nil, .createAvatar) = controller.subject {
|
||||
cameraRect = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: 0.0), size: CGSize(width: itemWidth, height: itemWidth))
|
||||
}
|
||||
if self.cameraView == nil && self.modernCameraView == nil {
|
||||
cameraRect = nil
|
||||
}
|
||||
@ -1667,6 +1672,9 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
})
|
||||
}
|
||||
}
|
||||
Queue.mainQueue().after(0.01) {
|
||||
strongSelf.cameraWrapperView.superview?.addSubview(strongSelf.cameraWrapperView)
|
||||
}
|
||||
})
|
||||
|
||||
if let avatarEditorPreviewView = self.avatarEditorPreviewView {
|
||||
@ -1869,9 +1877,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
self.titleView.subtitle = presentationData.strings.MediaPicker_CreateSticker
|
||||
self.titleView.isEnabled = true
|
||||
case .createAvatar:
|
||||
//TODO:localize
|
||||
self.titleView.title = presentationData.strings.MediaPicker_Recents
|
||||
self.titleView.subtitle = "Set new profile photo"
|
||||
self.titleView.subtitle = presentationData.strings.MediaPicker_SetNewPhoto
|
||||
self.titleView.isEnabled = true
|
||||
case .story:
|
||||
self.titleView.title = presentationData.strings.MediaPicker_Recents
|
||||
@ -1880,6 +1887,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
||||
self.titleView.title = presentationData.strings.Conversation_Theme_ChooseWallpaperTitle
|
||||
case .addImage:
|
||||
self.titleView.title = presentationData.strings.MediaPicker_AddImage
|
||||
case .cover:
|
||||
self.titleView.title = presentationData.strings.MediaPicker_ChooseCover
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -3379,8 +3388,7 @@ public func avatarMediaPickerController(
|
||||
var mainButtonState: AttachmentMainButtonState?
|
||||
|
||||
if canDelete {
|
||||
//TODO:localize
|
||||
mainButtonState = AttachmentMainButtonState(text: "Remove Photo", font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false)
|
||||
mainButtonState = AttachmentMainButtonState(text: presentationData.strings.MediaPicker_RemovePhoto, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false)
|
||||
}
|
||||
|
||||
let mediaPickerController = MediaPickerScreenImpl(
|
||||
@ -3477,6 +3485,68 @@ public func avatarMediaPickerController(
|
||||
return controller
|
||||
}
|
||||
|
||||
|
||||
|
||||
public func coverMediaPickerController(
|
||||
context: AccountContext,
|
||||
completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void,
|
||||
dismissed: @escaping () -> Void
|
||||
) -> ViewController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
let updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) = (presentationData, .single(presentationData))
|
||||
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: {
|
||||
return nil
|
||||
})
|
||||
controller.requestController = { [weak controller] _, present in
|
||||
let mediaPickerController = MediaPickerScreenImpl(
|
||||
context: context,
|
||||
updatedPresentationData: updatedPresentationData,
|
||||
peer: nil,
|
||||
threadTitle: nil,
|
||||
chatLocation: nil,
|
||||
bannedSendPhotos: nil,
|
||||
bannedSendVideos: nil,
|
||||
subject: .assets(nil, .cover)
|
||||
)
|
||||
mediaPickerController.customSelection = { controller, result in
|
||||
if let result = result as? PHAsset {
|
||||
controller.updateHiddenMediaId(result.localIdentifier)
|
||||
if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
|
||||
let transitionOut: (Bool?) -> (UIView, CGRect)? = { isNew in
|
||||
if let isNew {
|
||||
if isNew {
|
||||
controller.updateHiddenMediaId(nil)
|
||||
if let transitionView = controller.defaultTransitionView() {
|
||||
return (transitionView, transitionView.bounds)
|
||||
}
|
||||
} else if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
|
||||
return (transitionView, transitionView.bounds)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
completion(result, transitionView, transitionView.bounds, controller.transitionImage(for: result.localIdentifier), false, transitionOut, { [weak controller] in
|
||||
controller?.updateHiddenMediaId(nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
mediaPickerController.openAvatarEditor = { [weak controller] in
|
||||
completion(nil, nil, .zero, nil, false, { _ in return nil }, {
|
||||
})
|
||||
controller?.dismiss(animated: true)
|
||||
}
|
||||
present(mediaPickerController, mediaPickerController.mediaPickerContext)
|
||||
}
|
||||
controller.willDismiss = {
|
||||
dismissed()
|
||||
}
|
||||
controller.navigationPresentation = .flatModal
|
||||
controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
return controller
|
||||
}
|
||||
|
||||
private class SelectedButtonNode: HighlightableButtonNode {
|
||||
private let background = ASImageNode()
|
||||
private let icon = ASImageNode()
|
||||
|
@ -59,6 +59,8 @@ func requiredBoostSubjectLevel(subject: BoostSubject, group: Bool, context: Acco
|
||||
return configuration.minGroupEmojiPackLevel
|
||||
case .noAds:
|
||||
return configuration.minChannelRestrictAdsLevel
|
||||
case .wearGift:
|
||||
return configuration.minChannelWearGiftLevel
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,6 +242,7 @@ private final class LevelSectionComponent: CombinedComponent {
|
||||
case audioTranscription
|
||||
case emojiPack
|
||||
case noAds
|
||||
case wearGift
|
||||
|
||||
func title(strings: PresentationStrings, isGroup: Bool) -> String {
|
||||
switch self {
|
||||
@ -269,6 +272,8 @@ private final class LevelSectionComponent: CombinedComponent {
|
||||
return strings.GroupBoost_Table_Group_EmojiPack
|
||||
case .noAds:
|
||||
return strings.ChannelBoost_Table_NoAds
|
||||
case .wearGift:
|
||||
return strings.ChannelBoost_Table_WearGift
|
||||
}
|
||||
}
|
||||
|
||||
@ -300,6 +305,8 @@ private final class LevelSectionComponent: CombinedComponent {
|
||||
return "Premium/BoostPerk/EmojiPack"
|
||||
case .noAds:
|
||||
return "Premium/BoostPerk/NoAds"
|
||||
case .wearGift:
|
||||
return "Premium/BoostPerk/NoAds"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -638,6 +645,8 @@ private final class SheetContent: CombinedComponent {
|
||||
textString = strings.GroupBoost_EnableEmojiPackLevelText("\(requiredLevel)").string
|
||||
case .noAds:
|
||||
textString = strings.ChannelBoost_EnableNoAdsLevelText("\(requiredLevel)").string
|
||||
case .wearGift:
|
||||
textString = strings.ChannelBoost_EnableNoAdsLevelText("\(requiredLevel)").string
|
||||
}
|
||||
} else {
|
||||
let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining))
|
||||
@ -783,15 +792,6 @@ private final class SheetContent: CombinedComponent {
|
||||
availableSize: CGSize(width: 90.0, height: 90.0),
|
||||
transition: .immediate
|
||||
)
|
||||
// let icon = icon.update(
|
||||
// component: LottieComponent(
|
||||
// content: LottieComponent.AppBundleContent(name: iconName),
|
||||
// playOnce: state.playOnce
|
||||
// ),
|
||||
// availableSize: CGSize(width: 70, height: 70),
|
||||
// transition: .immediate
|
||||
// )
|
||||
|
||||
context.add(icon
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + iconBackground.size.height / 2.0))
|
||||
)
|
||||
@ -1162,6 +1162,9 @@ private final class SheetContent: CombinedComponent {
|
||||
if !isGroup && level >= requiredBoostSubjectLevel(subject: .noAds, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
perks.append(.noAds)
|
||||
}
|
||||
// if !isGroup && level >= requiredBoostSubjectLevel(subject: .wearGift, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||
// perks.append(.wearGift)
|
||||
// }
|
||||
|
||||
levelItems.append(
|
||||
AnyComponentWithIdentity(
|
||||
@ -1461,6 +1464,8 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
||||
titleString = strings.GroupBoost_EmojiPack
|
||||
case .noAds:
|
||||
titleString = strings.ChannelBoost_NoAds
|
||||
case .wearGift:
|
||||
titleString = strings.ChannelBoost_WearGift
|
||||
}
|
||||
} else {
|
||||
titleString = isGroup == true ? strings.GroupBoost_Title_Current : strings.ChannelBoost_Title_Current
|
||||
|
@ -200,6 +200,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
||||
case dismissedBusinessLinksBadge = 73
|
||||
case dismissedBusinessChatbotsBadge = 74
|
||||
case captionAboveMediaTooltip = 75
|
||||
case channelSendGiftTooltip = 76
|
||||
|
||||
var key: ValueBoxKey {
|
||||
let v = ValueBoxKey(length: 4)
|
||||
@ -544,6 +545,10 @@ private struct ApplicationSpecificNoticeKeys {
|
||||
static func captionAboveMediaTooltip() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.captionAboveMediaTooltip.key)
|
||||
}
|
||||
|
||||
static func channelSendGiftTooltip() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.channelSendGiftTooltip.key)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ApplicationSpecificNotice {
|
||||
@ -2303,4 +2308,31 @@ public struct ApplicationSpecificNotice {
|
||||
return Int(previousValue)
|
||||
}
|
||||
}
|
||||
|
||||
public static func getChannelSendGiftTooltip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||
return accountManager.transaction { transaction -> Int32 in
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.channelSendGiftTooltip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||
return value.value
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func incrementChannelSendGiftTooltip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1) -> Signal<Int, NoError> {
|
||||
return accountManager.transaction { transaction -> Int in
|
||||
var currentValue: Int32 = 0
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.channelSendGiftTooltip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||
currentValue = value.value
|
||||
}
|
||||
let previousValue = currentValue
|
||||
currentValue += Int32(count)
|
||||
|
||||
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) {
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.channelSendGiftTooltip(), entry)
|
||||
}
|
||||
|
||||
return Int(previousValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1066,7 +1066,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
attributedString = mutableString
|
||||
case .prizeStars:
|
||||
attributedString = NSAttributedString(string: strings.Notification_StarsPrize, font: titleFont, textColor: primaryTextColor)
|
||||
case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _, _, _, _):
|
||||
case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _, _, peerId, senderId, _):
|
||||
if !forAdditionalServiceMessage {
|
||||
if let text {
|
||||
let mutableAttributedString = NSMutableAttributedString(attributedString: stringWithAppliedEntities(text, entities: entities ?? [], baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false, message: message._asMessage()))
|
||||
@ -1090,13 +1090,28 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Self_Bought(starsPrice)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
||||
} else if message.author?.id == accountPeerId {
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_SentYou(starsPrice)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
||||
} else if let peerId {
|
||||
peerIds = [(1, peerId)]
|
||||
var peerName = ""
|
||||
if let name = message.peers[peerId].flatMap(EnginePeer.init)?.compactDisplayTitle {
|
||||
peerName = name
|
||||
}
|
||||
if let senderId {
|
||||
peerIds.insert((0, senderId), at: 0)
|
||||
if let name = message.peers[senderId].flatMap(EnginePeer.init)?.compactDisplayTitle {
|
||||
authorName = name
|
||||
}
|
||||
}
|
||||
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
||||
attributes[2] = boldAttributes
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Channel_Sent(authorName, peerName, starsPrice)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
} else {
|
||||
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
||||
attributes[1] = boldAttributes
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Sent(authorName, starsPrice)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
}
|
||||
}
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _):
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, _, _, _):
|
||||
if case let .unique(gift) = gift {
|
||||
if !forAdditionalServiceMessage {
|
||||
attributedString = NSAttributedString(string: "\(gift.title) #\(gift.number)", font: titleFont, textColor: primaryTextColor)
|
||||
|
@ -13,6 +13,8 @@ import ChatPresentationInterfaceState
|
||||
import ChatInputPanelNode
|
||||
import AccountContext
|
||||
import OldChannelsController
|
||||
import TooltipUI
|
||||
import TelegramNotices
|
||||
|
||||
private enum SubscriberAction: Equatable {
|
||||
case join
|
||||
@ -146,6 +148,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
private let activityIndicator: UIActivityIndicatorView
|
||||
|
||||
private let helpButton: HighlightableButtonNode
|
||||
private let giftButton: HighlightableButtonNode
|
||||
|
||||
private var action: SubscriberAction?
|
||||
|
||||
@ -176,6 +179,9 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
self.badgeText.isHidden = true
|
||||
|
||||
self.helpButton = HighlightableButtonNode()
|
||||
self.helpButton.isHidden = true
|
||||
self.giftButton = HighlightableButtonNode()
|
||||
self.giftButton.isHidden = true
|
||||
|
||||
self.discussButton.addSubnode(self.discussButtonText)
|
||||
self.discussButton.addSubnode(self.badgeBackground)
|
||||
@ -189,10 +195,12 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
self.addSubnode(self.discussButton)
|
||||
self.view.addSubview(self.activityIndicator)
|
||||
self.addSubnode(self.helpButton)
|
||||
self.addSubnode(self.giftButton)
|
||||
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
self.discussButton.addTarget(self, action: #selector(self.discussPressed), forControlEvents: .touchUpInside)
|
||||
self.helpButton.addTarget(self, action: #selector(self.helpPressed), forControlEvents: .touchUpInside)
|
||||
self.giftButton.addTarget(self, action: #selector(self.giftPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -207,6 +215,10 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
@objc private func giftPressed() {
|
||||
self.interfaceInteraction?.openPremiumGift()
|
||||
}
|
||||
|
||||
@objc private func helpPressed() {
|
||||
self.interfaceInteraction?.presentGigagroupHelp()
|
||||
}
|
||||
@ -301,6 +313,51 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
return self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: transition, interfaceState: interfaceState, metrics: metrics, force: false)
|
||||
}
|
||||
|
||||
private var displayedGiftTooltip = false
|
||||
private func presentGiftTooltip() {
|
||||
guard let context = self.context, !self.displayedGiftTooltip else {
|
||||
return
|
||||
}
|
||||
self.displayedGiftTooltip = true
|
||||
|
||||
let _ = (ApplicationSpecificNotice.getChannelSendGiftTooltip(accountManager: context.sharedContext.accountManager)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] count in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard count < 2 else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = ApplicationSpecificNotice.incrementChannelSendGiftTooltip(accountManager: context.sharedContext.accountManager).start()
|
||||
|
||||
Queue.mainQueue().after(0.4, {
|
||||
let absoluteFrame = self.giftButton.view.convert(self.giftButton.bounds, to: nil)
|
||||
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY + 11.0), size: CGSize())
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let text: String = presentationData.strings.Chat_SendGiftTooltip
|
||||
|
||||
let tooltipController = TooltipScreen(
|
||||
account: context.account,
|
||||
sharedContext: context.sharedContext,
|
||||
text: .plain(text: text),
|
||||
balancedTextLayout: false,
|
||||
style: .wide,
|
||||
arrowStyle: .small,
|
||||
icon: nil,
|
||||
location: .point(location, .bottom),
|
||||
displayDuration: .default,
|
||||
inset: 8.0,
|
||||
shouldDismissOnTouch: { _, _ in
|
||||
return .ignore
|
||||
}
|
||||
)
|
||||
self.interfaceInteraction?.presentControllerInCurrent(tooltipController, nil)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
private func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, force: Bool) -> CGFloat {
|
||||
self.layoutData = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, isSecondary, metrics)
|
||||
|
||||
@ -311,6 +368,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
if previousState?.theme !== interfaceState.theme {
|
||||
self.badgeBackground.image = PresentationResourcesChatList.badgeBackgroundActive(interfaceState.theme, diameter: 20.0)
|
||||
self.helpButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/Help"), color: interfaceState.theme.chat.inputPanel.panelControlAccentColor), for: .normal)
|
||||
self.giftButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/Gift"), color: interfaceState.theme.chat.inputPanel.panelControlAccentColor), for: .normal)
|
||||
}
|
||||
|
||||
if let context = self.context, let peer = interfaceState.renderedPeer?.peer, previousState?.renderedPeer?.peer == nil || !peer.isEqual(previousState!.renderedPeer!.peer!) || previousState?.theme !== interfaceState.theme || previousState?.strings !== interfaceState.strings || previousState?.peerIsMuted != interfaceState.peerIsMuted || previousState?.pinnedMessage != interfaceState.pinnedMessage || force {
|
||||
@ -356,17 +414,29 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
let buttonWidth = self.button.calculateSizeThatFits(CGSize(width: width, height: panelHeight)).width + 24.0
|
||||
self.button.frame = CGRect(origin: CGPoint(x: floor((width - buttonWidth) / 2.0), y: 0.0), size: CGSize(width: buttonWidth, height: panelHeight))
|
||||
|
||||
if let peer = interfaceState.renderedPeer?.peer as? TelegramChannel, peer.flags.contains(.isGigagroup) {
|
||||
self.helpButton.isHidden = false
|
||||
if let peer = interfaceState.renderedPeer?.peer as? TelegramChannel {
|
||||
if case .broadcast = peer.info {
|
||||
self.giftButton.isHidden = false
|
||||
self.helpButton.isHidden = true
|
||||
|
||||
self.presentGiftTooltip()
|
||||
} else if peer.flags.contains(.isGigagroup) {
|
||||
self.giftButton.isHidden = true
|
||||
self.helpButton.isHidden = false
|
||||
}
|
||||
} else {
|
||||
self.giftButton.isHidden = true
|
||||
self.helpButton.isHidden = true
|
||||
}
|
||||
} else {
|
||||
self.button.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: width - leftInset - rightInset, height: panelHeight))
|
||||
self.giftButton.isHidden = true
|
||||
self.helpButton.isHidden = true
|
||||
}
|
||||
self.giftButton.frame = CGRect(x: width - rightInset - panelHeight - 5.0, y: -3.0, width: panelHeight, height: panelHeight)
|
||||
self.helpButton.frame = CGRect(x: width - rightInset - panelHeight, y: 0.0, width: panelHeight, height: panelHeight)
|
||||
} else {
|
||||
self.giftButton.isHidden = true
|
||||
self.helpButton.isHidden = true
|
||||
|
||||
let availableWidth = min(600.0, width - leftInset - rightInset)
|
||||
|
@ -469,16 +469,19 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
buttonTitle = item.presentationData.strings.Notification_PremiumPrize_View
|
||||
hasServiceMessage = false
|
||||
}
|
||||
case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, _, _, _):
|
||||
case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, _, channelPeerId, senderPeerId, _):
|
||||
if case let .generic(gift) = gift {
|
||||
isStarGift = true
|
||||
let authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||
var authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||
|
||||
let isSelfGift = item.message.id.peerId == item.context.account.peerId
|
||||
let isChannelGift = item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel
|
||||
let isChannelGift = item.message.id.peerId.namespace == Namespaces.Peer.CloudChannel || channelPeerId != nil
|
||||
if isSelfGift {
|
||||
title = item.presentationData.strings.Notification_StarGift_Self_Title
|
||||
} else {
|
||||
if let senderPeerId, let name = item.message.peers[senderPeerId].flatMap(EnginePeer.init)?.compactDisplayTitle {
|
||||
authorName = name
|
||||
}
|
||||
title = item.presentationData.strings.Notification_StarGift_Title(authorName).string
|
||||
}
|
||||
if let giftText, !giftText.isEmpty {
|
||||
@ -554,7 +557,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
buttonTitle = item.presentationData.strings.Notification_StarGift_View
|
||||
}
|
||||
}
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, isRefunded):
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, isRefunded, _, _, _):
|
||||
if case let .unique(uniqueGift) = gift {
|
||||
isStarGift = true
|
||||
let authorName: String
|
||||
|
@ -328,7 +328,7 @@ public extension EmojiPagerContentComponent {
|
||||
} else {
|
||||
itemGroupIndexById[groupId] = itemGroups.count
|
||||
|
||||
let title = context.sharedContext.currentPresentationData.with({ $0 }).strings.EmojiInput_TrendingEmoji
|
||||
let title = strings.EmojiInput_TrendingEmoji
|
||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: title, subtitle: nil, badge: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 0, isClearable: false, headerItem: nil, items: [resultItem]))
|
||||
}
|
||||
}
|
||||
@ -614,7 +614,6 @@ public extension EmojiPagerContentComponent {
|
||||
}
|
||||
|
||||
if let uniqueGifts, !uniqueGifts.items.isEmpty {
|
||||
//TODO:localize
|
||||
let groupId = "collectible"
|
||||
let groupIndex: Int
|
||||
if let current = itemGroupIndexById[groupId] {
|
||||
@ -622,7 +621,7 @@ public extension EmojiPagerContentComponent {
|
||||
} else {
|
||||
groupIndex = itemGroups.count
|
||||
itemGroupIndexById[groupId] = groupIndex
|
||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: "COLLECTIBLES".uppercased(), subtitle: nil, badge: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 2, isClearable: false, headerItem: nil, items: []))
|
||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.EmojiInput_SectionTitleCollectibles.uppercased(), subtitle: nil, badge: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 2, isClearable: false, headerItem: nil, items: []))
|
||||
}
|
||||
|
||||
for item in uniqueGifts.items {
|
||||
|
@ -642,8 +642,7 @@ final class GiftOptionsScreenComponent: Component {
|
||||
if isSelfGift {
|
||||
premiumTitleString = strings.Gift_Options_GiftSelf_Title
|
||||
} else if isChannelGift {
|
||||
//TODO:localize
|
||||
premiumTitleString = "Send a Gift"
|
||||
premiumTitleString = strings.Gift_Options_GiftChannel_Title
|
||||
} else {
|
||||
premiumTitleString = strings.Gift_Options_Premium_Title
|
||||
}
|
||||
@ -675,8 +674,7 @@ final class GiftOptionsScreenComponent: Component {
|
||||
if isSelfGift {
|
||||
premiumDescriptionRawString = strings.Gift_Options_GiftSelf_Text
|
||||
} else if isChannelGift {
|
||||
//TODO:localize
|
||||
premiumDescriptionRawString = "Select a gift to show appreciation for **\(peerName)**."
|
||||
premiumDescriptionRawString = strings.Gift_Options_GiftChannel_Text(peerName).string
|
||||
} else {
|
||||
premiumDescriptionRawString = strings.Gift_Options_Premium_Text(peerName).string
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ final class ChatGiftPreviewItemNode: ListViewItemNode {
|
||||
case let .starGift(gift):
|
||||
media = [
|
||||
TelegramMediaAction(
|
||||
action: .starGift(gift: .generic(gift), convertStars: gift.convertStars, text: item.text, entities: item.entities, nameHidden: false, savedToProfile: false, converted: false, upgraded: false, canUpgrade: true, upgradeStars: item.upgradeStars, isRefunded: false, upgradeMessageId: nil, peerId: nil, senderId: nil)
|
||||
action: .starGift(gift: .generic(gift), convertStars: gift.convertStars, text: item.text, entities: item.entities, nameHidden: false, savedToProfile: false, converted: false, upgraded: false, canUpgrade: true, upgradeStars: item.upgradeStars, isRefunded: false, upgradeMessageId: nil, peerId: nil, senderId: nil, savedId: nil)
|
||||
)
|
||||
]
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import BlurredBackgroundComponent
|
||||
import ProgressNavigationButtonNode
|
||||
import Markdown
|
||||
import GiftViewScreen
|
||||
import UndoUI
|
||||
|
||||
final class GiftSetupScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
@ -321,7 +322,16 @@ final class GiftSetupScreenComponent: Component {
|
||||
guard let component = self.component, case let .starGift(starGift) = component.subject, let starsContext = component.context.starsContext, let starsState = starsContext.currentState else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let context = component.context
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let peerId = component.peerId
|
||||
|
||||
var finalPrice = starGift.price
|
||||
if self.includeUpgrade, let upgradeStars = starGift.upgradeStars {
|
||||
finalPrice += upgradeStars
|
||||
}
|
||||
|
||||
let proceed = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
@ -331,7 +341,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
self.state?.updated()
|
||||
|
||||
let entities = generateChatInputTextEntities(self.textInputState.text)
|
||||
let source: BotPaymentInvoiceSource = .starGift(hideName: self.hideName, includeUpgrade: self.includeUpgrade, peerId: component.peerId, giftId: starGift.id, text: self.textInputState.text.string, entities: entities)
|
||||
let source: BotPaymentInvoiceSource = .starGift(hideName: self.hideName, includeUpgrade: self.includeUpgrade, peerId: peerId, giftId: starGift.id, text: self.textInputState.text.string, entities: entities)
|
||||
|
||||
let inputData = BotCheckoutController.InputData.fetch(context: component.context, source: source)
|
||||
|> map(Optional.init)
|
||||
@ -359,22 +369,43 @@ final class GiftSetupScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { !($0 is GiftSetupScreen) && !($0 is GiftOptionsScreenProtocol) && !($0 is PeerInfoScreen) && !($0 is ContactSelectionController) }
|
||||
var foundController = false
|
||||
for controller in controllers.reversed() {
|
||||
if let chatController = controller as? ChatController, case .peer(id: component.peerId) = chatController.chatLocation {
|
||||
chatController.hintPlayNextOutgoingGift()
|
||||
foundController = true
|
||||
break
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { !($0 is GiftSetupScreen) && !($0 is GiftOptionsScreenProtocol) }
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
|
||||
let tooltipController = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .sticker(
|
||||
context: context,
|
||||
file: starGift.file,
|
||||
loop: true,
|
||||
title: nil,
|
||||
text: presentationData.strings.Gift_Send_Success(self.peerMap[peerId]?.compactDisplayTitle ?? "", presentationData.strings.Gift_Send_Success_Stars(Int32(starGift.price))).string,
|
||||
undoText: nil,
|
||||
customAction: nil
|
||||
),
|
||||
action: { _ in return true }
|
||||
)
|
||||
(navigationController.viewControllers.last as? ViewController)?.present(tooltipController, in: .current)
|
||||
} else {
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { !($0 is GiftSetupScreen) && !($0 is GiftOptionsScreenProtocol) && !($0 is PeerInfoScreen) && !($0 is ContactSelectionController) }
|
||||
var foundController = false
|
||||
for controller in controllers.reversed() {
|
||||
if let chatController = controller as? ChatController, case .peer(id: component.peerId) = chatController.chatLocation {
|
||||
chatController.hintPlayNextOutgoingGift()
|
||||
foundController = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundController {
|
||||
let chatController = component.context.sharedContext.makeChatController(context: component.context, chatLocation: .peer(id: component.peerId), subject: nil, botStart: nil, mode: .standard(.default), params: nil)
|
||||
chatController.hintPlayNextOutgoingGift()
|
||||
controllers.append(chatController)
|
||||
}
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
if !foundController {
|
||||
let chatController = component.context.sharedContext.makeChatController(context: component.context, chatLocation: .peer(id: component.peerId), subject: nil, botStart: nil, mode: .standard(.default), params: nil)
|
||||
chatController.hintPlayNextOutgoingGift()
|
||||
controllers.append(chatController)
|
||||
}
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
|
||||
starsContext.load(force: true)
|
||||
@ -403,7 +434,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
})
|
||||
}
|
||||
|
||||
if starsState.balance < StarsAmount(value: starGift.price, nanos: 0) {
|
||||
if starsState.balance < StarsAmount(value: finalPrice, nanos: 0) {
|
||||
let _ = (self.optionsPromise.get()
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
@ -415,7 +446,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
context: component.context,
|
||||
starsContext: starsContext,
|
||||
options: options ?? [],
|
||||
purpose: .starGift(peerId: component.peerId, requiredStars: starGift.price),
|
||||
purpose: .starGift(peerId: component.peerId, requiredStars: finalPrice),
|
||||
completion: { [weak self, weak starsContext] stars in
|
||||
guard let self, let starsContext else {
|
||||
return
|
||||
@ -642,8 +673,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
if isSelfGift {
|
||||
navigationTitleString = environment.strings.Gift_SendSelf_Title
|
||||
} else if isChannelGift {
|
||||
//TODO:localize
|
||||
navigationTitleString = "Gift Preview"
|
||||
navigationTitleString = environment.strings.Gift_SendChannel_Title
|
||||
} else {
|
||||
navigationTitleString = environment.strings.Gift_Send_TitleTo(peerName).string
|
||||
}
|
||||
@ -988,8 +1018,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
if isSelfGift {
|
||||
hideSectionFooterString = environment.strings.Gift_SendSelf_HideMyName_Info
|
||||
} else if isChannelGift {
|
||||
//TODO:localize
|
||||
hideSectionFooterString = "Hide my name and message from visitors of this channel. The channel admins will still see them."
|
||||
hideSectionFooterString = environment.strings.Gift_SendChannel_HideMyName_Info
|
||||
} else {
|
||||
hideSectionFooterString = environment.strings.Gift_Send_HideMyName_Info(peerName, peerName).string
|
||||
}
|
||||
|
@ -385,6 +385,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
let strings = environment.strings
|
||||
let dateTimeFormat = environment.dateTimeFormat
|
||||
let nameDisplayOrder = component.context.sharedContext.currentPresentationData.with { $0 }.nameDisplayOrder
|
||||
let controller = environment.controller
|
||||
|
||||
let state = context.state
|
||||
let subject = state.subject
|
||||
@ -410,6 +411,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
var upgradeStars: Int64?
|
||||
var uniqueGift: StarGift.UniqueGift?
|
||||
var isSelfGift = false
|
||||
var isChannelGift = false
|
||||
|
||||
if case let .soldOutGift(gift) = subject {
|
||||
animationFile = gift.file
|
||||
@ -444,7 +446,12 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
uniqueGift = gift
|
||||
}
|
||||
savedToProfile = arguments.savedToProfile
|
||||
incoming = arguments.incoming || arguments.peerId == component.context.account.peerId
|
||||
if let reference = arguments.reference, case .peer = reference {
|
||||
isChannelGift = true
|
||||
incoming = true
|
||||
} else {
|
||||
incoming = arguments.incoming || arguments.peerId == component.context.account.peerId
|
||||
}
|
||||
nameHidden = arguments.nameHidden
|
||||
|
||||
isSelfGift = arguments.messageId?.peerId == component.context.account.peerId
|
||||
@ -482,6 +489,9 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
return
|
||||
}
|
||||
if state.inWearPreview {
|
||||
if let controller = controller() as? GiftViewScreen {
|
||||
controller.dismissAllTooltips()
|
||||
}
|
||||
state.inWearPreview = false
|
||||
state.updated(transition: .spring(duration: 0.4))
|
||||
} else if state.inUpgradePreview {
|
||||
@ -849,8 +859,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/Collectible/Tradable",
|
||||
iconColor: linkColor,
|
||||
badge: strings.Gift_Upgrade_Soon
|
||||
iconColor: linkColor
|
||||
))
|
||||
)
|
||||
)
|
||||
@ -937,9 +946,9 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
} else if let convertStars, !upgraded {
|
||||
if !converted {
|
||||
if canUpgrade || upgradeStars != nil {
|
||||
descriptionText = strings.Gift_View_KeepUpgradeOrConvertDescription(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(convertStars))).string
|
||||
descriptionText = isChannelGift ? strings.Gift_View_KeepUpgradeOrConvertDescription_Channel(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(convertStars))).string : strings.Gift_View_KeepUpgradeOrConvertDescription(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(convertStars))).string
|
||||
} else {
|
||||
descriptionText = strings.Gift_View_KeepOrConvertDescription(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(convertStars))).string
|
||||
descriptionText = isChannelGift ? strings.Gift_View_KeepOrConvertDescription_Channel(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(convertStars))).string : strings.Gift_View_KeepOrConvertDescription(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(convertStars))).string
|
||||
}
|
||||
} else {
|
||||
descriptionText = strings.Gift_View_ConvertedDescription(strings.Gift_View_ConvertedDescription_Stars(Int32(convertStars))).string
|
||||
@ -1152,7 +1161,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
),
|
||||
action: {
|
||||
component.openPeer(peer)
|
||||
Queue.mainQueue().after(1.0, {
|
||||
Queue.mainQueue().after(0.6, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
@ -1188,7 +1197,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
),
|
||||
action: {
|
||||
component.openPeer(peer)
|
||||
Queue.mainQueue().after(1.0, {
|
||||
Queue.mainQueue().after(0.6, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
@ -1215,7 +1224,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
isBot = true
|
||||
}
|
||||
let fromComponent: AnyComponent<Empty>
|
||||
if incoming && !peer.isDeleted && !isBot {
|
||||
if incoming && !peer.isDeleted && !isBot && !isChannelGift {
|
||||
fromComponent = AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(
|
||||
@ -1231,7 +1240,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
),
|
||||
action: {
|
||||
component.openPeer(peer)
|
||||
Queue.mainQueue().after(1.0, {
|
||||
Queue.mainQueue().after(0.6, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
@ -1247,7 +1256,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
)),
|
||||
action: {
|
||||
component.sendGift(peerId)
|
||||
Queue.mainQueue().after(1.0, {
|
||||
Queue.mainQueue().after(0.6, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
@ -1267,7 +1276,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
),
|
||||
action: {
|
||||
component.openPeer(peer)
|
||||
Queue.mainQueue().after(1.0, {
|
||||
Queue.mainQueue().after(0.6, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
@ -1315,7 +1324,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
effectAlignment: .center,
|
||||
action: {
|
||||
component.transferGift()
|
||||
Queue.mainQueue().after(1.0, {
|
||||
Queue.mainQueue().after(0.6, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
@ -1326,9 +1335,10 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
)
|
||||
context.add(transferButton
|
||||
.position(CGPoint(x: sideInset + buttonWidth / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0))
|
||||
.appear(.default(scale: true, alpha: true))
|
||||
.disappear(.default(scale: true, alpha: true))
|
||||
)
|
||||
|
||||
let controller = environment.controller
|
||||
let wearButton = wearButton.update(
|
||||
component: PlainButtonComponent(
|
||||
content: AnyComponent(
|
||||
@ -1345,7 +1355,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
state.pendingWear = false
|
||||
state.updated(transition: .spring(duration: 0.4))
|
||||
|
||||
component.showAttributeInfo(statusTag, "You took off \(uniqueGift.title) #\(uniqueGift.number)")
|
||||
component.showAttributeInfo(statusTag, strings.Gift_View_TookOff("\(uniqueGift.title) #\(uniqueGift.number)").string)
|
||||
} else {
|
||||
if let controller = controller() as? GiftViewScreen {
|
||||
controller.dismissAllTooltips()
|
||||
@ -1362,6 +1372,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
)
|
||||
context.add(wearButton
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0))
|
||||
.appear(.default(scale: true, alpha: true))
|
||||
.disappear(.default(scale: true, alpha: true))
|
||||
)
|
||||
|
||||
let shareButton = shareButton.update(
|
||||
@ -1383,6 +1395,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
)
|
||||
context.add(shareButton
|
||||
.position(CGPoint(x: context.availableSize.width - sideInset - buttonWidth / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0))
|
||||
.appear(.default(scale: true, alpha: true))
|
||||
.disappear(.default(scale: true, alpha: true))
|
||||
)
|
||||
}
|
||||
|
||||
@ -1506,7 +1520,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
if let mention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention, let peer = state.peerMap[mention.peerId] {
|
||||
component.openPeer(peer)
|
||||
Queue.mainQueue().after(1.0, {
|
||||
Queue.mainQueue().after(0.6, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
@ -1734,11 +1748,11 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
let descriptionText: String
|
||||
if savedToProfile {
|
||||
descriptionText = strings.Gift_View_DisplayedInfoHide
|
||||
descriptionText = isChannelGift ? strings.Gift_View_DisplayedInfoHide_Channel : strings.Gift_View_DisplayedInfoHide
|
||||
} else if let upgradeStars, upgradeStars > 0 && !upgraded {
|
||||
descriptionText = strings.Gift_View_HiddenInfoShow
|
||||
descriptionText = isChannelGift ? strings.Gift_View_HiddenInfoShow_Channel : strings.Gift_View_HiddenInfoShow
|
||||
} else {
|
||||
descriptionText = strings.Gift_View_HiddenInfo
|
||||
descriptionText = isChannelGift ? strings.Gift_View_HiddenInfo_Channel : strings.Gift_View_HiddenInfo
|
||||
}
|
||||
|
||||
let textFont = Font.regular(13.0)
|
||||
@ -1769,7 +1783,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
},
|
||||
tapAction: { _, _ in
|
||||
component.updateSavedToProfile(!savedToProfile)
|
||||
Queue.mainQueue().after(1.0, {
|
||||
Queue.mainQueue().after(0.6, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
@ -1794,26 +1808,74 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
)
|
||||
let buttonChild: _UpdatedChildComponent
|
||||
if state.inWearPreview, let uniqueGift {
|
||||
let buttonContent: AnyComponentWithIdentity<Empty>
|
||||
if !component.context.isPremium {
|
||||
buttonContent = AnyComponentWithIdentity(
|
||||
id: AnyHashable("wear_locked"),
|
||||
component: AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable("icon"),
|
||||
component: AnyComponent(BundleIconComponent(name: "Chat/Stickers/Lock", tintColor: theme.list.itemCheckColors.foregroundColor))
|
||||
),
|
||||
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))))
|
||||
)
|
||||
], spacing: 3.0)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
buttonContent = AnyComponentWithIdentity(
|
||||
id: AnyHashable("wear"),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Wear_Start, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
)
|
||||
}
|
||||
|
||||
buttonChild = button.update(
|
||||
component: ButtonComponent(
|
||||
background: buttonBackground,
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("wear"),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Wear_Start, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
),
|
||||
content: buttonContent,
|
||||
isEnabled: true,
|
||||
displaysProgress: false,
|
||||
action: { [weak state] in
|
||||
if let state {
|
||||
state.pendingWear = true
|
||||
state.pendingTakeOff = false
|
||||
state.inWearPreview = false
|
||||
state.updated(transition: .spring(duration: 0.4))
|
||||
|
||||
let _ = component.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).start()
|
||||
|
||||
Queue.mainQueue().after(0.2) {
|
||||
component.showAttributeInfo(statusTag, "You put on \(uniqueGift.title) #\(uniqueGift.number)")
|
||||
let context = component.context
|
||||
if !context.isPremium, 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)
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
)
|
||||
controller.present(tooltipController, in: .window(.root))
|
||||
} else {
|
||||
state.pendingWear = true
|
||||
state.pendingTakeOff = false
|
||||
state.inWearPreview = false
|
||||
state.updated(transition: .spring(duration: 0.4))
|
||||
|
||||
let _ = component.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).start()
|
||||
|
||||
Queue.mainQueue().after(0.2) {
|
||||
component.showAttributeInfo(statusTag, strings.Gift_View_PutOn("\(uniqueGift.title) #\(uniqueGift.number)").string)
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
@ -1901,7 +1963,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
transition: context.transition
|
||||
)
|
||||
} else if incoming && !converted && !savedToProfile {
|
||||
let buttonTitle = savedToProfile ? strings.Gift_View_Hide : strings.Gift_View_Display
|
||||
let buttonTitle = isChannelGift ? strings.Gift_View_Display_Channel : strings.Gift_View_Display
|
||||
buttonChild = button.update(
|
||||
component: ButtonComponent(
|
||||
background: buttonBackground,
|
||||
@ -2131,9 +2193,21 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
case let .message(message):
|
||||
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
|
||||
switch action.action {
|
||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, _, upgradeMessageId, _, _):
|
||||
return (message.id.peerId, message.author?.id, message.author?.compactDisplayTitle, message.id, .message(messageId: message.id), message.flags.contains(.Incoming), gift, message.timestamp, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, nil, nil, upgradeMessageId)
|
||||
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, _):
|
||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, _, upgradeMessageId, peerId, senderId, savedId):
|
||||
var reference: StarGiftReference
|
||||
if let peerId, let savedId {
|
||||
reference = .peer(peerId: peerId, id: savedId)
|
||||
} else {
|
||||
reference = .message(messageId: message.id)
|
||||
}
|
||||
return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, message.flags.contains(.Incoming), gift, message.timestamp, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, nil, nil, upgradeMessageId)
|
||||
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, _, peerId, senderId, savedId):
|
||||
var reference: StarGiftReference
|
||||
if let peerId, let savedId {
|
||||
reference = .peer(peerId: peerId, id: savedId)
|
||||
} else {
|
||||
reference = .message(messageId: message.id)
|
||||
}
|
||||
var incoming = false
|
||||
if isUpgrade {
|
||||
if message.author?.id != message.id.peerId {
|
||||
@ -2146,7 +2220,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
} else {
|
||||
incoming = message.flags.contains(.Incoming)
|
||||
}
|
||||
return (message.id.peerId, message.author?.id, message.author?.compactDisplayTitle, message.id, .message(messageId: message.id), incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, false, false, false, nil, transferStars, canExportDate, nil)
|
||||
return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, false, false, false, nil, transferStars, canExportDate, nil)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@ -2545,7 +2619,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
return
|
||||
}
|
||||
openPeerImpl?(peer)
|
||||
Queue.mainQueue().after(1.0) {
|
||||
Queue.mainQueue().after(0.6) {
|
||||
self?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
})
|
||||
@ -3398,13 +3472,16 @@ private final class GiftViewContextReferenceContentSource: ContextReferenceConte
|
||||
private final class HeaderButtonComponent: CombinedComponent {
|
||||
let title: String
|
||||
let iconName: String
|
||||
let isLocked: Bool
|
||||
|
||||
public init(
|
||||
title: String,
|
||||
iconName: String
|
||||
iconName: String,
|
||||
isLocked: Bool = false
|
||||
) {
|
||||
self.title = title
|
||||
self.iconName = iconName
|
||||
self.isLocked = isLocked
|
||||
}
|
||||
|
||||
static func ==(lhs: HeaderButtonComponent, rhs: HeaderButtonComponent) -> Bool {
|
||||
@ -3414,6 +3491,9 @@ private final class HeaderButtonComponent: CombinedComponent {
|
||||
if lhs.iconName != rhs.iconName {
|
||||
return false
|
||||
}
|
||||
if lhs.isLocked != rhs.isLocked {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -3421,6 +3501,7 @@ private final class HeaderButtonComponent: CombinedComponent {
|
||||
let background = Child(RoundedRectangle.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let icon = Child(BundleIconComponent.self)
|
||||
let lockIcon = Child(BundleIconComponent.self)
|
||||
|
||||
return { context in
|
||||
let component = context.component
|
||||
@ -3448,7 +3529,7 @@ private final class HeaderButtonComponent: CombinedComponent {
|
||||
context.add(icon
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 22.0))
|
||||
)
|
||||
|
||||
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
@ -3463,8 +3544,27 @@ private final class HeaderButtonComponent: CombinedComponent {
|
||||
availableSize: CGSize(width: context.availableSize.width - 16.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
var totalTitleWidth = title.size.width
|
||||
var titleOriginX = context.availableSize.width / 2.0 - totalTitleWidth / 2.0
|
||||
if component.isLocked {
|
||||
let titleSpacing: CGFloat = 2.0
|
||||
let lockIcon = lockIcon.update(
|
||||
component: BundleIconComponent(
|
||||
name: "Chat List/StatusLockIcon",
|
||||
tintColor: UIColor.white
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
transition: .immediate
|
||||
)
|
||||
totalTitleWidth += lockIcon.size.width + titleSpacing
|
||||
titleOriginX = context.availableSize.width / 2.0 - totalTitleWidth / 2.0
|
||||
context.add(lockIcon
|
||||
.position(CGPoint(x: titleOriginX + lockIcon.size.width / 2.0, y: 42.0))
|
||||
)
|
||||
titleOriginX += lockIcon.size.width + titleSpacing
|
||||
}
|
||||
context.add(title
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 42.0))
|
||||
.position(CGPoint(x: titleOriginX + title.size.width / 2.0, y: 42.0))
|
||||
)
|
||||
|
||||
return context.availableSize
|
||||
|
@ -173,7 +173,8 @@ private final class GiftWithdrawAlertContentNode: AlertContentNode {
|
||||
photo: nil,
|
||||
media: [],
|
||||
uniqueGift: nil,
|
||||
backgroundColor: .clear
|
||||
backgroundColor: .clear,
|
||||
size: avatarSize
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
|
@ -0,0 +1,45 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import TextFormat
|
||||
|
||||
public extension MediaEditorScreenImpl {
|
||||
static func makeEditVideoCoverController(
|
||||
context: AccountContext,
|
||||
video: MediaEditorScreenImpl.Subject,
|
||||
completed: @escaping () -> Void = {},
|
||||
willDismiss: @escaping () -> Void = {},
|
||||
update: @escaping (Disposable?) -> Void
|
||||
) -> MediaEditorScreenImpl? {
|
||||
let controller = MediaEditorScreenImpl(
|
||||
context: context,
|
||||
mode: .storyEditor,
|
||||
subject: .single(video),
|
||||
isEditing: true,
|
||||
isEditingCover: true,
|
||||
forwardSource: nil,
|
||||
initialCaption: nil,
|
||||
initialPrivacy: nil,
|
||||
initialMediaAreas: nil,
|
||||
initialVideoPosition: 0.0,
|
||||
transitionIn: .noAnimation,
|
||||
transitionOut: { finished, isNew in
|
||||
return nil
|
||||
},
|
||||
completion: { result, commit in
|
||||
if let _ = result.coverTimestamp {
|
||||
|
||||
}
|
||||
commit({})
|
||||
}
|
||||
)
|
||||
controller.willDismiss = willDismiss
|
||||
controller.navigationPresentation = .flatModal
|
||||
|
||||
return controller
|
||||
}
|
||||
}
|
@ -864,7 +864,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
case .storyEditor:
|
||||
doneButtonTitle = isEditingStory ? environment.strings.Story_Editor_Done.uppercased() : environment.strings.Story_Editor_Next.uppercased()
|
||||
doneButtonIcon = UIImage(bundleImageName: "Media Editor/Next")!
|
||||
case .stickerEditor, .avatarEditor:
|
||||
case .stickerEditor, .avatarEditor, .coverEditor:
|
||||
doneButtonTitle = nil
|
||||
doneButtonIcon = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/Apply"), color: .white)!
|
||||
case .botPreview:
|
||||
@ -1060,9 +1060,14 @@ final class MediaEditorScreenComponent: Component {
|
||||
)
|
||||
|
||||
var isAvatarEditor = false
|
||||
var isCoverEditor = false
|
||||
if case .avatarEditor = controller.mode {
|
||||
isAvatarEditor = true
|
||||
|
||||
} else if case .coverEditor = controller.mode {
|
||||
isCoverEditor = true
|
||||
}
|
||||
|
||||
if isAvatarEditor || isCoverEditor {
|
||||
drawButtonFrame.origin.x = stickerButtonFrame.origin.x
|
||||
|
||||
if let rotateButtonView = self.rotateButton.view {
|
||||
@ -1104,7 +1109,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
if !isAvatarEditor, let textButtonView = self.textButton.view {
|
||||
if !isAvatarEditor && !isCoverEditor, let textButtonView = self.textButton.view {
|
||||
if textButtonView.superview == nil {
|
||||
self.addSubview(textButtonView)
|
||||
}
|
||||
@ -1115,7 +1120,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
if !isAvatarEditor, let stickerButtonView = self.stickerButton.view {
|
||||
if !isAvatarEditor && !isCoverEditor, let stickerButtonView = self.stickerButton.view {
|
||||
if stickerButtonView.superview == nil {
|
||||
self.addSubview(stickerButtonView)
|
||||
}
|
||||
@ -2688,6 +2693,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
case stickerEditor(mode: StickerEditorMode)
|
||||
case botPreview
|
||||
case avatarEditor
|
||||
case coverEditor(dimensions: CGSize)
|
||||
}
|
||||
|
||||
public enum TransitionIn {
|
||||
@ -2882,14 +2888,17 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
|
||||
var isStickerEditor = false
|
||||
var isAvatarEditor = false
|
||||
var isCoverEditor = false
|
||||
if case .stickerEditor = controller.mode {
|
||||
isStickerEditor = true
|
||||
} else if case .avatarEditor = controller.mode {
|
||||
isAvatarEditor = true
|
||||
} else if case .coverEditor = controller.mode {
|
||||
isCoverEditor = true
|
||||
}
|
||||
|
||||
self.entitiesContainerView = UIView(frame: CGRect(origin: .zero, size: storyDimensions))
|
||||
self.entitiesView = DrawingEntitiesView(context: controller.context, size: storyDimensions, hasBin: !isStickerEditor && !isAvatarEditor, isStickerEditor: isStickerEditor)
|
||||
self.entitiesView = DrawingEntitiesView(context: controller.context, size: storyDimensions, hasBin: !isStickerEditor && !isAvatarEditor && !isCoverEditor, isStickerEditor: isStickerEditor)
|
||||
self.entitiesView.getEntityCenterPosition = {
|
||||
return CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0)
|
||||
}
|
||||
@ -2957,6 +2966,10 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
let stickerBackgroundView = UIImageView()
|
||||
self.stickerBackgroundView = stickerBackgroundView
|
||||
self.previewContainerView.addSubview(stickerBackgroundView)
|
||||
case .coverEditor:
|
||||
let stickerBackgroundView = UIImageView()
|
||||
self.stickerBackgroundView = stickerBackgroundView
|
||||
self.previewContainerView.addSubview(stickerBackgroundView)
|
||||
default:
|
||||
self.previewContainerView.addSubview(self.gradientView)
|
||||
}
|
||||
@ -2970,7 +2983,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
self.entitiesView.addSubview(self.drawingView)
|
||||
|
||||
switch controller.mode {
|
||||
case .stickerEditor, .avatarEditor:
|
||||
case .stickerEditor, .avatarEditor, .coverEditor:
|
||||
let stickerOverlayLayer = SimpleShapeLayer()
|
||||
stickerOverlayLayer.fillColor = UIColor(rgb: 0x000000, alpha: 0.7).cgColor
|
||||
stickerOverlayLayer.fillRule = .evenOdd
|
||||
@ -3174,7 +3187,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
} else {
|
||||
mediaEntity.scale = storyDimensions.width / fittedSize.width
|
||||
}
|
||||
case .stickerEditor, .avatarEditor:
|
||||
case .stickerEditor, .avatarEditor, .coverEditor:
|
||||
if fittedSize.height > fittedSize.width {
|
||||
mediaEntity.scale = storyDimensions.width / fittedSize.width
|
||||
} else {
|
||||
@ -3216,6 +3229,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
mediaEditorMode = .sticker
|
||||
} else if case .avatarEditor = controller.mode {
|
||||
mediaEditorMode = .avatar
|
||||
} else if case .coverEditor = controller.mode {
|
||||
mediaEditorMode = .avatar
|
||||
}
|
||||
|
||||
if let mediaEntityView = self.entitiesView.add(mediaEntity, announce: false) as? DrawingMediaEntityView {
|
||||
@ -3367,7 +3382,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
}
|
||||
} else if case let .gift(gift) = effectiveSubject {
|
||||
isGift = true
|
||||
let media: [Media] = [TelegramMediaAction(action: .starGiftUnique(gift: .unique(gift), isUpgrade: false, isTransferred: false, savedToProfile: false, canExportDate: nil, transferStars: nil, isRefunded: false))]
|
||||
let media: [Media] = [TelegramMediaAction(action: .starGiftUnique(gift: .unique(gift), isUpgrade: false, isTransferred: false, savedToProfile: false, canExportDate: nil, transferStars: nil, isRefunded: false, peerId: nil, senderId: nil, savedId: nil))]
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: self.context.account.peerId, namespace: Namespaces.Message.Cloud, id: -1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: media, peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
|
||||
messages = .single([message])
|
||||
} else {
|
||||
@ -3860,6 +3875,9 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
} else if case .avatarEditor = controller.mode {
|
||||
hasSwipeToDismiss = false
|
||||
hasSwipeToEnhance = false
|
||||
} else if case .coverEditor = controller.mode {
|
||||
hasSwipeToDismiss = false
|
||||
hasSwipeToEnhance = false
|
||||
} else if self.isCollageTimelineOpen {
|
||||
hasSwipeToEnhance = false
|
||||
}
|
||||
@ -4064,7 +4082,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
} else {
|
||||
initialScale = self.previewContainerView.bounds.width / image.size.width
|
||||
}
|
||||
case .stickerEditor, .avatarEditor:
|
||||
case .stickerEditor, .avatarEditor, .coverEditor:
|
||||
if image.size.height > image.size.width {
|
||||
initialScale = self.previewContainerView.bounds.width / image.size.width
|
||||
} else {
|
||||
@ -5137,7 +5155,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
controller.requestStickerCompletion(animated: true)
|
||||
case .botPreview:
|
||||
controller.requestStoryCompletion(animated: true)
|
||||
case .avatarEditor:
|
||||
case .avatarEditor, .coverEditor:
|
||||
controller.requestStoryCompletion(animated: true)
|
||||
}
|
||||
}
|
||||
@ -5404,7 +5422,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
var hasInteractiveStickers = true
|
||||
if let controller = self.controller {
|
||||
switch controller.mode {
|
||||
case .stickerEditor, .botPreview, .avatarEditor:
|
||||
case .stickerEditor, .botPreview, .avatarEditor, .coverEditor:
|
||||
hasInteractiveStickers = false
|
||||
default:
|
||||
break
|
||||
@ -5885,7 +5903,11 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
transition.setFrame(view: self.selectionContainerView, frame: CGRect(origin: .zero, size: previewFrame.size))
|
||||
|
||||
if let stickerBackgroundView = self.stickerBackgroundView, let stickerOverlayLayer = self.stickerOverlayLayer, let stickerFrameLayer = self.stickerFrameLayer {
|
||||
let stickerFrameWidth = floorToScreenPixels(previewSize.width * 0.97)
|
||||
var stickerFrameFraction: CGFloat = 1.0
|
||||
if case .avatarEditor = controller.mode {
|
||||
stickerFrameFraction = 0.97
|
||||
}
|
||||
let stickerFrameWidth = floorToScreenPixels(previewSize.width * stickerFrameFraction)
|
||||
stickerOverlayLayer.frame = CGRect(origin: .zero, size: previewSize)
|
||||
|
||||
let stickerFrameRect = CGRect(origin: CGPoint(x: floorToScreenPixels((previewSize.width - stickerFrameWidth) / 2.0), y: floorToScreenPixels((previewSize.height - stickerFrameWidth) / 2.0)), size: CGSize(width: stickerFrameWidth, height: stickerFrameWidth))
|
||||
@ -5897,6 +5919,10 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
case .avatarEditor:
|
||||
overlayInnerRect = UIBezierPath(cgPath: CGPath(ellipseIn: stickerFrameRect, transform: nil))
|
||||
stickerFrameLayer.isHidden = true
|
||||
case let .coverEditor(dimensions):
|
||||
let fittedSize = dimensions.aspectFilled(stickerFrameRect.size)
|
||||
overlayInnerRect = UIBezierPath(rect: fittedSize.centered(around: stickerFrameRect.center))
|
||||
stickerFrameLayer.isHidden = true
|
||||
default:
|
||||
overlayInnerRect = UIBezierPath(cgPath: CGPath(roundedRect: stickerFrameRect, cornerWidth: stickerFrameWidth / 8.0, cornerHeight: stickerFrameWidth / 8.0, transform: nil))
|
||||
}
|
||||
@ -6770,7 +6796,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
let context = self.context
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: presentationData.strings.Story_Editor_TooltipPremiumReaction, undoText: nil, customAction: nil), elevatedLayout: true, position: .top, animateInAsReplacement: false, blurred: true, action: { [weak self] action in
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: presentationData.strings.Story_Editor_TooltipPremiumReaction, undoText: nil, customAction: nil), elevatedLayout: true, position: .top, animateInAsReplacement: false, appearance: UndoOverlayController.Appearance(isBlurred: true), action: { [weak self] action in
|
||||
if case .info = action, let self {
|
||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .storiesExpirationDurations, forceDark: true, dismissed: nil)
|
||||
self.push(controller)
|
||||
@ -6884,7 +6910,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
||||
save = presentationData.strings.Story_Editor_DraftKeepMedia
|
||||
}
|
||||
text = presentationData.strings.Story_Editor_DraftDiscaedText
|
||||
case .stickerEditor, .botPreview, .avatarEditor:
|
||||
case .stickerEditor, .botPreview, .avatarEditor, .coverEditor:
|
||||
title = presentationData.strings.Story_Editor_DraftDiscardMedia
|
||||
text = presentationData.strings.Story_Editor_DiscardText
|
||||
}
|
||||
|
@ -1560,6 +1560,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
}
|
||||
}
|
||||
|
||||
let profileGiftsContext = ProfileGiftsContext(account: context.account, peerId: peerId)
|
||||
|
||||
return combineLatest(
|
||||
context.account.viewTracker.peerView(peerId, updateData: true),
|
||||
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, isMyProfile: false, chatLocationContextHolder: chatLocationContextHolder),
|
||||
@ -1601,6 +1603,12 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
availablePanes = availablePanesValue
|
||||
}
|
||||
}
|
||||
|
||||
if availablePanes != nil, let cachedData = peerView.cachedData as? CachedChannelData {
|
||||
if let starGiftsCount = cachedData.starGiftsCount, starGiftsCount > 0 {
|
||||
availablePanes?.insert(.gifts, at: hasStories ? 1 : 0)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
availablePanes = nil
|
||||
}
|
||||
@ -1665,7 +1673,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
starsRevenueStatsContext: starsRevenueContextAndState.0,
|
||||
revenueStatsState: revenueContextAndState.1,
|
||||
revenueStatsContext: revenueContextAndState.0,
|
||||
profileGiftsContext: nil,
|
||||
profileGiftsContext: profileGiftsContext,
|
||||
premiumGiftOptions: [],
|
||||
webAppPermissions: nil
|
||||
)
|
||||
|
@ -463,7 +463,15 @@ private final class PeerInfoPendingPane {
|
||||
let paneNode: PeerInfoPaneNode
|
||||
switch key {
|
||||
case .gifts:
|
||||
paneNode = PeerInfoGiftsPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, openPeerContextAction: openPeerContextAction, profileGifts: data.profileGiftsContext!)
|
||||
var canManage = false
|
||||
if let peer = data.peer {
|
||||
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
if channel.hasPermission(.sendSomething) {
|
||||
canManage = true
|
||||
}
|
||||
}
|
||||
}
|
||||
paneNode = PeerInfoGiftsPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, openPeerContextAction: openPeerContextAction, profileGifts: data.profileGiftsContext!, canManage: canManage)
|
||||
case .stories, .storyArchive, .botPreview:
|
||||
var canManage = false
|
||||
if let peer = data.peer {
|
||||
|
@ -6467,8 +6467,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
if let cachedData = strongSelf.data?.cachedData as? CachedChannelData {
|
||||
if case .broadcast = channel.info {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Send a Gift", badge: nil, icon: { theme in
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_SendGift, badge: nil, icon: { theme in
|
||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
@ -9597,7 +9596,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}
|
||||
}
|
||||
|
||||
func openAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) {
|
||||
func oldOpenAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) {
|
||||
guard let peer = self.data?.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: self.data?.threadData) else {
|
||||
return
|
||||
}
|
||||
@ -12772,7 +12771,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
||||
|
||||
public func openAvatarSetup(completedWithUploadingImage: @escaping (UIImage, Signal<PeerInfoAvatarUploadStatus, NoError>) -> UIView?) {
|
||||
let proceed = { [weak self] in
|
||||
self?.newopenAvatarForEditing(completedWithUploadingImage: completedWithUploadingImage)
|
||||
self?.openAvatarForEditing(completedWithUploadingImage: completedWithUploadingImage)
|
||||
}
|
||||
if !self.isNodeLoaded {
|
||||
self.loadDisplayNode()
|
||||
@ -12796,10 +12795,6 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
||||
})
|
||||
}
|
||||
|
||||
func openAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) {
|
||||
self.controllerNode.openAvatarForEditing(mode: mode, fromGallery: fromGallery, completion: completion)
|
||||
}
|
||||
|
||||
static func openPeer(context: AccountContext, peerId: PeerId, navigation: ChatControllerInteractionNavigateToPeer, navigationController: NavigationController) {
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).startStandalone(next: { peer in
|
||||
|
@ -19,7 +19,7 @@ import LegacyComponents
|
||||
import LegacyMediaPickerUI
|
||||
|
||||
extension PeerInfoScreenImpl {
|
||||
func newopenAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }, completedWithUploadingImage: @escaping (UIImage, Signal<PeerInfoAvatarUploadStatus, NoError>) -> UIView? = { _, _ in nil }) {
|
||||
func openAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }, completedWithUploadingImage: @escaping (UIImage, Signal<PeerInfoAvatarUploadStatus, NoError>) -> UIView? = { _, _ in nil }) {
|
||||
guard let data = self.controllerNode.data, let peer = data.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: data.threadData) else {
|
||||
return
|
||||
}
|
||||
@ -170,7 +170,6 @@ extension PeerInfoScreenImpl {
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
dismissImpl?()
|
||||
} as (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
||||
)
|
||||
@ -200,6 +199,7 @@ extension PeerInfoScreenImpl {
|
||||
}
|
||||
navigationController.setViewControllers(viewControllers, animated: false)
|
||||
}
|
||||
|
||||
}
|
||||
mainController.navigationPresentation = .flatModal
|
||||
mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
@ -284,12 +284,11 @@ extension PeerInfoScreenImpl {
|
||||
(self.navigationController?.topViewController as? ViewController)?.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
|
||||
public func updateProfilePhoto(_ image: UIImage, mode: PeerInfoAvatarEditingMode, uploadStatus: Promise<PeerInfoAvatarUploadStatus>?) {
|
||||
private func setupProfilePhotoUpload(image: UIImage, mode: PeerInfoAvatarEditingMode, indefiniteProgress: Bool) -> LocalFileMediaResource? {
|
||||
guard let data = image.jpegData(compressionQuality: 0.6) else {
|
||||
uploadStatus?.set(.single(.done))
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
if self.controllerNode.headerNode.isAvatarExpanded {
|
||||
self.controllerNode.headerNode.ignoreCollapse = true
|
||||
self.controllerNode.headerNode.updateIsAvatarExpanded(false, transition: .immediate)
|
||||
@ -303,14 +302,25 @@ extension PeerInfoScreenImpl {
|
||||
|
||||
if [.suggest, .fallback].contains(mode) {
|
||||
} else {
|
||||
if indefiniteProgress {
|
||||
self.controllerNode.state = self.controllerNode.state.withAvatarUploadProgress(.indefinite)
|
||||
}
|
||||
self.controllerNode.state = self.controllerNode.state.withUpdatingAvatar(.image(representation))
|
||||
}
|
||||
|
||||
if let (layout, navigationHeight) = self.controllerNode.validLayout {
|
||||
self.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: mode == .custom ? .animated(duration: 0.2, curve: .easeInOut) : .immediate, additive: false)
|
||||
}
|
||||
self.controllerNode.headerNode.ignoreCollapse = false
|
||||
|
||||
return resource
|
||||
}
|
||||
|
||||
public func updateProfilePhoto(_ image: UIImage, mode: PeerInfoAvatarEditingMode, uploadStatus: Promise<PeerInfoAvatarUploadStatus>?) {
|
||||
guard let resource = setupProfilePhotoUpload(image: image, mode: mode, indefiniteProgress: false) else {
|
||||
uploadStatus?.set(.single(.done))
|
||||
return
|
||||
}
|
||||
|
||||
let postbox = self.context.account.postbox
|
||||
let signal: Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError>
|
||||
if self.isSettings || self.isMyProfile {
|
||||
@ -405,23 +415,228 @@ extension PeerInfoScreenImpl {
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
// public func updateProfileVideo(_ image: UIImage, video: MediaEditorScreenImpl.MediaResult.VideoResult, values: MediaEditorValues, mode: PeerInfoAvatarEditingMode) {
|
||||
// var markup: UploadPeerPhotoMarkup? = nil
|
||||
// if let fileId = adjustments?.documentId, let backgroundColors = adjustments?.colors as? [Int32], fileId != 0 {
|
||||
// if let packId = adjustments?.stickerPackId, let accessHash = adjustments?.stickerPackAccessHash, packId != 0 {
|
||||
// markup = .sticker(packReference: .id(id: packId, accessHash: accessHash), fileId: fileId, backgroundColors: backgroundColors)
|
||||
// } else {
|
||||
// markup = .emoji(fileId: fileId, backgroundColors: backgroundColors)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// var videoStartTimestamp: Double? = nil
|
||||
// if let adjustments = adjustments, adjustments.videoStartValue > 0.0 {
|
||||
// videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue
|
||||
// }
|
||||
//
|
||||
// var uploadVideo = true
|
||||
// if let _ = markup {
|
||||
// if let data = self.context.currentAppConfiguration.with({ $0 }).data, let uploadVideoValue = data["upload_markup_video"] as? Bool, uploadVideoValue {
|
||||
// uploadVideo = true
|
||||
// } else {
|
||||
// uploadVideo = false
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// guard let photoResource = self.setupProfilePhotoUpload(image: image, mode: mode, indefiniteProgress: !uploadVideo) else {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// let context = self.context
|
||||
//
|
||||
// let videoResource: Signal<TelegramMediaResource?, UploadPeerPhotoError>
|
||||
// if uploadVideo {
|
||||
// let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max)).mp4"
|
||||
// let videoExport = MediaEditorVideoExport(
|
||||
// postbox: context.account.postbox,
|
||||
// subject: .image(image: image),
|
||||
// configuration: configuration,
|
||||
// outputPath: path
|
||||
// )
|
||||
//
|
||||
// videoResource = Signal<TelegramMediaResource?, UploadPeerPhotoError> { [weak self] subscriber in
|
||||
// let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in
|
||||
// if let paintingData = adjustments.paintingData, paintingData.hasAnimation {
|
||||
// return LegacyPaintEntityRenderer(postbox: account.postbox, adjustments: adjustments)
|
||||
// } else {
|
||||
// return nil
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4")
|
||||
// let uploadInterface = LegacyLiveUploadInterface(context: context)
|
||||
// let signal: SSignal
|
||||
// if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer {
|
||||
// let durationSignal: SSignal = SSignal(generator: { subscriber in
|
||||
// let disposable = (entityRenderer.duration()).start(next: { duration in
|
||||
// subscriber.putNext(duration)
|
||||
// subscriber.putCompletion()
|
||||
// })
|
||||
//
|
||||
// return SBlockDisposable(block: {
|
||||
// disposable.dispose()
|
||||
// })
|
||||
// })
|
||||
// signal = durationSignal.map(toSignal: { duration -> SSignal in
|
||||
// if let duration = duration as? Double {
|
||||
// return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)!
|
||||
// } else {
|
||||
// return SSignal.single(nil)
|
||||
// }
|
||||
// })
|
||||
// } else if let asset = asset as? AVAsset {
|
||||
// signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)!
|
||||
// } else {
|
||||
// signal = SSignal.complete()
|
||||
// }
|
||||
//
|
||||
// let signalDisposable = signal.start(next: { next in
|
||||
// if let result = next as? TGMediaVideoConversionResult {
|
||||
// if let image = result.coverImage, let data = image.jpegData(compressionQuality: 0.7) {
|
||||
// account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
|
||||
// }
|
||||
//
|
||||
// if let timestamp = videoStartTimestamp {
|
||||
// videoStartTimestamp = max(0.0, min(timestamp, result.duration - 0.05))
|
||||
// }
|
||||
//
|
||||
// var value = stat()
|
||||
// if stat(result.fileURL.path, &value) == 0 {
|
||||
// if let data = try? Data(contentsOf: result.fileURL) {
|
||||
// let resource: TelegramMediaResource
|
||||
// if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult {
|
||||
// resource = LocalFileMediaResource(fileId: liveUploadData.id)
|
||||
// } else {
|
||||
// resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
// }
|
||||
// account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
||||
// subscriber.putNext(resource)
|
||||
//
|
||||
// EngineTempBox.shared.dispose(tempFile)
|
||||
// }
|
||||
// }
|
||||
// subscriber.putCompletion()
|
||||
// } else if let strongSelf = self, let progress = next as? NSNumber {
|
||||
// Queue.mainQueue().async {
|
||||
// strongSelf.controllerNode.state = strongSelf.controllerNode.state.withAvatarUploadProgress(.value(CGFloat(progress.floatValue * 0.45)))
|
||||
// if let (layout, navigationHeight) = strongSelf.controllerNode.validLayout {
|
||||
// strongSelf.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }, error: { _ in
|
||||
// }, completed: nil)
|
||||
//
|
||||
// let disposable = ActionDisposable {
|
||||
// signalDisposable?.dispose()
|
||||
// }
|
||||
//
|
||||
// return ActionDisposable {
|
||||
// disposable.dispose()
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// videoResource = .single(nil)
|
||||
// }
|
||||
//
|
||||
// var dismissStatus: (() -> Void)?
|
||||
// if [.suggest, .fallback, .accept].contains(mode) {
|
||||
// let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: { [weak self] in
|
||||
// self?.controllerNode.updateAvatarDisposable.set(nil)
|
||||
// dismissStatus?()
|
||||
// }))
|
||||
// dismissStatus = { [weak statusController] in
|
||||
// statusController?.dismiss()
|
||||
// }
|
||||
// if let topController = self.navigationController?.topViewController as? ViewController {
|
||||
// topController.presentInGlobalOverlay(statusController)
|
||||
// } else if let topController = self.parentController?.topViewController as? ViewController {
|
||||
// topController.presentInGlobalOverlay(statusController)
|
||||
// } else {
|
||||
// self.presentInGlobalOverlay(statusController)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// let peerId = self.peerId
|
||||
// let isSettings = self.isSettings
|
||||
// let isMyProfile = self.isMyProfile
|
||||
// self.controllerNode.updateAvatarDisposable.set((videoResource
|
||||
// |> mapToSignal { videoResource -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> in
|
||||
// if isSettings || isMyProfile {
|
||||
// if case .fallback = mode {
|
||||
// return context.engine.accountData.updateFallbackPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in
|
||||
// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
|
||||
// })
|
||||
// } else {
|
||||
// return context.engine.accountData.updateAccountPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in
|
||||
// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
|
||||
// })
|
||||
// }
|
||||
// } else if case .custom = mode {
|
||||
// return context.engine.contacts.updateContactPhoto(peerId: peerId, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mode: .custom, mapResourceToAvatarSizes: { resource, representations in
|
||||
// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
|
||||
// })
|
||||
// } else if case .suggest = mode {
|
||||
// return context.engine.contacts.updateContactPhoto(peerId: peerId, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mode: .suggest, mapResourceToAvatarSizes: { resource, representations in
|
||||
// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
|
||||
// })
|
||||
// } else {
|
||||
// return context.engine.peers.updatePeerPhoto(peerId: peerId, photo: context.engine.peers.uploadedPeerPhoto(resource: photoResource), video: videoResource.flatMap { context.engine.peers.uploadedPeerVideo(resource: $0) |> map(Optional.init) }, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in
|
||||
// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// |> deliverOnMainQueue).startStrict(next: { [weak self] result in
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// switch result {
|
||||
// case .complete:
|
||||
// strongSelf.controllerNode.state = strongSelf.controllerNode.state.withUpdatingAvatar(nil).withAvatarUploadProgress(nil)
|
||||
// case let .progress(value):
|
||||
// strongSelf.controllerNode.state = strongSelf.controllerNode.state.withAvatarUploadProgress(.value(CGFloat(0.45 + value * 0.55)))
|
||||
// }
|
||||
// if let (layout, navigationHeight) = strongSelf.controllerNode.validLayout {
|
||||
// strongSelf.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
|
||||
// }
|
||||
//
|
||||
// if case .complete = result {
|
||||
// dismissStatus?()
|
||||
//
|
||||
// let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.peerId))
|
||||
// |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
|
||||
// if let strongSelf = self, let peer {
|
||||
// switch mode {
|
||||
// case .fallback:
|
||||
// (strongSelf.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: nil, text: strongSelf.presentationData.strings.Privacy_ProfilePhoto_PublicVideoSuccess, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||
// case .custom:
|
||||
// strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessVideoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||
//
|
||||
// let _ = (strongSelf.context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, peerId: strongSelf.peerId, fetch: peerInfoProfilePhotos(context: strongSelf.context, peerId: strongSelf.peerId)) |> ignoreValues).startStandalone()
|
||||
// case .suggest:
|
||||
// if let navigationController = (strongSelf.navigationController as? NavigationController) {
|
||||
// strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), keepStack: .default, completion: { _ in
|
||||
// }))
|
||||
// }
|
||||
// case .accept:
|
||||
// (strongSelf.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccess, text: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccessText, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in
|
||||
// if case .info = action {
|
||||
// self?.parentController?.openSettings()
|
||||
// }
|
||||
// return false
|
||||
// }), in: .current)
|
||||
// default:
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }))
|
||||
// }
|
||||
|
||||
public func updateProfileVideo(_ image: UIImage, asset: Any?, adjustments: TGVideoEditAdjustments?, mode: PeerInfoAvatarEditingMode) {
|
||||
guard let data = image.jpegData(compressionQuality: 0.6) else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.controllerNode.headerNode.isAvatarExpanded {
|
||||
self.controllerNode.headerNode.ignoreCollapse = true
|
||||
self.controllerNode.headerNode.updateIsAvatarExpanded(false, transition: .immediate)
|
||||
self.controllerNode.updateNavigationExpansionPresentation(isExpanded: false, animated: true)
|
||||
}
|
||||
self.controllerNode.scrollNode.view.setContentOffset(CGPoint(), animated: false)
|
||||
|
||||
let photoResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
self.context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
|
||||
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: mode == .custom ? true : false)
|
||||
|
||||
var markup: UploadPeerPhotoMarkup? = nil
|
||||
if let fileId = adjustments?.documentId, let backgroundColors = adjustments?.colors as? [Int32], fileId != 0 {
|
||||
if let packId = adjustments?.stickerPackId, let accessHash = adjustments?.stickerPackAccessHash, packId != 0 {
|
||||
@ -439,19 +654,9 @@ extension PeerInfoScreenImpl {
|
||||
uploadVideo = false
|
||||
}
|
||||
}
|
||||
|
||||
if [.suggest, .fallback].contains(mode) {
|
||||
} else {
|
||||
self.controllerNode.state = self.controllerNode.state.withUpdatingAvatar(.image(representation))
|
||||
if !uploadVideo {
|
||||
self.controllerNode.state = self.controllerNode.state.withAvatarUploadProgress(.indefinite)
|
||||
}
|
||||
guard let photoResource = self.setupProfilePhotoUpload(image: image, mode: mode, indefiniteProgress: !uploadVideo) else {
|
||||
return
|
||||
}
|
||||
|
||||
if let (layout, navigationHeight) = self.controllerNode.validLayout {
|
||||
self.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: mode == .custom ? .animated(duration: 0.2, curve: .easeInOut) : .immediate, additive: false)
|
||||
}
|
||||
self.controllerNode.headerNode.ignoreCollapse = false
|
||||
|
||||
var videoStartTimestamp: Double? = nil
|
||||
if let adjustments = adjustments, adjustments.videoStartValue > 0.0 {
|
||||
|
@ -52,6 +52,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftViewScreen",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/Components/BalancedTextComponent",
|
||||
"//submodules/TelegramUI/Components/CheckComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -22,11 +22,14 @@ import GiftItemComponent
|
||||
import PlainButtonComponent
|
||||
import GiftViewScreen
|
||||
import SolidRoundedButtonNode
|
||||
import UndoUI
|
||||
import CheckComponent
|
||||
|
||||
public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate {
|
||||
private let context: AccountContext
|
||||
private let peerId: PeerId
|
||||
private let profileGifts: ProfileGiftsContext
|
||||
private let canManage: Bool
|
||||
|
||||
private var dataDisposable: Disposable?
|
||||
|
||||
@ -38,10 +41,11 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let scrollNode: ASScrollNode
|
||||
|
||||
private var unlockBackground: NavigationBackgroundNode?
|
||||
private var unlockSeparator: ASDisplayNode?
|
||||
private var unlockText: ComponentView<Empty>?
|
||||
private var unlockButton: SolidRoundedButtonNode?
|
||||
private var footerText: ComponentView<Empty>?
|
||||
private var panelBackground: NavigationBackgroundNode?
|
||||
private var panelSeparator: ASDisplayNode?
|
||||
private var panelButton: SolidRoundedButtonNode?
|
||||
private var panelCheck: ComponentView<Empty>?
|
||||
|
||||
private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, presentationData: PresentationData)?
|
||||
|
||||
@ -68,12 +72,13 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
|
||||
private var starsItems: [AnyHashable: ComponentView<Empty>] = [:]
|
||||
|
||||
public init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, openPeerContextAction: @escaping (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void, profileGifts: ProfileGiftsContext) {
|
||||
public init(context: AccountContext, peerId: PeerId, chatControllerInteraction: ChatControllerInteraction, openPeerContextAction: @escaping (Bool, Peer, ASDisplayNode, ContextGesture?) -> Void, profileGifts: ProfileGiftsContext, canManage: Bool) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.chatControllerInteraction = chatControllerInteraction
|
||||
self.openPeerContextAction = openPeerContextAction
|
||||
self.profileGifts = profileGifts
|
||||
self.canManage = canManage
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.scrollNode = ASScrollNode()
|
||||
@ -125,15 +130,16 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
self.updateScrolling(transition: .immediate)
|
||||
}
|
||||
|
||||
private var notify = false
|
||||
func updateScrolling(transition: ComponentTransition) {
|
||||
if let starsProducts = self.starsProducts, let params = self.currentParams {
|
||||
let optionSpacing: CGFloat = 10.0
|
||||
let sideInset = params.sideInset + 16.0
|
||||
let itemsSideInset = params.sideInset + 16.0
|
||||
|
||||
let defaultItemsInRow = 3
|
||||
let itemsInRow = max(1, min(starsProducts.count, defaultItemsInRow))
|
||||
let defaultOptionWidth = (params.size.width - sideInset * 2.0 - optionSpacing * CGFloat(defaultItemsInRow - 1)) / CGFloat(defaultItemsInRow)
|
||||
let optionWidth = (params.size.width - sideInset * 2.0 - optionSpacing * CGFloat(itemsInRow - 1)) / CGFloat(itemsInRow)
|
||||
let defaultOptionWidth = (params.size.width - itemsSideInset * 2.0 - optionSpacing * CGFloat(defaultItemsInRow - 1)) / CGFloat(defaultItemsInRow)
|
||||
let optionWidth = (params.size.width - itemsSideInset * 2.0 - optionSpacing * CGFloat(itemsInRow - 1)) / CGFloat(itemsInRow)
|
||||
|
||||
let starsOptionSize = CGSize(width: optionWidth, height: defaultOptionWidth)
|
||||
|
||||
@ -142,7 +148,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
let topInset: CGFloat = 60.0
|
||||
|
||||
var validIds: [AnyHashable] = []
|
||||
var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: starsOptionSize)
|
||||
var itemFrame = CGRect(origin: CGPoint(x: itemsSideInset, y: topInset), size: starsOptionSize)
|
||||
|
||||
var index: Int32 = 0
|
||||
for product in starsProducts {
|
||||
@ -233,22 +239,22 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
self.profileGifts.updateStarGiftAddedToProfile(reference: reference, added: added)
|
||||
},
|
||||
convertToStars: { [weak self] in
|
||||
guard let self else {
|
||||
guard let self, let reference = product.reference else {
|
||||
return
|
||||
}
|
||||
self.profileGifts.convertStarGift(reference: product.reference)
|
||||
self.profileGifts.convertStarGift(reference: reference)
|
||||
},
|
||||
transferGift: { [weak self] prepaid, peerId in
|
||||
guard let self else {
|
||||
guard let self, let reference = product.reference else {
|
||||
return
|
||||
}
|
||||
self.profileGifts.transferStarGift(prepaid: prepaid, reference: product.reference, peerId: peerId)
|
||||
self.profileGifts.transferStarGift(prepaid: prepaid, reference: reference, peerId: peerId)
|
||||
},
|
||||
upgradeGift: { [weak self] formId, keepOriginalInfo in
|
||||
guard let self else {
|
||||
guard let self, let reference = product.reference else {
|
||||
return .never()
|
||||
}
|
||||
return self.profileGifts.upgradeStarGift(formId: formId, reference: product.reference, keepOriginalInfo: keepOriginalInfo)
|
||||
return self.profileGifts.upgradeStarGift(formId: formId, reference: reference, keepOriginalInfo: keepOriginalInfo)
|
||||
},
|
||||
shareStory: { [weak self] in
|
||||
guard let self, case let .unique(uniqueGift) = product.gift, let parentController = self.parentController else {
|
||||
@ -278,7 +284,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
}
|
||||
itemFrame.origin.x += itemFrame.width + optionSpacing
|
||||
if itemFrame.maxX > params.size.width {
|
||||
itemFrame.origin.x = sideInset
|
||||
itemFrame.origin.x = itemsSideInset
|
||||
itemFrame.origin.y += starsOptionSize.height + optionSpacing
|
||||
}
|
||||
index += 1
|
||||
@ -306,93 +312,164 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
|
||||
var bottomScrollInset: CGFloat = 0.0
|
||||
var contentHeight = ceil(CGFloat(starsProducts.count) / 3.0) * (starsOptionSize.height + optionSpacing) - optionSpacing + topInset + 16.0
|
||||
if self.peerId == self.context.account.peerId {
|
||||
let transition = ComponentTransition.immediate
|
||||
|
||||
let size = params.size
|
||||
let sideInset = params.sideInset
|
||||
let bottomInset = params.bottomInset
|
||||
let presentationData = params.presentationData
|
||||
|
||||
let themeUpdated = self.theme !== presentationData.theme
|
||||
self.theme = presentationData.theme
|
||||
|
||||
let unlockText: ComponentView<Empty>
|
||||
let unlockBackground: NavigationBackgroundNode
|
||||
let unlockSeparator: ASDisplayNode
|
||||
let unlockButton: SolidRoundedButtonNode
|
||||
if let current = self.unlockText {
|
||||
unlockText = current
|
||||
} else {
|
||||
unlockText = ComponentView<Empty>()
|
||||
self.unlockText = unlockText
|
||||
}
|
||||
|
||||
if let current = self.unlockBackground {
|
||||
unlockBackground = current
|
||||
} else {
|
||||
unlockBackground = NavigationBackgroundNode(color: presentationData.theme.rootController.tabBar.backgroundColor)
|
||||
self.addSubnode(unlockBackground)
|
||||
self.unlockBackground = unlockBackground
|
||||
}
|
||||
|
||||
if let current = self.unlockSeparator {
|
||||
unlockSeparator = current
|
||||
} else {
|
||||
unlockSeparator = ASDisplayNode()
|
||||
self.addSubnode(unlockSeparator)
|
||||
self.unlockSeparator = unlockSeparator
|
||||
}
|
||||
|
||||
if let current = self.unlockButton {
|
||||
unlockButton = current
|
||||
} else {
|
||||
unlockButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: presentationData.theme), height: 50.0, cornerRadius: 10.0)
|
||||
self.view.addSubview(unlockButton.view)
|
||||
self.unlockButton = unlockButton
|
||||
|
||||
unlockButton.title = params.presentationData.strings.PeerInfo_Gifts_Send
|
||||
|
||||
unlockButton.pressed = { [weak self] in
|
||||
self?.buttonPressed()
|
||||
}
|
||||
}
|
||||
|
||||
if themeUpdated {
|
||||
unlockBackground.updateColor(color: presentationData.theme.rootController.tabBar.backgroundColor, transition: .immediate)
|
||||
unlockSeparator.backgroundColor = presentationData.theme.rootController.tabBar.separatorColor
|
||||
unlockButton.updateTheme(SolidRoundedButtonTheme(theme: presentationData.theme))
|
||||
|
||||
let transition = ComponentTransition.immediate
|
||||
|
||||
let size = params.size
|
||||
let sideInset = params.sideInset
|
||||
let bottomInset = params.bottomInset
|
||||
let presentationData = params.presentationData
|
||||
|
||||
let themeUpdated = self.theme !== presentationData.theme
|
||||
self.theme = presentationData.theme
|
||||
|
||||
let panelBackground: NavigationBackgroundNode
|
||||
let panelSeparator: ASDisplayNode
|
||||
let panelButton: SolidRoundedButtonNode
|
||||
|
||||
if let current = self.panelBackground {
|
||||
panelBackground = current
|
||||
} else {
|
||||
panelBackground = NavigationBackgroundNode(color: presentationData.theme.rootController.tabBar.backgroundColor)
|
||||
self.addSubnode(panelBackground)
|
||||
self.panelBackground = panelBackground
|
||||
}
|
||||
|
||||
if let current = self.panelSeparator {
|
||||
panelSeparator = current
|
||||
} else {
|
||||
panelSeparator = ASDisplayNode()
|
||||
self.addSubnode(panelSeparator)
|
||||
self.panelSeparator = panelSeparator
|
||||
}
|
||||
|
||||
if let current = self.panelButton {
|
||||
panelButton = current
|
||||
} else {
|
||||
panelButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: presentationData.theme), height: 50.0, cornerRadius: 10.0)
|
||||
self.view.addSubview(panelButton.view)
|
||||
self.panelButton = panelButton
|
||||
|
||||
panelButton.title = self.peerId == self.context.account.peerId ? params.presentationData.strings.PeerInfo_Gifts_Send : params.presentationData.strings.PeerInfo_Gifts_SendGift
|
||||
|
||||
panelButton.pressed = { [weak self] in
|
||||
self?.buttonPressed()
|
||||
}
|
||||
}
|
||||
|
||||
if themeUpdated {
|
||||
panelBackground.updateColor(color: presentationData.theme.rootController.tabBar.backgroundColor, transition: .immediate)
|
||||
panelSeparator.backgroundColor = presentationData.theme.rootController.tabBar.separatorColor
|
||||
panelButton.updateTheme(SolidRoundedButtonTheme(theme: presentationData.theme))
|
||||
}
|
||||
|
||||
let textFont = Font.regular(13.0)
|
||||
let boldTextFont = Font.semibold(13.0)
|
||||
let textColor = presentationData.theme.list.itemSecondaryTextColor
|
||||
let linkColor = presentationData.theme.list.itemAccentColor
|
||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: boldTextFont, textColor: linkColor), linkAttribute: { _ in
|
||||
return nil
|
||||
})
|
||||
|
||||
var scrollOffset: CGFloat = max(0.0, size.height - params.visibleHeight)
|
||||
|
||||
let buttonSideInset = sideInset + 16.0
|
||||
let buttonSize = CGSize(width: size.width - buttonSideInset * 2.0, height: 50.0)
|
||||
var bottomPanelHeight = bottomInset + buttonSize.height + 8.0
|
||||
if params.visibleHeight < 110.0 {
|
||||
scrollOffset -= bottomPanelHeight
|
||||
}
|
||||
|
||||
transition.setFrame(view: panelButton.view, frame: CGRect(origin: CGPoint(x: buttonSideInset, y: size.height - bottomInset - buttonSize.height - scrollOffset), size: buttonSize))
|
||||
let _ = panelButton.updateLayout(width: buttonSize.width, transition: .immediate)
|
||||
|
||||
if self.canManage {
|
||||
bottomPanelHeight -= 9.0
|
||||
|
||||
let textFont = Font.regular(13.0)
|
||||
let boldTextFont = Font.semibold(13.0)
|
||||
let textColor = presentationData.theme.list.itemSecondaryTextColor
|
||||
let linkColor = presentationData.theme.list.itemAccentColor
|
||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: boldTextFont, textColor: linkColor), linkAttribute: { _ in
|
||||
return nil
|
||||
})
|
||||
|
||||
var scrollOffset: CGFloat = max(0.0, size.height - params.visibleHeight)
|
||||
|
||||
let buttonSideInset = sideInset + 16.0
|
||||
let buttonSize = CGSize(width: size.width - buttonSideInset * 2.0, height: 50.0)
|
||||
let bottomPanelHeight = bottomInset + buttonSize.height + 8.0
|
||||
if params.visibleHeight < 110.0 {
|
||||
scrollOffset -= bottomPanelHeight
|
||||
let panelCheck: ComponentView<Empty>
|
||||
if let current = self.panelCheck {
|
||||
panelCheck = current
|
||||
} else {
|
||||
panelCheck = ComponentView<Empty>()
|
||||
self.panelCheck = panelCheck
|
||||
}
|
||||
let checkTheme = CheckComponent.Theme(
|
||||
backgroundColor: presentationData.theme.list.itemCheckColors.fillColor,
|
||||
strokeColor: presentationData.theme.list.itemCheckColors.foregroundColor,
|
||||
borderColor: presentationData.theme.list.itemCheckColors.strokeColor,
|
||||
overlayBorder: false,
|
||||
hasInset: false,
|
||||
hasShadow: false
|
||||
)
|
||||
|
||||
transition.setFrame(view: unlockButton.view, frame: CGRect(origin: CGPoint(x: buttonSideInset, y: size.height - bottomInset - buttonSize.height - scrollOffset), size: buttonSize))
|
||||
let _ = unlockButton.updateLayout(width: buttonSize.width, transition: .immediate)
|
||||
|
||||
transition.setFrame(view: unlockBackground.view, frame: CGRect(x: 0.0, y: size.height - bottomInset - buttonSize.height - 8.0 - scrollOffset, width: size.width, height: bottomPanelHeight))
|
||||
unlockBackground.update(size: CGSize(width: size.width, height: bottomPanelHeight), transition: transition.containedViewLayoutTransition)
|
||||
transition.setFrame(view: unlockSeparator.view, frame: CGRect(x: 0.0, y: size.height - bottomInset - buttonSize.height - 8.0 - scrollOffset, width: size.width, height: UIScreenPixel))
|
||||
|
||||
let unlockSize = unlockText.update(
|
||||
let panelCheckSize = panelCheck.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
PlainButtonComponent(
|
||||
content: AnyComponent(HStack([
|
||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(CheckComponent(
|
||||
theme: checkTheme,
|
||||
size: CGSize(width: 22.0, height: 22.0),
|
||||
selected: self.notify
|
||||
))),
|
||||
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: presentationData.strings.PeerInfo_Gifts_ChannelNotify, font: Font.regular(17.0), textColor: presentationData.theme.list.itemPrimaryTextColor))
|
||||
)))
|
||||
],
|
||||
spacing: 16.0
|
||||
)),
|
||||
effectAlignment: .center,
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.notify = !self.notify
|
||||
|
||||
if self.notify {
|
||||
let controller = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.PeerInfo_Gifts_ChannelNotifyTooltip, customUndoText: nil, timeout: nil),
|
||||
appearance: UndoOverlayController.Appearance(bottomInset: 53.0),
|
||||
action: { _ in return true }
|
||||
)
|
||||
self.chatControllerInteraction.presentController(controller, nil)
|
||||
}
|
||||
self.updateScrolling(transition: .immediate)
|
||||
},
|
||||
animateAlpha: false,
|
||||
animateScale: false
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: buttonSize
|
||||
)
|
||||
if let panelCheckView = panelCheck.view {
|
||||
if panelCheckView.superview == nil {
|
||||
self.view.addSubview(panelCheckView)
|
||||
}
|
||||
panelCheckView.frame = CGRect(origin: CGPoint(x: floor((size.width - panelCheckSize.width) / 2.0), y: size.height - bottomInset - panelCheckSize.height - 11.0 - scrollOffset), size: panelCheckSize)
|
||||
}
|
||||
panelButton.isHidden = true
|
||||
}
|
||||
|
||||
transition.setFrame(view: panelBackground.view, frame: CGRect(x: 0.0, y: size.height - bottomPanelHeight - scrollOffset, width: size.width, height: bottomPanelHeight))
|
||||
panelBackground.update(size: CGSize(width: size.width, height: bottomPanelHeight), transition: transition.containedViewLayoutTransition)
|
||||
transition.setFrame(view: panelSeparator.view, frame: CGRect(x: 0.0, y: size.height - bottomPanelHeight - scrollOffset, width: size.width, height: UIScreenPixel))
|
||||
|
||||
if self.peerId == self.context.account.peerId {
|
||||
let footerText: ComponentView<Empty>
|
||||
if let current = self.footerText {
|
||||
footerText = current
|
||||
} else {
|
||||
footerText = ComponentView<Empty>()
|
||||
self.footerText = footerText
|
||||
}
|
||||
let footerTextSize = footerText.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
BalancedTextComponent(
|
||||
text: .markdown(text: params.presentationData.strings.PeerInfo_Gifts_Info, attributes: markdownAttributes),
|
||||
text: .markdown(text: presentationData.strings.PeerInfo_Gifts_Info, attributes: markdownAttributes),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.2
|
||||
@ -401,18 +478,19 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width - 32.0, height: 200.0)
|
||||
)
|
||||
if let view = unlockText.view {
|
||||
if let view = footerText.view {
|
||||
if view.superview == nil {
|
||||
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.buttonPressed)))
|
||||
self.scrollNode.view.addSubview(view)
|
||||
}
|
||||
transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: floor((size.width - unlockSize.width) / 2.0), y: contentHeight), size: unlockSize))
|
||||
transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: floor((size.width - footerTextSize.width) / 2.0), y: contentHeight), size: footerTextSize))
|
||||
}
|
||||
contentHeight += unlockSize.height
|
||||
contentHeight += bottomPanelHeight
|
||||
|
||||
bottomScrollInset = bottomPanelHeight - 40.0
|
||||
contentHeight += footerTextSize.height
|
||||
}
|
||||
contentHeight += bottomPanelHeight
|
||||
|
||||
bottomScrollInset = bottomPanelHeight - 40.0
|
||||
|
||||
contentHeight += params.bottomInset
|
||||
|
||||
self.scrollNode.view.scrollIndicatorInsets = UIEdgeInsets(top: 50.0, left: 0.0, bottom: bottomScrollInset, right: 0.0)
|
||||
@ -430,16 +508,21 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
let _ = (self.context.account.stateManager.contactBirthdays
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] birthdays in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings(birthdays), completion: nil)
|
||||
controller.navigationPresentation = .modal
|
||||
if self.peerId == self.context.account.peerId {
|
||||
let _ = (self.context.account.stateManager.contactBirthdays
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] birthdays in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings(birthdays), completion: nil)
|
||||
controller.navigationPresentation = .modal
|
||||
self.chatControllerInteraction.navigationController()?.pushViewController(controller)
|
||||
})
|
||||
} else {
|
||||
let controller = self.context.sharedContext.makeGiftOptionsController(context: self.context, peerId: self.peerId, premiumOptions: [], hasBirthday: false)
|
||||
self.chatControllerInteraction.navigationController()?.pushViewController(controller)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, navigationHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
|
||||
|
@ -883,7 +883,11 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
||||
selectedMedia = image
|
||||
break
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
selectedMedia = file
|
||||
if let cover = file.videoCover {
|
||||
selectedMedia = cover
|
||||
} else {
|
||||
selectedMedia = file
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -3435,7 +3435,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
elevatedLayout: false,
|
||||
position: .top,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
})))
|
||||
@ -3456,7 +3456,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
elevatedLayout: false,
|
||||
position: .top,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
})))
|
||||
@ -3492,7 +3492,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
elevatedLayout: false,
|
||||
position: .top,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { [weak self] action in
|
||||
guard let self, let component = self.component else {
|
||||
return false
|
||||
@ -3539,7 +3539,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
elevatedLayout: false,
|
||||
position: .top,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { [weak self] action in
|
||||
guard let self, let component = self.component else {
|
||||
return false
|
||||
@ -4292,7 +4292,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
storeAttributedTextInPasteboard(text)
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let undoController = UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, blurred: true, action: { _ in true })
|
||||
let undoController = UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, appearance: UndoOverlayController.Appearance(isBlurred: true), action: { _ in true })
|
||||
self.sendMessageContext.tooltipScreen?.dismiss()
|
||||
self.sendMessageContext.tooltipScreen = undoController
|
||||
component.controller()?.present(undoController, in: .current)
|
||||
@ -4750,7 +4750,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
component.controller()?.push(controller)
|
||||
}), elevatedLayout: false, animateInAsReplacement: false, blurred: true, action: { _ in true })
|
||||
}), elevatedLayout: false, animateInAsReplacement: false, appearance: UndoOverlayController.Appearance(isBlurred: true), action: { _ in true })
|
||||
component.controller()?.present(undoController, in: .current)
|
||||
}
|
||||
}
|
||||
@ -5025,7 +5025,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
content: .info(title: nil, text: text, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
)
|
||||
self.sendMessageContext.tooltipScreen = controller
|
||||
@ -5471,7 +5471,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { [weak self] action in
|
||||
guard let self else {
|
||||
return false
|
||||
@ -6196,7 +6196,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
content: .info(title: nil, text: component.strings.Story_ToastRemovedFromProfileText, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
} else {
|
||||
@ -6205,7 +6205,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
content: .info(title: component.strings.Story_ToastSavedToProfileTitle, text: component.strings.Story_ToastSavedToProfileText, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
}
|
||||
@ -6263,7 +6263,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
content: .linkCopied(title: nil, text: component.strings.Story_ToastLinkCopied),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
}
|
||||
@ -6403,7 +6403,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
content: .info(title: nil, text: isGroup ? presentationData.strings.Story_ToastRemovedFromGroupText : presentationData.strings.Story_ToastRemovedFromChannelText, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
)
|
||||
} else {
|
||||
@ -6412,7 +6412,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
content: .info(title: isGroup ? presentationData.strings.Story_ToastSavedToGroupTitle : presentationData.strings.Story_ToastSavedToChannelTitle, text: isGroup ? presentationData.strings.Story_ToastSavedToGroupText : presentationData.strings.Story_ToastSavedToChannelText, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
}
|
||||
@ -6476,7 +6476,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
content: .linkCopied(title: nil, text: component.strings.Story_ToastLinkCopied),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
}
|
||||
@ -6730,7 +6730,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
], title: nil, text: component.strings.StoryFeed_TooltipNotifyOn(component.slice.effectivePeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string, customUndoText: nil, timeout: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
} else {
|
||||
@ -6745,7 +6745,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
], title: nil, text: component.strings.StoryFeed_TooltipNotifyOff(component.slice.effectivePeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string, customUndoText: nil, timeout: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
}
|
||||
@ -6775,7 +6775,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
content: .linkCopied(title: nil, text: component.strings.Story_ToastLinkCopied),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
}
|
||||
@ -6819,7 +6819,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
content: .info(title: title, text: text, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
blurred: true,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), in: .current)
|
||||
|
||||
@ -6995,44 +6995,6 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
},
|
||||
requestSelectMessages: nil
|
||||
)
|
||||
|
||||
// let options: [PeerReportOption] = [.spam, .violence, .pornography, .childAbuse, .copyright, .illegalDrugs, .personalDetails, .other]
|
||||
// presentPeerReportOptions(
|
||||
// context: component.context,
|
||||
// parent: controller,
|
||||
// contextController: c,
|
||||
// backAction: { _ in },
|
||||
// subject: .story(component.slice.effectivePeer.id, component.slice.item.storyItem.id),
|
||||
// options: options,
|
||||
// passthrough: true,
|
||||
// forceTheme: defaultDarkPresentationTheme,
|
||||
// isDetailedReportingVisible: { [weak self] isReporting in
|
||||
// guard let self else {
|
||||
// return
|
||||
// }
|
||||
// self.isReporting = isReporting
|
||||
// self.updateIsProgressPaused()
|
||||
// },
|
||||
// completion: { [weak self] reason, _ in
|
||||
// guard let self, let component = self.component, let controller = component.controller(), let reason else {
|
||||
// return
|
||||
// }
|
||||
// let _ = component.context.engine.peers.reportPeerStory(peerId: component.slice.effectivePeer.id, storyId: component.slice.item.storyItem.id, reason: reason, message: "").startStandalone()
|
||||
// controller.present(
|
||||
// UndoOverlayController(
|
||||
// presentationData: presentationData,
|
||||
// content: .emoji(
|
||||
// name: "PoliceCar",
|
||||
// text: presentationData.strings.Report_Succeed
|
||||
// ),
|
||||
// elevatedLayout: false,
|
||||
// blurred: true,
|
||||
// action: { _ in return false }
|
||||
// )
|
||||
// , in: .current
|
||||
// )
|
||||
// }
|
||||
// )
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/Gift.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/Gift.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "gift_24.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/Gift.imageset/gift_24.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/Gift.imageset/gift_24.pdf
vendored
Normal file
Binary file not shown.
@ -1199,7 +1199,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return true
|
||||
case .starGift, .starGiftUnique:
|
||||
let controller = strongSelf.context.sharedContext.makeGiftViewScreen(context: strongSelf.context, message: EngineMessage(message), shareStory: { [weak self] in
|
||||
if let self, case let .starGiftUnique(gift, _, _, _, _, _, _) = action.action, case let .unique(uniqueGift) = gift {
|
||||
if let self, case let .starGiftUnique(gift, _, _, _, _, _, _, _, _, _) = action.action, case let .unique(uniqueGift) = gift {
|
||||
Queue.mainQueue().after(0.15) {
|
||||
let controller = self.context.sharedContext.makeStorySharingScreen(context: self.context, subject: .gift(uniqueGift), parentController: self)
|
||||
self.push(controller)
|
||||
|
@ -1241,6 +1241,69 @@ extension ChatControllerImpl {
|
||||
controller.legacyCompletion = { signals, silently, scheduleTime, parameters, getAnimatedTransitionSource, sendCompletion in
|
||||
completion(signals, silently, scheduleTime, parameters, getAnimatedTransitionSource, sendCompletion)
|
||||
}
|
||||
controller.editCover = { [weak self] dimensions, completion in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var dismissImpl: (() -> Void)?
|
||||
let mainController = coverMediaPickerController(
|
||||
context: self.context,
|
||||
completion: { result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in
|
||||
let subject: Signal<MediaEditorScreenImpl.Subject?, NoError>
|
||||
if let asset = result as? PHAsset {
|
||||
subject = .single(.asset(asset))
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
let editorController = MediaEditorScreenImpl(
|
||||
context: self.context,
|
||||
mode: .coverEditor(dimensions: dimensions),
|
||||
subject: subject,
|
||||
transitionIn: fromCamera ? .camera : transitionView.flatMap({ .gallery(
|
||||
MediaEditorScreenImpl.TransitionIn.GalleryTransitionIn(
|
||||
sourceView: $0,
|
||||
sourceRect: transitionRect,
|
||||
sourceImage: transitionImage
|
||||
)
|
||||
) }),
|
||||
transitionOut: { finished, isNew in
|
||||
if !finished, let transitionView {
|
||||
return MediaEditorScreenImpl.TransitionOut(
|
||||
destinationView: transitionView,
|
||||
destinationRect: transitionView.bounds,
|
||||
destinationCornerRadius: 0.0
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}, completion: { result, commit in
|
||||
if case let .image(image, _) = result.media {
|
||||
completion(image)
|
||||
commit({})
|
||||
}
|
||||
dismissImpl?()
|
||||
} as (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
||||
)
|
||||
editorController.cancelled = { _ in
|
||||
cancelled()
|
||||
}
|
||||
self.push(editorController)
|
||||
}, dismissed: {
|
||||
|
||||
}
|
||||
)
|
||||
(self.navigationController as? NavigationController)?.pushViewController(mainController, animated: true)
|
||||
dismissImpl = { [weak self, weak mainController] in
|
||||
if let self, let navigationController = self.navigationController, let mainController {
|
||||
var viewControllers = navigationController.viewControllers
|
||||
viewControllers = viewControllers.filter { c in
|
||||
return c !== mainController
|
||||
}
|
||||
navigationController.setViewControllers(viewControllers, animated: false)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
present(controller, mediaPickerContext)
|
||||
}
|
||||
|
||||
|
@ -1123,7 +1123,15 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
|
||||
if data.messageActions.options.contains(.sendGift) {
|
||||
let sendGiftTitle: String
|
||||
if message.effectivelyIncoming(context.account.peerId) {
|
||||
var isIncoming = message.effectivelyIncoming(context.account.peerId)
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction, case let .starGiftUnique(_, isUpgrade, _, _, _, _, _, _, _, _) = action.action {
|
||||
if isUpgrade && message.author?.id == context.account.peerId {
|
||||
isIncoming = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if isIncoming {
|
||||
let peerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? ""
|
||||
sendGiftTitle = chatPresentationInterfaceState.strings.Conversation_ContextMenuSendGiftTo(peerName).string
|
||||
} else {
|
||||
|
@ -2450,26 +2450,26 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}
|
||||
|
||||
presentExportAlertImpl = { [weak controller] in
|
||||
guard let controller, case let .starGiftTransfer(_, _, _, _, canExportDate) = source, let canExportDate else {
|
||||
guard let controller, case let .starGiftTransfer(_, _, gift, _, canExportDate) = source, let canExportDate else {
|
||||
return
|
||||
}
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
let title: String
|
||||
let text: String
|
||||
if currentTime > canExportDate {
|
||||
title = presentationData.strings.Gift_Transfer_UpdateRequired_Title
|
||||
text = presentationData.strings.Gift_Transfer_UpdateRequired_Text
|
||||
if currentTime > canExportDate || "".isEmpty {
|
||||
let alertController = giftWithdrawAlertController(context: context, gift: gift, commit: {
|
||||
|
||||
})
|
||||
controller.present(alertController, in: .window(.root))
|
||||
} else {
|
||||
let delta = canExportDate - currentTime
|
||||
let days: Int32 = Int32(ceil(Float(delta) / 86400.0))
|
||||
let daysString = presentationData.strings.Gift_Transfer_UnlockPending_Text_Days(days)
|
||||
title = presentationData.strings.Gift_Transfer_UnlockPending_Title
|
||||
text = presentationData.strings.Gift_Transfer_UnlockPending_Text(daysString).string
|
||||
let title = presentationData.strings.Gift_Transfer_UnlockPending_Title
|
||||
let text = presentationData.strings.Gift_Transfer_UnlockPending_Text(daysString).string
|
||||
let alertController = textAlertController(context: context, title: title, text: text, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
|
||||
])
|
||||
controller.present(alertController, in: .window(.root))
|
||||
}
|
||||
let alertController = textAlertController(context: context, title: title, text: text, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
|
||||
])
|
||||
controller.present(alertController, in: .window(.root))
|
||||
}
|
||||
|
||||
presentTransferAlertImpl = { [weak controller] peer in
|
||||
|
@ -82,6 +82,18 @@ public final class UndoOverlayController: ViewController {
|
||||
case bottom
|
||||
}
|
||||
|
||||
public struct Appearance {
|
||||
public var isBlurred: Bool?
|
||||
public var sideInset: CGFloat?
|
||||
public var bottomInset: CGFloat?
|
||||
|
||||
public init(isBlurred: Bool? = nil, sideInset: CGFloat? = nil, bottomInset: CGFloat? = nil) {
|
||||
self.isBlurred = isBlurred
|
||||
self.sideInset = sideInset
|
||||
self.bottomInset = bottomInset
|
||||
}
|
||||
}
|
||||
|
||||
private let presentationData: PresentationData
|
||||
public var content: UndoOverlayContent {
|
||||
didSet {
|
||||
@ -94,7 +106,7 @@ public final class UndoOverlayController: ViewController {
|
||||
private var action: (UndoOverlayAction) -> Bool
|
||||
private let additionalView: (() -> UndoOverlayControllerAdditionalView?)?
|
||||
|
||||
private let blurred: Bool
|
||||
private let appearance: Appearance?
|
||||
private var didPlayPresentationAnimation = false
|
||||
private var dismissed = false
|
||||
|
||||
@ -102,13 +114,13 @@ public final class UndoOverlayController: ViewController {
|
||||
|
||||
public var tag: Any?
|
||||
|
||||
public init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, position: Position = .bottom, animateInAsReplacement: Bool = false, blurred: Bool = false, action: @escaping (UndoOverlayAction) -> Bool, additionalView: (() -> UndoOverlayControllerAdditionalView?)? = nil) {
|
||||
public init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool = false, position: Position = .bottom, animateInAsReplacement: Bool = false, appearance: Appearance? = nil, action: @escaping (UndoOverlayAction) -> Bool, additionalView: (() -> UndoOverlayControllerAdditionalView?)? = nil) {
|
||||
self.presentationData = presentationData
|
||||
self.content = content
|
||||
self.elevatedLayout = elevatedLayout
|
||||
self.position = position
|
||||
self.animateInAsReplacement = animateInAsReplacement
|
||||
self.blurred = blurred
|
||||
self.appearance = appearance
|
||||
self.action = action
|
||||
self.additionalView = additionalView
|
||||
|
||||
@ -122,7 +134,7 @@ public final class UndoOverlayController: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = UndoOverlayControllerNode(presentationData: self.presentationData, content: self.content, elevatedLayout: self.elevatedLayout, placementPosition: self.position, blurred: self.blurred, additionalView: self.additionalView, action: { [weak self] value in
|
||||
self.displayNode = UndoOverlayControllerNode(presentationData: self.presentationData, content: self.content, elevatedLayout: self.elevatedLayout, placementPosition: self.position, appearance: self.appearance, additionalView: self.additionalView, action: { [weak self] value in
|
||||
return self?.action(value) ?? false
|
||||
}, dismiss: { [weak self] in
|
||||
self?.dismiss()
|
||||
|
@ -59,7 +59,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
private let dismiss: () -> Void
|
||||
|
||||
private var content: UndoOverlayContent
|
||||
private let blurred: Bool
|
||||
private let appearance: UndoOverlayController.Appearance?
|
||||
|
||||
private let additionalView: UndoOverlayControllerAdditionalView?
|
||||
|
||||
@ -77,11 +77,11 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
private var fetchResourceDisposable: Disposable?
|
||||
|
||||
init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, placementPosition: UndoOverlayController.Position, blurred: Bool, additionalView: (() -> UndoOverlayControllerAdditionalView?)?, action: @escaping (UndoOverlayAction) -> Bool, dismiss: @escaping () -> Void) {
|
||||
init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, placementPosition: UndoOverlayController.Position, appearance: UndoOverlayController.Appearance?, additionalView: (() -> UndoOverlayControllerAdditionalView?)?, action: @escaping (UndoOverlayAction) -> Bool, dismiss: @escaping () -> Void) {
|
||||
self.presentationData = presentationData
|
||||
self.elevatedLayout = elevatedLayout
|
||||
self.placementPosition = placementPosition
|
||||
self.blurred = blurred
|
||||
self.appearance = appearance
|
||||
self.content = content
|
||||
|
||||
self.additionalView = additionalView?()
|
||||
@ -1544,7 +1544,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.undoButtonNode = HighlightTrackingButtonNode()
|
||||
|
||||
self.panelNode = ASDisplayNode()
|
||||
if presentationData.theme.overallDarkAppearance && !self.blurred {
|
||||
if presentationData.theme.overallDarkAppearance && !(self.appearance?.isBlurred == true) {
|
||||
self.panelNode.backgroundColor = presentationData.theme.rootController.tabBar.backgroundColor
|
||||
} else {
|
||||
self.panelNode.backgroundColor = .clear
|
||||
@ -1904,7 +1904,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
let rightInset: CGFloat = 16.0
|
||||
var contentHeight: CGFloat = 20.0
|
||||
|
||||
let margin: CGFloat = 12.0
|
||||
let margin: CGFloat = self.appearance?.sideInset ?? 12.0
|
||||
let leftMargin = margin + layout.insets(options: []).left
|
||||
|
||||
let buttonTextSize = self.undoButtonTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude))
|
||||
@ -1964,7 +1964,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
case .top:
|
||||
break
|
||||
case .bottom:
|
||||
if self.elevatedLayout {
|
||||
if let bottomInset = self.appearance?.bottomInset {
|
||||
insets.bottom += bottomInset
|
||||
} else if self.elevatedLayout {
|
||||
insets.bottom += 49.0
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user