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.Remains_any" = "%@ left";
|
||||||
"Gift.Send.Sold_1" = "%@ sold";
|
"Gift.Send.Sold_1" = "%@ sold";
|
||||||
"Gift.Send.Sold_any" = "%@ 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.ErrorUnknown" = "An error occurred. Please try again.";
|
||||||
"Gift.Send.ErrorOutOfStock" = "Sorry, this gift is sold out. Please choose another gift.";
|
"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.TakeOff" = "take off";
|
||||||
"Gift.View.Header.Share" = "share";
|
"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";
|
"Conversation.AddToContactsLong" = "Add to Contacts";
|
||||||
|
|
||||||
"PeerInfo.PaneRecommendedBots" = "Similar Bots";
|
"PeerInfo.PaneRecommendedBots" = "Similar Bots";
|
||||||
@ -13696,3 +13704,42 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"ChatListFilter.NameEnableAnimations" = "Enable Animations";
|
"ChatListFilter.NameEnableAnimations" = "Enable Animations";
|
||||||
"ChatListFilter.NameDisableAnimations" = "Disable 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 audioTranscription
|
||||||
case emojiPack
|
case emojiPack
|
||||||
case noAds
|
case noAds
|
||||||
|
case wearGift
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum StarsPurchasePurpose: Equatable {
|
public enum StarsPurchasePurpose: Equatable {
|
||||||
@ -157,6 +158,7 @@ public struct PremiumConfiguration {
|
|||||||
minChannelWallpaperLevel: 9,
|
minChannelWallpaperLevel: 9,
|
||||||
minChannelCustomWallpaperLevel: 10,
|
minChannelCustomWallpaperLevel: 10,
|
||||||
minChannelRestrictAdsLevel: 50,
|
minChannelRestrictAdsLevel: 50,
|
||||||
|
minChannelWearGiftLevel: 8,
|
||||||
minGroupProfileIconLevel: 7,
|
minGroupProfileIconLevel: 7,
|
||||||
minGroupEmojiStatusLevel: 8,
|
minGroupEmojiStatusLevel: 8,
|
||||||
minGroupWallpaperLevel: 9,
|
minGroupWallpaperLevel: 9,
|
||||||
@ -185,6 +187,7 @@ public struct PremiumConfiguration {
|
|||||||
public let minChannelWallpaperLevel: Int32
|
public let minChannelWallpaperLevel: Int32
|
||||||
public let minChannelCustomWallpaperLevel: Int32
|
public let minChannelCustomWallpaperLevel: Int32
|
||||||
public let minChannelRestrictAdsLevel: Int32
|
public let minChannelRestrictAdsLevel: Int32
|
||||||
|
public let minChannelWearGiftLevel: Int32
|
||||||
public let minGroupProfileIconLevel: Int32
|
public let minGroupProfileIconLevel: Int32
|
||||||
public let minGroupEmojiStatusLevel: Int32
|
public let minGroupEmojiStatusLevel: Int32
|
||||||
public let minGroupWallpaperLevel: Int32
|
public let minGroupWallpaperLevel: Int32
|
||||||
@ -212,6 +215,7 @@ public struct PremiumConfiguration {
|
|||||||
minChannelWallpaperLevel: Int32,
|
minChannelWallpaperLevel: Int32,
|
||||||
minChannelCustomWallpaperLevel: Int32,
|
minChannelCustomWallpaperLevel: Int32,
|
||||||
minChannelRestrictAdsLevel: Int32,
|
minChannelRestrictAdsLevel: Int32,
|
||||||
|
minChannelWearGiftLevel: Int32,
|
||||||
minGroupProfileIconLevel: Int32,
|
minGroupProfileIconLevel: Int32,
|
||||||
minGroupEmojiStatusLevel: Int32,
|
minGroupEmojiStatusLevel: Int32,
|
||||||
minGroupWallpaperLevel: Int32,
|
minGroupWallpaperLevel: Int32,
|
||||||
@ -238,6 +242,7 @@ public struct PremiumConfiguration {
|
|||||||
self.minChannelWallpaperLevel = minChannelWallpaperLevel
|
self.minChannelWallpaperLevel = minChannelWallpaperLevel
|
||||||
self.minChannelCustomWallpaperLevel = minChannelCustomWallpaperLevel
|
self.minChannelCustomWallpaperLevel = minChannelCustomWallpaperLevel
|
||||||
self.minChannelRestrictAdsLevel = minChannelRestrictAdsLevel
|
self.minChannelRestrictAdsLevel = minChannelRestrictAdsLevel
|
||||||
|
self.minChannelWearGiftLevel = minChannelWearGiftLevel
|
||||||
self.minGroupProfileIconLevel = minGroupProfileIconLevel
|
self.minGroupProfileIconLevel = minGroupProfileIconLevel
|
||||||
self.minGroupEmojiStatusLevel = minGroupEmojiStatusLevel
|
self.minGroupEmojiStatusLevel = minGroupEmojiStatusLevel
|
||||||
self.minGroupWallpaperLevel = minGroupWallpaperLevel
|
self.minGroupWallpaperLevel = minGroupWallpaperLevel
|
||||||
@ -272,6 +277,7 @@ public struct PremiumConfiguration {
|
|||||||
minChannelWallpaperLevel: get(data["channel_wallpaper_level_min"]) ?? defaultValue.minChannelWallpaperLevel,
|
minChannelWallpaperLevel: get(data["channel_wallpaper_level_min"]) ?? defaultValue.minChannelWallpaperLevel,
|
||||||
minChannelCustomWallpaperLevel: get(data["channel_custom_wallpaper_level_min"]) ?? defaultValue.minChannelCustomWallpaperLevel,
|
minChannelCustomWallpaperLevel: get(data["channel_custom_wallpaper_level_min"]) ?? defaultValue.minChannelCustomWallpaperLevel,
|
||||||
minChannelRestrictAdsLevel: get(data["channel_restrict_sponsored_level_min"]) ?? defaultValue.minChannelRestrictAdsLevel,
|
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,
|
minGroupProfileIconLevel: get(data["group_profile_bg_icon_level_min"]) ?? defaultValue.minGroupProfileIconLevel,
|
||||||
minGroupEmojiStatusLevel: get(data["group_emoji_status_level_min"]) ?? defaultValue.minGroupEmojiStatusLevel,
|
minGroupEmojiStatusLevel: get(data["group_emoji_status_level_min"]) ?? defaultValue.minGroupEmojiStatusLevel,
|
||||||
minGroupWallpaperLevel: get(data["group_wallpaper_level_min"]) ?? defaultValue.minGroupWallpaperLevel,
|
minGroupWallpaperLevel: get(data["group_wallpaper_level_min"]) ?? defaultValue.minGroupWallpaperLevel,
|
||||||
|
@ -1875,9 +1875,8 @@ public final class ChatListNode: ListView {
|
|||||||
}))
|
}))
|
||||||
case .premiumGrace:
|
case .premiumGrace:
|
||||||
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .gracePremium).startStandalone()
|
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
|
case .setupPhoto:
|
||||||
// return true
|
let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .setupPhoto).startStandalone()
|
||||||
// }))
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1994,11 +1993,6 @@ public final class ChatListNode: ListView {
|
|||||||
starsSubscriptionsContextPromise.get()
|
starsSubscriptionsContextPromise.get()
|
||||||
)
|
)
|
||||||
|> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, data, birthdays, starsSubscriptionsContext -> Signal<ChatListNotice?, NoError> in
|
|> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, data, birthdays, starsSubscriptionsContext -> Signal<ChatListNotice?, NoError> in
|
||||||
#if DEBUG
|
|
||||||
var suggestions = suggestions
|
|
||||||
suggestions.insert(.setupPhoto, at: 0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
let (accountPeer, birthday) = data
|
let (accountPeer, birthday) = data
|
||||||
|
|
||||||
if let newSessionReview = newSessionReviews.first {
|
if let newSessionReview = newSessionReviews.first {
|
||||||
|
@ -294,7 +294,7 @@ public class DrawingReactionEntityView: DrawingStickerEntityView {
|
|||||||
let context = self.context
|
let context = self.context
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
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 {
|
if case .info = action, let self {
|
||||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .storiesExpirationDurations, forceDark: true, dismissed: nil)
|
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .storiesExpirationDurations, forceDark: true, dismissed: nil)
|
||||||
self.containerView?.push(controller)
|
self.containerView?.push(controller)
|
||||||
|
@ -464,7 +464,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||||||
storeAttributedTextInPasteboard(text)
|
storeAttributedTextInPasteboard(text)
|
||||||
|
|
||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
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)
|
self.controllerInteraction?.presentController(undoController, nil)
|
||||||
case .share:
|
case .share:
|
||||||
|
@ -62,6 +62,10 @@
|
|||||||
- (void)setImage:(UIImage *)image thumbnailImage:(UIImage *)thumbnailImage forItem:(id<TGMediaEditableItem>)item synchronous:(bool)synchronous;
|
- (void)setImage:(UIImage *)image thumbnailImage:(UIImage *)thumbnailImage forItem:(id<TGMediaEditableItem>)item synchronous:(bool)synchronous;
|
||||||
- (void)setFullSizeImage:(UIImage *)image forItem:(id<TGMediaEditableItem>)item;
|
- (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;
|
- (void)setTemporaryRep:(id)rep forItem:(id<TGMediaEditableItem>)item;
|
||||||
|
|
||||||
- (SSignal *)fullSizeImageUrlForItem:(id<TGMediaEditableItem>)item;
|
- (SSignal *)fullSizeImageUrlForItem:(id<TGMediaEditableItem>)item;
|
||||||
|
@ -31,6 +31,9 @@
|
|||||||
- (void)prepareForEditing;
|
- (void)prepareForEditing;
|
||||||
- (void)returnFromEditing;
|
- (void)returnFromEditing;
|
||||||
|
|
||||||
|
- (void)prepareForCoverEditing;
|
||||||
|
- (void)returnFromCoverEditing;
|
||||||
|
|
||||||
- (UIImage *)screenImage;
|
- (UIImage *)screenImage;
|
||||||
- (UIImage *)transitionImage;
|
- (UIImage *)transitionImage;
|
||||||
- (CGRect)editorTransitionViewRect;
|
- (CGRect)editorTransitionViewRect;
|
||||||
|
@ -120,6 +120,8 @@
|
|||||||
|
|
||||||
@property (nonatomic, copy) id<TGCaptionPanelView> _Nullable(^ _Nullable captionPanelView)(void);
|
@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;
|
- (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;
|
- (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];
|
CGSize dimensions = [TGMediaVideoConverter dimensionsFor:asset.originalSize adjustments:adjustments preset:preset];
|
||||||
NSTimeInterval duration = adjustments.trimApplied ? (adjustments.trimEndValue - adjustments.trimStartValue) : asset.videoDuration;
|
NSTimeInterval duration = adjustments.trimApplied ? (adjustments.trimEndValue - adjustments.trimStartValue) : asset.videoDuration;
|
||||||
|
|
||||||
|
UIImage *coverImage = [editingContext coverImageForItem:asset];
|
||||||
|
|
||||||
[signals addObject:[thumbnailSignal map:^id(UIImage *image)
|
[signals addObject:[thumbnailSignal map:^id(UIImage *image)
|
||||||
{
|
{
|
||||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||||
@ -1341,6 +1343,7 @@
|
|||||||
dict[@"adjustments"] = adjustments;
|
dict[@"adjustments"] = adjustments;
|
||||||
dict[@"dimensions"] = [NSValue valueWithCGSize:dimensions];
|
dict[@"dimensions"] = [NSValue valueWithCGSize:dimensions];
|
||||||
dict[@"duration"] = @(duration);
|
dict[@"duration"] = @(duration);
|
||||||
|
dict[@"coverImage"] = coverImage;
|
||||||
|
|
||||||
if (adjustments.paintingData.stickers.count > 0)
|
if (adjustments.paintingData.stickers.count > 0)
|
||||||
dict[@"stickers"] = adjustments.paintingData.stickers;
|
dict[@"stickers"] = adjustments.paintingData.stickers;
|
||||||
|
@ -106,6 +106,8 @@
|
|||||||
TGMemoryImageCache *_originalImageCache;
|
TGMemoryImageCache *_originalImageCache;
|
||||||
TGMemoryImageCache *_originalThumbnailImageCache;
|
TGMemoryImageCache *_originalThumbnailImageCache;
|
||||||
|
|
||||||
|
TGMemoryImageCache *_coverImageCache;
|
||||||
|
|
||||||
TGModernCache *_diskCache;
|
TGModernCache *_diskCache;
|
||||||
NSURL *_fullSizeResultsUrl;
|
NSURL *_fullSizeResultsUrl;
|
||||||
NSURL *_paintingDatasUrl;
|
NSURL *_paintingDatasUrl;
|
||||||
@ -119,6 +121,7 @@
|
|||||||
|
|
||||||
SPipe *_representationPipe;
|
SPipe *_representationPipe;
|
||||||
SPipe *_thumbnailImagePipe;
|
SPipe *_thumbnailImagePipe;
|
||||||
|
SPipe *_coverImagePipe;
|
||||||
SPipe *_adjustmentsPipe;
|
SPipe *_adjustmentsPipe;
|
||||||
SPipe *_captionPipe;
|
SPipe *_captionPipe;
|
||||||
SPipe *_timerPipe;
|
SPipe *_timerPipe;
|
||||||
@ -166,6 +169,9 @@
|
|||||||
_originalThumbnailImageCache = [[TGMemoryImageCache alloc] initWithSoftMemoryLimit:[[self class] thumbnailImageSoftMemoryLimit]
|
_originalThumbnailImageCache = [[TGMemoryImageCache alloc] initWithSoftMemoryLimit:[[self class] thumbnailImageSoftMemoryLimit]
|
||||||
hardMemoryLimit:[[self class] thumbnailImageHardMemoryLimit]];
|
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]];
|
NSString *diskCachePath = [[[LegacyComponentsGlobals provider] dataStoragePath] stringByAppendingPathComponent:[[self class] diskCachePath]];
|
||||||
_diskCache = [[TGModernCache alloc] initWithPath:diskCachePath size:[[self class] diskMemoryLimit]];
|
_diskCache = [[TGModernCache alloc] initWithPath:diskCachePath size:[[self class] diskMemoryLimit]];
|
||||||
|
|
||||||
@ -194,6 +200,7 @@
|
|||||||
_thumbnailImagePipe = [[SPipe alloc] init];
|
_thumbnailImagePipe = [[SPipe alloc] init];
|
||||||
_adjustmentsPipe = [[SPipe alloc] init];
|
_adjustmentsPipe = [[SPipe alloc] init];
|
||||||
_captionPipe = [[SPipe alloc] init];
|
_captionPipe = [[SPipe alloc] init];
|
||||||
|
_coverImagePipe = [[SPipe alloc] init];
|
||||||
_timerPipe = [[SPipe alloc] init];
|
_timerPipe = [[SPipe alloc] init];
|
||||||
_spoilerPipe = [[SPipe alloc] init];
|
_spoilerPipe = [[SPipe alloc] init];
|
||||||
_pricePipe = [[SPipe alloc] init];
|
_pricePipe = [[SPipe alloc] init];
|
||||||
@ -908,6 +915,46 @@
|
|||||||
[_faces removeObjectForKey:itemId];
|
[_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
|
- (void)setFullSizeImage:(UIImage *)image forItem:(id<TGMediaEditableItem>)item
|
||||||
{
|
{
|
||||||
NSString *itemId = [self _contextualIdForItemId:item.uniqueIdentifier];
|
NSString *itemId = [self _contextualIdForItemId:item.uniqueIdentifier];
|
||||||
@ -1167,6 +1214,11 @@
|
|||||||
return [NSString stringWithFormat:@"%@://%@", [self thumbnailImageUriScheme], itemId];
|
return [NSString stringWithFormat:@"%@://%@", [self thumbnailImageUriScheme], itemId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (NSString *)_coverImageUriForItemId:(NSString *)itemId
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"%@://%@", @"photo-editor-cover", itemId];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Constants
|
#pragma mark - Constants
|
||||||
|
|
||||||
+ (NSString *)imageUriScheme
|
+ (NSString *)imageUriScheme
|
||||||
|
@ -92,10 +92,17 @@
|
|||||||
TGMediaPickerGroupButton *_groupButton;
|
TGMediaPickerGroupButton *_groupButton;
|
||||||
TGMediaPickerCameraButton *_cameraButton;
|
TGMediaPickerCameraButton *_cameraButton;
|
||||||
|
|
||||||
|
TGMediaPickerCoverButton *_coverButton;
|
||||||
|
TGModernButton *_cancelCoverButton;
|
||||||
|
TGModernButton *_saveCoverButton;
|
||||||
|
TGMediaPickerCoverButton *_coverGalleryButton;
|
||||||
|
UILabel *_coverTitleLabel;
|
||||||
|
|
||||||
TGMediaPickerPhotoStripView *_selectedPhotosView;
|
TGMediaPickerPhotoStripView *_selectedPhotosView;
|
||||||
|
|
||||||
SMetaDisposable *_adjustmentsDisposable;
|
SMetaDisposable *_adjustmentsDisposable;
|
||||||
SMetaDisposable *_captionDisposable;
|
SMetaDisposable *_captionDisposable;
|
||||||
|
SMetaDisposable *_coverDisposable;
|
||||||
SMetaDisposable *_itemAvailabilityDisposable;
|
SMetaDisposable *_itemAvailabilityDisposable;
|
||||||
SMetaDisposable *_itemSelectedDisposable;
|
SMetaDisposable *_itemSelectedDisposable;
|
||||||
id<SDisposable> _selectionChangedDisposable;
|
id<SDisposable> _selectionChangedDisposable;
|
||||||
@ -136,6 +143,7 @@
|
|||||||
|
|
||||||
_adjustmentsDisposable = [[SMetaDisposable alloc] init];
|
_adjustmentsDisposable = [[SMetaDisposable alloc] init];
|
||||||
_captionDisposable = [[SMetaDisposable alloc] init];
|
_captionDisposable = [[SMetaDisposable alloc] init];
|
||||||
|
_coverDisposable = [[SMetaDisposable alloc] init];
|
||||||
_itemSelectedDisposable = [[SMetaDisposable alloc] init];
|
_itemSelectedDisposable = [[SMetaDisposable alloc] init];
|
||||||
_itemAvailabilityDisposable = [[SMetaDisposable alloc] init];
|
_itemAvailabilityDisposable = [[SMetaDisposable alloc] init];
|
||||||
_tooltipDismissDisposable = [[SMetaDisposable alloc] init];
|
_tooltipDismissDisposable = [[SMetaDisposable alloc] init];
|
||||||
@ -200,7 +208,7 @@
|
|||||||
[[NSUserDefaults standardUserDefaults] setObject:@(3) forKey:@"TG_displayedMediaTimerTooltip_v3"];
|
[[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.hidden = true;
|
||||||
_muteButton.adjustsImageWhenHighlighted = false;
|
_muteButton.adjustsImageWhenHighlighted = false;
|
||||||
[_muteButton setBackgroundImage:[TGPhotoEditorInterfaceAssets gifBackgroundImage] forState:UIControlStateNormal];
|
[_muteButton setBackgroundImage:[TGPhotoEditorInterfaceAssets gifBackgroundImage] forState:UIControlStateNormal];
|
||||||
@ -239,6 +247,16 @@
|
|||||||
// [_cameraButton setHidden:true animated:false];
|
// [_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)
|
if (_selectionContext != nil)
|
||||||
{
|
{
|
||||||
_checkButton = [[TGCheckButtonView alloc] initWithStyle:TGCheckButtonStyleGallery];
|
_checkButton = [[TGCheckButtonView alloc] initWithStyle:TGCheckButtonStyleGallery];
|
||||||
@ -270,7 +288,7 @@
|
|||||||
[_wrapperView addSubview:_photoCounterButton];
|
[_wrapperView addSubview:_photoCounterButton];
|
||||||
|
|
||||||
_selectionChangedDisposable = [[_selectionContext selectionChangedSignal] startStrictWithNext:^(id next)
|
_selectionChangedDisposable = [[_selectionContext selectionChangedSignal] startStrictWithNext:^(id next)
|
||||||
{
|
{
|
||||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||||
if (strongSelf == nil)
|
if (strongSelf == nil)
|
||||||
return;
|
return;
|
||||||
@ -290,7 +308,7 @@
|
|||||||
if (_editingContext != nil)
|
if (_editingContext != nil)
|
||||||
{
|
{
|
||||||
_timersChangedDisposable = [_editingContext.timersUpdatedSignal startStrictWithNext:^(__unused NSNumber *next)
|
_timersChangedDisposable = [_editingContext.timersUpdatedSignal startStrictWithNext:^(__unused NSNumber *next)
|
||||||
{
|
{
|
||||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||||
if (strongSelf == nil)
|
if (strongSelf == nil)
|
||||||
return;
|
return;
|
||||||
@ -299,7 +317,7 @@
|
|||||||
} file:__FILE_NAME__ line:__LINE__];
|
} file:__FILE_NAME__ line:__LINE__];
|
||||||
|
|
||||||
_adjustmentsChangedDisposable = [_editingContext.adjustmentsUpdatedSignal startStrictWithNext:^(__unused NSNumber *next)
|
_adjustmentsChangedDisposable = [_editingContext.adjustmentsUpdatedSignal startStrictWithNext:^(__unused NSNumber *next)
|
||||||
{
|
{
|
||||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||||
if (strongSelf == nil)
|
if (strongSelf == nil)
|
||||||
return;
|
return;
|
||||||
@ -377,7 +395,7 @@
|
|||||||
offset = -keyboardHeight / 2.0f;
|
offset = -keyboardHeight / 2.0f;
|
||||||
|
|
||||||
[UIView animateWithDuration:duration delay:0.0f options:animationCurve animations:^
|
[UIView animateWithDuration:duration delay:0.0f options:animationCurve animations:^
|
||||||
{
|
{
|
||||||
if (strongSelf->_scrollViewOffsetRequested != nil)
|
if (strongSelf->_scrollViewOffsetRequested != nil)
|
||||||
strongSelf->_scrollViewOffsetRequested(offset);
|
strongSelf->_scrollViewOffsetRequested(offset);
|
||||||
} completion:nil];
|
} completion:nil];
|
||||||
@ -423,6 +441,34 @@
|
|||||||
|
|
||||||
if ([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad)
|
if ([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad)
|
||||||
[_wrapperView addSubview:_landscapeToolbarView];
|
[_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;
|
return self;
|
||||||
}
|
}
|
||||||
@ -433,6 +479,7 @@
|
|||||||
|
|
||||||
[_adjustmentsDisposable dispose];
|
[_adjustmentsDisposable dispose];
|
||||||
[_captionDisposable dispose];
|
[_captionDisposable dispose];
|
||||||
|
[_coverDisposable dispose];
|
||||||
[_itemSelectedDisposable dispose];
|
[_itemSelectedDisposable dispose];
|
||||||
[_itemAvailabilityDisposable dispose];
|
[_itemAvailabilityDisposable dispose];
|
||||||
[_selectionChangedDisposable dispose];
|
[_selectionChangedDisposable dispose];
|
||||||
@ -472,7 +519,7 @@
|
|||||||
|
|
||||||
bool groupingButtonVisible = _groupButton != nil && onlyGroupableMedia && _selectionContext.count > 1;
|
bool groupingButtonVisible = _groupButton != nil && onlyGroupableMedia && _selectionContext.count > 1;
|
||||||
dispatch_async(dispatch_get_main_queue(), ^
|
dispatch_async(dispatch_get_main_queue(), ^
|
||||||
{
|
{
|
||||||
[_groupButton setInternalHidden:!groupingButtonVisible animated:true];
|
[_groupButton setInternalHidden:!groupingButtonVisible animated:true];
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -587,7 +634,7 @@
|
|||||||
[_checkButton setNumber:[_selectionContext indexOfItem:selectableItem]];
|
[_checkButton setNumber:[_selectionContext indexOfItem:selectableItem]];
|
||||||
signal = [_selectionContext itemInformativeSelectedSignal:selectableItem];
|
signal = [_selectionContext itemInformativeSelectedSignal:selectableItem];
|
||||||
[_itemSelectedDisposable setDisposable:[signal startStrictWithNext:^(TGMediaSelectionChange *next)
|
[_itemSelectedDisposable setDisposable:[signal startStrictWithNext:^(TGMediaSelectionChange *next)
|
||||||
{
|
{
|
||||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||||
if (strongSelf == nil)
|
if (strongSelf == nil)
|
||||||
return;
|
return;
|
||||||
@ -601,7 +648,7 @@
|
|||||||
|
|
||||||
__weak TGModernGalleryItemView *weakItemView = itemView;
|
__weak TGModernGalleryItemView *weakItemView = itemView;
|
||||||
[_itemAvailabilityDisposable setDisposable:[[[itemView contentAvailabilityStateSignal] deliverOn:[SQueue mainQueue]] startStrictWithNext:^(id next)
|
[_itemAvailabilityDisposable setDisposable:[[[itemView contentAvailabilityStateSignal] deliverOn:[SQueue mainQueue]] startStrictWithNext:^(id next)
|
||||||
{
|
{
|
||||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||||
__strong TGModernGalleryItemView *strongItemView = weakItemView;
|
__strong TGModernGalleryItemView *strongItemView = weakItemView;
|
||||||
if (strongSelf == nil || strongItemView == nil)
|
if (strongSelf == nil || strongItemView == nil)
|
||||||
@ -630,6 +677,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
strongSelf->_muteButton.hidden = !sendableAsGif;
|
strongSelf->_muteButton.hidden = !sendableAsGif;
|
||||||
|
|
||||||
|
bool canHaveCover = [strongItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]];
|
||||||
|
strongSelf->_coverButton.hidden = !canHaveCover;
|
||||||
}
|
}
|
||||||
} file:__FILE_NAME__ line:__LINE__]];
|
} file:__FILE_NAME__ line:__LINE__]];
|
||||||
|
|
||||||
@ -682,28 +732,28 @@
|
|||||||
|
|
||||||
NSArray *items = @
|
NSArray *items = @
|
||||||
[
|
[
|
||||||
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Camera.Discard") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Camera.Discard") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
|
||||||
{
|
{
|
||||||
__strong TGMenuSheetController *strongController = weakController;
|
__strong TGMenuSheetController *strongController = weakController;
|
||||||
if (strongController == nil)
|
if (strongController == nil)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||||
if (strongSelf == nil)
|
if (strongSelf == nil)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
strongSelf->_capturing = false;
|
strongSelf->_capturing = false;
|
||||||
strongSelf->_closePressed();
|
strongSelf->_closePressed();
|
||||||
|
|
||||||
[strongController dismissAnimated:true manual:false completion:nil];
|
[strongController dismissAnimated:true manual:false completion:nil];
|
||||||
}],
|
}],
|
||||||
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel fontSize:20.0 action:^
|
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel fontSize:20.0 action:^
|
||||||
{
|
{
|
||||||
__strong TGMenuSheetController *strongController = weakController;
|
__strong TGMenuSheetController *strongController = weakController;
|
||||||
if (strongController != nil)
|
if (strongController != nil)
|
||||||
[strongController dismissAnimated:true];
|
[strongController dismissAnimated:true];
|
||||||
}]
|
}]
|
||||||
];
|
];
|
||||||
|
|
||||||
[controller setItemViews:items];
|
[controller setItemViews:items];
|
||||||
controller.sourceRect = ^
|
controller.sourceRect = ^
|
||||||
@ -777,6 +827,119 @@
|
|||||||
[self updateGroupingButtonVisibility];
|
[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
|
- (void)updateEditorButtonsForItem:(id<TGModernGalleryItem>)item animated:(bool)animated
|
||||||
{
|
{
|
||||||
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
|
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
|
||||||
@ -791,6 +954,14 @@
|
|||||||
return;
|
return;
|
||||||
[strongSelf->_captionMixin setCaption:caption animated:animated];
|
[strongSelf->_captionMixin setCaption:caption animated:animated];
|
||||||
} file:__FILE_NAME__ line:__LINE__]];
|
} 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)
|
if (_editingContext == nil || _editingContext.inhibitEditing)
|
||||||
@ -938,7 +1109,6 @@
|
|||||||
qualityButton.iconImage = icon;
|
qualityButton.iconImage = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool willShowTimerTooltip = false;
|
|
||||||
TGPhotoEditorButton *timerButton = [_portraitToolbarView buttonForTab:TGPhotoEditorTimerTab];
|
TGPhotoEditorButton *timerButton = [_portraitToolbarView buttonForTab:TGPhotoEditorTimerTab];
|
||||||
if (timerButton != nil)
|
if (timerButton != nil)
|
||||||
{
|
{
|
||||||
@ -959,7 +1129,6 @@
|
|||||||
|
|
||||||
if ([self shouldDisplayTooltip])
|
if ([self shouldDisplayTooltip])
|
||||||
{
|
{
|
||||||
willShowTimerTooltip = true;
|
|
||||||
TGDispatchAfter(0.5, dispatch_get_main_queue(), ^
|
TGDispatchAfter(0.5, dispatch_get_main_queue(), ^
|
||||||
{
|
{
|
||||||
if (!TGIsPad() && self.frame.size.width > self.frame.size.height)
|
if (!TGIsPad() && self.frame.size.width > self.frame.size.height)
|
||||||
@ -1098,6 +1267,7 @@
|
|||||||
{
|
{
|
||||||
_checkButton.alpha = alpha;
|
_checkButton.alpha = alpha;
|
||||||
_muteButton.alpha = alpha;
|
_muteButton.alpha = alpha;
|
||||||
|
_coverButton.alpha = alpha;
|
||||||
_arrowView.alpha = alpha * 0.6f;
|
_arrowView.alpha = alpha * 0.6f;
|
||||||
_recipientLabel.alpha = alpha * 0.6;
|
_recipientLabel.alpha = alpha * 0.6;
|
||||||
} completion:^(BOOL finished)
|
} completion:^(BOOL finished)
|
||||||
@ -1106,6 +1276,7 @@
|
|||||||
{
|
{
|
||||||
_checkButton.userInteractionEnabled = !hidden;
|
_checkButton.userInteractionEnabled = !hidden;
|
||||||
_muteButton.userInteractionEnabled = !hidden;
|
_muteButton.userInteractionEnabled = !hidden;
|
||||||
|
_coverButton.userInteractionEnabled = !hidden;
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@ -1129,6 +1300,9 @@
|
|||||||
_muteButton.alpha = alpha;
|
_muteButton.alpha = alpha;
|
||||||
_muteButton.userInteractionEnabled = !hidden;
|
_muteButton.userInteractionEnabled = !hidden;
|
||||||
|
|
||||||
|
_coverButton.alpha = alpha;
|
||||||
|
_coverButton.userInteractionEnabled = !hidden;
|
||||||
|
|
||||||
_arrowView.alpha = alpha * 0.6f;
|
_arrowView.alpha = alpha * 0.6f;
|
||||||
_recipientLabel.alpha = alpha * 0.6;
|
_recipientLabel.alpha = alpha * 0.6;
|
||||||
}
|
}
|
||||||
@ -1145,7 +1319,11 @@
|
|||||||
[_groupButton setHidden:true animated:animated];
|
[_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);
|
CGFloat alpha = (hidden ? 0.0f : 1.0f);
|
||||||
if (animated)
|
if (animated)
|
||||||
@ -1154,8 +1332,9 @@
|
|||||||
{
|
{
|
||||||
_checkButton.alpha = alpha;
|
_checkButton.alpha = alpha;
|
||||||
_muteButton.alpha = alpha;
|
_muteButton.alpha = alpha;
|
||||||
|
_coverButton.alpha = alpha;
|
||||||
_arrowView.alpha = alpha * 0.6;
|
_arrowView.alpha = alpha * 0.6;
|
||||||
_recipientLabel.alpha = alpha;
|
_recipientLabel.alpha = alpha * 0.6;
|
||||||
_portraitToolbarView.alpha = alpha;
|
_portraitToolbarView.alpha = alpha;
|
||||||
_landscapeToolbarView.alpha = alpha;
|
_landscapeToolbarView.alpha = alpha;
|
||||||
_captionMixin.inputPanelView.alpha = alpha;
|
_captionMixin.inputPanelView.alpha = alpha;
|
||||||
@ -1166,6 +1345,7 @@
|
|||||||
{
|
{
|
||||||
_checkButton.userInteractionEnabled = !hidden;
|
_checkButton.userInteractionEnabled = !hidden;
|
||||||
_muteButton.userInteractionEnabled = !hidden;
|
_muteButton.userInteractionEnabled = !hidden;
|
||||||
|
_coverButton.userInteractionEnabled = !hidden;
|
||||||
_portraitToolbarView.userInteractionEnabled = !hidden;
|
_portraitToolbarView.userInteractionEnabled = !hidden;
|
||||||
_landscapeToolbarView.userInteractionEnabled = !hidden;
|
_landscapeToolbarView.userInteractionEnabled = !hidden;
|
||||||
_captionMixin.inputPanelView.userInteractionEnabled = !hidden;
|
_captionMixin.inputPanelView.userInteractionEnabled = !hidden;
|
||||||
@ -1193,6 +1373,9 @@
|
|||||||
_muteButton.alpha = alpha;
|
_muteButton.alpha = alpha;
|
||||||
_muteButton.userInteractionEnabled = !hidden;
|
_muteButton.userInteractionEnabled = !hidden;
|
||||||
|
|
||||||
|
_coverButton.alpha = alpha;
|
||||||
|
_coverButton.userInteractionEnabled = !hidden;
|
||||||
|
|
||||||
_arrowView.alpha = alpha * 0.6;
|
_arrowView.alpha = alpha * 0.6;
|
||||||
_recipientLabel.alpha = alpha;
|
_recipientLabel.alpha = alpha;
|
||||||
|
|
||||||
@ -1219,7 +1402,7 @@
|
|||||||
if (!_groupButton.hidden)
|
if (!_groupButton.hidden)
|
||||||
[_groupButton setHidden:true animated:animated];
|
[_groupButton setHidden:true animated:animated];
|
||||||
|
|
||||||
[self setItemHeaderViewHidden:hidden animated:animated];
|
[self setItemHeaderViewHidden:!keepHeader && hidden animated:animated];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
@ -1431,6 +1614,10 @@
|
|||||||
|| view == _muteButton
|
|| view == _muteButton
|
||||||
|| view == _groupButton
|
|| view == _groupButton
|
||||||
|| view == _cameraButton
|
|| view == _cameraButton
|
||||||
|
|| view == _coverButton
|
||||||
|
|| view == _cancelCoverButton
|
||||||
|
|| view == _saveCoverButton
|
||||||
|
|| view == _coverGalleryButton
|
||||||
|| [view isDescendantOfView:_headerWrapperView]
|
|| [view isDescendantOfView:_headerWrapperView]
|
||||||
|| [view isDescendantOfView:_portraitToolbarView]
|
|| [view isDescendantOfView:_portraitToolbarView]
|
||||||
|| [view isDescendantOfView:_landscapeToolbarView]
|
|| [view isDescendantOfView:_landscapeToolbarView]
|
||||||
@ -1492,7 +1679,7 @@
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1526,6 +1713,34 @@
|
|||||||
return frame;
|
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)_checkButtonFrameForOrientation:(UIInterfaceOrientation)orientation screenEdges:(UIEdgeInsets)screenEdges hasHeaderView:(bool)hasHeaderView
|
||||||
{
|
{
|
||||||
CGRect frame = CGRectZero;
|
CGRect frame = CGRectZero;
|
||||||
@ -1712,6 +1927,14 @@
|
|||||||
portraitToolbarViewBottomEdge = screenEdges.bottom;
|
portraitToolbarViewBottomEdge = screenEdges.bottom;
|
||||||
_portraitToolbarView.frame = CGRectMake(screenEdges.left, portraitToolbarViewBottomEdge - TGPhotoEditorToolbarSize - _safeAreaInset.bottom, self.frame.size.width, TGPhotoEditorToolbarSize + _safeAreaInset.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;
|
UIEdgeInsets captionEdgeInsets = screenEdges;
|
||||||
captionEdgeInsets.bottom = _portraitToolbarView.frame.size.height;
|
captionEdgeInsets.bottom = _portraitToolbarView.frame.size.height;
|
||||||
[_captionMixin updateLayoutWithFrame:self.bounds edgeInsets:captionEdgeInsets animated:false];
|
[_captionMixin updateLayoutWithFrame:self.bounds edgeInsets:captionEdgeInsets animated:false];
|
||||||
@ -1752,9 +1975,9 @@
|
|||||||
{
|
{
|
||||||
[UIView performWithoutAnimation:^
|
[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);
|
_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];
|
_muteButton.frame = [self _muteButtonFrameForOrientation:orientation screenEdges:screenEdges hasHeaderView:true];
|
||||||
_checkButton.frame = [self _checkButtonFrameForOrientation:orientation screenEdges:screenEdges hasHeaderView:hasHeaderView];
|
_checkButton.frame = [self _checkButtonFrameForOrientation:orientation screenEdges:screenEdges hasHeaderView:hasHeaderView];
|
||||||
_groupButton.frame = [self _groupButtonFrameForOrientation: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:^
|
[UIView performWithoutAnimation:^
|
||||||
{
|
{
|
||||||
_cameraButton.frame = [self _cameraButtonFrameForOrientation:orientation screenEdges:screenEdges hasHeaderView:hasHeaderView panelVisible:_selectedPhotosView != nil && !_selectedPhotosView.isInternalHidden];
|
_cameraButton.frame = [self _cameraButtonFrameForOrientation:orientation screenEdges:screenEdges hasHeaderView:hasHeaderView panelVisible:_selectedPhotosView != nil && !_selectedPhotosView.isInternalHidden];
|
||||||
|
@ -67,6 +67,9 @@
|
|||||||
UIView *_headerView;
|
UIView *_headerView;
|
||||||
UIView *_scrubberPanelView;
|
UIView *_scrubberPanelView;
|
||||||
TGMediaPickerGalleryVideoScrubber *_scrubberView;
|
TGMediaPickerGalleryVideoScrubber *_scrubberView;
|
||||||
|
|
||||||
|
TGMediaPickerGalleryVideoScrubber *_coverScrubberView;
|
||||||
|
|
||||||
bool _wasPlayingBeforeScrubbing;
|
bool _wasPlayingBeforeScrubbing;
|
||||||
bool _appeared;
|
bool _appeared;
|
||||||
bool _scrubbingPanelPresented;
|
bool _scrubbingPanelPresented;
|
||||||
@ -225,13 +228,21 @@
|
|||||||
//scrubberBackgroundView.backgroundColor = [TGPhotoEditorInterfaceAssets toolbarTransparentBackgroundColor];
|
//scrubberBackgroundView.backgroundColor = [TGPhotoEditorInterfaceAssets toolbarTransparentBackgroundColor];
|
||||||
[_scrubberPanelView addSubview:scrubberBackgroundView];
|
[_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.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||||
_scrubberView.dataSource = self;
|
_scrubberView.dataSource = self;
|
||||||
_scrubberView.delegate = self;
|
_scrubberView.delegate = self;
|
||||||
headerView.scrubberView = _scrubberView;
|
headerView.scrubberView = _scrubberView;
|
||||||
[_scrubberPanelView addSubview:_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 = [[UILabel alloc] initWithFrame:CGRectMake(0, 10.0f, _scrubberPanelView.frame.size.width, 21)];
|
||||||
_fileInfoLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
_fileInfoLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||||
_fileInfoLabel.backgroundColor = [UIColor clearColor];
|
_fileInfoLabel.backgroundColor = [UIColor clearColor];
|
||||||
@ -420,6 +431,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
_scrubberView.allowsTrimming = false;
|
_scrubberView.allowsTrimming = false;
|
||||||
|
_coverScrubberView.allowsTrimming = false;
|
||||||
_videoDimensions = item.dimensions;
|
_videoDimensions = item.dimensions;
|
||||||
|
|
||||||
if (_entitiesView == nil) {
|
if (_entitiesView == nil) {
|
||||||
@ -596,6 +608,12 @@
|
|||||||
strongSelf->_scrubberView.trimStartValue = adjustments.trimStartValue;
|
strongSelf->_scrubberView.trimStartValue = adjustments.trimStartValue;
|
||||||
strongSelf->_scrubberView.trimEndValue = adjustments.trimEndValue;
|
strongSelf->_scrubberView.trimEndValue = adjustments.trimEndValue;
|
||||||
strongSelf->_scrubberView.value = adjustments.trimStartValue;
|
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->_scrubberView setTrimApplied:(adjustments.trimStartValue > 0 || adjustments.trimEndValue < videoDuration)];
|
||||||
strongSelf->_shouldResetScrubber = false;
|
strongSelf->_shouldResetScrubber = false;
|
||||||
}
|
}
|
||||||
@ -603,14 +621,21 @@
|
|||||||
{
|
{
|
||||||
strongSelf->_scrubberView.trimStartValue = 0;
|
strongSelf->_scrubberView.trimStartValue = 0;
|
||||||
strongSelf->_scrubberView.trimEndValue = videoDuration;
|
strongSelf->_scrubberView.trimEndValue = videoDuration;
|
||||||
|
|
||||||
|
strongSelf->_coverScrubberView.trimStartValue = 0;
|
||||||
|
strongSelf->_coverScrubberView.trimEndValue = videoDuration;
|
||||||
|
|
||||||
[strongSelf->_scrubberView setTrimApplied:false];
|
[strongSelf->_scrubberView setTrimApplied:false];
|
||||||
strongSelf->_shouldResetScrubber = true;
|
strongSelf->_shouldResetScrubber = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[strongSelf->_scrubberView reloadData];
|
[strongSelf->_scrubberView reloadData];
|
||||||
|
[strongSelf->_coverScrubberView reloadData];
|
||||||
|
|
||||||
if (!strongSelf->_appeared)
|
if (!strongSelf->_appeared)
|
||||||
{
|
{
|
||||||
[strongSelf->_scrubberView resetToStart];
|
[strongSelf->_scrubberView resetToStart];
|
||||||
|
[strongSelf->_coverScrubberView resetToStart];
|
||||||
strongSelf->_appeared = true;
|
strongSelf->_appeared = true;
|
||||||
}
|
}
|
||||||
} file:__FILE_NAME__ line:__LINE__]];
|
} file:__FILE_NAME__ line:__LINE__]];
|
||||||
@ -629,6 +654,7 @@
|
|||||||
if (afterReload) {
|
if (afterReload) {
|
||||||
_cachedThumbnails = nil;
|
_cachedThumbnails = nil;
|
||||||
[_scrubberView reloadData];
|
[_scrubberView reloadData];
|
||||||
|
[_coverScrubberView reloadData];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
[self setScrubbingPanelHidden:false animated:true];
|
[self setScrubbingPanelHidden:false animated:true];
|
||||||
@ -648,8 +674,10 @@
|
|||||||
|
|
||||||
if (hidden)
|
if (hidden)
|
||||||
{
|
{
|
||||||
if (!_scrubbingPanelPresented)
|
if (!_scrubbingPanelPresented) {
|
||||||
[_scrubberView ignoreThumbnails];
|
[_scrubberView ignoreThumbnails];
|
||||||
|
[_coverScrubberView ignoreThumbnails];
|
||||||
|
}
|
||||||
|
|
||||||
_scrubbingPanelPresented = false;
|
_scrubbingPanelPresented = false;
|
||||||
|
|
||||||
@ -680,6 +708,7 @@
|
|||||||
|
|
||||||
[_scrubberPanelView layoutSubviews];
|
[_scrubberPanelView layoutSubviews];
|
||||||
[_scrubberView layoutSubviews];
|
[_scrubberView layoutSubviews];
|
||||||
|
[_coverScrubberView layoutSubviews];
|
||||||
|
|
||||||
void (^changeBlock)(void) = ^
|
void (^changeBlock)(void) = ^
|
||||||
{
|
{
|
||||||
@ -732,6 +761,18 @@
|
|||||||
[self setPlayButtonHidden:false animated:true];
|
[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
|
- (void)setFrame:(CGRect)frame
|
||||||
{
|
{
|
||||||
bool frameChanged = !CGRectEqualToRect(frame, self.frame);
|
bool frameChanged = !CGRectEqualToRect(frame, self.frame);
|
||||||
@ -741,6 +782,7 @@
|
|||||||
if (_appeared && frameChanged)
|
if (_appeared && frameChanged)
|
||||||
{
|
{
|
||||||
[_scrubberView resetThumbnails];
|
[_scrubberView resetThumbnails];
|
||||||
|
[_coverScrubberView resetThumbnails];
|
||||||
|
|
||||||
[_scrubberPanelView setNeedsLayout];
|
[_scrubberPanelView setNeedsLayout];
|
||||||
[_scrubberPanelView layoutIfNeeded];
|
[_scrubberPanelView layoutIfNeeded];
|
||||||
@ -748,6 +790,7 @@
|
|||||||
dispatch_async(dispatch_get_main_queue(), ^
|
dispatch_async(dispatch_get_main_queue(), ^
|
||||||
{
|
{
|
||||||
[_scrubberView reloadThumbnails];
|
[_scrubberView reloadThumbnails];
|
||||||
|
[_coverScrubberView reloadThumbnails];
|
||||||
[_scrubberPanelView layoutSubviews];
|
[_scrubberPanelView layoutSubviews];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1116,6 +1159,7 @@
|
|||||||
self.isPlaying = false;
|
self.isPlaying = false;
|
||||||
[_scrubberView setIsPlaying:false];
|
[_scrubberView setIsPlaying:false];
|
||||||
[_scrubberView resetToStart];
|
[_scrubberView resetToStart];
|
||||||
|
[_coverScrubberView resetToStart];
|
||||||
|
|
||||||
[_positionTimer invalidate];
|
[_positionTimer invalidate];
|
||||||
_positionTimer = nil;
|
_positionTimer = nil;
|
||||||
@ -1249,6 +1293,7 @@
|
|||||||
{
|
{
|
||||||
[self _seekToPosition:_scrubberView.trimStartValue manual:false];
|
[self _seekToPosition:_scrubberView.trimStartValue manual:false];
|
||||||
[_scrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
|
[_scrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
|
||||||
|
[_coverScrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
|
||||||
}
|
}
|
||||||
|
|
||||||
[_player play];
|
[_player play];
|
||||||
@ -1310,10 +1355,12 @@
|
|||||||
_positionTimer = nil;
|
_positionTimer = nil;
|
||||||
|
|
||||||
[_scrubberView resetToStart];
|
[_scrubberView resetToStart];
|
||||||
|
[_coverScrubberView resetToStart];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[_scrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
|
[_scrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
|
||||||
|
[_coverScrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self _seekToPosition:_scrubberView.trimStartValue manual:false];
|
[self _seekToPosition:_scrubberView.trimStartValue manual:false];
|
||||||
@ -1322,6 +1369,7 @@
|
|||||||
- (void)positionTimerEvent
|
- (void)positionTimerEvent
|
||||||
{
|
{
|
||||||
[_scrubberView setValue:CMTimeGetSeconds(_player.currentItem.currentTime)];
|
[_scrubberView setValue:CMTimeGetSeconds(_player.currentItem.currentTime)];
|
||||||
|
[_coverScrubberView setValue:CMTimeGetSeconds(_player.currentItem.currentTime)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_seekToPosition:(NSTimeInterval)position manual:(bool)__unused manual
|
- (void)_seekToPosition:(NSTimeInterval)position manual:(bool)__unused manual
|
||||||
@ -1396,6 +1444,11 @@
|
|||||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber valueDidChange:(NSTimeInterval)position
|
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber valueDidChange:(NSTimeInterval)position
|
||||||
{
|
{
|
||||||
[self _seekToPosition:position manual:true];
|
[self _seekToPosition:position manual:true];
|
||||||
|
if (videoScrubber == _scrubberView) {
|
||||||
|
[_coverScrubberView setValue:position resetPosition:true];
|
||||||
|
} else {
|
||||||
|
[_scrubberView setValue:position resetPosition:true];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Trimming
|
#pragma mark Trimming
|
||||||
@ -1426,6 +1479,10 @@
|
|||||||
[self updatePlayerRange:videoScrubber.trimEndValue];
|
[self updatePlayerRange:videoScrubber.trimEndValue];
|
||||||
[self updateEditAdjusments];
|
[self updateEditAdjusments];
|
||||||
|
|
||||||
|
_coverScrubberView.trimStartValue = videoScrubber.trimStartValue;
|
||||||
|
_coverScrubberView.trimEndValue = videoScrubber.trimEndValue;
|
||||||
|
[_coverScrubberView _layoutTrimCurtainViews];
|
||||||
|
|
||||||
[self setPlayButtonHidden:false animated:true];
|
[self setPlayButtonHidden:false animated:true];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1622,8 +1679,12 @@
|
|||||||
return [SSignal single:thumbnails];
|
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)
|
if (timestamps.count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1692,8 +1753,10 @@
|
|||||||
|
|
||||||
[images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger index, __unused BOOL *stop)
|
[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->_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:^
|
} completed:^
|
||||||
{
|
{
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
@property (nonatomic, readonly) bool isScrubbing;
|
@property (nonatomic, readonly) bool isScrubbing;
|
||||||
@property (nonatomic, assign) bool isPlaying;
|
@property (nonatomic, assign) bool isPlaying;
|
||||||
@property (nonatomic, assign) NSTimeInterval value;
|
@property (nonatomic, assign) NSTimeInterval value;
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame cover:(bool)cover;
|
||||||
|
|
||||||
- (void)setValue:(NSTimeInterval)value resetPosition:(bool)resetPosition;
|
- (void)setValue:(NSTimeInterval)value resetPosition:(bool)resetPosition;
|
||||||
|
|
||||||
- (void)setTrimApplied:(bool)trimApplied;
|
- (void)setTrimApplied:(bool)trimApplied;
|
||||||
@ -48,6 +51,7 @@
|
|||||||
- (CGPoint)scrubberPositionForPosition:(NSTimeInterval)position;
|
- (CGPoint)scrubberPositionForPosition:(NSTimeInterval)position;
|
||||||
|
|
||||||
- (void)_updateScrubberAnimationsAndResetCurrentPosition:(bool)resetCurrentPosition;
|
- (void)_updateScrubberAnimationsAndResetCurrentPosition:(bool)resetCurrentPosition;
|
||||||
|
- (void)_layoutTrimCurtainViews;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@ typedef enum
|
|||||||
UIView *_zoomedThumbnailWrapperView;
|
UIView *_zoomedThumbnailWrapperView;
|
||||||
UIView *_summaryThumbnailWrapperView;
|
UIView *_summaryThumbnailWrapperView;
|
||||||
TGMediaPickerGalleryVideoTrimView *_trimView;
|
TGMediaPickerGalleryVideoTrimView *_trimView;
|
||||||
UIView *_leftCurtainView;
|
UIImageView *_leftCurtainView;
|
||||||
UIView *_rightCurtainView;
|
UIImageView *_rightCurtainView;
|
||||||
UIControl *_scrubberHandle;
|
UIControl *_scrubberHandle;
|
||||||
|
|
||||||
UIControl *_dotHandle;
|
UIControl *_dotHandle;
|
||||||
@ -86,7 +86,7 @@ typedef enum
|
|||||||
|
|
||||||
@implementation TGMediaPickerGalleryVideoScrubber
|
@implementation TGMediaPickerGalleryVideoScrubber
|
||||||
|
|
||||||
- (instancetype)initWithFrame:(CGRect)frame
|
- (instancetype)initWithFrame:(CGRect)frame cover:(bool)cover
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:frame];
|
self = [super initWithFrame:frame];
|
||||||
if (self != nil)
|
if (self != nil)
|
||||||
@ -105,7 +105,7 @@ typedef enum
|
|||||||
_currentTimeLabel.layer.shadowOpacity = 0.6;
|
_currentTimeLabel.layer.shadowOpacity = 0.6;
|
||||||
_currentTimeLabel.layer.rasterizationScale = TGScreenScaling();
|
_currentTimeLabel.layer.rasterizationScale = TGScreenScaling();
|
||||||
_currentTimeLabel.layer.shouldRasterize = true;
|
_currentTimeLabel.layer.shouldRasterize = true;
|
||||||
[self addSubview:_currentTimeLabel];
|
//[self addSubview:_currentTimeLabel];
|
||||||
|
|
||||||
_inverseTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(frame.size.width - 108, 4, 100, 15)];
|
_inverseTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(frame.size.width - 108, 4, 100, 15)];
|
||||||
_inverseTimeLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
|
_inverseTimeLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
|
||||||
@ -120,9 +120,9 @@ typedef enum
|
|||||||
_inverseTimeLabel.layer.shadowOpacity = 0.6;
|
_inverseTimeLabel.layer.shadowOpacity = 0.6;
|
||||||
_inverseTimeLabel.layer.rasterizationScale = TGScreenScaling();
|
_inverseTimeLabel.layer.rasterizationScale = TGScreenScaling();
|
||||||
_inverseTimeLabel.layer.shouldRasterize = true;
|
_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);
|
_wrapperView.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -10, -5, -10);
|
||||||
[self addSubview:_wrapperView];
|
[self addSubview:_wrapperView];
|
||||||
|
|
||||||
@ -131,19 +131,45 @@ typedef enum
|
|||||||
|
|
||||||
_summaryThumbnailWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 32)];
|
_summaryThumbnailWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 32)];
|
||||||
_summaryThumbnailWrapperView.clipsToBounds = true;
|
_summaryThumbnailWrapperView.clipsToBounds = true;
|
||||||
_summaryThumbnailWrapperView.layer.cornerRadius = 5.0;
|
_summaryThumbnailWrapperView.layer.cornerRadius = 9.0;
|
||||||
[_wrapperView addSubview:_summaryThumbnailWrapperView];
|
[_wrapperView addSubview:_summaryThumbnailWrapperView];
|
||||||
|
|
||||||
_leftCurtainView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
static dispatch_once_t onceToken;
|
||||||
_leftCurtainView.backgroundColor = [[TGPhotoEditorInterfaceAssets toolbarBackgroundColor] colorWithAlphaComponent:0.8f];
|
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.clipsToBounds = true;
|
||||||
_leftCurtainView.layer.cornerRadius = 5.0;
|
|
||||||
[_wrapperView addSubview:_leftCurtainView];
|
[_wrapperView addSubview:_leftCurtainView];
|
||||||
|
|
||||||
_rightCurtainView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
_rightCurtainView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
||||||
_rightCurtainView.backgroundColor = [[TGPhotoEditorInterfaceAssets toolbarBackgroundColor] colorWithAlphaComponent:0.8f];
|
_rightCurtainView.image = [rightCurtain stretchableImageWithLeftCapWidth:11 topCapHeight:20];
|
||||||
_rightCurtainView.clipsToBounds = true;
|
_rightCurtainView.clipsToBounds = true;
|
||||||
_rightCurtainView.layer.cornerRadius = 5.0;
|
|
||||||
[_wrapperView addSubview:_rightCurtainView];
|
[_wrapperView addSubview:_rightCurtainView];
|
||||||
|
|
||||||
__weak TGMediaPickerGalleryVideoScrubber *weakSelf = self;
|
__weak TGMediaPickerGalleryVideoScrubber *weakSelf = self;
|
||||||
@ -308,9 +334,9 @@ typedef enum
|
|||||||
[_wrapperView addSubview:_dotHandle];
|
[_wrapperView addSubview:_dotHandle];
|
||||||
|
|
||||||
static UIImage *dotFrameImage = nil;
|
static UIImage *dotFrameImage = nil;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken2;
|
||||||
dispatch_once(&onceToken, ^
|
dispatch_once(&onceToken2, ^
|
||||||
{
|
{
|
||||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(_dotHandle.frame.size.width, _dotHandle.frame.size.height), false, 0.0f);
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(_dotHandle.frame.size.width, _dotHandle.frame.size.height), false, 0.0f);
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
|
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
|
||||||
@ -337,25 +363,33 @@ typedef enum
|
|||||||
_dotFrameView.image = dotFrameImage;
|
_dotFrameView.image = dotFrameImage;
|
||||||
[_dotHandle addSubview:_dotFrameView];
|
[_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);
|
_scrubberHandle.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -12, -5, -12);
|
||||||
[_wrapperView addSubview:_scrubberHandle];
|
[_wrapperView addSubview:_scrubberHandle];
|
||||||
|
|
||||||
static UIImage *handleViewImage = nil;
|
UIImage *handleViewImage = nil;
|
||||||
static dispatch_once_t onceToken2;
|
|
||||||
dispatch_once(&onceToken2, ^
|
|
||||||
{
|
{
|
||||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(_scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height), false, 0.0f);
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(_scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height), false, 0.0f);
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 0.5f, [UIColor colorWithWhite:0.0f alpha:0.65f].CGColor);
|
CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 0.5f, [UIColor colorWithWhite:0.0f alpha:0.65f].CGColor);
|
||||||
CGContextSetFillColorWithColor(context, [UIColor whiteColor].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];
|
if (cover) {
|
||||||
[path fill];
|
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();
|
handleViewImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
UIGraphicsEndImageContext();
|
UIGraphicsEndImageContext();
|
||||||
});
|
}
|
||||||
|
|
||||||
UIImageView *scrubberImageView = [[UIImageView alloc] initWithFrame:_scrubberHandle.bounds];
|
UIImageView *scrubberImageView = [[UIImageView alloc] initWithFrame:_scrubberHandle.bounds];
|
||||||
scrubberImageView.image = handleViewImage;
|
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
|
- (void)setThumbnailImage:(UIImage *)image forTimestamp:(NSTimeInterval)__unused timestamp index:(NSInteger)index isSummaryThubmnail:(bool)isSummaryThumbnail last:(bool)last
|
||||||
{
|
{
|
||||||
bool exists = false;
|
|
||||||
if (isSummaryThumbnail)
|
if (isSummaryThumbnail)
|
||||||
{
|
{
|
||||||
if (index == 0 && _summaryThumbnailViews.count > 0 && _summaryThumbnailSnapshotView == nil) {
|
if (index == 0 && _summaryThumbnailViews.count > 0 && _summaryThumbnailSnapshotView == nil) {
|
||||||
@ -790,7 +823,6 @@ typedef enum
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_summaryThumbnailViews.count >= index + 1) {
|
if (_summaryThumbnailViews.count >= index + 1) {
|
||||||
exists = true;
|
|
||||||
[_summaryThumbnailViews[index] setImage:image animated:true];
|
[_summaryThumbnailViews[index] setImage:image animated:true];
|
||||||
} else {
|
} else {
|
||||||
TGMediaPickerGalleryVideoScrubberThumbnailView *thumbnailView = [[TGMediaPickerGalleryVideoScrubberThumbnailView alloc] initWithImage:image originalSize:_originalSize cropRect:_cropRect cropOrientation:_cropOrientation cropMirrored:_cropMirrored];
|
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)
|
if (orientation == UIImageOrientationLeft || orientation == UIImageOrientationRight)
|
||||||
aspectRatio = 1.0f / aspectRatio;
|
aspectRatio = 1.0f / aspectRatio;
|
||||||
return CGSizeMake(CGCeil(36.0f * aspectRatio), 36.0f);
|
return CGSizeMake(CGCeil(40.0f * aspectRatio), 40.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_layoutSummaryThumbnailViewsForZoom:(bool)forZoom
|
- (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 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;
|
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
|
- (void)_layoutTrimViewZoomedIn:(bool)zoomedIn
|
||||||
@ -1462,8 +1494,8 @@ typedef enum
|
|||||||
CGRect scrubbingRect = [self _scrubbingRect];
|
CGRect scrubbingRect = [self _scrubbingRect];
|
||||||
CGRect normalScrubbingRect = [self _scrubbingRectZoomedIn:false];
|
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);
|
_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) - 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);
|
_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
|
- (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];
|
[self _layoutTrimViewZoomedIn:_zoomedIn];
|
||||||
|
|
||||||
CGRect scrubbingRect = [self _scrubbingRect];
|
CGRect scrubbingRect = [self _scrubbingRect];
|
||||||
if (isnan(scrubbingRect.origin.x) || isnan(scrubbingRect.origin.y))
|
if (isnan(scrubbingRect.origin.x) || isnan(scrubbingRect.origin.y))
|
||||||
return;
|
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;
|
_zoomedThumbnailWrapperView.frame = _summaryThumbnailWrapperView.frame;
|
||||||
|
|
||||||
[self _updateScrubberAnimationsAndResetCurrentPosition:true];
|
[self _updateScrubberAnimationsAndResetCurrentPosition:true];
|
||||||
|
@ -10,5 +10,6 @@
|
|||||||
@property (nonatomic, assign) bool trimmingEnabled;
|
@property (nonatomic, assign) bool trimmingEnabled;
|
||||||
|
|
||||||
- (void)setTrimming:(bool)trimming animated:(bool)animated;
|
- (void)setTrimming:(bool)trimming animated:(bool)animated;
|
||||||
|
- (void)setTrimmingEnabled:(bool)trimmingEnabled animated:(bool)animated;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
{
|
{
|
||||||
UIButton *_leftSegmentView;
|
UIButton *_leftSegmentView;
|
||||||
UIButton *_rightSegmentView;
|
UIButton *_rightSegmentView;
|
||||||
|
UIImageView *_borderView;
|
||||||
|
|
||||||
|
UIImageView *_leftCapsuleView;
|
||||||
|
UIImageView *_rightCapsuleView;
|
||||||
|
|
||||||
UILongPressGestureRecognizer *_startHandlePressGestureRecognizer;
|
UILongPressGestureRecognizer *_startHandlePressGestureRecognizer;
|
||||||
UILongPressGestureRecognizer *_endHandlePressGestureRecognizer;
|
UILongPressGestureRecognizer *_endHandlePressGestureRecognizer;
|
||||||
@ -34,28 +38,48 @@
|
|||||||
{
|
{
|
||||||
self.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -25, -5, -25);
|
self.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -25, -5, -25);
|
||||||
|
|
||||||
UIColor *normalColor = UIColorRGB(0x4d4d4d);
|
UIColor *normalColor = UIColorRGB(0xffffff);
|
||||||
UIColor *accentColor = [TGPhotoEditorInterfaceAssets accentColor];
|
UIColor *accentColor = UIColorRGB(0xf8d74a);
|
||||||
|
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
static UIImage *handle;
|
static UIImage *handle;
|
||||||
|
static UIImage *border;
|
||||||
dispatch_once(&onceToken, ^
|
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];
|
CGContextSetFillColorWithColor(context, normalColor.CGColor);
|
||||||
[[UIBezierPath bezierPathWithRoundedRect:CGRectMake(0.0f, 0.0f, 12.0f, 36.0f) byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomLeft cornerRadii:CGSizeMake(4.0f, 4.0f)] fill];
|
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();
|
handle = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
UIGraphicsEndImageContext();
|
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 *leftImage = handle;
|
||||||
UIImage *leftHighlightedImage = TGTintedImage(handle, accentColor);
|
UIImage *leftHighlightedImage = TGTintedImage(handle, accentColor);
|
||||||
UIImage *rightImage = [UIImage imageWithCGImage:handle.CGImage scale:handle.scale orientation:UIImageOrientationUpMirrored];
|
UIImage *rightImage = [UIImage imageWithCGImage:handle.CGImage scale:handle.scale orientation:UIImageOrientationUpMirrored];
|
||||||
UIImage *rightHighlightedImage = [UIImage imageWithCGImage:leftHighlightedImage.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.adjustsImageWhenHighlighted = false;
|
||||||
[_leftSegmentView setBackgroundImage:leftImage forState:UIControlStateNormal];
|
[_leftSegmentView setBackgroundImage:leftImage forState:UIControlStateNormal];
|
||||||
[_leftSegmentView setBackgroundImage:leftHighlightedImage forState:UIControlStateSelected];
|
[_leftSegmentView setBackgroundImage:leftHighlightedImage forState:UIControlStateSelected];
|
||||||
@ -63,7 +87,7 @@
|
|||||||
_leftSegmentView.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -25, -5, -10);
|
_leftSegmentView.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -25, -5, -10);
|
||||||
[self addSubview:_leftSegmentView];
|
[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.adjustsImageWhenHighlighted = false;
|
||||||
[_rightSegmentView setBackgroundImage:rightImage forState:UIControlStateNormal];
|
[_rightSegmentView setBackgroundImage:rightImage forState:UIControlStateNormal];
|
||||||
[_rightSegmentView setBackgroundImage:rightHighlightedImage forState:UIControlStateSelected];
|
[_rightSegmentView setBackgroundImage:rightHighlightedImage forState:UIControlStateSelected];
|
||||||
@ -71,6 +95,22 @@
|
|||||||
_rightSegmentView.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -10, -5, -25);
|
_rightSegmentView.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -10, -5, -25);
|
||||||
[self addSubview:_rightSegmentView];
|
[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 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleHandlePress:)];
|
||||||
_startHandlePressGestureRecognizer.delegate = self;
|
_startHandlePressGestureRecognizer.delegate = self;
|
||||||
_startHandlePressGestureRecognizer.minimumPressDuration = 0.1f;
|
_startHandlePressGestureRecognizer.minimumPressDuration = 0.1f;
|
||||||
@ -98,24 +138,39 @@
|
|||||||
|
|
||||||
_leftSegmentView.hidden = !trimmingEnabled;
|
_leftSegmentView.hidden = !trimmingEnabled;
|
||||||
_rightSegmentView.hidden = !trimmingEnabled;
|
_rightSegmentView.hidden = !trimmingEnabled;
|
||||||
|
_borderView.hidden = !trimmingEnabled;
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[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
|
- (void)setTrimming:(bool)trimming animated:(bool)animated
|
||||||
{
|
{
|
||||||
if (animated)
|
if (animated)
|
||||||
{
|
{
|
||||||
[UIView animateWithDuration:0.15f animations:^
|
[UIView animateWithDuration:0.15f animations:^
|
||||||
{
|
{
|
||||||
[_leftSegmentView setSelected:trimming];
|
[_leftSegmentView setSelected:trimming];
|
||||||
[_rightSegmentView setSelected:trimming];
|
[_rightSegmentView setSelected:trimming];
|
||||||
}];
|
[_borderView setHighlighted:trimming];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[_leftSegmentView setSelected:trimming];
|
[_leftSegmentView setSelected:trimming];
|
||||||
[_rightSegmentView setSelected:trimming];
|
[_rightSegmentView setSelected:trimming];
|
||||||
|
[_borderView setHighlighted:trimming];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,6 +282,7 @@
|
|||||||
|
|
||||||
_leftSegmentView.frame = CGRectMake(0, 0, handleWidth, self.frame.size.height);
|
_leftSegmentView.frame = CGRectMake(0, 0, handleWidth, self.frame.size.height);
|
||||||
_rightSegmentView.frame = CGRectMake(self.frame.size.width - handleWidth, 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
|
@end
|
||||||
|
@ -30,3 +30,12 @@
|
|||||||
- (void)setInternalHidden:(bool)internalHidden animated:(bool)animated;
|
- (void)setInternalHidden:(bool)internalHidden animated:(bool)animated;
|
||||||
|
|
||||||
@end
|
@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);
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f);
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
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));
|
CGContextFillEllipseInRect(context, CGRectMake(3.5f, 1.0f, 31.0f, 31.0f));
|
||||||
|
|
||||||
@ -778,3 +778,118 @@ const CGFloat TGPhotoCounterButtonMaskFade = 18;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@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, assign) UIEdgeInsets safeAreaInset;
|
||||||
@property (nonatomic, strong) UIView *panelView;
|
@property (nonatomic, strong) UIView *panelView;
|
||||||
@property (nonatomic, strong) TGMediaPickerGalleryVideoScrubber *scrubberView;
|
@property (nonatomic, strong) TGMediaPickerGalleryVideoScrubber *scrubberView;
|
||||||
|
@property (nonatomic, strong) TGMediaPickerGalleryVideoScrubber *coverScrubberView;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
- (void)layoutSubviews
|
- (void)layoutSubviews
|
||||||
{
|
{
|
||||||
_scrubberView.frame = CGRectMake(_safeAreaInset.left, _scrubberView.frame.origin.y, self.frame.size.width - _safeAreaInset.left - _safeAreaInset.right, _scrubberView.frame.size.height);
|
_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
|
@end
|
||||||
|
@ -360,7 +360,7 @@
|
|||||||
[_dotImageView addGestureRecognizer:dotTapRecognizer];
|
[_dotImageView addGestureRecognizer:dotTapRecognizer];
|
||||||
|
|
||||||
if ([self presentedForAvatarCreation] && _item.isVideo) {
|
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.minimumLength = 3.0;
|
||||||
_scrubberView.layer.allowsGroupOpacity = true;
|
_scrubberView.layer.allowsGroupOpacity = true;
|
||||||
_scrubberView.hasDotPicker = true;
|
_scrubberView.hasDotPicker = true;
|
||||||
|
@ -123,11 +123,11 @@
|
|||||||
static UIImage *muteBackground;
|
static UIImage *muteBackground;
|
||||||
dispatch_once(&onceToken, ^
|
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);
|
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0);
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.3f).CGColor);
|
CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.5f).CGColor);
|
||||||
CGContextFillEllipseInRect(context, CGRectInset(rect, 3, 3));
|
CGContextFillEllipseInRect(context, rect);
|
||||||
|
|
||||||
muteBackground = UIGraphicsGetImageFromCurrentImageContext();
|
muteBackground = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
UIGraphicsEndImageContext();
|
UIGraphicsEndImageContext();
|
||||||
@ -173,7 +173,7 @@
|
|||||||
{
|
{
|
||||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f);
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f);
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
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));
|
CGContextFillEllipseInRect(context, CGRectMake(3.5f, 1.0f, 31.0f, 31.0f));
|
||||||
|
|
||||||
@ -198,7 +198,7 @@
|
|||||||
{
|
{
|
||||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f);
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f);
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
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));
|
CGContextFillEllipseInRect(context, CGRectMake(3.5f, 1.0f, 31.0f, 31.0f));
|
||||||
|
|
||||||
@ -358,7 +358,7 @@
|
|||||||
{
|
{
|
||||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(30.0f, 30.0f), false, 0.0f);
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(30.0f, 30.0f), false, 0.0f);
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
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];
|
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0.5f, 0.5f, 29.0f, 29.0f) cornerRadius:8.5f];
|
||||||
CGContextAddPath(context, path.CGPath);
|
CGContextAddPath(context, path.CGPath);
|
||||||
|
@ -127,7 +127,7 @@ private enum LegacyAssetVideoData {
|
|||||||
private enum LegacyAssetItem {
|
private enum LegacyAssetItem {
|
||||||
case image(data: LegacyAssetImageData, thumbnail: UIImage?, caption: NSAttributedString?, stickers: [FileMediaReference])
|
case image(data: LegacyAssetImageData, thumbnail: UIImage?, caption: NSAttributedString?, stickers: [FileMediaReference])
|
||||||
case file(data: LegacyAssetImageData, thumbnail: UIImage?, mimeType: String, name: String, caption: NSAttributedString?)
|
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 {
|
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" {
|
if (dict["type"] as! NSString) == "editedPhoto" || (dict["type"] as! NSString) == "capturedPhoto" {
|
||||||
let image = dict["image"] as! UIImage
|
let image = dict["image"] as! UIImage
|
||||||
let thumbnail = dict["previewImage"] as? UIImage
|
let thumbnail = dict["previewImage"] as? UIImage
|
||||||
|
let cover = dict["coverImage"] as? UIImage
|
||||||
|
|
||||||
var result: [AnyHashable : Any] = [:]
|
var result: [AnyHashable : Any] = [:]
|
||||||
if let isAnimation = dict["isAnimation"] as? NSNumber, isAnimation.boolValue {
|
if let isAnimation = dict["isAnimation"] as? NSNumber, isAnimation.boolValue {
|
||||||
let url: String? = (dict["url"] as? String) ?? (dict["url"] as? URL)?.path
|
let url: String? = (dict["url"] as? String) ?? (dict["url"] as? URL)?.path
|
||||||
if let url = url {
|
if let url = url {
|
||||||
let dimensions = image.size
|
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 {
|
} 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)
|
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 dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
||||||
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
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
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,6 +231,7 @@ public func legacyAssetPickerItemGenerator() -> ((Any?, NSAttributedString?, Str
|
|||||||
}
|
}
|
||||||
} else if (dict["type"] as! NSString) == "video" {
|
} else if (dict["type"] as! NSString) == "video" {
|
||||||
let thumbnail = dict["previewImage"] as? UIImage
|
let thumbnail = dict["previewImage"] as? UIImage
|
||||||
|
let cover = dict["coverImage"] as? UIImage
|
||||||
var asFile = false
|
var asFile = false
|
||||||
if let document = dict["document"] as? NSNumber, document.boolValue {
|
if let document = dict["document"] as? NSNumber, document.boolValue {
|
||||||
asFile = true
|
asFile = true
|
||||||
@ -237,17 +239,18 @@ public func legacyAssetPickerItemGenerator() -> ((Any?, NSAttributedString?, Str
|
|||||||
|
|
||||||
if let asset = dict["asset"] as? TGMediaAsset {
|
if let asset = dict["asset"] as? TGMediaAsset {
|
||||||
var result: [AnyHashable: Any] = [:]
|
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
|
return result
|
||||||
} else if let url = (dict["url"] as? String) ?? (dict["url"] as? URL)?.absoluteString {
|
} else if let url = (dict["url"] as? String) ?? (dict["url"] as? URL)?.absoluteString {
|
||||||
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
||||||
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
||||||
var result: [AnyHashable: Any] = [:]
|
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
|
return result
|
||||||
}
|
}
|
||||||
} else if (dict["type"] as! NSString) == "cameraVideo" {
|
} else if (dict["type"] as! NSString) == "cameraVideo" {
|
||||||
let thumbnail = dict["previewImage"] as? UIImage
|
let thumbnail = dict["previewImage"] as? UIImage
|
||||||
|
let cover = dict["coverImage"] as? UIImage
|
||||||
var asFile = false
|
var asFile = false
|
||||||
if let document = dict["document"] as? NSNumber, document.boolValue {
|
if let document = dict["document"] as? NSNumber, document.boolValue {
|
||||||
asFile = true
|
asFile = true
|
||||||
@ -259,7 +262,7 @@ public func legacyAssetPickerItemGenerator() -> ((Any?, NSAttributedString?, Str
|
|||||||
let dimensions = previewImage.pixelSize()
|
let dimensions = previewImage.pixelSize()
|
||||||
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
||||||
var result: [AnyHashable: Any] = [:]
|
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
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -756,7 +759,7 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A
|
|||||||
default:
|
default:
|
||||||
break
|
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 finalDimensions: CGSize
|
||||||
var finalDuration: Double
|
var finalDuration: Double
|
||||||
switch data {
|
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")))
|
let defaultPreset = TGMediaVideoConversionPreset(rawValue: UInt32(UserDefaults.standard.integer(forKey: "TG_preferredVideoPreset_v0")))
|
||||||
|
|
||||||
var preset: TGMediaVideoConversionPreset = TGMediaVideoConversionPresetCompressedMedium
|
var preset: TGMediaVideoConversionPreset = TGMediaVideoConversionPresetCompressedMedium
|
||||||
@ -891,7 +914,7 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A
|
|||||||
fileAttributes.append(.HasLinkedStickers)
|
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) {
|
if let timer = item.timer, timer > 0 && (timer <= 60 || timer == viewOnceTimeout) {
|
||||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
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 final class LegacyPaintStickersContext: NSObject, TGPhotoPaintStickersContext {
|
||||||
public var captionPanelView: (() -> TGCaptionPanelView?)?
|
public var captionPanelView: (() -> TGCaptionPanelView?)?
|
||||||
|
public var editCover: ((CGSize, @escaping (UIImage) -> Void) -> Void)?
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
|
|
||||||
|
@ -619,14 +619,18 @@ open class LegacyController: ViewController, PresentableController {
|
|||||||
override open func dismiss(completion: (() -> Void)? = nil) {
|
override open func dismiss(completion: (() -> Void)? = nil) {
|
||||||
self.view.endEditing(true)
|
self.view.endEditing(true)
|
||||||
switch self.presentation {
|
switch self.presentation {
|
||||||
case .modal:
|
case .modal:
|
||||||
self.controllerNode.animateModalOut { [weak self] in
|
self.controllerNode.animateModalOut { [weak self] in
|
||||||
self?.presentingViewController?.dismiss(animated: false, completion: completion)
|
self?.presentingViewController?.dismiss(animated: false, completion: completion)
|
||||||
}
|
}
|
||||||
case .custom:
|
case .custom:
|
||||||
|
if let _ = self.navigationController as? NavigationController {
|
||||||
|
super.dismiss(animated: false, completion: completion)
|
||||||
|
} else {
|
||||||
self.presentingViewController?.dismiss(animated: false, completion: completion)
|
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.currentSize = size
|
||||||
self.backgroundView.frame = CGRect(origin: .zero, size: size)
|
self.backgroundView.frame = CGRect(origin: .zero, size: size)
|
||||||
|
|
||||||
//TODO:localize
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let labelSize = self.label.update(
|
let labelSize = self.label.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
MultilineTextComponent(
|
MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Use an Emoji",
|
string: presentationData.strings.MediaPicker_UseAnEmoji,
|
||||||
font: Font.semibold(14.0),
|
font: Font.semibold(12.0),
|
||||||
textColor: .white
|
textColor: .white
|
||||||
)),
|
)),
|
||||||
textShadowColor: UIColor(white: 0.0, alpha: 0.4),
|
textShadowColor: UIColor(white: 0.0, alpha: 0.3),
|
||||||
textShadowBlur: 4.0
|
textShadowBlur: 3.0
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
environment: {},
|
environment: {},
|
||||||
@ -104,7 +104,7 @@ final class AvatarEditorPreviewView: UIView {
|
|||||||
if view.superview == nil {
|
if view.superview == nil {
|
||||||
self.addSubview(view)
|
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 {
|
guard !self.files.isEmpty else {
|
||||||
|
@ -101,7 +101,7 @@ enum LegacyMediaPickerGallerySource {
|
|||||||
case selection(item: TGMediaSelectableItem)
|
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 reminder = peer?.id == context.account.peerId
|
||||||
let hasSilentPosting = hasSilentPosting && 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 = {
|
paintStickersContext.captionPanelView = {
|
||||||
return getCaptionPanelView()
|
return getCaptionPanelView()
|
||||||
}
|
}
|
||||||
|
paintStickersContext.editCover = { dimensions, completion in
|
||||||
|
editCover(dimensions, completion)
|
||||||
|
}
|
||||||
|
|
||||||
let controller = TGModernGalleryController(context: legacyController.context)!
|
let controller = TGModernGalleryController(context: legacyController.context)!
|
||||||
controller.asyncTransitionIn = true
|
controller.asyncTransitionIn = true
|
||||||
|
@ -160,6 +160,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
case wallpaper
|
case wallpaper
|
||||||
case story
|
case story
|
||||||
case addImage
|
case addImage
|
||||||
|
case cover
|
||||||
case createSticker
|
case createSticker
|
||||||
case createAvatar
|
case createAvatar
|
||||||
}
|
}
|
||||||
@ -234,6 +235,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dismissAll: () -> Void = { }
|
var dismissAll: () -> Void = { }
|
||||||
|
public var editCover: (CGSize, @escaping (UIImage) -> Void) -> Void = { _, _ in }
|
||||||
|
|
||||||
private class Node: ViewControllerTracingNode, ASGestureRecognizerDelegate {
|
private class Node: ViewControllerTracingNode, ASGestureRecognizerDelegate {
|
||||||
enum DisplayMode {
|
enum DisplayMode {
|
||||||
@ -311,7 +313,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
self.presentationData = controller.presentationData
|
self.presentationData = controller.presentationData
|
||||||
|
|
||||||
var assetType: PHAssetMediaType?
|
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
|
assetType = .image
|
||||||
}
|
}
|
||||||
let mediaAssetsContext = MediaAssetsContext(assetType: assetType)
|
let mediaAssetsContext = MediaAssetsContext(assetType: assetType)
|
||||||
@ -522,7 +524,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
self.gridNode.scrollView.alwaysBounceVertical = true
|
self.gridNode.scrollView.alwaysBounceVertical = true
|
||||||
self.gridNode.scrollView.showsVerticalScrollIndicator = false
|
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 {
|
} else {
|
||||||
let selectionGesture = MediaPickerGridSelectionGesture<TGMediaSelectableItem>()
|
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
|
}, presentSchedulePicker: controller.presentSchedulePicker, presentTimerPicker: controller.presentTimerPicker, getCaptionPanelView: controller.getCaptionPanelView, present: { [weak self] c, a in
|
||||||
self?.currentGalleryParentController = c
|
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
|
}, finishedTransitionIn: { [weak self] in
|
||||||
self?.openingMedia = false
|
self?.openingMedia = false
|
||||||
self?.hasGallery = true
|
self?.hasGallery = true
|
||||||
@ -1227,6 +1231,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
self?.updateIsCameraActive()
|
self?.updateIsCameraActive()
|
||||||
}, dismissAll: { [weak self] in
|
}, dismissAll: { [weak self] in
|
||||||
self?.controller?.dismissAll()
|
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()
|
self?.updateIsCameraActive()
|
||||||
}, dismissAll: { [weak self] in
|
}, dismissAll: { [weak self] in
|
||||||
self?.controller?.dismissAll()
|
self?.controller?.dismissAll()
|
||||||
|
}, editCover: { _, _ in
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1540,9 +1548,6 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
|
|
||||||
var cutoutRect: CGRect?
|
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))
|
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 {
|
if self.cameraView == nil && self.modernCameraView == nil {
|
||||||
cameraRect = 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 {
|
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.subtitle = presentationData.strings.MediaPicker_CreateSticker
|
||||||
self.titleView.isEnabled = true
|
self.titleView.isEnabled = true
|
||||||
case .createAvatar:
|
case .createAvatar:
|
||||||
//TODO:localize
|
|
||||||
self.titleView.title = presentationData.strings.MediaPicker_Recents
|
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
|
self.titleView.isEnabled = true
|
||||||
case .story:
|
case .story:
|
||||||
self.titleView.title = presentationData.strings.MediaPicker_Recents
|
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
|
self.titleView.title = presentationData.strings.Conversation_Theme_ChooseWallpaperTitle
|
||||||
case .addImage:
|
case .addImage:
|
||||||
self.titleView.title = presentationData.strings.MediaPicker_AddImage
|
self.titleView.title = presentationData.strings.MediaPicker_AddImage
|
||||||
|
case .cover:
|
||||||
|
self.titleView.title = presentationData.strings.MediaPicker_ChooseCover
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3379,8 +3388,7 @@ public func avatarMediaPickerController(
|
|||||||
var mainButtonState: AttachmentMainButtonState?
|
var mainButtonState: AttachmentMainButtonState?
|
||||||
|
|
||||||
if canDelete {
|
if canDelete {
|
||||||
//TODO:localize
|
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)
|
||||||
mainButtonState = AttachmentMainButtonState(text: "Remove Photo", font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mediaPickerController = MediaPickerScreenImpl(
|
let mediaPickerController = MediaPickerScreenImpl(
|
||||||
@ -3477,6 +3485,68 @@ public func avatarMediaPickerController(
|
|||||||
return controller
|
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 class SelectedButtonNode: HighlightableButtonNode {
|
||||||
private let background = ASImageNode()
|
private let background = ASImageNode()
|
||||||
private let icon = ASImageNode()
|
private let icon = ASImageNode()
|
||||||
|
@ -59,6 +59,8 @@ func requiredBoostSubjectLevel(subject: BoostSubject, group: Bool, context: Acco
|
|||||||
return configuration.minGroupEmojiPackLevel
|
return configuration.minGroupEmojiPackLevel
|
||||||
case .noAds:
|
case .noAds:
|
||||||
return configuration.minChannelRestrictAdsLevel
|
return configuration.minChannelRestrictAdsLevel
|
||||||
|
case .wearGift:
|
||||||
|
return configuration.minChannelWearGiftLevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,6 +242,7 @@ private final class LevelSectionComponent: CombinedComponent {
|
|||||||
case audioTranscription
|
case audioTranscription
|
||||||
case emojiPack
|
case emojiPack
|
||||||
case noAds
|
case noAds
|
||||||
|
case wearGift
|
||||||
|
|
||||||
func title(strings: PresentationStrings, isGroup: Bool) -> String {
|
func title(strings: PresentationStrings, isGroup: Bool) -> String {
|
||||||
switch self {
|
switch self {
|
||||||
@ -269,6 +272,8 @@ private final class LevelSectionComponent: CombinedComponent {
|
|||||||
return strings.GroupBoost_Table_Group_EmojiPack
|
return strings.GroupBoost_Table_Group_EmojiPack
|
||||||
case .noAds:
|
case .noAds:
|
||||||
return strings.ChannelBoost_Table_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"
|
return "Premium/BoostPerk/EmojiPack"
|
||||||
case .noAds:
|
case .noAds:
|
||||||
return "Premium/BoostPerk/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
|
textString = strings.GroupBoost_EnableEmojiPackLevelText("\(requiredLevel)").string
|
||||||
case .noAds:
|
case .noAds:
|
||||||
textString = strings.ChannelBoost_EnableNoAdsLevelText("\(requiredLevel)").string
|
textString = strings.ChannelBoost_EnableNoAdsLevelText("\(requiredLevel)").string
|
||||||
|
case .wearGift:
|
||||||
|
textString = strings.ChannelBoost_EnableNoAdsLevelText("\(requiredLevel)").string
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining))
|
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),
|
availableSize: CGSize(width: 90.0, height: 90.0),
|
||||||
transition: .immediate
|
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
|
context.add(icon
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + iconBackground.size.height / 2.0))
|
.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) {
|
if !isGroup && level >= requiredBoostSubjectLevel(subject: .noAds, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||||
perks.append(.noAds)
|
perks.append(.noAds)
|
||||||
}
|
}
|
||||||
|
// if !isGroup && level >= requiredBoostSubjectLevel(subject: .wearGift, group: isGroup, context: component.context, configuration: premiumConfiguration) {
|
||||||
|
// perks.append(.wearGift)
|
||||||
|
// }
|
||||||
|
|
||||||
levelItems.append(
|
levelItems.append(
|
||||||
AnyComponentWithIdentity(
|
AnyComponentWithIdentity(
|
||||||
@ -1461,6 +1464,8 @@ private final class BoostLevelsContainerComponent: CombinedComponent {
|
|||||||
titleString = strings.GroupBoost_EmojiPack
|
titleString = strings.GroupBoost_EmojiPack
|
||||||
case .noAds:
|
case .noAds:
|
||||||
titleString = strings.ChannelBoost_NoAds
|
titleString = strings.ChannelBoost_NoAds
|
||||||
|
case .wearGift:
|
||||||
|
titleString = strings.ChannelBoost_WearGift
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
titleString = isGroup == true ? strings.GroupBoost_Title_Current : strings.ChannelBoost_Title_Current
|
titleString = isGroup == true ? strings.GroupBoost_Title_Current : strings.ChannelBoost_Title_Current
|
||||||
|
@ -200,6 +200,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
|||||||
case dismissedBusinessLinksBadge = 73
|
case dismissedBusinessLinksBadge = 73
|
||||||
case dismissedBusinessChatbotsBadge = 74
|
case dismissedBusinessChatbotsBadge = 74
|
||||||
case captionAboveMediaTooltip = 75
|
case captionAboveMediaTooltip = 75
|
||||||
|
case channelSendGiftTooltip = 76
|
||||||
|
|
||||||
var key: ValueBoxKey {
|
var key: ValueBoxKey {
|
||||||
let v = ValueBoxKey(length: 4)
|
let v = ValueBoxKey(length: 4)
|
||||||
@ -544,6 +545,10 @@ private struct ApplicationSpecificNoticeKeys {
|
|||||||
static func captionAboveMediaTooltip() -> NoticeEntryKey {
|
static func captionAboveMediaTooltip() -> NoticeEntryKey {
|
||||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.captionAboveMediaTooltip.key)
|
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 {
|
public struct ApplicationSpecificNotice {
|
||||||
@ -2303,4 +2308,31 @@ public struct ApplicationSpecificNotice {
|
|||||||
return Int(previousValue)
|
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
|
attributedString = mutableString
|
||||||
case .prizeStars:
|
case .prizeStars:
|
||||||
attributedString = NSAttributedString(string: strings.Notification_StarsPrize, font: titleFont, textColor: primaryTextColor)
|
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 !forAdditionalServiceMessage {
|
||||||
if let text {
|
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()))
|
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])
|
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Self_Bought(starsPrice)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
||||||
} else if message.author?.id == accountPeerId {
|
} else if message.author?.id == accountPeerId {
|
||||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_SentYou(starsPrice)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
|
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 {
|
} else {
|
||||||
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
|
||||||
attributes[1] = boldAttributes
|
attributes[1] = boldAttributes
|
||||||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Sent(authorName, starsPrice)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
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 case let .unique(gift) = gift {
|
||||||
if !forAdditionalServiceMessage {
|
if !forAdditionalServiceMessage {
|
||||||
attributedString = NSAttributedString(string: "\(gift.title) #\(gift.number)", font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: "\(gift.title) #\(gift.number)", font: titleFont, textColor: primaryTextColor)
|
||||||
|
@ -13,6 +13,8 @@ import ChatPresentationInterfaceState
|
|||||||
import ChatInputPanelNode
|
import ChatInputPanelNode
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import OldChannelsController
|
import OldChannelsController
|
||||||
|
import TooltipUI
|
||||||
|
import TelegramNotices
|
||||||
|
|
||||||
private enum SubscriberAction: Equatable {
|
private enum SubscriberAction: Equatable {
|
||||||
case join
|
case join
|
||||||
@ -146,6 +148,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||||||
private let activityIndicator: UIActivityIndicatorView
|
private let activityIndicator: UIActivityIndicatorView
|
||||||
|
|
||||||
private let helpButton: HighlightableButtonNode
|
private let helpButton: HighlightableButtonNode
|
||||||
|
private let giftButton: HighlightableButtonNode
|
||||||
|
|
||||||
private var action: SubscriberAction?
|
private var action: SubscriberAction?
|
||||||
|
|
||||||
@ -176,6 +179,9 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||||||
self.badgeText.isHidden = true
|
self.badgeText.isHidden = true
|
||||||
|
|
||||||
self.helpButton = HighlightableButtonNode()
|
self.helpButton = HighlightableButtonNode()
|
||||||
|
self.helpButton.isHidden = true
|
||||||
|
self.giftButton = HighlightableButtonNode()
|
||||||
|
self.giftButton.isHidden = true
|
||||||
|
|
||||||
self.discussButton.addSubnode(self.discussButtonText)
|
self.discussButton.addSubnode(self.discussButtonText)
|
||||||
self.discussButton.addSubnode(self.badgeBackground)
|
self.discussButton.addSubnode(self.badgeBackground)
|
||||||
@ -189,10 +195,12 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||||||
self.addSubnode(self.discussButton)
|
self.addSubnode(self.discussButton)
|
||||||
self.view.addSubview(self.activityIndicator)
|
self.view.addSubview(self.activityIndicator)
|
||||||
self.addSubnode(self.helpButton)
|
self.addSubnode(self.helpButton)
|
||||||
|
self.addSubnode(self.giftButton)
|
||||||
|
|
||||||
self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||||
self.discussButton.addTarget(self, action: #selector(self.discussPressed), forControlEvents: .touchUpInside)
|
self.discussButton.addTarget(self, action: #selector(self.discussPressed), forControlEvents: .touchUpInside)
|
||||||
self.helpButton.addTarget(self, action: #selector(self.helpPressed), forControlEvents: .touchUpInside)
|
self.helpButton.addTarget(self, action: #selector(self.helpPressed), forControlEvents: .touchUpInside)
|
||||||
|
self.giftButton.addTarget(self, action: #selector(self.giftPressed), forControlEvents: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -207,6 +215,10 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||||||
return super.hitTest(point, with: event)
|
return super.hitTest(point, with: event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func giftPressed() {
|
||||||
|
self.interfaceInteraction?.openPremiumGift()
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func helpPressed() {
|
@objc private func helpPressed() {
|
||||||
self.interfaceInteraction?.presentGigagroupHelp()
|
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)
|
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 {
|
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)
|
self.layoutData = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, isSecondary, metrics)
|
||||||
|
|
||||||
@ -311,6 +368,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||||||
if previousState?.theme !== interfaceState.theme {
|
if previousState?.theme !== interfaceState.theme {
|
||||||
self.badgeBackground.image = PresentationResourcesChatList.badgeBackgroundActive(interfaceState.theme, diameter: 20.0)
|
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.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 {
|
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
|
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))
|
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) {
|
if let peer = interfaceState.renderedPeer?.peer as? TelegramChannel {
|
||||||
self.helpButton.isHidden = false
|
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 {
|
} else {
|
||||||
|
self.giftButton.isHidden = true
|
||||||
self.helpButton.isHidden = true
|
self.helpButton.isHidden = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.button.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: width - leftInset - rightInset, height: panelHeight))
|
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.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)
|
self.helpButton.frame = CGRect(x: width - rightInset - panelHeight, y: 0.0, width: panelHeight, height: panelHeight)
|
||||||
} else {
|
} else {
|
||||||
|
self.giftButton.isHidden = true
|
||||||
self.helpButton.isHidden = true
|
self.helpButton.isHidden = true
|
||||||
|
|
||||||
let availableWidth = min(600.0, width - leftInset - rightInset)
|
let availableWidth = min(600.0, width - leftInset - rightInset)
|
||||||
|
@ -469,16 +469,19 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
buttonTitle = item.presentationData.strings.Notification_PremiumPrize_View
|
buttonTitle = item.presentationData.strings.Notification_PremiumPrize_View
|
||||||
hasServiceMessage = false
|
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 {
|
if case let .generic(gift) = gift {
|
||||||
isStarGift = true
|
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 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 {
|
if isSelfGift {
|
||||||
title = item.presentationData.strings.Notification_StarGift_Self_Title
|
title = item.presentationData.strings.Notification_StarGift_Self_Title
|
||||||
} else {
|
} 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
|
title = item.presentationData.strings.Notification_StarGift_Title(authorName).string
|
||||||
}
|
}
|
||||||
if let giftText, !giftText.isEmpty {
|
if let giftText, !giftText.isEmpty {
|
||||||
@ -554,7 +557,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
buttonTitle = item.presentationData.strings.Notification_StarGift_View
|
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 {
|
if case let .unique(uniqueGift) = gift {
|
||||||
isStarGift = true
|
isStarGift = true
|
||||||
let authorName: String
|
let authorName: String
|
||||||
|
@ -328,7 +328,7 @@ public extension EmojiPagerContentComponent {
|
|||||||
} else {
|
} else {
|
||||||
itemGroupIndexById[groupId] = itemGroups.count
|
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]))
|
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 {
|
if let uniqueGifts, !uniqueGifts.items.isEmpty {
|
||||||
//TODO:localize
|
|
||||||
let groupId = "collectible"
|
let groupId = "collectible"
|
||||||
let groupIndex: Int
|
let groupIndex: Int
|
||||||
if let current = itemGroupIndexById[groupId] {
|
if let current = itemGroupIndexById[groupId] {
|
||||||
@ -622,7 +621,7 @@ public extension EmojiPagerContentComponent {
|
|||||||
} else {
|
} else {
|
||||||
groupIndex = itemGroups.count
|
groupIndex = itemGroups.count
|
||||||
itemGroupIndexById[groupId] = groupIndex
|
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 {
|
for item in uniqueGifts.items {
|
||||||
|
@ -642,8 +642,7 @@ final class GiftOptionsScreenComponent: Component {
|
|||||||
if isSelfGift {
|
if isSelfGift {
|
||||||
premiumTitleString = strings.Gift_Options_GiftSelf_Title
|
premiumTitleString = strings.Gift_Options_GiftSelf_Title
|
||||||
} else if isChannelGift {
|
} else if isChannelGift {
|
||||||
//TODO:localize
|
premiumTitleString = strings.Gift_Options_GiftChannel_Title
|
||||||
premiumTitleString = "Send a Gift"
|
|
||||||
} else {
|
} else {
|
||||||
premiumTitleString = strings.Gift_Options_Premium_Title
|
premiumTitleString = strings.Gift_Options_Premium_Title
|
||||||
}
|
}
|
||||||
@ -675,8 +674,7 @@ final class GiftOptionsScreenComponent: Component {
|
|||||||
if isSelfGift {
|
if isSelfGift {
|
||||||
premiumDescriptionRawString = strings.Gift_Options_GiftSelf_Text
|
premiumDescriptionRawString = strings.Gift_Options_GiftSelf_Text
|
||||||
} else if isChannelGift {
|
} else if isChannelGift {
|
||||||
//TODO:localize
|
premiumDescriptionRawString = strings.Gift_Options_GiftChannel_Text(peerName).string
|
||||||
premiumDescriptionRawString = "Select a gift to show appreciation for **\(peerName)**."
|
|
||||||
} else {
|
} else {
|
||||||
premiumDescriptionRawString = strings.Gift_Options_Premium_Text(peerName).string
|
premiumDescriptionRawString = strings.Gift_Options_Premium_Text(peerName).string
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ final class ChatGiftPreviewItemNode: ListViewItemNode {
|
|||||||
case let .starGift(gift):
|
case let .starGift(gift):
|
||||||
media = [
|
media = [
|
||||||
TelegramMediaAction(
|
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 ProgressNavigationButtonNode
|
||||||
import Markdown
|
import Markdown
|
||||||
import GiftViewScreen
|
import GiftViewScreen
|
||||||
|
import UndoUI
|
||||||
|
|
||||||
final class GiftSetupScreenComponent: Component {
|
final class GiftSetupScreenComponent: Component {
|
||||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||||
@ -322,6 +323,15 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
return
|
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
|
let proceed = { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -331,7 +341,7 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
self.state?.updated()
|
self.state?.updated()
|
||||||
|
|
||||||
let entities = generateChatInputTextEntities(self.textInputState.text)
|
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)
|
let inputData = BotCheckoutController.InputData.fetch(context: component.context, source: source)
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
@ -359,22 +369,43 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var controllers = navigationController.viewControllers
|
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
controllers = controllers.filter { !($0 is GiftSetupScreen) && !($0 is GiftOptionsScreenProtocol) && !($0 is PeerInfoScreen) && !($0 is ContactSelectionController) }
|
var controllers = navigationController.viewControllers
|
||||||
var foundController = false
|
controllers = controllers.filter { !($0 is GiftSetupScreen) && !($0 is GiftOptionsScreenProtocol) }
|
||||||
for controller in controllers.reversed() {
|
navigationController.setViewControllers(controllers, animated: true)
|
||||||
if let chatController = controller as? ChatController, case .peer(id: component.peerId) = chatController.chatLocation {
|
|
||||||
chatController.hintPlayNextOutgoingGift()
|
let tooltipController = UndoOverlayController(
|
||||||
foundController = true
|
presentationData: presentationData,
|
||||||
break
|
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)
|
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()
|
let _ = (self.optionsPromise.get()
|
||||||
|> filter { $0 != nil }
|
|> filter { $0 != nil }
|
||||||
|> take(1)
|
|> take(1)
|
||||||
@ -415,7 +446,7 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
context: component.context,
|
context: component.context,
|
||||||
starsContext: starsContext,
|
starsContext: starsContext,
|
||||||
options: options ?? [],
|
options: options ?? [],
|
||||||
purpose: .starGift(peerId: component.peerId, requiredStars: starGift.price),
|
purpose: .starGift(peerId: component.peerId, requiredStars: finalPrice),
|
||||||
completion: { [weak self, weak starsContext] stars in
|
completion: { [weak self, weak starsContext] stars in
|
||||||
guard let self, let starsContext else {
|
guard let self, let starsContext else {
|
||||||
return
|
return
|
||||||
@ -642,8 +673,7 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
if isSelfGift {
|
if isSelfGift {
|
||||||
navigationTitleString = environment.strings.Gift_SendSelf_Title
|
navigationTitleString = environment.strings.Gift_SendSelf_Title
|
||||||
} else if isChannelGift {
|
} else if isChannelGift {
|
||||||
//TODO:localize
|
navigationTitleString = environment.strings.Gift_SendChannel_Title
|
||||||
navigationTitleString = "Gift Preview"
|
|
||||||
} else {
|
} else {
|
||||||
navigationTitleString = environment.strings.Gift_Send_TitleTo(peerName).string
|
navigationTitleString = environment.strings.Gift_Send_TitleTo(peerName).string
|
||||||
}
|
}
|
||||||
@ -988,8 +1018,7 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
if isSelfGift {
|
if isSelfGift {
|
||||||
hideSectionFooterString = environment.strings.Gift_SendSelf_HideMyName_Info
|
hideSectionFooterString = environment.strings.Gift_SendSelf_HideMyName_Info
|
||||||
} else if isChannelGift {
|
} else if isChannelGift {
|
||||||
//TODO:localize
|
hideSectionFooterString = environment.strings.Gift_SendChannel_HideMyName_Info
|
||||||
hideSectionFooterString = "Hide my name and message from visitors of this channel. The channel admins will still see them."
|
|
||||||
} else {
|
} else {
|
||||||
hideSectionFooterString = environment.strings.Gift_Send_HideMyName_Info(peerName, peerName).string
|
hideSectionFooterString = environment.strings.Gift_Send_HideMyName_Info(peerName, peerName).string
|
||||||
}
|
}
|
||||||
|
@ -385,6 +385,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
let strings = environment.strings
|
let strings = environment.strings
|
||||||
let dateTimeFormat = environment.dateTimeFormat
|
let dateTimeFormat = environment.dateTimeFormat
|
||||||
let nameDisplayOrder = component.context.sharedContext.currentPresentationData.with { $0 }.nameDisplayOrder
|
let nameDisplayOrder = component.context.sharedContext.currentPresentationData.with { $0 }.nameDisplayOrder
|
||||||
|
let controller = environment.controller
|
||||||
|
|
||||||
let state = context.state
|
let state = context.state
|
||||||
let subject = state.subject
|
let subject = state.subject
|
||||||
@ -410,6 +411,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
var upgradeStars: Int64?
|
var upgradeStars: Int64?
|
||||||
var uniqueGift: StarGift.UniqueGift?
|
var uniqueGift: StarGift.UniqueGift?
|
||||||
var isSelfGift = false
|
var isSelfGift = false
|
||||||
|
var isChannelGift = false
|
||||||
|
|
||||||
if case let .soldOutGift(gift) = subject {
|
if case let .soldOutGift(gift) = subject {
|
||||||
animationFile = gift.file
|
animationFile = gift.file
|
||||||
@ -444,7 +446,12 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
uniqueGift = gift
|
uniqueGift = gift
|
||||||
}
|
}
|
||||||
savedToProfile = arguments.savedToProfile
|
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
|
nameHidden = arguments.nameHidden
|
||||||
|
|
||||||
isSelfGift = arguments.messageId?.peerId == component.context.account.peerId
|
isSelfGift = arguments.messageId?.peerId == component.context.account.peerId
|
||||||
@ -482,6 +489,9 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if state.inWearPreview {
|
if state.inWearPreview {
|
||||||
|
if let controller = controller() as? GiftViewScreen {
|
||||||
|
controller.dismissAllTooltips()
|
||||||
|
}
|
||||||
state.inWearPreview = false
|
state.inWearPreview = false
|
||||||
state.updated(transition: .spring(duration: 0.4))
|
state.updated(transition: .spring(duration: 0.4))
|
||||||
} else if state.inUpgradePreview {
|
} else if state.inUpgradePreview {
|
||||||
@ -849,8 +859,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
textColor: secondaryTextColor,
|
textColor: secondaryTextColor,
|
||||||
accentColor: linkColor,
|
accentColor: linkColor,
|
||||||
iconName: "Premium/Collectible/Tradable",
|
iconName: "Premium/Collectible/Tradable",
|
||||||
iconColor: linkColor,
|
iconColor: linkColor
|
||||||
badge: strings.Gift_Upgrade_Soon
|
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -937,9 +946,9 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
} else if let convertStars, !upgraded {
|
} else if let convertStars, !upgraded {
|
||||||
if !converted {
|
if !converted {
|
||||||
if canUpgrade || upgradeStars != nil {
|
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 {
|
} 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 {
|
} else {
|
||||||
descriptionText = strings.Gift_View_ConvertedDescription(strings.Gift_View_ConvertedDescription_Stars(Int32(convertStars))).string
|
descriptionText = strings.Gift_View_ConvertedDescription(strings.Gift_View_ConvertedDescription_Stars(Int32(convertStars))).string
|
||||||
@ -1152,7 +1161,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
),
|
),
|
||||||
action: {
|
action: {
|
||||||
component.openPeer(peer)
|
component.openPeer(peer)
|
||||||
Queue.mainQueue().after(1.0, {
|
Queue.mainQueue().after(0.6, {
|
||||||
component.cancel(false)
|
component.cancel(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1188,7 +1197,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
),
|
),
|
||||||
action: {
|
action: {
|
||||||
component.openPeer(peer)
|
component.openPeer(peer)
|
||||||
Queue.mainQueue().after(1.0, {
|
Queue.mainQueue().after(0.6, {
|
||||||
component.cancel(false)
|
component.cancel(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1215,7 +1224,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
isBot = true
|
isBot = true
|
||||||
}
|
}
|
||||||
let fromComponent: AnyComponent<Empty>
|
let fromComponent: AnyComponent<Empty>
|
||||||
if incoming && !peer.isDeleted && !isBot {
|
if incoming && !peer.isDeleted && !isBot && !isChannelGift {
|
||||||
fromComponent = AnyComponent(
|
fromComponent = AnyComponent(
|
||||||
HStack([
|
HStack([
|
||||||
AnyComponentWithIdentity(
|
AnyComponentWithIdentity(
|
||||||
@ -1231,7 +1240,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
),
|
),
|
||||||
action: {
|
action: {
|
||||||
component.openPeer(peer)
|
component.openPeer(peer)
|
||||||
Queue.mainQueue().after(1.0, {
|
Queue.mainQueue().after(0.6, {
|
||||||
component.cancel(false)
|
component.cancel(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1247,7 +1256,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
)),
|
)),
|
||||||
action: {
|
action: {
|
||||||
component.sendGift(peerId)
|
component.sendGift(peerId)
|
||||||
Queue.mainQueue().after(1.0, {
|
Queue.mainQueue().after(0.6, {
|
||||||
component.cancel(false)
|
component.cancel(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1267,7 +1276,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
),
|
),
|
||||||
action: {
|
action: {
|
||||||
component.openPeer(peer)
|
component.openPeer(peer)
|
||||||
Queue.mainQueue().after(1.0, {
|
Queue.mainQueue().after(0.6, {
|
||||||
component.cancel(false)
|
component.cancel(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1315,7 +1324,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
effectAlignment: .center,
|
effectAlignment: .center,
|
||||||
action: {
|
action: {
|
||||||
component.transferGift()
|
component.transferGift()
|
||||||
Queue.mainQueue().after(1.0, {
|
Queue.mainQueue().after(0.6, {
|
||||||
component.cancel(false)
|
component.cancel(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1326,9 +1335,10 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
context.add(transferButton
|
context.add(transferButton
|
||||||
.position(CGPoint(x: sideInset + buttonWidth / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0))
|
.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(
|
let wearButton = wearButton.update(
|
||||||
component: PlainButtonComponent(
|
component: PlainButtonComponent(
|
||||||
content: AnyComponent(
|
content: AnyComponent(
|
||||||
@ -1345,7 +1355,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
state.pendingWear = false
|
state.pendingWear = false
|
||||||
state.updated(transition: .spring(duration: 0.4))
|
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 {
|
} else {
|
||||||
if let controller = controller() as? GiftViewScreen {
|
if let controller = controller() as? GiftViewScreen {
|
||||||
controller.dismissAllTooltips()
|
controller.dismissAllTooltips()
|
||||||
@ -1362,6 +1372,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
context.add(wearButton
|
context.add(wearButton
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0))
|
.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(
|
let shareButton = shareButton.update(
|
||||||
@ -1383,6 +1395,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
context.add(shareButton
|
context.add(shareButton
|
||||||
.position(CGPoint(x: context.availableSize.width - sideInset - buttonWidth / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0))
|
.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] {
|
if let mention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention, let peer = state.peerMap[mention.peerId] {
|
||||||
component.openPeer(peer)
|
component.openPeer(peer)
|
||||||
Queue.mainQueue().after(1.0, {
|
Queue.mainQueue().after(0.6, {
|
||||||
component.cancel(false)
|
component.cancel(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1734,11 +1748,11 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
let descriptionText: String
|
let descriptionText: String
|
||||||
if savedToProfile {
|
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 {
|
} else if let upgradeStars, upgradeStars > 0 && !upgraded {
|
||||||
descriptionText = strings.Gift_View_HiddenInfoShow
|
descriptionText = isChannelGift ? strings.Gift_View_HiddenInfoShow_Channel : strings.Gift_View_HiddenInfoShow
|
||||||
} else {
|
} else {
|
||||||
descriptionText = strings.Gift_View_HiddenInfo
|
descriptionText = isChannelGift ? strings.Gift_View_HiddenInfo_Channel : strings.Gift_View_HiddenInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
let textFont = Font.regular(13.0)
|
let textFont = Font.regular(13.0)
|
||||||
@ -1769,7 +1783,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
},
|
},
|
||||||
tapAction: { _, _ in
|
tapAction: { _, _ in
|
||||||
component.updateSavedToProfile(!savedToProfile)
|
component.updateSavedToProfile(!savedToProfile)
|
||||||
Queue.mainQueue().after(1.0, {
|
Queue.mainQueue().after(0.6, {
|
||||||
component.cancel(false)
|
component.cancel(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1794,26 +1808,74 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
let buttonChild: _UpdatedChildComponent
|
let buttonChild: _UpdatedChildComponent
|
||||||
if state.inWearPreview, let uniqueGift {
|
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(
|
buttonChild = button.update(
|
||||||
component: ButtonComponent(
|
component: ButtonComponent(
|
||||||
background: buttonBackground,
|
background: buttonBackground,
|
||||||
content: AnyComponentWithIdentity(
|
content: buttonContent,
|
||||||
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))))
|
|
||||||
),
|
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
displaysProgress: false,
|
displaysProgress: false,
|
||||||
action: { [weak state] in
|
action: { [weak state] in
|
||||||
if let state {
|
if let state {
|
||||||
state.pendingWear = true
|
let context = component.context
|
||||||
state.pendingTakeOff = false
|
if !context.isPremium, let controller = controller() as? GiftViewScreen {
|
||||||
state.inWearPreview = false
|
controller.dismissAllTooltips()
|
||||||
state.updated(transition: .spring(duration: 0.4))
|
|
||||||
|
|
||||||
let _ = component.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).start()
|
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))
|
||||||
|
|
||||||
Queue.mainQueue().after(0.2) {
|
let _ = component.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).start()
|
||||||
component.showAttributeInfo(statusTag, "You put on \(uniqueGift.title) #\(uniqueGift.number)")
|
|
||||||
|
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
|
transition: context.transition
|
||||||
)
|
)
|
||||||
} else if incoming && !converted && !savedToProfile {
|
} 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(
|
buttonChild = button.update(
|
||||||
component: ButtonComponent(
|
component: ButtonComponent(
|
||||||
background: buttonBackground,
|
background: buttonBackground,
|
||||||
@ -2131,9 +2193,21 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
case let .message(message):
|
case let .message(message):
|
||||||
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
|
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
|
||||||
switch action.action {
|
switch action.action {
|
||||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, _, upgradeMessageId, _, _):
|
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, _, upgradeMessageId, peerId, senderId, savedId):
|
||||||
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)
|
var reference: StarGiftReference
|
||||||
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, _):
|
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
|
var incoming = false
|
||||||
if isUpgrade {
|
if isUpgrade {
|
||||||
if message.author?.id != message.id.peerId {
|
if message.author?.id != message.id.peerId {
|
||||||
@ -2146,7 +2220,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
} else {
|
} else {
|
||||||
incoming = message.flags.contains(.Incoming)
|
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:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2545,7 +2619,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
openPeerImpl?(peer)
|
openPeerImpl?(peer)
|
||||||
Queue.mainQueue().after(1.0) {
|
Queue.mainQueue().after(0.6) {
|
||||||
self?.dismiss(animated: false, completion: nil)
|
self?.dismiss(animated: false, completion: nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -3398,13 +3472,16 @@ private final class GiftViewContextReferenceContentSource: ContextReferenceConte
|
|||||||
private final class HeaderButtonComponent: CombinedComponent {
|
private final class HeaderButtonComponent: CombinedComponent {
|
||||||
let title: String
|
let title: String
|
||||||
let iconName: String
|
let iconName: String
|
||||||
|
let isLocked: Bool
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
title: String,
|
title: String,
|
||||||
iconName: String
|
iconName: String,
|
||||||
|
isLocked: Bool = false
|
||||||
) {
|
) {
|
||||||
self.title = title
|
self.title = title
|
||||||
self.iconName = iconName
|
self.iconName = iconName
|
||||||
|
self.isLocked = isLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: HeaderButtonComponent, rhs: HeaderButtonComponent) -> Bool {
|
static func ==(lhs: HeaderButtonComponent, rhs: HeaderButtonComponent) -> Bool {
|
||||||
@ -3414,6 +3491,9 @@ private final class HeaderButtonComponent: CombinedComponent {
|
|||||||
if lhs.iconName != rhs.iconName {
|
if lhs.iconName != rhs.iconName {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.isLocked != rhs.isLocked {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3421,6 +3501,7 @@ private final class HeaderButtonComponent: CombinedComponent {
|
|||||||
let background = Child(RoundedRectangle.self)
|
let background = Child(RoundedRectangle.self)
|
||||||
let title = Child(MultilineTextComponent.self)
|
let title = Child(MultilineTextComponent.self)
|
||||||
let icon = Child(BundleIconComponent.self)
|
let icon = Child(BundleIconComponent.self)
|
||||||
|
let lockIcon = Child(BundleIconComponent.self)
|
||||||
|
|
||||||
return { context in
|
return { context in
|
||||||
let component = context.component
|
let component = context.component
|
||||||
@ -3463,8 +3544,27 @@ private final class HeaderButtonComponent: CombinedComponent {
|
|||||||
availableSize: CGSize(width: context.availableSize.width - 16.0, height: context.availableSize.height),
|
availableSize: CGSize(width: context.availableSize.width - 16.0, height: context.availableSize.height),
|
||||||
transition: .immediate
|
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
|
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
|
return context.availableSize
|
||||||
|
@ -173,7 +173,8 @@ private final class GiftWithdrawAlertContentNode: AlertContentNode {
|
|||||||
photo: nil,
|
photo: nil,
|
||||||
media: [],
|
media: [],
|
||||||
uniqueGift: nil,
|
uniqueGift: nil,
|
||||||
backgroundColor: .clear
|
backgroundColor: .clear,
|
||||||
|
size: avatarSize
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
environment: {},
|
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:
|
case .storyEditor:
|
||||||
doneButtonTitle = isEditingStory ? environment.strings.Story_Editor_Done.uppercased() : environment.strings.Story_Editor_Next.uppercased()
|
doneButtonTitle = isEditingStory ? environment.strings.Story_Editor_Done.uppercased() : environment.strings.Story_Editor_Next.uppercased()
|
||||||
doneButtonIcon = UIImage(bundleImageName: "Media Editor/Next")!
|
doneButtonIcon = UIImage(bundleImageName: "Media Editor/Next")!
|
||||||
case .stickerEditor, .avatarEditor:
|
case .stickerEditor, .avatarEditor, .coverEditor:
|
||||||
doneButtonTitle = nil
|
doneButtonTitle = nil
|
||||||
doneButtonIcon = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/Apply"), color: .white)!
|
doneButtonIcon = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/Apply"), color: .white)!
|
||||||
case .botPreview:
|
case .botPreview:
|
||||||
@ -1060,9 +1060,14 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
)
|
)
|
||||||
|
|
||||||
var isAvatarEditor = false
|
var isAvatarEditor = false
|
||||||
|
var isCoverEditor = false
|
||||||
if case .avatarEditor = controller.mode {
|
if case .avatarEditor = controller.mode {
|
||||||
isAvatarEditor = true
|
isAvatarEditor = true
|
||||||
|
} else if case .coverEditor = controller.mode {
|
||||||
|
isCoverEditor = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isAvatarEditor || isCoverEditor {
|
||||||
drawButtonFrame.origin.x = stickerButtonFrame.origin.x
|
drawButtonFrame.origin.x = stickerButtonFrame.origin.x
|
||||||
|
|
||||||
if let rotateButtonView = self.rotateButton.view {
|
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 {
|
if textButtonView.superview == nil {
|
||||||
self.addSubview(textButtonView)
|
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 {
|
if stickerButtonView.superview == nil {
|
||||||
self.addSubview(stickerButtonView)
|
self.addSubview(stickerButtonView)
|
||||||
}
|
}
|
||||||
@ -2688,6 +2693,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||||||
case stickerEditor(mode: StickerEditorMode)
|
case stickerEditor(mode: StickerEditorMode)
|
||||||
case botPreview
|
case botPreview
|
||||||
case avatarEditor
|
case avatarEditor
|
||||||
|
case coverEditor(dimensions: CGSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum TransitionIn {
|
public enum TransitionIn {
|
||||||
@ -2882,14 +2888,17 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||||||
|
|
||||||
var isStickerEditor = false
|
var isStickerEditor = false
|
||||||
var isAvatarEditor = false
|
var isAvatarEditor = false
|
||||||
|
var isCoverEditor = false
|
||||||
if case .stickerEditor = controller.mode {
|
if case .stickerEditor = controller.mode {
|
||||||
isStickerEditor = true
|
isStickerEditor = true
|
||||||
} else if case .avatarEditor = controller.mode {
|
} else if case .avatarEditor = controller.mode {
|
||||||
isAvatarEditor = true
|
isAvatarEditor = true
|
||||||
|
} else if case .coverEditor = controller.mode {
|
||||||
|
isCoverEditor = true
|
||||||
}
|
}
|
||||||
|
|
||||||
self.entitiesContainerView = UIView(frame: CGRect(origin: .zero, size: storyDimensions))
|
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 = {
|
self.entitiesView.getEntityCenterPosition = {
|
||||||
return CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0)
|
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()
|
let stickerBackgroundView = UIImageView()
|
||||||
self.stickerBackgroundView = stickerBackgroundView
|
self.stickerBackgroundView = stickerBackgroundView
|
||||||
self.previewContainerView.addSubview(stickerBackgroundView)
|
self.previewContainerView.addSubview(stickerBackgroundView)
|
||||||
|
case .coverEditor:
|
||||||
|
let stickerBackgroundView = UIImageView()
|
||||||
|
self.stickerBackgroundView = stickerBackgroundView
|
||||||
|
self.previewContainerView.addSubview(stickerBackgroundView)
|
||||||
default:
|
default:
|
||||||
self.previewContainerView.addSubview(self.gradientView)
|
self.previewContainerView.addSubview(self.gradientView)
|
||||||
}
|
}
|
||||||
@ -2970,7 +2983,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||||||
self.entitiesView.addSubview(self.drawingView)
|
self.entitiesView.addSubview(self.drawingView)
|
||||||
|
|
||||||
switch controller.mode {
|
switch controller.mode {
|
||||||
case .stickerEditor, .avatarEditor:
|
case .stickerEditor, .avatarEditor, .coverEditor:
|
||||||
let stickerOverlayLayer = SimpleShapeLayer()
|
let stickerOverlayLayer = SimpleShapeLayer()
|
||||||
stickerOverlayLayer.fillColor = UIColor(rgb: 0x000000, alpha: 0.7).cgColor
|
stickerOverlayLayer.fillColor = UIColor(rgb: 0x000000, alpha: 0.7).cgColor
|
||||||
stickerOverlayLayer.fillRule = .evenOdd
|
stickerOverlayLayer.fillRule = .evenOdd
|
||||||
@ -3174,7 +3187,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||||||
} else {
|
} else {
|
||||||
mediaEntity.scale = storyDimensions.width / fittedSize.width
|
mediaEntity.scale = storyDimensions.width / fittedSize.width
|
||||||
}
|
}
|
||||||
case .stickerEditor, .avatarEditor:
|
case .stickerEditor, .avatarEditor, .coverEditor:
|
||||||
if fittedSize.height > fittedSize.width {
|
if fittedSize.height > fittedSize.width {
|
||||||
mediaEntity.scale = storyDimensions.width / fittedSize.width
|
mediaEntity.scale = storyDimensions.width / fittedSize.width
|
||||||
} else {
|
} else {
|
||||||
@ -3216,6 +3229,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||||||
mediaEditorMode = .sticker
|
mediaEditorMode = .sticker
|
||||||
} else if case .avatarEditor = controller.mode {
|
} else if case .avatarEditor = controller.mode {
|
||||||
mediaEditorMode = .avatar
|
mediaEditorMode = .avatar
|
||||||
|
} else if case .coverEditor = controller.mode {
|
||||||
|
mediaEditorMode = .avatar
|
||||||
}
|
}
|
||||||
|
|
||||||
if let mediaEntityView = self.entitiesView.add(mediaEntity, announce: false) as? DrawingMediaEntityView {
|
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 {
|
} else if case let .gift(gift) = effectiveSubject {
|
||||||
isGift = true
|
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: [:])
|
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])
|
messages = .single([message])
|
||||||
} else {
|
} else {
|
||||||
@ -3860,6 +3875,9 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||||||
} else if case .avatarEditor = controller.mode {
|
} else if case .avatarEditor = controller.mode {
|
||||||
hasSwipeToDismiss = false
|
hasSwipeToDismiss = false
|
||||||
hasSwipeToEnhance = false
|
hasSwipeToEnhance = false
|
||||||
|
} else if case .coverEditor = controller.mode {
|
||||||
|
hasSwipeToDismiss = false
|
||||||
|
hasSwipeToEnhance = false
|
||||||
} else if self.isCollageTimelineOpen {
|
} else if self.isCollageTimelineOpen {
|
||||||
hasSwipeToEnhance = false
|
hasSwipeToEnhance = false
|
||||||
}
|
}
|
||||||
@ -4064,7 +4082,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||||||
} else {
|
} else {
|
||||||
initialScale = self.previewContainerView.bounds.width / image.size.width
|
initialScale = self.previewContainerView.bounds.width / image.size.width
|
||||||
}
|
}
|
||||||
case .stickerEditor, .avatarEditor:
|
case .stickerEditor, .avatarEditor, .coverEditor:
|
||||||
if image.size.height > image.size.width {
|
if image.size.height > image.size.width {
|
||||||
initialScale = self.previewContainerView.bounds.width / image.size.width
|
initialScale = self.previewContainerView.bounds.width / image.size.width
|
||||||
} else {
|
} else {
|
||||||
@ -5137,7 +5155,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||||||
controller.requestStickerCompletion(animated: true)
|
controller.requestStickerCompletion(animated: true)
|
||||||
case .botPreview:
|
case .botPreview:
|
||||||
controller.requestStoryCompletion(animated: true)
|
controller.requestStoryCompletion(animated: true)
|
||||||
case .avatarEditor:
|
case .avatarEditor, .coverEditor:
|
||||||
controller.requestStoryCompletion(animated: true)
|
controller.requestStoryCompletion(animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5404,7 +5422,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||||||
var hasInteractiveStickers = true
|
var hasInteractiveStickers = true
|
||||||
if let controller = self.controller {
|
if let controller = self.controller {
|
||||||
switch controller.mode {
|
switch controller.mode {
|
||||||
case .stickerEditor, .botPreview, .avatarEditor:
|
case .stickerEditor, .botPreview, .avatarEditor, .coverEditor:
|
||||||
hasInteractiveStickers = false
|
hasInteractiveStickers = false
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -5885,7 +5903,11 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||||||
transition.setFrame(view: self.selectionContainerView, frame: CGRect(origin: .zero, size: previewFrame.size))
|
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 {
|
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)
|
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))
|
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:
|
case .avatarEditor:
|
||||||
overlayInnerRect = UIBezierPath(cgPath: CGPath(ellipseIn: stickerFrameRect, transform: nil))
|
overlayInnerRect = UIBezierPath(cgPath: CGPath(ellipseIn: stickerFrameRect, transform: nil))
|
||||||
stickerFrameLayer.isHidden = true
|
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:
|
default:
|
||||||
overlayInnerRect = UIBezierPath(cgPath: CGPath(roundedRect: stickerFrameRect, cornerWidth: stickerFrameWidth / 8.0, cornerHeight: stickerFrameWidth / 8.0, transform: nil))
|
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 context = self.context
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
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 {
|
if case .info = action, let self {
|
||||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .storiesExpirationDurations, forceDark: true, dismissed: nil)
|
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .storiesExpirationDurations, forceDark: true, dismissed: nil)
|
||||||
self.push(controller)
|
self.push(controller)
|
||||||
@ -6884,7 +6910,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||||||
save = presentationData.strings.Story_Editor_DraftKeepMedia
|
save = presentationData.strings.Story_Editor_DraftKeepMedia
|
||||||
}
|
}
|
||||||
text = presentationData.strings.Story_Editor_DraftDiscaedText
|
text = presentationData.strings.Story_Editor_DraftDiscaedText
|
||||||
case .stickerEditor, .botPreview, .avatarEditor:
|
case .stickerEditor, .botPreview, .avatarEditor, .coverEditor:
|
||||||
title = presentationData.strings.Story_Editor_DraftDiscardMedia
|
title = presentationData.strings.Story_Editor_DraftDiscardMedia
|
||||||
text = presentationData.strings.Story_Editor_DiscardText
|
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(
|
return combineLatest(
|
||||||
context.account.viewTracker.peerView(peerId, updateData: true),
|
context.account.viewTracker.peerView(peerId, updateData: true),
|
||||||
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, isMyProfile: false, chatLocationContextHolder: chatLocationContextHolder),
|
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
|
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 {
|
} else {
|
||||||
availablePanes = nil
|
availablePanes = nil
|
||||||
}
|
}
|
||||||
@ -1665,7 +1673,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
starsRevenueStatsContext: starsRevenueContextAndState.0,
|
starsRevenueStatsContext: starsRevenueContextAndState.0,
|
||||||
revenueStatsState: revenueContextAndState.1,
|
revenueStatsState: revenueContextAndState.1,
|
||||||
revenueStatsContext: revenueContextAndState.0,
|
revenueStatsContext: revenueContextAndState.0,
|
||||||
profileGiftsContext: nil,
|
profileGiftsContext: profileGiftsContext,
|
||||||
premiumGiftOptions: [],
|
premiumGiftOptions: [],
|
||||||
webAppPermissions: nil
|
webAppPermissions: nil
|
||||||
)
|
)
|
||||||
|
@ -463,7 +463,15 @@ private final class PeerInfoPendingPane {
|
|||||||
let paneNode: PeerInfoPaneNode
|
let paneNode: PeerInfoPaneNode
|
||||||
switch key {
|
switch key {
|
||||||
case .gifts:
|
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:
|
case .stories, .storyArchive, .botPreview:
|
||||||
var canManage = false
|
var canManage = false
|
||||||
if let peer = data.peer {
|
if let peer = data.peer {
|
||||||
|
@ -6467,8 +6467,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
} else if let channel = peer as? TelegramChannel {
|
} else if let channel = peer as? TelegramChannel {
|
||||||
if let cachedData = strongSelf.data?.cachedData as? CachedChannelData {
|
if let cachedData = strongSelf.data?.cachedData as? CachedChannelData {
|
||||||
if case .broadcast = channel.info {
|
if case .broadcast = channel.info {
|
||||||
//TODO:localize
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_SendGift, badge: nil, icon: { theme in
|
||||||
items.append(.action(ContextMenuActionItem(text: "Send a Gift", badge: nil, icon: { theme in
|
|
||||||
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor)
|
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
f(.dismissWithoutContent)
|
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 {
|
guard let peer = self.data?.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: self.data?.threadData) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -12772,7 +12771,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||||||
|
|
||||||
public func openAvatarSetup(completedWithUploadingImage: @escaping (UIImage, Signal<PeerInfoAvatarUploadStatus, NoError>) -> UIView?) {
|
public func openAvatarSetup(completedWithUploadingImage: @escaping (UIImage, Signal<PeerInfoAvatarUploadStatus, NoError>) -> UIView?) {
|
||||||
let proceed = { [weak self] in
|
let proceed = { [weak self] in
|
||||||
self?.newopenAvatarForEditing(completedWithUploadingImage: completedWithUploadingImage)
|
self?.openAvatarForEditing(completedWithUploadingImage: completedWithUploadingImage)
|
||||||
}
|
}
|
||||||
if !self.isNodeLoaded {
|
if !self.isNodeLoaded {
|
||||||
self.loadDisplayNode()
|
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) {
|
static func openPeer(context: AccountContext, peerId: PeerId, navigation: ChatControllerInteractionNavigateToPeer, navigationController: NavigationController) {
|
||||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||||
|> deliverOnMainQueue).startStandalone(next: { peer in
|
|> deliverOnMainQueue).startStandalone(next: { peer in
|
||||||
|
@ -19,7 +19,7 @@ import LegacyComponents
|
|||||||
import LegacyMediaPickerUI
|
import LegacyMediaPickerUI
|
||||||
|
|
||||||
extension PeerInfoScreenImpl {
|
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 {
|
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
|
return
|
||||||
}
|
}
|
||||||
@ -170,7 +170,6 @@ extension PeerInfoScreenImpl {
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
} as (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
} as (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
||||||
)
|
)
|
||||||
@ -200,6 +199,7 @@ extension PeerInfoScreenImpl {
|
|||||||
}
|
}
|
||||||
navigationController.setViewControllers(viewControllers, animated: false)
|
navigationController.setViewControllers(viewControllers, animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
mainController.navigationPresentation = .flatModal
|
mainController.navigationPresentation = .flatModal
|
||||||
mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||||
@ -284,10 +284,9 @@ extension PeerInfoScreenImpl {
|
|||||||
(self.navigationController?.topViewController as? ViewController)?.present(actionSheet, in: .window(.root))
|
(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 {
|
guard let data = image.jpegData(compressionQuality: 0.6) else {
|
||||||
uploadStatus?.set(.single(.done))
|
return nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.controllerNode.headerNode.isAvatarExpanded {
|
if self.controllerNode.headerNode.isAvatarExpanded {
|
||||||
@ -303,14 +302,25 @@ extension PeerInfoScreenImpl {
|
|||||||
|
|
||||||
if [.suggest, .fallback].contains(mode) {
|
if [.suggest, .fallback].contains(mode) {
|
||||||
} else {
|
} else {
|
||||||
|
if indefiniteProgress {
|
||||||
|
self.controllerNode.state = self.controllerNode.state.withAvatarUploadProgress(.indefinite)
|
||||||
|
}
|
||||||
self.controllerNode.state = self.controllerNode.state.withUpdatingAvatar(.image(representation))
|
self.controllerNode.state = self.controllerNode.state.withUpdatingAvatar(.image(representation))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (layout, navigationHeight) = self.controllerNode.validLayout {
|
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.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: mode == .custom ? .animated(duration: 0.2, curve: .easeInOut) : .immediate, additive: false)
|
||||||
}
|
}
|
||||||
self.controllerNode.headerNode.ignoreCollapse = 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 postbox = self.context.account.postbox
|
||||||
let signal: Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError>
|
let signal: Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError>
|
||||||
if self.isSettings || self.isMyProfile {
|
if self.isSettings || self.isMyProfile {
|
||||||
@ -406,22 +416,227 @@ 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) {
|
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
|
var markup: UploadPeerPhotoMarkup? = nil
|
||||||
if let fileId = adjustments?.documentId, let backgroundColors = adjustments?.colors as? [Int32], fileId != 0 {
|
if let fileId = adjustments?.documentId, let backgroundColors = adjustments?.colors as? [Int32], fileId != 0 {
|
||||||
if let packId = adjustments?.stickerPackId, let accessHash = adjustments?.stickerPackAccessHash, packId != 0 {
|
if let packId = adjustments?.stickerPackId, let accessHash = adjustments?.stickerPackAccessHash, packId != 0 {
|
||||||
@ -439,20 +654,10 @@ extension PeerInfoScreenImpl {
|
|||||||
uploadVideo = false
|
uploadVideo = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
guard let photoResource = self.setupProfilePhotoUpload(image: image, mode: mode, indefiniteProgress: !uploadVideo) else {
|
||||||
if [.suggest, .fallback].contains(mode) {
|
return
|
||||||
} else {
|
|
||||||
self.controllerNode.state = self.controllerNode.state.withUpdatingAvatar(.image(representation))
|
|
||||||
if !uploadVideo {
|
|
||||||
self.controllerNode.state = self.controllerNode.state.withAvatarUploadProgress(.indefinite)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
var videoStartTimestamp: Double? = nil
|
||||||
if let adjustments = adjustments, adjustments.videoStartValue > 0.0 {
|
if let adjustments = adjustments, adjustments.videoStartValue > 0.0 {
|
||||||
videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue
|
videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue
|
||||||
|
@ -52,6 +52,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/Gifts/GiftViewScreen",
|
"//submodules/TelegramUI/Components/Gifts/GiftViewScreen",
|
||||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||||
"//submodules/Components/BalancedTextComponent",
|
"//submodules/Components/BalancedTextComponent",
|
||||||
|
"//submodules/TelegramUI/Components/CheckComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -22,11 +22,14 @@ import GiftItemComponent
|
|||||||
import PlainButtonComponent
|
import PlainButtonComponent
|
||||||
import GiftViewScreen
|
import GiftViewScreen
|
||||||
import SolidRoundedButtonNode
|
import SolidRoundedButtonNode
|
||||||
|
import UndoUI
|
||||||
|
import CheckComponent
|
||||||
|
|
||||||
public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate {
|
public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let peerId: PeerId
|
private let peerId: PeerId
|
||||||
private let profileGifts: ProfileGiftsContext
|
private let profileGifts: ProfileGiftsContext
|
||||||
|
private let canManage: Bool
|
||||||
|
|
||||||
private var dataDisposable: Disposable?
|
private var dataDisposable: Disposable?
|
||||||
|
|
||||||
@ -38,10 +41,11 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
|||||||
private let backgroundNode: ASDisplayNode
|
private let backgroundNode: ASDisplayNode
|
||||||
private let scrollNode: ASScrollNode
|
private let scrollNode: ASScrollNode
|
||||||
|
|
||||||
private var unlockBackground: NavigationBackgroundNode?
|
private var footerText: ComponentView<Empty>?
|
||||||
private var unlockSeparator: ASDisplayNode?
|
private var panelBackground: NavigationBackgroundNode?
|
||||||
private var unlockText: ComponentView<Empty>?
|
private var panelSeparator: ASDisplayNode?
|
||||||
private var unlockButton: SolidRoundedButtonNode?
|
private var panelButton: SolidRoundedButtonNode?
|
||||||
|
private var panelCheck: ComponentView<Empty>?
|
||||||
|
|
||||||
private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, presentationData: PresentationData)?
|
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>] = [:]
|
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.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.chatControllerInteraction = chatControllerInteraction
|
self.chatControllerInteraction = chatControllerInteraction
|
||||||
self.openPeerContextAction = openPeerContextAction
|
self.openPeerContextAction = openPeerContextAction
|
||||||
self.profileGifts = profileGifts
|
self.profileGifts = profileGifts
|
||||||
|
self.canManage = canManage
|
||||||
|
|
||||||
self.backgroundNode = ASDisplayNode()
|
self.backgroundNode = ASDisplayNode()
|
||||||
self.scrollNode = ASScrollNode()
|
self.scrollNode = ASScrollNode()
|
||||||
@ -125,15 +130,16 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
|||||||
self.updateScrolling(transition: .immediate)
|
self.updateScrolling(transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var notify = false
|
||||||
func updateScrolling(transition: ComponentTransition) {
|
func updateScrolling(transition: ComponentTransition) {
|
||||||
if let starsProducts = self.starsProducts, let params = self.currentParams {
|
if let starsProducts = self.starsProducts, let params = self.currentParams {
|
||||||
let optionSpacing: CGFloat = 10.0
|
let optionSpacing: CGFloat = 10.0
|
||||||
let sideInset = params.sideInset + 16.0
|
let itemsSideInset = params.sideInset + 16.0
|
||||||
|
|
||||||
let defaultItemsInRow = 3
|
let defaultItemsInRow = 3
|
||||||
let itemsInRow = max(1, min(starsProducts.count, defaultItemsInRow))
|
let itemsInRow = max(1, min(starsProducts.count, defaultItemsInRow))
|
||||||
let defaultOptionWidth = (params.size.width - sideInset * 2.0 - optionSpacing * CGFloat(defaultItemsInRow - 1)) / CGFloat(defaultItemsInRow)
|
let defaultOptionWidth = (params.size.width - itemsSideInset * 2.0 - optionSpacing * CGFloat(defaultItemsInRow - 1)) / CGFloat(defaultItemsInRow)
|
||||||
let optionWidth = (params.size.width - sideInset * 2.0 - optionSpacing * CGFloat(itemsInRow - 1)) / CGFloat(itemsInRow)
|
let optionWidth = (params.size.width - itemsSideInset * 2.0 - optionSpacing * CGFloat(itemsInRow - 1)) / CGFloat(itemsInRow)
|
||||||
|
|
||||||
let starsOptionSize = CGSize(width: optionWidth, height: defaultOptionWidth)
|
let starsOptionSize = CGSize(width: optionWidth, height: defaultOptionWidth)
|
||||||
|
|
||||||
@ -142,7 +148,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
|||||||
let topInset: CGFloat = 60.0
|
let topInset: CGFloat = 60.0
|
||||||
|
|
||||||
var validIds: [AnyHashable] = []
|
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
|
var index: Int32 = 0
|
||||||
for product in starsProducts {
|
for product in starsProducts {
|
||||||
@ -233,22 +239,22 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
|||||||
self.profileGifts.updateStarGiftAddedToProfile(reference: reference, added: added)
|
self.profileGifts.updateStarGiftAddedToProfile(reference: reference, added: added)
|
||||||
},
|
},
|
||||||
convertToStars: { [weak self] in
|
convertToStars: { [weak self] in
|
||||||
guard let self else {
|
guard let self, let reference = product.reference else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.profileGifts.convertStarGift(reference: product.reference)
|
self.profileGifts.convertStarGift(reference: reference)
|
||||||
},
|
},
|
||||||
transferGift: { [weak self] prepaid, peerId in
|
transferGift: { [weak self] prepaid, peerId in
|
||||||
guard let self else {
|
guard let self, let reference = product.reference else {
|
||||||
return
|
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
|
upgradeGift: { [weak self] formId, keepOriginalInfo in
|
||||||
guard let self else {
|
guard let self, let reference = product.reference else {
|
||||||
return .never()
|
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
|
shareStory: { [weak self] in
|
||||||
guard let self, case let .unique(uniqueGift) = product.gift, let parentController = self.parentController else {
|
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
|
itemFrame.origin.x += itemFrame.width + optionSpacing
|
||||||
if itemFrame.maxX > params.size.width {
|
if itemFrame.maxX > params.size.width {
|
||||||
itemFrame.origin.x = sideInset
|
itemFrame.origin.x = itemsSideInset
|
||||||
itemFrame.origin.y += starsOptionSize.height + optionSpacing
|
itemFrame.origin.y += starsOptionSize.height + optionSpacing
|
||||||
}
|
}
|
||||||
index += 1
|
index += 1
|
||||||
@ -306,93 +312,164 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
|||||||
|
|
||||||
var bottomScrollInset: CGFloat = 0.0
|
var bottomScrollInset: CGFloat = 0.0
|
||||||
var contentHeight = ceil(CGFloat(starsProducts.count) / 3.0) * (starsOptionSize.height + optionSpacing) - optionSpacing + topInset + 16.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
|
let transition = ComponentTransition.immediate
|
||||||
self.theme = presentationData.theme
|
|
||||||
|
|
||||||
let unlockText: ComponentView<Empty>
|
let size = params.size
|
||||||
let unlockBackground: NavigationBackgroundNode
|
let sideInset = params.sideInset
|
||||||
let unlockSeparator: ASDisplayNode
|
let bottomInset = params.bottomInset
|
||||||
let unlockButton: SolidRoundedButtonNode
|
let presentationData = params.presentationData
|
||||||
if let current = self.unlockText {
|
|
||||||
unlockText = current
|
let themeUpdated = self.theme !== presentationData.theme
|
||||||
} else {
|
self.theme = presentationData.theme
|
||||||
unlockText = ComponentView<Empty>()
|
|
||||||
self.unlockText = unlockText
|
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 let current = self.unlockBackground {
|
if themeUpdated {
|
||||||
unlockBackground = current
|
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 panelCheck: ComponentView<Empty>
|
||||||
|
if let current = self.panelCheck {
|
||||||
|
panelCheck = current
|
||||||
} else {
|
} else {
|
||||||
unlockBackground = NavigationBackgroundNode(color: presentationData.theme.rootController.tabBar.backgroundColor)
|
panelCheck = ComponentView<Empty>()
|
||||||
self.addSubnode(unlockBackground)
|
self.panelCheck = panelCheck
|
||||||
self.unlockBackground = unlockBackground
|
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
if let current = self.unlockSeparator {
|
let panelCheckSize = panelCheck.update(
|
||||||
unlockSeparator = current
|
transition: .immediate,
|
||||||
} else {
|
component: AnyComponent(
|
||||||
unlockSeparator = ASDisplayNode()
|
PlainButtonComponent(
|
||||||
self.addSubnode(unlockSeparator)
|
content: AnyComponent(HStack([
|
||||||
self.unlockSeparator = unlockSeparator
|
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 let current = self.unlockButton {
|
if self.notify {
|
||||||
unlockButton = current
|
let controller = UndoOverlayController(
|
||||||
} else {
|
presentationData: presentationData,
|
||||||
unlockButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: presentationData.theme), height: 50.0, cornerRadius: 10.0)
|
content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.PeerInfo_Gifts_ChannelNotifyTooltip, customUndoText: nil, timeout: nil),
|
||||||
self.view.addSubview(unlockButton.view)
|
appearance: UndoOverlayController.Appearance(bottomInset: 53.0),
|
||||||
self.unlockButton = unlockButton
|
action: { _ in return true }
|
||||||
|
)
|
||||||
unlockButton.title = params.presentationData.strings.PeerInfo_Gifts_Send
|
self.chatControllerInteraction.presentController(controller, nil)
|
||||||
|
}
|
||||||
unlockButton.pressed = { [weak self] in
|
self.updateScrolling(transition: .immediate)
|
||||||
self?.buttonPressed()
|
},
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
if themeUpdated {
|
transition.setFrame(view: panelBackground.view, frame: CGRect(x: 0.0, y: size.height - bottomPanelHeight - scrollOffset, width: size.width, height: bottomPanelHeight))
|
||||||
unlockBackground.updateColor(color: presentationData.theme.rootController.tabBar.backgroundColor, transition: .immediate)
|
panelBackground.update(size: CGSize(width: size.width, height: bottomPanelHeight), transition: transition.containedViewLayoutTransition)
|
||||||
unlockSeparator.backgroundColor = presentationData.theme.rootController.tabBar.separatorColor
|
transition.setFrame(view: panelSeparator.view, frame: CGRect(x: 0.0, y: size.height - bottomPanelHeight - scrollOffset, width: size.width, height: UIScreenPixel))
|
||||||
unlockButton.updateTheme(SolidRoundedButtonTheme(theme: presentationData.theme))
|
|
||||||
|
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(
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
BalancedTextComponent(
|
BalancedTextComponent(
|
||||||
text: .markdown(text: params.presentationData.strings.PeerInfo_Gifts_Info, attributes: markdownAttributes),
|
text: .markdown(text: presentationData.strings.PeerInfo_Gifts_Info, attributes: markdownAttributes),
|
||||||
horizontalAlignment: .center,
|
horizontalAlignment: .center,
|
||||||
maximumNumberOfLines: 0,
|
maximumNumberOfLines: 0,
|
||||||
lineSpacing: 0.2
|
lineSpacing: 0.2
|
||||||
@ -401,18 +478,19 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
|||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: size.width - 32.0, height: 200.0)
|
containerSize: CGSize(width: size.width - 32.0, height: 200.0)
|
||||||
)
|
)
|
||||||
if let view = unlockText.view {
|
if let view = footerText.view {
|
||||||
if view.superview == nil {
|
if view.superview == nil {
|
||||||
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.buttonPressed)))
|
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.buttonPressed)))
|
||||||
self.scrollNode.view.addSubview(view)
|
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 += footerTextSize.height
|
||||||
contentHeight += bottomPanelHeight
|
|
||||||
|
|
||||||
bottomScrollInset = bottomPanelHeight - 40.0
|
|
||||||
}
|
}
|
||||||
|
contentHeight += bottomPanelHeight
|
||||||
|
|
||||||
|
bottomScrollInset = bottomPanelHeight - 40.0
|
||||||
|
|
||||||
contentHeight += params.bottomInset
|
contentHeight += params.bottomInset
|
||||||
|
|
||||||
self.scrollNode.view.scrollIndicatorInsets = UIEdgeInsets(top: 50.0, left: 0.0, bottom: bottomScrollInset, right: 0.0)
|
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() {
|
@objc private func buttonPressed() {
|
||||||
let _ = (self.context.account.stateManager.contactBirthdays
|
if self.peerId == self.context.account.peerId {
|
||||||
|> take(1)
|
let _ = (self.context.account.stateManager.contactBirthdays
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] birthdays in
|
|> take(1)
|
||||||
guard let self else {
|
|> deliverOnMainQueue).start(next: { [weak self] birthdays in
|
||||||
return
|
guard let self else {
|
||||||
}
|
return
|
||||||
let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings(birthdays), completion: nil)
|
}
|
||||||
controller.navigationPresentation = .modal
|
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)
|
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) {
|
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
|
selectedMedia = image
|
||||||
break
|
break
|
||||||
} else if let file = media as? TelegramMediaFile {
|
} else if let file = media as? TelegramMediaFile {
|
||||||
selectedMedia = file
|
if let cover = file.videoCover {
|
||||||
|
selectedMedia = cover
|
||||||
|
} else {
|
||||||
|
selectedMedia = file
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3435,7 +3435,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
position: .top,
|
position: .top,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), nil)
|
), nil)
|
||||||
})))
|
})))
|
||||||
@ -3456,7 +3456,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
position: .top,
|
position: .top,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), nil)
|
), nil)
|
||||||
})))
|
})))
|
||||||
@ -3492,7 +3492,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
position: .top,
|
position: .top,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { [weak self] action in
|
action: { [weak self] action in
|
||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return false
|
return false
|
||||||
@ -3539,7 +3539,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
position: .top,
|
position: .top,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { [weak self] action in
|
action: { [weak self] action in
|
||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return false
|
return false
|
||||||
@ -4292,7 +4292,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
storeAttributedTextInPasteboard(text)
|
storeAttributedTextInPasteboard(text)
|
||||||
|
|
||||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
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?.dismiss()
|
||||||
self.sendMessageContext.tooltipScreen = undoController
|
self.sendMessageContext.tooltipScreen = undoController
|
||||||
component.controller()?.present(undoController, in: .current)
|
component.controller()?.present(undoController, in: .current)
|
||||||
@ -4750,7 +4750,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
controller?.replace(with: c)
|
controller?.replace(with: c)
|
||||||
}
|
}
|
||||||
component.controller()?.push(controller)
|
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)
|
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),
|
content: .info(title: nil, text: text, timeout: nil, customUndoText: nil),
|
||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
)
|
)
|
||||||
self.sendMessageContext.tooltipScreen = controller
|
self.sendMessageContext.tooltipScreen = controller
|
||||||
@ -5471,7 +5471,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
),
|
),
|
||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { [weak self] action in
|
action: { [weak self] action in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return false
|
return false
|
||||||
@ -6196,7 +6196,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
content: .info(title: nil, text: component.strings.Story_ToastRemovedFromProfileText, timeout: nil, customUndoText: nil),
|
content: .info(title: nil, text: component.strings.Story_ToastRemovedFromProfileText, timeout: nil, customUndoText: nil),
|
||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), nil)
|
), nil)
|
||||||
} else {
|
} 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),
|
content: .info(title: component.strings.Story_ToastSavedToProfileTitle, text: component.strings.Story_ToastSavedToProfileText, timeout: nil, customUndoText: nil),
|
||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), nil)
|
), nil)
|
||||||
}
|
}
|
||||||
@ -6263,7 +6263,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
content: .linkCopied(title: nil, text: component.strings.Story_ToastLinkCopied),
|
content: .linkCopied(title: nil, text: component.strings.Story_ToastLinkCopied),
|
||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), nil)
|
), 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),
|
content: .info(title: nil, text: isGroup ? presentationData.strings.Story_ToastRemovedFromGroupText : presentationData.strings.Story_ToastRemovedFromChannelText, timeout: nil, customUndoText: nil),
|
||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
)
|
)
|
||||||
} else {
|
} 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),
|
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,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), nil)
|
), nil)
|
||||||
}
|
}
|
||||||
@ -6476,7 +6476,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
content: .linkCopied(title: nil, text: component.strings.Story_ToastLinkCopied),
|
content: .linkCopied(title: nil, text: component.strings.Story_ToastLinkCopied),
|
||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), nil)
|
), 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),
|
], title: nil, text: component.strings.StoryFeed_TooltipNotifyOn(component.slice.effectivePeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string, customUndoText: nil, timeout: nil),
|
||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), nil)
|
), nil)
|
||||||
} else {
|
} 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),
|
], title: nil, text: component.strings.StoryFeed_TooltipNotifyOff(component.slice.effectivePeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string, customUndoText: nil, timeout: nil),
|
||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), nil)
|
), nil)
|
||||||
}
|
}
|
||||||
@ -6775,7 +6775,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
content: .linkCopied(title: nil, text: component.strings.Story_ToastLinkCopied),
|
content: .linkCopied(title: nil, text: component.strings.Story_ToastLinkCopied),
|
||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), nil)
|
), nil)
|
||||||
}
|
}
|
||||||
@ -6819,7 +6819,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
content: .info(title: title, text: text, timeout: nil, customUndoText: nil),
|
content: .info(title: title, text: text, timeout: nil, customUndoText: nil),
|
||||||
elevatedLayout: false,
|
elevatedLayout: false,
|
||||||
animateInAsReplacement: false,
|
animateInAsReplacement: false,
|
||||||
blurred: true,
|
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||||
action: { _ in return false }
|
action: { _ in return false }
|
||||||
), in: .current)
|
), in: .current)
|
||||||
|
|
||||||
@ -6995,44 +6995,6 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
},
|
},
|
||||||
requestSelectMessages: nil
|
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
|
return true
|
||||||
case .starGift, .starGiftUnique:
|
case .starGift, .starGiftUnique:
|
||||||
let controller = strongSelf.context.sharedContext.makeGiftViewScreen(context: strongSelf.context, message: EngineMessage(message), shareStory: { [weak self] in
|
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) {
|
Queue.mainQueue().after(0.15) {
|
||||||
let controller = self.context.sharedContext.makeStorySharingScreen(context: self.context, subject: .gift(uniqueGift), parentController: self)
|
let controller = self.context.sharedContext.makeStorySharingScreen(context: self.context, subject: .gift(uniqueGift), parentController: self)
|
||||||
self.push(controller)
|
self.push(controller)
|
||||||
|
@ -1241,6 +1241,69 @@ extension ChatControllerImpl {
|
|||||||
controller.legacyCompletion = { signals, silently, scheduleTime, parameters, getAnimatedTransitionSource, sendCompletion in
|
controller.legacyCompletion = { signals, silently, scheduleTime, parameters, getAnimatedTransitionSource, sendCompletion in
|
||||||
completion(signals, silently, scheduleTime, parameters, getAnimatedTransitionSource, sendCompletion)
|
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)
|
present(controller, mediaPickerContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1123,7 +1123,15 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
|
|
||||||
if data.messageActions.options.contains(.sendGift) {
|
if data.messageActions.options.contains(.sendGift) {
|
||||||
let sendGiftTitle: String
|
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 ?? ""
|
let peerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? ""
|
||||||
sendGiftTitle = chatPresentationInterfaceState.strings.Conversation_ContextMenuSendGiftTo(peerName).string
|
sendGiftTitle = chatPresentationInterfaceState.strings.Conversation_ContextMenuSendGiftTo(peerName).string
|
||||||
} else {
|
} else {
|
||||||
|
@ -2450,26 +2450,26 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
presentExportAlertImpl = { [weak controller] in
|
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
|
return
|
||||||
}
|
}
|
||||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
let title: String
|
if currentTime > canExportDate || "".isEmpty {
|
||||||
let text: String
|
let alertController = giftWithdrawAlertController(context: context, gift: gift, commit: {
|
||||||
if currentTime > canExportDate {
|
|
||||||
title = presentationData.strings.Gift_Transfer_UpdateRequired_Title
|
})
|
||||||
text = presentationData.strings.Gift_Transfer_UpdateRequired_Text
|
controller.present(alertController, in: .window(.root))
|
||||||
} else {
|
} else {
|
||||||
let delta = canExportDate - currentTime
|
let delta = canExportDate - currentTime
|
||||||
let days: Int32 = Int32(ceil(Float(delta) / 86400.0))
|
let days: Int32 = Int32(ceil(Float(delta) / 86400.0))
|
||||||
let daysString = presentationData.strings.Gift_Transfer_UnlockPending_Text_Days(days)
|
let daysString = presentationData.strings.Gift_Transfer_UnlockPending_Text_Days(days)
|
||||||
title = presentationData.strings.Gift_Transfer_UnlockPending_Title
|
let title = presentationData.strings.Gift_Transfer_UnlockPending_Title
|
||||||
text = presentationData.strings.Gift_Transfer_UnlockPending_Text(daysString).string
|
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
|
presentTransferAlertImpl = { [weak controller] peer in
|
||||||
|
@ -82,6 +82,18 @@ public final class UndoOverlayController: ViewController {
|
|||||||
case bottom
|
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
|
private let presentationData: PresentationData
|
||||||
public var content: UndoOverlayContent {
|
public var content: UndoOverlayContent {
|
||||||
didSet {
|
didSet {
|
||||||
@ -94,7 +106,7 @@ public final class UndoOverlayController: ViewController {
|
|||||||
private var action: (UndoOverlayAction) -> Bool
|
private var action: (UndoOverlayAction) -> Bool
|
||||||
private let additionalView: (() -> UndoOverlayControllerAdditionalView?)?
|
private let additionalView: (() -> UndoOverlayControllerAdditionalView?)?
|
||||||
|
|
||||||
private let blurred: Bool
|
private let appearance: Appearance?
|
||||||
private var didPlayPresentationAnimation = false
|
private var didPlayPresentationAnimation = false
|
||||||
private var dismissed = false
|
private var dismissed = false
|
||||||
|
|
||||||
@ -102,13 +114,13 @@ public final class UndoOverlayController: ViewController {
|
|||||||
|
|
||||||
public var tag: Any?
|
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.presentationData = presentationData
|
||||||
self.content = content
|
self.content = content
|
||||||
self.elevatedLayout = elevatedLayout
|
self.elevatedLayout = elevatedLayout
|
||||||
self.position = position
|
self.position = position
|
||||||
self.animateInAsReplacement = animateInAsReplacement
|
self.animateInAsReplacement = animateInAsReplacement
|
||||||
self.blurred = blurred
|
self.appearance = appearance
|
||||||
self.action = action
|
self.action = action
|
||||||
self.additionalView = additionalView
|
self.additionalView = additionalView
|
||||||
|
|
||||||
@ -122,7 +134,7 @@ public final class UndoOverlayController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
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
|
return self?.action(value) ?? false
|
||||||
}, dismiss: { [weak self] in
|
}, dismiss: { [weak self] in
|
||||||
self?.dismiss()
|
self?.dismiss()
|
||||||
|
@ -59,7 +59,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
private let dismiss: () -> Void
|
private let dismiss: () -> Void
|
||||||
|
|
||||||
private var content: UndoOverlayContent
|
private var content: UndoOverlayContent
|
||||||
private let blurred: Bool
|
private let appearance: UndoOverlayController.Appearance?
|
||||||
|
|
||||||
private let additionalView: UndoOverlayControllerAdditionalView?
|
private let additionalView: UndoOverlayControllerAdditionalView?
|
||||||
|
|
||||||
@ -77,11 +77,11 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
private var fetchResourceDisposable: Disposable?
|
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.presentationData = presentationData
|
||||||
self.elevatedLayout = elevatedLayout
|
self.elevatedLayout = elevatedLayout
|
||||||
self.placementPosition = placementPosition
|
self.placementPosition = placementPosition
|
||||||
self.blurred = blurred
|
self.appearance = appearance
|
||||||
self.content = content
|
self.content = content
|
||||||
|
|
||||||
self.additionalView = additionalView?()
|
self.additionalView = additionalView?()
|
||||||
@ -1544,7 +1544,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
self.undoButtonNode = HighlightTrackingButtonNode()
|
self.undoButtonNode = HighlightTrackingButtonNode()
|
||||||
|
|
||||||
self.panelNode = ASDisplayNode()
|
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
|
self.panelNode.backgroundColor = presentationData.theme.rootController.tabBar.backgroundColor
|
||||||
} else {
|
} else {
|
||||||
self.panelNode.backgroundColor = .clear
|
self.panelNode.backgroundColor = .clear
|
||||||
@ -1904,7 +1904,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
let rightInset: CGFloat = 16.0
|
let rightInset: CGFloat = 16.0
|
||||||
var contentHeight: CGFloat = 20.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 leftMargin = margin + layout.insets(options: []).left
|
||||||
|
|
||||||
let buttonTextSize = self.undoButtonTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude))
|
let buttonTextSize = self.undoButtonTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude))
|
||||||
@ -1964,7 +1964,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
case .top:
|
case .top:
|
||||||
break
|
break
|
||||||
case .bottom:
|
case .bottom:
|
||||||
if self.elevatedLayout {
|
if let bottomInset = self.appearance?.bottomInset {
|
||||||
|
insets.bottom += bottomInset
|
||||||
|
} else if self.elevatedLayout {
|
||||||
insets.bottom += 49.0
|
insets.bottom += 49.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user