Avatar fixes

This commit is contained in:
Ilya Laktyushin 2022-12-17 20:24:26 +04:00
parent 2d25f92cbe
commit fe904af993
31 changed files with 303 additions and 115 deletions

View File

@ -8512,7 +8512,7 @@ Sorry for the inconvenience.";
"Common.Paste" = "Paste";
"PhotoEditor.SelectCoverFrameSuggestion" = "Choose a cover for profile video";
"PhotoEditor.SelectCoverFrameSuggestion" = "Choose a cover for this video";
"GroupInfo.TitleMembers_1" = "%@ Member";
"GroupInfo.TitleMembers_any" = "%@ Members";

View File

@ -608,9 +608,7 @@ private struct Line {
}
final class Texture {
#if !targetEnvironment(simulator)
let buffer: MTLBuffer?
#endif
let width: Int
let height: Int
@ -628,6 +626,7 @@ final class Texture {
if #available(iOS 12.0, *) {
#if targetEnvironment(simulator)
self.buffer = nil
let textureDescriptor = MTLTextureDescriptor()
textureDescriptor.textureType = .type2D
textureDescriptor.pixelFormat = .bgra8Unorm

View File

@ -409,13 +409,6 @@ final class PenTool: DrawingElement {
let r = min(limitRange, length)
// var r = limitRange - length
// if r < 0 {
// r = 0
// }
// print(r * lowerer)
return (r * lowerer) + constant
}

View File

@ -978,6 +978,7 @@ public class StickerPickerScreen: ViewController {
var dismissing = false
if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) || (self.isExpanded && bounds.minY.isZero && velocity.y > 1800.0) {
self.controller?.completion(nil)
self.controller?.dismiss(animated: true, completion: nil)
dismissing = true
} else if self.isExpanded {

View File

@ -22,6 +22,7 @@ public class MediaDustNode: ASDisplayNode {
private var isExploding = false
public var revealed: () -> Void = {}
public var tapped: () -> Void = {}
public override init() {
self.emitterNode = ASDisplayNode()
@ -120,6 +121,8 @@ public class MediaDustNode: ASDisplayNode {
return
}
self.tapped()
self.isRevealed = true
self.isExploding = true
@ -255,8 +258,7 @@ public class MediaDustNode: ASDisplayNode {
self.emitter?.birthRate = min(100000.0, square * 0.02)
}
}
public func update(size: CGSize, color: UIColor, transition: ContainedViewLayoutTransition) {
self.currentParams = (size, color)

View File

@ -34,6 +34,7 @@
@property (nonatomic) bool hasSchedule;
@property (nonatomic) bool reminder;
@property (nonatomic) bool forum;
@property (nonatomic) bool isSuggesting;
@property (nonatomic, copy) void (^presentScheduleController)(bool, void (^)(int32_t));
@property (nonatomic, copy) void (^presentTimerController)(void (^)(int32_t));

View File

@ -64,6 +64,10 @@ typedef enum
@property (nonatomic, assign) bool hasSilentPosting;
@property (nonatomic, assign) bool hasSchedule;
@property (nonatomic, assign) bool reminder;
@property (nonatomic, assign) bool forum;
@property (nonatomic, assign) bool isSuggesting;
@property (nonatomic, copy) void (^presentScheduleController)(bool, void (^)(int32_t));
@property (nonatomic, copy) void (^presentTimerController)(void (^)(int32_t));

View File

@ -30,7 +30,7 @@ typedef void (^TGMediaAvatarPresentImpl)(id<LegacyComponentsContext>, void (^)(U
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController hasDeleteButton:(bool)hasDeleteButton saveEditedPhotos:(bool)saveEditedPhotos saveCapturedMedia:(bool)saveCapturedMedia;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController hasDeleteButton:(bool)hasDeleteButton personalPhoto:(bool)personalPhoto saveEditedPhotos:(bool)saveEditedPhotos saveCapturedMedia:(bool)saveCapturedMedia;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController hasSearchButton:(bool)hasSearchButton hasDeleteButton:(bool)hasDeleteButton hasViewButton:(bool)hasViewButton personalPhoto:(bool)personalPhoto isVideo:(bool)isVideo saveEditedPhotos:(bool)saveEditedPhotos saveCapturedMedia:(bool)saveCapturedMedia signup:(bool)signup forum:(bool)forum title:(NSString *)title;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController hasSearchButton:(bool)hasSearchButton hasDeleteButton:(bool)hasDeleteButton hasViewButton:(bool)hasViewButton personalPhoto:(bool)personalPhoto isVideo:(bool)isVideo saveEditedPhotos:(bool)saveEditedPhotos saveCapturedMedia:(bool)saveCapturedMedia signup:(bool)signup forum:(bool)forum title:(NSString *)title isSuggesting:(bool)isSuggesting;
- (TGMenuSheetController *)present;
@end

View File

@ -30,6 +30,10 @@
@property (nonatomic, assign) bool hasSilentPosting;
@property (nonatomic, assign) bool hasSchedule;
@property (nonatomic, assign) bool reminder;
@property (nonatomic, assign) bool forum;
@property (nonatomic, assign) bool isSuggesting;
@property (nonatomic, copy) void (^presentScheduleController)(bool, void (^)(int32_t));
@property (nonatomic, copy) void (^presentTimerController)(void (^)(int32_t));

View File

@ -22,7 +22,8 @@ typedef enum {
TGPhotoEditorControllerWebIntent = (1 << 3),
TGPhotoEditorControllerVideoIntent = (1 << 4),
TGPhotoEditorControllerForumAvatarIntent = (1 << 5),
TGPhotoEditorControllerSuggestedAvatarIntent = (1 << 6)
TGPhotoEditorControllerSuggestedAvatarIntent = (1 << 6),
TGPhotoEditorControllerSuggestingAvatarIntent = (1 << 7)
} TGPhotoEditorControllerIntent;
@interface TGPhotoEditorController : TGOverlayController

View File

@ -915,6 +915,9 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
if (_forum) {
intent |= TGPhotoEditorControllerForumAvatarIntent;
}
if (_isSuggesting) {
intent |= TGPhotoEditorControllerSuggestingAvatarIntent;
}
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:[windowManager context] item:editableItem intent:intent adjustments:nil caption:nil screenImage:thumbnailImage availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
controller.editingContext = _editingContext;

View File

@ -260,6 +260,8 @@
pickerController.hasSilentPosting = strongController.hasSilentPosting;
pickerController.hasSchedule = strongController.hasSchedule;
pickerController.reminder = strongController.reminder;
pickerController.forum = strongController.forum;
pickerController.isSuggesting = strongController.isSuggesting;
pickerController.presentScheduleController = strongController.presentScheduleController;
pickerController.presentTimerController = strongController.presentTimerController;
[strongController pushViewController:pickerController animated:true];
@ -363,6 +365,16 @@
self.pickerController.reminder = reminder;
}
- (void)setForum:(bool)forum {
_forum = forum;
self.pickerController.forum = forum;
}
- (void)setIsSuggesting:(bool)isSuggesting {
_isSuggesting = isSuggesting;
self.pickerController.isSuggesting = isSuggesting;
}
- (void)setPresentScheduleController:(void (^)(bool, void (^)(int32_t)))presentScheduleController {
_presentScheduleController = [presentScheduleController copy];
self.pickerController.presentScheduleController = presentScheduleController;

View File

@ -435,6 +435,14 @@
intent = TGPhotoEditorControllerSignupAvatarIntent;
}
if (self.forum) {
intent |= TGPhotoEditorControllerForumAvatarIntent;
}
if (self.isSuggesting) {
intent |= TGPhotoEditorControllerSuggestingAvatarIntent;
}
id<TGMediaEditableItem> editableItem = asset;
if (asset.type == TGMediaAssetGifType) {
editableItem = [[TGCameraCapturedVideo alloc] initWithAsset:asset livePhoto:false];

View File

@ -28,6 +28,7 @@
bool _isVideo;
bool _forum;
NSString *_title;
bool _isSuggesting;
}
@end
@ -40,10 +41,10 @@
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController hasDeleteButton:(bool)hasDeleteButton personalPhoto:(bool)personalPhoto saveEditedPhotos:(bool)saveEditedPhotos saveCapturedMedia:(bool)saveCapturedMedia
{
return [self initWithContext:context parentController:parentController hasSearchButton:false hasDeleteButton:hasDeleteButton hasViewButton:false personalPhoto:personalPhoto isVideo:false saveEditedPhotos:saveEditedPhotos saveCapturedMedia:saveCapturedMedia signup:false forum:false title:nil];
return [self initWithContext:context parentController:parentController hasSearchButton:false hasDeleteButton:hasDeleteButton hasViewButton:false personalPhoto:personalPhoto isVideo:false saveEditedPhotos:saveEditedPhotos saveCapturedMedia:saveCapturedMedia signup:false forum:false title:nil isSuggesting:false];
}
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController hasSearchButton:(bool)hasSearchButton hasDeleteButton:(bool)hasDeleteButton hasViewButton:(bool)hasViewButton personalPhoto:(bool)personalPhoto isVideo:(bool)isVideo saveEditedPhotos:(bool)saveEditedPhotos saveCapturedMedia:(bool)saveCapturedMedia signup:(bool)signup forum:(bool)forum title:(NSString *)title
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController hasSearchButton:(bool)hasSearchButton hasDeleteButton:(bool)hasDeleteButton hasViewButton:(bool)hasViewButton personalPhoto:(bool)personalPhoto isVideo:(bool)isVideo saveEditedPhotos:(bool)saveEditedPhotos saveCapturedMedia:(bool)saveCapturedMedia signup:(bool)signup forum:(bool)forum title:(NSString *)title isSuggesting:(bool)isSuggesting
{
self = [super init];
if (self != nil)
@ -60,6 +61,7 @@
_signup = signup;
_forum = forum;
_title = title;
_isSuggesting = isSuggesting;
}
return self;
}
@ -99,6 +101,7 @@
}
TGAttachmentCarouselItemView *carouselItem = [[TGAttachmentCarouselItemView alloc] initWithContext:_context camera:true selfPortrait:_personalPhoto forProfilePhoto:true assetType:_signup ? TGMediaAssetPhotoType : TGMediaAssetAnyType saveEditedPhotos:_saveEditedPhotos allowGrouping:false];
carouselItem.isSuggesting = _isSuggesting;
carouselItem.forum = _forum;
carouselItem.stickersContext = _stickersContext;
carouselItem.parentController = _parentController;
@ -456,6 +459,8 @@
TGMediaAssetsController *controller = [TGMediaAssetsController controllerWithContext:context assetGroup:group intent:strongSelf->_signup ? TGMediaAssetsControllerSetSignupProfilePhotoIntent : TGMediaAssetsControllerSetProfilePhotoIntent recipientName:nil saveEditedPhotos:strongSelf->_saveEditedPhotos allowGrouping:false selectionLimit:10];
__weak TGMediaAssetsController *weakController = controller;
controller.stickersContext = _stickersContext;
controller.forum = _forum;
controller.isSuggesting = _isSuggesting;
controller.avatarCompletionBlock = ^(UIImage *resultImage)
{
__strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;

View File

@ -27,7 +27,7 @@
@property (nonatomic, strong) id<TGPhotoPaintStickersContext> stickersContext;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView isForum:(bool)isForum isSuggestion:(bool)isSuggestion senderName:(NSString *)senderName;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView isForum:(bool)isForum isSuggestion:(bool)isSuggestion isSuggesting:(bool)isSuggesting senderName:(NSString *)senderName;
- (void)setImage:(UIImage *)image;
- (void)setSnapshotImage:(UIImage *)snapshotImage;

View File

@ -57,6 +57,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
bool _isForum;
bool _isSuggestion;
bool _isSuggesting;
NSString *_senderName;
}
@ -67,7 +68,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
@implementation TGPhotoAvatarPreviewController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView isForum:(bool)isForum isSuggestion:(bool)isSuggestion senderName:(NSString *)senderName {
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView isForum:(bool)isForum isSuggestion:(bool)isSuggestion isSuggesting:(bool)isSuggesting senderName:(NSString *)senderName {
self = [super initWithContext:context];
if (self != nil)
{
@ -75,6 +76,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
self.previewView = previewView;
_isForum = isForum;
_isSuggestion = isSuggestion;
_isSuggesting = isSuggesting;
_senderName = senderName;
}
return self;
@ -184,7 +186,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_coverLabel.backgroundColor = [UIColor clearColor];
_coverLabel.font = TGSystemFontOfSize(14.0f);
_coverLabel.textColor = [UIColor whiteColor];
_coverLabel.text = _isSuggestion ? TGLocalized(@"PhotoEditor.SelectCoverFrameSuggestion") : TGLocalized(@"PhotoEditor.SelectCoverFrame");
_coverLabel.text = _isSuggesting ? TGLocalized(@"PhotoEditor.SelectCoverFrameSuggestion") : TGLocalized(@"PhotoEditor.SelectCoverFrame");
[_coverLabel sizeToFit];
[_portraitToolsWrapperView addSubview:_coverLabel];
@ -207,7 +209,9 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_subtitleLabel.numberOfLines = 2;
_subtitleLabel.textAlignment = NSTextAlignmentCenter;
_subtitleLabel.text = [NSString stringWithFormat:self.item.isVideo ? TGLocalized(@"Conversation.SuggestedVideoText") : TGLocalized(@"Conversation.SuggestedPhotoText"), _senderName];
[_wrapperView addSubview:_subtitleLabel];
if (!self.item.isVideo) {
[_wrapperView addSubview:_subtitleLabel];
}
if (!self.item.isVideo) {
_cancelButton = [[TGModernButton alloc] init];
@ -215,7 +219,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_cancelButton.titleLabel.font = TGSystemFontOfSize(17.0);
[_cancelButton sizeToFit];
[_cancelButton addTarget:self action:@selector(cancelButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[_wrapperView addSubview:_cancelButton];
if (_stickersContext != nil) {
_doneButton = [_stickersContext solidRoundedButton:self.item.isVideo ? TGLocalized(@"PhotoEditor.SetAsMyVideo") : TGLocalized(@"PhotoEditor.SetAsMyPhoto") action:^{
@ -887,7 +890,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_coverLabel.frame = CGRectMake(floor((_portraitToolsWrapperView.frame.size.width - _coverLabel.frame.size.width) / 2.0), CGRectGetMaxY(_scrubberView.frame) + 6.0, _coverLabel.frame.size.width, _coverLabel.frame.size.height);
_titleLabel.frame = CGRectMake(screenEdges.left + floor((referenceSize.width - _titleLabel.frame.size.width) / 2.0), screenEdges.top + floor((44.0 - _titleLabel.frame.size.height) / 2.0), _titleLabel.frame.size.width, _titleLabel.frame.size.height);
_subtitleLabel.frame = CGRectMake(screenEdges.left + floor((referenceSize.width - _subtitleLabel.frame.size.width) / 2.0), screenEdges.bottom - 56.0 - buttonSize.height - subtitleSize.height - 8.0, subtitleSize.width, subtitleSize.height);
_subtitleLabel.frame = CGRectMake(screenEdges.left + floor((referenceSize.width - _subtitleLabel.frame.size.width) / 2.0), screenEdges.bottom - 56.0 - buttonSize.height - subtitleSize.height - 16.0, subtitleSize.width, subtitleSize.height);
_cancelButton.frame = CGRectMake(screenEdges.left + 16.0, screenEdges.top + floor((44.0 - _cancelButton.frame.size.height) / 2.0), _cancelButton.frame.size.width, _cancelButton.frame.size.height);

View File

@ -1145,6 +1145,11 @@
return _intent & (TGPhotoEditorControllerSuggestedAvatarIntent);
}
- (bool)presentedForSuggestingAvatar
{
return _intent & (TGPhotoEditorControllerSuggestingAvatarIntent);
}
#pragma mark - Transition
- (void)transitionIn
@ -1317,7 +1322,7 @@
TGPhotoEditorBackButton backButtonType = TGPhotoEditorBackButtonCancel;
TGPhotoEditorDoneButton doneButtonType = TGPhotoEditorDoneButtonCheck;
if ([self presentedForSuggestedAvatar]) {
if ([self presentedForSuggestedAvatar] && !_item.isVideo) {
[_portraitToolbarView setCancelDoneButtonsHidden:tab == TGPhotoEditorCropTab animated:!isInitialAppearance];
}
@ -1336,7 +1341,7 @@
{
bool skipInitialTransition = (![self presentedFromCamera] && self.navigationController != nil) || self.skipInitialTransition;
TGPhotoAvatarPreviewController *cropController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView isForum:[self presentedForForumAvatarCreation] isSuggestion:[self presentedForSuggestedAvatar] senderName:self.senderName];
TGPhotoAvatarPreviewController *cropController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView isForum:[self presentedForForumAvatarCreation] isSuggestion:[self presentedForSuggestedAvatar] isSuggesting:[self presentedForSuggestingAvatar] senderName:self.senderName];
cropController.stickersContext = _stickersContext;
cropController.scrubberView = _scrubberView;
cropController.dotImageView = _dotImageView;

View File

@ -545,18 +545,18 @@
if (hidden) {
_cancelButton.modernHighlight = false;
}
_cancelButton.hidden = false;
// _cancelButton.hidden = false;
_doneButton.hidden = false;
[UIView animateWithDuration:0.2f
animations:^
{
_cancelButton.alpha = targetAlpha;
// _cancelButton.alpha = targetAlpha;
_doneButton.alpha = targetAlpha;
} completion:^(__unused BOOL finished)
{
_animatingCancelDoneButtons = false;
_cancelButton.hidden = hidden;
// _cancelButton.hidden = hidden;
_doneButton.hidden = hidden;
if (hidden) {
@ -571,9 +571,9 @@
}
else
{
_cancelButton.alpha = targetAlpha;
// _cancelButton.alpha = targetAlpha;
_doneButton.alpha = targetAlpha;
_cancelButton.hidden = hidden;
// _cancelButton.hidden = hidden;
_doneButton.hidden = hidden;
}
}

View File

@ -17,9 +17,7 @@
id<LegacyComponentsOverlayWindowManager> windowManager = [context makeOverlayWindowManager];
id<TGMediaEditableItem> editableItem;
if (image != nil) {
editableItem = image;
} else if (video != nil) {
if (video != nil) {
if (![video.path.lowercaseString hasSuffix:@".mp4"]) {
NSString *tmpPath = NSTemporaryDirectory();
int64_t fileId = 0;
@ -31,6 +29,8 @@
}
editableItem = [[TGCameraCapturedVideo alloc] initWithURL:video];
} else if (image != nil) {
editableItem = image;
}
void (^present)(UIImage *) = ^(UIImage *screenImage) {

View File

@ -23,7 +23,7 @@ public func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, t
present(legacyController, nil)
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: false, hasDeleteButton: false, hasViewButton: openCurrent != nil, personalPhoto: true, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: signup, forum: false, title: nil)!
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: false, hasDeleteButton: false, hasViewButton: openCurrent != nil, personalPhoto: true, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: signup, forum: false, title: nil, isSuggesting: false)!
let _ = holder.swap(mixin)
mixin.didFinishWithImage = { image in
guard let image = image else {
@ -53,21 +53,36 @@ public func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, t
}
public func legacyAvatarEditor(context: AccountContext, media: AnyMediaReference, transitionView: UIView?, senderName: String? = nil, present: @escaping (ViewController, Any?) -> Void, imageCompletion: @escaping (UIImage) -> Void, videoCompletion: @escaping (UIImage, URL, TGVideoEditAdjustments) -> Void) {
let _ = (fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: media)
|> deliverOnMainQueue).start(next: { (value, isImage) in
guard case let .data(data) = value, data.complete else {
return
let isVideo = !((media.media as? TelegramMediaImage)?.videoRepresentations.isEmpty ?? true)
let imageSignal = fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: media, forceVideo: false)
|> map { (value, _) -> (UIImage?, Bool) in
if case let .data(data) = value, data.complete {
return (UIImage(contentsOfFile: data.path), true)
} else {
return (nil, false)
}
var image: UIImage?
var url: URL?
if let maybeImage = UIImage(contentsOfFile: data.path) {
image = maybeImage
} else if data.complete {
url = URL(fileURLWithPath: data.path)
}
let videoSignal = isVideo ? fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: media, forceVideo: true)
|> map { (value, isImage) -> (URL?, Bool) in
if case let .data(data) = value, data.complete && !isImage {
return (URL(fileURLWithPath: data.path), true)
} else {
return (nil, false)
}
if image == nil && url == nil {
} : .single((nil, true))
let signals = combineLatest(queue: Queue.mainQueue(),
imageSignal,
videoSignal
)
|> filter { image, video in
return image.1 && video.1
}
let _ = signals.start(next: { image, video in
if image.0 == nil && video.0 == nil {
return
}
@ -92,7 +107,7 @@ public func legacyAvatarEditor(context: AccountContext, media: AnyMediaReference
present(legacyController, nil)
TGPhotoVideoEditor.present(with: legacyController.context, parentController: emptyController, image: image, video: url, stickersContext: paintStickersContext, transitionView: transitionView, senderName: senderName, didFinishWithImage: { image in
TGPhotoVideoEditor.present(with: legacyController.context, parentController: emptyController, image: image.0, video: video.0, stickersContext: paintStickersContext, transitionView: transitionView, senderName: senderName, didFinishWithImage: { image in
if let image = image {
imageCompletion(image)
}

View File

@ -518,6 +518,7 @@ private let fadeWidth: CGFloat = 70.0
public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
private let context: AccountContext
private let isSettings: Bool
public var peer: Peer?
public let controlsContainerNode: ASDisplayNode
@ -648,8 +649,9 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
self.playerUpdateTimer = nil
}
public init(context: AccountContext) {
public init(context: AccountContext, isSettings: Bool = false) {
self.context = context
self.isSettings = isSettings
self.contentNode = ASDisplayNode()
@ -780,8 +782,10 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode {
self.controlsContainerNode.addSubnode(self.stripContainerNode)
self.controlsClippingNode.addSubnode(self.controlsContainerNode)
self.controlsClippingOffsetNode.addSubnode(self.controlsClippingNode)
self.stripContainerNode.addSubnode(self.setByYouNode)
self.stripContainerNode.addSubnode(self.setByYouImageNode)
if self.isSettings {
self.stripContainerNode.addSubnode(self.setByYouNode)
self.stripContainerNode.addSubnode(self.setByYouImageNode)
}
self.view.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in
guard let strongSelf = self else {

View File

@ -15,14 +15,17 @@ public enum FetchMediaDataState {
case data(MediaResourceData)
}
public func fetchMediaData(context: AccountContext, postbox: Postbox, userLocation: MediaResourceUserLocation, mediaReference: AnyMediaReference) -> Signal<(FetchMediaDataState, Bool), NoError> {
public func fetchMediaData(context: AccountContext, postbox: Postbox, userLocation: MediaResourceUserLocation, mediaReference: AnyMediaReference, forceVideo: Bool = false) -> Signal<(FetchMediaDataState, Bool), NoError> {
var resource: MediaResource?
var isImage = true
var fileExtension: String?
var userContentType: MediaResourceUserContentType = .other
if let image = mediaReference.media as? TelegramMediaImage {
userContentType = .image
if let representation = largestImageRepresentation(image.representations) {
if let video = image.videoRepresentations.first, forceVideo {
resource = video.resource
isImage = false
} else if let representation = largestImageRepresentation(image.representations) {
resource = representation.resource
}
} else if let file = mediaReference.media as? TelegramMediaFile {

View File

@ -627,8 +627,8 @@ public func privacyAndSecurityController(
hasTwoStepAuth: Bool? = nil,
loginEmailPattern: Signal<String?, NoError>? = nil,
updatedTwoStepAuthData: (() -> Void)? = nil,
requestPublicPhotoSetup: (() -> Void)? = nil,
requestPublicPhotoRemove: (() -> Void)? = nil
requestPublicPhotoSetup: ((@escaping (UIImage?) -> Void) -> Void)? = nil,
requestPublicPhotoRemove: ((@escaping () -> Void) -> Void)? = nil
) -> ViewController {
let statePromise = ValuePromise(PrivacyAndSecurityControllerState(), ignoreRepeated: true)
let stateValue = Atomic(value: PrivacyAndSecurityControllerState())
@ -819,10 +819,10 @@ public func privacyAndSecurityController(
|> deliverOnMainQueue
currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in
if let info = info {
pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .profilePhoto, current: info.profilePhoto, requestPublicPhotoSetup: {
requestPublicPhotoSetup?()
}, requestPublicPhotoRemove: {
requestPublicPhotoRemove?()
pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .profilePhoto, current: info.profilePhoto, requestPublicPhotoSetup: { completion in
requestPublicPhotoSetup?(completion)
}, requestPublicPhotoRemove: { completion in
requestPublicPhotoRemove?(completion)
}, updated: { updated, _, _ in
if let currentInfoDisposable = currentInfoDisposable {
let applySetting: Signal<Void, NoError> = privacySettingsPromise.get()

View File

@ -106,7 +106,7 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
case nobody(PresentationTheme, String, Bool)
case settingInfo(PresentationTheme, String, String)
case setPublicPhoto(PresentationTheme, String)
case removePublicPhoto(PresentationTheme, String, EnginePeer, TelegramMediaImage?)
case removePublicPhoto(PresentationTheme, String, EnginePeer, TelegramMediaImage?, UIImage?)
case publicPhotoInfo(PresentationTheme, String)
case exceptionsHeader(PresentationTheme, String)
case disableFor(PresentationTheme, String, String)
@ -253,8 +253,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
} else {
return false
}
case let .removePublicPhoto(lhsTheme, lhsText, lhsPeer, lhsRep):
if case let .removePublicPhoto(rhsTheme, rhsText, rhsPeer, rhsRep) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsPeer == rhsPeer, lhsRep == rhsRep {
case let .removePublicPhoto(lhsTheme, lhsText, lhsPeer, lhsRep, lhsImage):
if case let .removePublicPhoto(rhsTheme, rhsText, rhsPeer, rhsRep, rhsImage) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsPeer == rhsPeer, lhsRep == rhsRep, lhsImage === rhsImage {
return true
} else {
return false
@ -415,8 +415,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addPhotoIcon(theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
arguments.setPublicPhoto?()
})
case let .removePublicPhoto(_, text, peer, image):
return ItemListPeerActionItem(presentationData: presentationData, icon: nil, iconSignal: peerAvatarCompleteImage(account: arguments.context.account, peer: peer, forceProvidedRepresentation: true, representation: image?.representationForDisplayAtSize(PixelDimensions(width: 28, height: 28)), size: CGSize(width: 28.0, height: 28.0)), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
case let .removePublicPhoto(_, text, peer, image, completeImage):
return ItemListPeerActionItem(presentationData: presentationData, icon: completeImage, iconSignal: completeImage == nil ? peerAvatarCompleteImage(account: arguments.context.account, peer: peer, forceProvidedRepresentation: true, representation: image?.representationForDisplayAtSize(PixelDimensions(width: 28, height: 28)), size: CGSize(width: 28.0, height: 28.0)) : nil, title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
arguments.removePublicPhoto?()
})
case let .publicPhotoInfo(_, text):
@ -498,8 +498,10 @@ private struct SelectivePrivacySettingsControllerState: Equatable {
let callIntegrationAvailable: Bool?
let callIntegrationEnabled: Bool?
let phoneDiscoveryEnabled: Bool?
let uploadedPhoto: UIImage?
init(setting: SelectivePrivacySettingType, enableFor: [PeerId: SelectivePrivacyPeer], disableFor: [PeerId: SelectivePrivacyPeer], saving: Bool, callDataSaving: VoiceCallDataSaving?, callP2PMode: SelectivePrivacySettingType?, callP2PEnableFor: [PeerId: SelectivePrivacyPeer]?, callP2PDisableFor: [PeerId: SelectivePrivacyPeer]?, callIntegrationAvailable: Bool?, callIntegrationEnabled: Bool?, phoneDiscoveryEnabled: Bool?) {
init(setting: SelectivePrivacySettingType, enableFor: [PeerId: SelectivePrivacyPeer], disableFor: [PeerId: SelectivePrivacyPeer], saving: Bool, callDataSaving: VoiceCallDataSaving?, callP2PMode: SelectivePrivacySettingType?, callP2PEnableFor: [PeerId: SelectivePrivacyPeer]?, callP2PDisableFor: [PeerId: SelectivePrivacyPeer]?, callIntegrationAvailable: Bool?, callIntegrationEnabled: Bool?, phoneDiscoveryEnabled: Bool?, uploadedPhoto: UIImage?) {
self.setting = setting
self.enableFor = enableFor
self.disableFor = disableFor
@ -511,6 +513,7 @@ private struct SelectivePrivacySettingsControllerState: Equatable {
self.callIntegrationAvailable = callIntegrationAvailable
self.callIntegrationEnabled = callIntegrationEnabled
self.phoneDiscoveryEnabled = phoneDiscoveryEnabled
self.uploadedPhoto = uploadedPhoto
}
static func ==(lhs: SelectivePrivacySettingsControllerState, rhs: SelectivePrivacySettingsControllerState) -> Bool {
@ -547,44 +550,51 @@ private struct SelectivePrivacySettingsControllerState: Equatable {
if lhs.phoneDiscoveryEnabled != rhs.phoneDiscoveryEnabled {
return false
}
if lhs.uploadedPhoto !== rhs.uploadedPhoto {
return false
}
return true
}
func withUpdatedSetting(_ setting: SelectivePrivacySettingType) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled)
return SelectivePrivacySettingsControllerState(setting: setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled, uploadedPhoto: self.uploadedPhoto)
}
func withUpdatedEnableFor(_ enableFor: [PeerId: SelectivePrivacyPeer]) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled, uploadedPhoto: self.uploadedPhoto)
}
func withUpdatedDisableFor(_ disableFor: [PeerId: SelectivePrivacyPeer]) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled, uploadedPhoto: self.uploadedPhoto)
}
func withUpdatedSaving(_ saving: Bool) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled, uploadedPhoto: self.uploadedPhoto)
}
func withUpdatedCallP2PMode(_ mode: SelectivePrivacySettingType) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: mode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: mode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled, uploadedPhoto: self.uploadedPhoto)
}
func withUpdatedCallP2PEnableFor(_ enableFor: [PeerId: SelectivePrivacyPeer]) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: enableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: enableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled, uploadedPhoto: self.uploadedPhoto)
}
func withUpdatedCallP2PDisableFor(_ disableFor: [PeerId: SelectivePrivacyPeer]) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: disableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: disableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled, uploadedPhoto: self.uploadedPhoto)
}
func withUpdatedCallsIntegrationEnabled(_ enabled: Bool) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: enabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: enabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled, uploadedPhoto: self.uploadedPhoto)
}
func withUpdatedPhoneDiscoveryEnabled(_ phoneDiscoveryEnabled: Bool) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: phoneDiscoveryEnabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: phoneDiscoveryEnabled, uploadedPhoto: self.uploadedPhoto)
}
func withUpdatedUploadedPhoto(_ uploadedPhoto: UIImage?) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled, phoneDiscoveryEnabled: self.phoneDiscoveryEnabled, uploadedPhoto: uploadedPhoto)
}
}
@ -680,7 +690,7 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
if case .profilePhoto = kind, let peer = peer, state.setting != .everybody {
if let publicPhoto = publicPhoto {
entries.append(.setPublicPhoto(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto_UpdatePublicPhoto))
entries.append(.removePublicPhoto(presentationData.theme, !publicPhoto.videoRepresentations.isEmpty ? presentationData.strings.Privacy_ProfilePhoto_RemovePublicVideo : presentationData.strings.Privacy_ProfilePhoto_RemovePublicPhoto, peer, publicPhoto))
entries.append(.removePublicPhoto(presentationData.theme, !publicPhoto.videoRepresentations.isEmpty ? presentationData.strings.Privacy_ProfilePhoto_RemovePublicVideo : presentationData.strings.Privacy_ProfilePhoto_RemovePublicPhoto, peer, publicPhoto, state.uploadedPhoto))
} else {
entries.append(.setPublicPhoto(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto_SetPublicPhoto))
}
@ -738,8 +748,8 @@ func selectivePrivacySettingsController(
phoneDiscoveryEnabled: Bool? = nil,
voipConfiguration: VoipConfiguration? = nil,
callIntegrationAvailable: Bool? = nil,
requestPublicPhotoSetup: (() -> Void)? = nil,
requestPublicPhotoRemove: (() -> Void)? = nil,
requestPublicPhotoSetup: ((@escaping (UIImage?) -> Void) -> Void)? = nil,
requestPublicPhotoRemove: ((@escaping () -> Void) -> Void)? = nil,
updated: @escaping (SelectivePrivacySettings, (SelectivePrivacySettings, VoiceCallSettings)?, Bool?) -> Void
) -> ViewController {
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
@ -771,7 +781,7 @@ func selectivePrivacySettingsController(
}
}
let initialState = SelectivePrivacySettingsControllerState(setting: SelectivePrivacySettingType(current), enableFor: initialEnableFor, disableFor: initialDisableFor, saving: false, callDataSaving: callSettings?.1.dataSaving, callP2PMode: callSettings != nil ? SelectivePrivacySettingType(callSettings!.0) : nil, callP2PEnableFor: initialCallP2PEnableFor, callP2PDisableFor: initialCallP2PDisableFor, callIntegrationAvailable: callIntegrationAvailable, callIntegrationEnabled: callSettings?.1.enableSystemIntegration, phoneDiscoveryEnabled: phoneDiscoveryEnabled)
let initialState = SelectivePrivacySettingsControllerState(setting: SelectivePrivacySettingType(current), enableFor: initialEnableFor, disableFor: initialDisableFor, saving: false, callDataSaving: callSettings?.1.dataSaving, callP2PMode: callSettings != nil ? SelectivePrivacySettingType(callSettings!.0) : nil, callP2PEnableFor: initialCallP2PEnableFor, callP2PDisableFor: initialCallP2PDisableFor, callIntegrationAvailable: callIntegrationAvailable, callIntegrationEnabled: callSettings?.1.enableSystemIntegration, phoneDiscoveryEnabled: phoneDiscoveryEnabled, uploadedPhoto: nil)
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let stateValue = Atomic(value: initialState)
@ -1036,9 +1046,28 @@ func selectivePrivacySettingsController(
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}, setPublicPhoto: {
requestPublicPhotoSetup?()
requestPublicPhotoSetup?({ result in
var result = result
if let image = result {
result = generateImage(CGSize(width: 28.0, height: 28.0), contextGenerator: { size, context in
context.clear(CGRect(origin: .zero, size: size))
context.addPath(CGPath(ellipseIn: CGRect(origin: .zero, size: size), transform: nil))
context.clip()
if let cgImage = image.cgImage {
context.draw(cgImage, in: CGRect(origin: .zero, size: size))
}
}, opaque: false)
}
updateState { state in
return state.withUpdatedUploadedPhoto(result)
}
})
}, removePublicPhoto: {
requestPublicPhotoRemove?()
requestPublicPhotoRemove?({
updateState { state in
return state.withUpdatedUploadedPhoto(nil)
}
})
})
let peer = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
@ -1082,7 +1111,7 @@ func selectivePrivacySettingsController(
title = presentationData.strings.Privacy_VoiceMessages
}
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: selectivePrivacySettingsControllerEntries(presentationData: presentationData, kind: kind, state: state, peerName: peerName ?? "", phoneNumber: phoneNumber, peer: peer, publicPhoto: publicPhoto), style: .blocks, animateChanges: false)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: selectivePrivacySettingsControllerEntries(presentationData: presentationData, kind: kind, state: state, peerName: peerName ?? "", phoneNumber: phoneNumber, peer: peer, publicPhoto: publicPhoto), style: .blocks, animateChanges: true)
return (controllerState, (listState, arguments))
} |> afterDisposed {

View File

@ -6165,7 +6165,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
// return controller
// }
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasPhotos && !fromGallery, hasViewButton: false, personalPhoto: peerId.namespace == Namespaces.Peer.CloudUser, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: false, title: nil)!
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasPhotos && !fromGallery, hasViewButton: false, personalPhoto: peerId.namespace == Namespaces.Peer.CloudUser, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: false, title: nil, isSuggesting: false)!
mixin.forceDark = true
mixin.stickersContext = paintStickersContext
let _ = strongSelf.currentAvatarMixin.swap(mixin)

View File

@ -185,6 +185,9 @@ private class ExtendedMediaOverlayNode: ASDisplayNode {
private var maskView: UIView?
private var maskLayer: CAShapeLayer?
var isRevealed = false
var tapped: () -> Void = {}
override init() {
self.blurredImageNode = TransformImageNode()
@ -252,6 +255,12 @@ private class ExtendedMediaOverlayNode: ASDisplayNode {
self.maskLayer = maskLayer
}
func reveal() {
self.blurredImageNode.removeFromSupernode()
self.dustNode.removeFromSupernode()
self.isUserInteractionEnabled = false
}
func update(size: CGSize, text: String, imageSignal: (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize, CGSize)?, imageFrame: CGRect, corners: ImageCorners?) {
let spacing: CGFloat = 2.0
let padding: CGFloat = 10.0
@ -268,9 +277,13 @@ private class ExtendedMediaOverlayNode: ASDisplayNode {
self.isUserInteractionEnabled = !self.dustNode.isRevealed
self.dustNode.revealed = { [weak self] in
self?.isRevealed = true
self?.blurredImageNode.removeFromSupernode()
self?.isUserInteractionEnabled = false
}
self.dustNode.tapped = { [weak self] in
self?.tapped()
}
} else {
self.blurredImageNode.isHidden = true
self.isUserInteractionEnabled = false
@ -377,23 +390,30 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
var visibilityPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
var visibility: Bool = false {
didSet {
if let videoNode = self.videoNode {
if self.visibility {
if !videoNode.canAttachContent {
videoNode.canAttachContent = true
if videoNode.hasAttachedContext {
videoNode.play()
}
}
} else {
videoNode.canAttachContent = false
}
}
self.animatedStickerNode?.visibility = self.visibility
self.visibilityPromise.set(self.visibility)
self.updateVisibility()
}
}
private var internallyVisible = true
private func updateVisibility() {
let visibility = self.visibility && self.internallyVisible
if let videoNode = self.videoNode {
if visibility {
if !videoNode.canAttachContent {
videoNode.canAttachContent = true
if videoNode.hasAttachedContext {
videoNode.play()
}
}
} else {
videoNode.canAttachContent = false
}
}
self.animatedStickerNode?.visibility = visibility
self.visibilityPromise.set(visibility)
}
var activateLocalContent: (InteractiveMediaNodeActivateContent) -> Void = { _ in }
var activatePinch: ((PinchSourceContainerNode) -> Void)?
var updateMessageReaction: ((Message, ChatControllerInteractionReaction) -> Void)?
@ -1034,6 +1054,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
updateImageSignal = { synchronousLoad, _ in
return mediaGridMessageVideo(postbox: context.account.postbox, userLocation: .peer(message.id.peerId), videoReference: .message(message: MessageReference(message), media: file), onlyFullSize: currentMedia?.id?.namespace == Namespaces.Media.LocalFile, autoFetchFullSizeThumbnail: true)
}
updateBlurredImageSignal = { synchronousLoad, _ in
return chatSecretMessageVideo(account: context.account, userLocation: .peer(message.id.peerId), videoReference: .message(message: MessageReference(message), media: file))
}
}
}
@ -1319,6 +1342,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
}
}
if message.attributes.contains(where: { $0 is MediaSpoilerMessageAttribute }), strongSelf.extendedMediaOverlayNode == nil {
strongSelf.internallyVisible = false
}
if let videoNode = strongSelf.videoNode {
if !(replaceVideoNode ?? false), let decoration = videoNode.decoration as? ChatBubbleVideoDecoration, decoration.corners != corners {
decoration.updateCorners(corners)
@ -1327,7 +1355,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
videoNode.updateLayout(size: arguments.drawingSize, transition: .immediate)
videoNode.frame = CGRect(origin: CGPoint(), size: imageFrame.size)
if strongSelf.visibility {
if strongSelf.visibility && strongSelf.internallyVisible {
if !videoNode.canAttachContent {
videoNode.canAttachContent = true
if videoNode.hasAttachedContext {
@ -1892,6 +1920,10 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
if displaySpoiler {
if self.extendedMediaOverlayNode == nil {
let extendedMediaOverlayNode = ExtendedMediaOverlayNode()
extendedMediaOverlayNode.tapped = { [weak self] in
self?.internallyVisible = true
self?.updateVisibility()
}
self.extendedMediaOverlayNode = extendedMediaOverlayNode
self.pinchContainerNode.contentNode.insertSubnode(extendedMediaOverlayNode, aboveSubnode: self.imageNode)
}
@ -1979,6 +2011,12 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
}
func updateIsHidden(_ isHidden: Bool) {
if isHidden && !self.internallyVisible {
self.internallyVisible = true
self.updateVisibility()
self.extendedMediaOverlayNode?.reveal()
}
if let badgeNode = self.badgeNode, badgeNode.isHidden != isHidden {
if isHidden {
badgeNode.isHidden = true

View File

@ -14,6 +14,9 @@ import TelegramStringFormatting
import WallpaperBackgroundNode
import ReactionSelectionNode
import PhotoResources
import UniversalMediaPlayer
import TelegramUniversalVideoContent
import GalleryUI
class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode {
private var mediaBackgroundContent: WallpaperBubbleBackgroundNode?
@ -22,6 +25,10 @@ class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode
private let subtitleNode: TextNode
private let imageNode: TransformImageNode
fileprivate var videoNode: UniversalVideoNode?
private var videoContent: NativeVideoContent?
private var videoStartTimestamp: Double?
private let buttonNode: HighlightTrackingButtonNode
private let buttonStarsNode: PremiumStarsNode
private let buttonTitleNode: TextNode
@ -195,6 +202,7 @@ class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode
if let strongSelf = self {
strongSelf.item = item
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - imageSize.width) / 2.0), y: 13.0), size: imageSize)
if let photo = photo {
if mediaUpdated {
strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(context: item.context, userLocation: .peer(item.message.id.peerId), photoReference: .message(message: MessageReference(item.message), media: photo), displayAtSize: nil, storeToDownloadsPeerType: nil).start())
@ -207,11 +215,49 @@ class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleContentNode
let apply = makeImageLayout(arguments)
apply()
strongSelf.imageNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - imageSize.width) / 2.0), y: 13.0), size: imageSize)
strongSelf.imageNode.frame = imageFrame
}
if let photo = photo, let video = photo.videoRepresentations.last, let id = photo.id?.id {
let videoFileReference = FileMediaReference.message(message: MessageReference(item.message), media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: photo.representations, videoThumbnails: [], immediateThumbnailData: photo.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id, "action"), userLocation: .peer(item.message.id.peerId), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
if videoContent.id != strongSelf.videoContent?.id {
let mediaManager = item.context.sharedContext.mediaManager
let videoNode = UniversalVideoNode(postbox: item.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .secondaryOverlay)
videoNode.isUserInteractionEnabled = false
videoNode.ownsContentNodeUpdated = { [weak self] owns in
if let strongSelf = self {
strongSelf.videoNode?.isHidden = !owns
}
}
strongSelf.videoContent = videoContent
strongSelf.videoNode = videoNode
videoNode.updateLayout(size: imageSize, transition: .immediate)
videoNode.frame = imageFrame
videoNode.clipsToBounds = true
videoNode.cornerRadius = imageFrame.width / 2.0
strongSelf.addSubnode(videoNode)
videoNode.canAttachContent = true
if let videoStartTimestamp = video.startTimestamp {
videoNode.seek(videoStartTimestamp)
} else {
videoNode.seek(0.0)
}
videoNode.play()
}
} else if let videoNode = strongSelf.videoNode {
strongSelf.videoContent = nil
strongSelf.videoNode = nil
videoNode.removeFromSupernode()
}
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - width) / 2.0), y: 0.0), size: backgroundSize)
let mediaBackgroundFrame = imageFrame.insetBy(dx: -2.0, dy: -2.0)
let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - width) / 2.0), y: 0.0), size: backgroundSize)
let mediaBackgroundFrame = backgroundFrame.insetBy(dx: -2.0, dy: -2.0)
strongSelf.mediaBackgroundNode.frame = mediaBackgroundFrame
strongSelf.mediaBackgroundNode.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate)

View File

@ -454,7 +454,7 @@ public func createChannelController(context: AccountContext) -> ViewController {
}
}
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: false, title: nil)!
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: false, title: nil, isSuggesting: false)!
let _ = currentAvatarMixin.swap(mixin)
mixin.requestSearchController = { assetsController in
let controller = WebSearchController(context: context, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in

View File

@ -727,7 +727,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
}
}
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: false, title: nil)!
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: false, title: nil, isSuggesting: false)!
let _ = currentAvatarMixin.swap(mixin)
mixin.requestSearchController = { assetsController in
let controller = WebSearchController(context: context, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in

View File

@ -919,7 +919,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
self.avatarContainerNode = PeerInfoAvatarTransformContainerNode(context: context)
self.listContainerTransformNode = ASDisplayNode()
self.listContainerNode = PeerInfoAvatarListContainerNode(context: context)
self.listContainerNode = PeerInfoAvatarListContainerNode(context: context, isSettings: isSettings)
self.listContainerNode.clipsToBounds = true
self.listContainerNode.isHidden = true

View File

@ -2878,7 +2878,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
let galleryController = AvatarGalleryController(context: strongSelf.context, peer: peer, sourceCorners: .round, remoteEntries: entriesPromise, skipInitial: true, centralEntryIndex: centralEntry.flatMap { entries.firstIndex(of: $0) }, replaceRootController: { controller, ready in
})
galleryController.openAvatarSetup = { [weak self] completion in
self?.openAvatarForEditing(fromGallery: true, completion: completion)
self?.openAvatarForEditing(fromGallery: true, completion: { _ in
completion()
})
}
galleryController.avatarPhotoEditCompletion = { [weak self] image in
self?.updateProfilePhoto(image, mode: .generic)
@ -6810,7 +6812,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
dismissStatus = { [weak statusController] in
statusController?.dismiss()
}
self.controller?.presentInGlobalOverlay(statusController)
if let topController = self.controller?.navigationController?.topViewController as? ViewController {
topController.presentInGlobalOverlay(statusController)
} else {
self.controller?.presentInGlobalOverlay(statusController)
}
}
self.updateAvatarDisposable.set((signal
@ -6976,7 +6982,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
dismissStatus = { [weak statusController] in
statusController?.dismiss()
}
self.controller?.presentInGlobalOverlay(statusController)
if let topController = self.controller?.navigationController?.topViewController as? ViewController {
topController.presentInGlobalOverlay(statusController)
} else {
self.controller?.presentInGlobalOverlay(statusController)
}
}
let peerId = self.peerId
@ -7048,7 +7058,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}))
}
fileprivate func openAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping () -> Void = {}) {
fileprivate func openAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) {
guard let peer = self.data?.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: self.data?.threadData) else {
return
}
@ -7141,7 +7151,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
confirmationAction = nil
}
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasDeleteButton, hasViewButton: false, personalPhoto: strongSelf.isSettings, isVideo: currentIsVideo, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: isForum, title: title)!
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasDeleteButton, hasViewButton: false, personalPhoto: strongSelf.isSettings, isVideo: currentIsVideo, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: isForum, title: title, isSuggesting: mode == .suggest)!
mixin.stickersContext = paintStickersContext
let _ = strongSelf.currentAvatarMixin.swap(mixin)
mixin.requestSearchController = { [weak self] assetsController in
@ -7156,7 +7166,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
(strongSelf.controller?.navigationController?.topViewController as? ViewController)?.push(controller)
if fromGallery {
completion()
completion(nil)
}
}
if let confirmationTextPhoto, let confirmationAction {
@ -7181,13 +7191,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
mixin.didFinishWithImage = { [weak self] image in
if let image = image {
completion()
completion(image)
self?.updateProfilePhoto(image, mode: mode)
}
}
mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in
if let image = image, let asset = asset {
completion()
completion(image)
self?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: mode)
}
}
@ -7214,12 +7224,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
})
}
fileprivate func openAvatarRemoval(mode: PeerInfoAvatarEditingMode, peer: EnginePeer? = nil, item: PeerInfoAvatarListItem? = nil) {
fileprivate func openAvatarRemoval(mode: PeerInfoAvatarEditingMode, peer: EnginePeer? = nil, item: PeerInfoAvatarListItem? = nil, completion: @escaping () -> Void = {}) {
let proceed = { [weak self] in
guard let strongSelf = self else {
return
}
completion()
if let item = item {
strongSelf.deleteProfilePhoto(item)
}
@ -7392,13 +7404,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
)
}
}, requestPublicPhotoSetup: { [weak self] in
}, requestPublicPhotoSetup: { [weak self] completion in
if let strongSelf = self {
strongSelf.openAvatarForEditing(mode: .fallback)
strongSelf.openAvatarForEditing(mode: .fallback, completion: completion)
}
}, requestPublicPhotoRemove: { [weak self] in
}, requestPublicPhotoRemove: { [weak self] completion in
if let strongSelf = self {
strongSelf.openAvatarRemoval(mode: .fallback)
strongSelf.openAvatarRemoval(mode: .fallback, completion: completion)
}
}))
}