Various improvements

This commit is contained in:
Ilya Laktyushin 2025-01-20 05:46:17 +04:00
parent 575eb2ca5f
commit c73f24f5f2
18 changed files with 454 additions and 424 deletions

View File

@ -13715,6 +13715,7 @@ Sorry for the inconvenience.";
"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.";
"PeerInfo.Gifts.ChannelNotifyDisabledTooltip" = "You will not receive a message from Telegram when your channel receives a gift.";
"Notification.StarsGift.Channel.Sent" = "%1$@ sent a gift to %2$@ for %3$@";

View File

@ -87,7 +87,7 @@ public func calculateAvatarColors(context: AccountContext?, explicitColorIndex:
}
let colors: [UIColor]
if icon != .none && icon != .cameraIcon {
if icon != .none {
if case .deletedIcon = icon {
colors = AvatarNode.grayscaleColors
} else if case .phoneIcon = icon {
@ -120,6 +120,8 @@ public func calculateAvatarColors(context: AccountContext?, explicitColorIndex:
backgroundColors = theme.chatList.pinnedArchiveAvatarColor.backgroundColors.colors
}
colors = [backgroundColors.1, backgroundColors.0]
} else if case .cameraIcon = icon {
colors = AvatarNode.repostColors
} else {
colors = AvatarNode.grayscaleColors
}
@ -288,7 +290,7 @@ public final class AvatarNode: ASDisplayNode {
]
static let repostColors: [UIColor] = [
UIColor(rgb: 0x34C76F), UIColor(rgb: 0x3DA1FD)
UIColor(rgb: 0x3DA1FD), UIColor(rgb: 0x34C76F)
]
public final class ContentNode: ASDisplayNode {

View File

@ -68,15 +68,15 @@ public struct GridNodeLayout: Equatable {
public let scrollIndicatorInsets: UIEdgeInsets?
public let preloadSize: CGFloat
public let type: GridNodeLayoutType
public let cutout: CGRect?
public let cutouts: [CGRect]
public init(size: CGSize, insets: UIEdgeInsets, scrollIndicatorInsets: UIEdgeInsets? = nil, preloadSize: CGFloat, type: GridNodeLayoutType, cutout: CGRect? = nil) {
public init(size: CGSize, insets: UIEdgeInsets, scrollIndicatorInsets: UIEdgeInsets? = nil, preloadSize: CGFloat, type: GridNodeLayoutType, cutouts: [CGRect] = []) {
self.size = size
self.insets = insets
self.scrollIndicatorInsets = scrollIndicatorInsets
self.preloadSize = preloadSize
self.type = type
self.cutout = cutout
self.cutouts = cutouts
}
}
@ -568,8 +568,12 @@ open class GridNode: GridNodeScroller, ASScrollViewDelegate {
}
}
if let cutout = self.gridLayout.cutout, cutout.intersects(CGRect(origin: nextItemOrigin, size: itemSize)) {
nextItemOrigin.x += cutout.width + itemSpacing
if !self.gridLayout.cutouts.isEmpty, nextItemOrigin.y < itemSize.height * 3.0 {
for cutout in self.gridLayout.cutouts {
if cutout.intersects(CGRect(origin: nextItemOrigin, size: itemSize)) {
nextItemOrigin.x += cutout.width + itemSpacing
}
}
}
if !incrementedCurrentRow {

View File

@ -26,7 +26,7 @@ typedef void (^TGMediaAvatarPresentImpl)(id<LegacyComponentsContext>, void (^)(U
@property (nonatomic, copy) void (^requestSearchController)(TGMediaAssetsController *);
@property (nonatomic, copy) CGRect (^sourceRect)(void);
@property (nonatomic, copy) void (^requestAvatarEditor)(void (^)(UIImage *image, void(^commit)(void)), void (^)(UIImage *image, NSURL *asset, TGVideoEditAdjustments *adjustments, void(^commit)(void)));
@property (nonatomic, copy) void (^requestAvatarEditor)(void (^)(UIImage *image, void(^commit)(void)), void (^)(UIImage *image, NSURL *asset, id adjustments, id markup, void(^commit)(void)));
@property (nonatomic, strong) id<TGPhotoPaintStickersContext> stickersContext;

View File

@ -193,60 +193,60 @@
}];
[itemViews addObject:galleryItem];
if (!_signup) {
TGMenuSheetButtonItemView *viewItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"ProfilePhoto.SetEmoji") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
{
__strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
if (strongSelf != nil && strongSelf.requestAvatarEditor) {
strongSelf.requestAvatarEditor(^(UIImage *image, void (^commit)(void)) {
__strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (strongSelf.willFinishWithImage != nil) {
strongSelf.willFinishWithImage(image, ^{
if (strongSelf.didFinishWithImage != nil)
strongSelf.didFinishWithImage(image);
commit();
});
} else {
if (strongSelf.didFinishWithImage != nil)
strongSelf.didFinishWithImage(image);
commit();
}
}, ^(UIImage *image, NSURL *asset, TGVideoEditAdjustments *adjustments, void (^commit)(void)) {
__strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (strongSelf.willFinishWithVideo != nil) {
strongSelf.willFinishWithVideo(image, ^{
if (strongSelf.didFinishWithVideo != nil)
strongSelf.didFinishWithVideo(image, asset, adjustments);
commit();
});
} else {
if (strongSelf.didFinishWithVideo != nil)
strongSelf.didFinishWithVideo(image, asset, adjustments);
commit();
}
});
}
}];
[itemViews addObject:viewItem];
}
// if (!_signup) {
// TGMenuSheetButtonItemView *viewItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"ProfilePhoto.SetEmoji") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
// {
// __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;
// if (strongSelf == nil)
// return;
//
// __strong TGMenuSheetController *strongController = weakController;
// if (strongController == nil)
// return;
//
// [strongController dismissAnimated:true];
// if (strongSelf != nil && strongSelf.requestAvatarEditor) {
// strongSelf.requestAvatarEditor(^(UIImage *image, void (^commit)(void)) {
// __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;
// if (strongSelf == nil)
// return;
//
// if (strongSelf.willFinishWithImage != nil) {
// strongSelf.willFinishWithImage(image, ^{
// if (strongSelf.didFinishWithImage != nil)
// strongSelf.didFinishWithImage(image);
//
// commit();
// });
// } else {
// if (strongSelf.didFinishWithImage != nil)
// strongSelf.didFinishWithImage(image);
//
// commit();
// }
// }, ^(UIImage *image, NSURL *asset, id adjustments, id markup, void (^commit)(void)) {
// __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf;
// if (strongSelf == nil)
// return;
//
// if (strongSelf.willFinishWithVideo != nil) {
// strongSelf.willFinishWithVideo(image, ^{
// if (strongSelf.didFinishWithVideo != nil)
// strongSelf.didFinishWithVideo(image, asset, adjustments);
//
// commit();
// });
// } else {
// if (strongSelf.didFinishWithVideo != nil)
// strongSelf.didFinishWithVideo(image, asset, adjustments);
//
// commit();
// }
// });
// }
// }];
// [itemViews addObject:viewItem];
// }
// if (_hasSearchButton)
// {

View File

@ -1546,7 +1546,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
let itemSpacing: CGFloat = 1.0
let itemWidth = floorToScreenPixels((width - itemSpacing * CGFloat(itemsPerRow - 1)) / CGFloat(itemsPerRow))
var cutoutRect: CGRect?
var cutoutRects: [CGRect] = []
var cameraRect: CGRect? = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: 0.0), size: CGSize(width: itemWidth, height: itemWidth * 2.0 + 1.0))
if self.cameraView == nil && self.modernCameraView == nil {
cameraRect = nil
@ -1647,9 +1647,11 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: bounds.height)))
cutoutRect = cameraRect
if let cameraRect {
cutoutRects.append(cameraRect)
}
if let _ = self.avatarEditorPreviewView {
cutoutRect = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: 0.0), size: CGSize(width: cameraRect != nil ? itemWidth * 2.0 : itemWidth, height: itemWidth))
cutoutRects.append(CGRect(x: cameraRect != nil ? cameraRect!.maxX + itemSpacing : layout.safeInsets.left, y: 0.0, width: itemWidth, height: itemWidth))
}
var itemHeight = itemWidth
@ -1657,7 +1659,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
itemHeight = floor(itemWidth * 1.227)
}
let preloadSize: CGFloat = itemHeight// * 3.0
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: bounds.size, insets: gridInsets, scrollIndicatorInsets: nil, preloadSize: preloadSize, type: .fixed(itemSize: CGSize(width: itemWidth, height: itemHeight), fillWidth: true, lineSpacing: itemSpacing, itemSpacing: itemSpacing), cutout: cutoutRect), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, updateOpaqueState: nil, synchronousLoads: false), completion: { [weak self] _ in
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: bounds.size, insets: gridInsets, scrollIndicatorInsets: nil, preloadSize: preloadSize, type: .fixed(itemSize: CGSize(width: itemWidth, height: itemHeight), fillWidth: true, lineSpacing: itemSpacing, itemSpacing: itemSpacing), cutouts: cutoutRects), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, updateOpaqueState: nil, synchronousLoads: false), completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
@ -1680,9 +1682,6 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
if let avatarEditorPreviewView = self.avatarEditorPreviewView {
avatarEditorPreviewView.frame = CGRect(origin: CGPoint(x: cameraRect != nil ? cameraRect!.maxX + itemSpacing : layout.safeInsets.left, y: 0.0), size: CGSize(width: itemWidth, height: itemWidth))
avatarEditorPreviewView.updateLayout(size: CGSize(width: itemWidth, height: itemWidth))
if self.gridNode.view.subviews.last !== avatarEditorPreviewView {
self.gridNode.view.bringSubviewToFront(avatarEditorPreviewView)
}
}
if let selectionNode = self.selectionNode, let controller = self.controller {

View File

@ -419,8 +419,6 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
private let centralItemAttributesDisposable = DisposableSet();
public var openAvatarSetup: ((@escaping () -> Void) -> Void)?
public var avatarPhotoEditCompletion: ((UIImage) -> Void)?
public var avatarVideoEditCompletion: ((UIImage, URL, TGVideoEditAdjustments?) -> Void)?
public var removedEntry: ((AvatarGalleryEntry) -> Void)?

View File

@ -815,8 +815,13 @@ func _internal_upgradeStarGift(account: Account, formId: Int64?, reference: Star
case let .updateNewMessage(message, _, _):
if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: false) {
for media in message.media {
if let action = media as? TelegramMediaAction, case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, _, _, _) = action.action, case let .Id(messageId) = message.id {
let _ = messageId
if let action = media as? TelegramMediaAction, case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, peerId, _, savedId) = action.action, case let .Id(messageId) = message.id {
let reference: StarGiftReference
if let peerId, let savedId {
reference = .peer(peerId: peerId, id: savedId)
} else {
reference = .message(messageId: messageId)
}
return .single(ProfileGiftsContext.State.StarGift(
gift: gift,
reference: reference,
@ -1098,6 +1103,14 @@ private final class ProfileGiftsContextImpl {
}
}
func toggleStarGiftsNotifications(enabled: Bool) {
self.actionDisposable.set(
_internal_toggleStarGiftsNotifications(account: self.account, peerId: self.peerId, enabled: enabled).startStrict()
)
self.notificationsEnabled = enabled
self.pushState()
}
private func pushState() {
self._state = ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState, notificationsEnabled: self.notificationsEnabled)
self.stateValue.set(.single(ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState, notificationsEnabled: self.notificationsEnabled)))
@ -1313,7 +1326,7 @@ public final class ProfileGiftsContext {
impl.transferStarGift(prepaid: prepaid, reference: reference, peerId: peerId)
}
}
public func upgradeStarGift(formId: Int64?, reference: StarGiftReference, keepOriginalInfo: Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> {
return Signal { subscriber in
let disposable = MetaDisposable()
@ -1330,6 +1343,12 @@ public final class ProfileGiftsContext {
}
}
public func toggleStarGiftsNotifications(enabled: Bool) {
self.impl.with { impl in
impl.toggleStarGiftsNotifications(enabled: enabled)
}
}
public var currentState: ProfileGiftsContext.State? {
var state: ProfileGiftsContext.State?
self.impl.syncWith { impl in

View File

@ -1464,10 +1464,16 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot
case .giftCode, .stars, .starsGift, .starsChatSubscription, .starGift, .starGiftUpgrade, .starGiftTransfer:
receiptMessageId = nil
}
} else if case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, _, _, _) = action.action, case let .Id(messageId) = message.id {
} else if case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, peerId, _, savedId) = action.action, case let .Id(messageId) = message.id {
let reference: StarGiftReference
if let peerId, let savedId {
reference = .peer(peerId: peerId, id: savedId)
} else {
reference = .message(messageId: messageId)
}
resultGift = ProfileGiftsContext.State.StarGift(
gift: gift,
reference: .message(messageId: messageId),
reference: reference,
fromPeer: nil,
date: message.timestamp,
text: nil,

View File

@ -1374,31 +1374,50 @@ final class AvatarEditorScreenComponent: Component {
}
}
let colors: [NSNumber] = state.selectedBackground.colors.map { Int32(bitPattern: $0) as NSNumber }
let backgroundColors = state.selectedBackground.colors.map { Int32(bitPattern: $0) }
guard let codableEntity = CodableDrawingEntity(entity: entity) else {
return
}
let entitiesData = DrawingEntitiesView.encodeEntitiesData([entity])
let paintingData = TGPaintingData(
drawing: nil,
entitiesData: entitiesData,
image: nil,
stillImage: nil,
hasAnimation: entity.isAnimated,
stickers: []
)
let adjustments = PGPhotoEditorValues(
originalSize: size,
let values = MediaEditorValues(
peerId: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0)),
originalDimensions: PixelDimensions(size),
cropOffset: .zero,
cropRect: CGRect(origin: .zero, size: size),
cropScale: 1.0,
cropRotation: 0.0,
cropOrientation: .up,
cropLockedAspectRatio: 1.0,
cropMirrored: false,
cropMirroring: false,
cropOrientation: nil,
gradientColors: nil,
videoTrimRange: nil,
videoIsMuted: false,
videoIsFullHd: false,
videoIsMirrored: false,
videoVolume: nil,
additionalVideoPath: nil,
additionalVideoIsDual: false,
additionalVideoPosition: nil,
additionalVideoScale: nil,
additionalVideoRotation: nil,
additionalVideoPositionChanges: [],
additionalVideoTrimRange: nil,
additionalVideoOffset: nil,
additionalVideoVolume: nil,
collage: [],
nightTheme: false,
drawing: nil,
maskDrawing: nil,
entities: [codableEntity],
toolValues: [:],
paintingData: paintingData,
sendAsGif: true
audioTrack: nil,
audioTrackTrimRange: nil,
audioTrackOffset: nil,
audioTrackVolume: nil,
audioTrackSamples: nil,
collageTrackSamples: nil,
coverImageTimestamp: nil,
qualityPreset: .profileHigh
)
let preset: TGMediaVideoConversionPreset = TGMediaVideoConversionPresetProfileHigh
let combinedImage = generateImage(size, contextGenerator: { size, context in
let bounds = CGRect(origin: .zero, size: size)
@ -1415,12 +1434,19 @@ final class AvatarEditorScreenComponent: Component {
}, opaque: false)!
if entity.isAnimated {
let markup: UploadPeerPhotoMarkup
if stickerPackId != 0 {
controller.videoCompletion(combinedImage, tempUrl, TGVideoEditAdjustments(photoEditorValues: adjustments, preset: preset, stickerPackId: stickerPackId, stickerPackAccessHash: stickerPackAccessHash, documentId: fileId, colors: colors), { [weak controller] in
markup = .sticker(packReference: .id(id: stickerPackId, accessHash: stickerPackAccessHash), fileId: fileId, backgroundColors: backgroundColors)
} else {
markup = .emoji(fileId: fileId, backgroundColors: backgroundColors)
}
if stickerPackId != 0 {
controller.videoCompletion(combinedImage, tempUrl, values, markup, { [weak controller] in
controller?.dismiss()
})
} else {
controller.videoCompletion(combinedImage, tempUrl, TGVideoEditAdjustments(photoEditorValues: adjustments, preset: preset, documentId: fileId, colors: colors), { [weak controller] in
controller.videoCompletion(combinedImage, tempUrl, values, markup, { [weak controller] in
controller?.dismiss()
})
}
@ -1461,7 +1487,7 @@ public final class AvatarEditorScreen: ViewControllerComponentContainer {
}
public var imageCompletion: (UIImage, @escaping () -> Void) -> Void = { _, _ in }
public var videoCompletion: (UIImage, URL, TGVideoEditAdjustments, @escaping () -> Void) -> Void = { _, _, _, _ in }
public var videoCompletion: (UIImage, URL, MediaEditorValues, UploadPeerPhotoMarkup, @escaping () -> Void) -> Void = { _, _, _, _, _ in }
public static func inputData(context: AccountContext, isGroup: Bool) -> Signal<AvatarKeyboardInputData, NoError> {
let emojiItems = EmojiPagerContentComponent.emojiInputData(

View File

@ -3959,6 +3959,9 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
return
}
if gestureRecognizer.numberOfTouches == 2, let subject = self.subject, !self.entitiesView.hasSelection {
if case .avatarEditor = self.controller?.mode {
return
}
switch subject {
case .message, .gift:
return
@ -3979,6 +3982,9 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
return
}
if gestureRecognizer.numberOfTouches == 2, let subject = self.subject, !self.entitiesView.hasSelection {
if case .avatarEditor = self.controller?.mode {
return
}
switch subject {
case .message, .gift:
return
@ -3999,6 +4005,9 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
return
}
if gestureRecognizer.numberOfTouches == 2, let subject = self.subject, !self.entitiesView.hasSelection {
if case .avatarEditor = self.controller?.mode {
return
}
switch subject {
case .message, .gift:
return
@ -4168,19 +4177,20 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
let sourceAspectRatio = sourceLocalFrame.height / sourceLocalFrame.width
let duration: Double = 0.4
let timingFunction = kCAMediaTimingFunctionSpring
self.previewContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: self.previewContainerView.center, duration: duration, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.previewContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: self.previewContainerView.center, duration: duration, timingFunction: timingFunction, completion: { _ in
completion()
})
self.previewContainerView.layer.animateScale(from: sourceScale, to: 1.0, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
self.previewContainerView.layer.animateBounds(from: CGRect(origin: CGPoint(x: 0.0, y: (self.previewContainerView.bounds.height - self.previewContainerView.bounds.width * sourceAspectRatio) / 2.0), size: CGSize(width: self.previewContainerView.bounds.width, height: self.previewContainerView.bounds.width * sourceAspectRatio)), to: self.previewContainerView.bounds, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
self.previewContainerView.layer.animateBounds(from: CGRect(origin: CGPoint(x: 0.0, y: (self.previewContainerView.bounds.height - self.previewContainerView.bounds.width * sourceAspectRatio) / 2.0), size: CGSize(width: self.previewContainerView.bounds.width, height: self.previewContainerView.bounds.width * sourceAspectRatio)), to: self.previewContainerView.bounds, duration: duration, timingFunction: timingFunction)
self.backgroundDimView.isHidden = false
self.backgroundDimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
self.backgroundDimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.35)
if let componentView = self.componentHost.view {
componentView.layer.animatePosition(from: sourceLocalFrame.center, to: componentView.center, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
componentView.layer.animateScale(from: sourceScale, to: 1.0, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
componentView.layer.animateScale(from: sourceScale, to: 1.0, duration: duration, timingFunction: timingFunction)
componentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
}
@ -5231,7 +5241,9 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
let result = super.hitTest(point, with: event)
if result == self.componentHost.view {
let point = self.view.convert(point, to: self.previewContainerView)
return self.previewContainerView.hitTest(point, with: event)
if let previewResult = self.previewContainerView.hitTest(point, with: event) {
return previewResult
}
}
return result
}
@ -6189,7 +6201,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
fileprivate let transitionOut: (Bool, Bool?) -> TransitionOut?
public var cancelled: (Bool) -> Void = { _ in }
public var completion: (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void = { _, _ in }
public var willComplete: (UIImage?, Bool, @escaping () -> Void) -> Void
public var completion: (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void
public var dismissed: () -> Void = { }
public var willDismiss: () -> Void = { }
public var sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
@ -6222,6 +6235,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
initialLink: (url: String, name: String?)? = nil,
transitionIn: TransitionIn?,
transitionOut: @escaping (Bool, Bool?) -> TransitionOut?,
willComplete: @escaping (UIImage?, Bool, @escaping () -> Void) -> Void = { _, _, commit in commit() },
completion: @escaping (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void
) {
self.context = context
@ -6238,6 +6252,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
self.initialLink = initialLink
self.transitionIn = transitionIn
self.transitionOut = transitionOut
self.willComplete = willComplete
self.completion = completion
self.storiesBlockedPeers = BlockedPeersContext(account: context.account, subject: .stories)
@ -7401,13 +7416,18 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: inputImage, dimensions: storyDimensions, values: mediaEditor.values, time: firstFrameTime, textScale: 2.0, completion: { [weak self] coverImage in
if let self {
Logger.shared.log("MediaEditor", "Completed with video \(videoResult)")
self.completion(MediaEditorScreenImpl.Result(media: .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions), mediaAreas: mediaAreas, caption: caption, coverTimestamp: mediaEditor.values.coverImageTimestamp, options: self.state.privacy, stickers: stickers, randomId: randomId), { [weak self] finished in
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
self?.dismiss()
Queue.mainQueue().justDispatch {
finished()
}
self.willComplete(coverImage, true, { [weak self] in
guard let self else {
return
}
Logger.shared.log("MediaEditor", "Completed with video \(videoResult)")
self.completion(MediaEditorScreenImpl.Result(media: .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions), mediaAreas: mediaAreas, caption: caption, coverTimestamp: mediaEditor.values.coverImageTimestamp, options: self.state.privacy, stickers: stickers, randomId: randomId), { [weak self] finished in
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
self?.dismiss()
Queue.mainQueue().justDispatch {
finished()
}
})
})
})
}
@ -7430,18 +7450,23 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
}
makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: image, dimensions: storyDimensions, outputDimensions: outputDimensions, values: values, time: .zero, textScale: 2.0, completion: { [weak self] resultImage in
if let self, let resultImage {
Logger.shared.log("MediaEditor", "Completed with image \(resultImage)")
self.completion(MediaEditorScreenImpl.Result(media: .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), mediaAreas: mediaAreas, caption: caption, coverTimestamp: nil, options: self.state.privacy, stickers: stickers, randomId: randomId), { [weak self] finished in
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
self?.dismiss()
Queue.mainQueue().justDispatch {
finished()
}
self.willComplete(resultImage, false, { [weak self] in
guard let self else {
return
}
Logger.shared.log("MediaEditor", "Completed with image \(resultImage)")
self.completion(MediaEditorScreenImpl.Result(media: .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), mediaAreas: mediaAreas, caption: caption, coverTimestamp: nil, options: self.state.privacy, stickers: stickers, randomId: randomId), { [weak self] finished in
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
self?.dismiss()
Queue.mainQueue().justDispatch {
finished()
}
})
})
if case let .draft(draft, id) = actualSubject, id == nil {
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: true)
}
})
if case let .draft(draft, id) = actualSubject, id == nil {
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: true)
}
}
})
}

View File

@ -1579,9 +1579,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
hasSavedMessageTags,
isPremiumRequiredForStoryPosting,
starsRevenueContextAndState,
revenueContextAndState
revenueContextAndState,
profileGiftsContext.state
)
|> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium, recommendedChannels, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags, isPremiumRequiredForStoryPosting, starsRevenueContextAndState, revenueContextAndState -> PeerInfoScreenData in
|> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium, recommendedChannels, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags, isPremiumRequiredForStoryPosting, starsRevenueContextAndState, revenueContextAndState, profileGiftsState -> PeerInfoScreenData in
var availablePanes = availablePanes
if let hasStories {
if hasStories {
@ -1605,7 +1606,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
}
if availablePanes != nil, let cachedData = peerView.cachedData as? CachedChannelData {
if let starGiftsCount = cachedData.starGiftsCount, starGiftsCount > 0 {
if (cachedData.starGiftsCount ?? 0) > 0 || (profileGiftsState.count ?? 0) > 0 {
availablePanes?.insert(.gifts, at: hasStories ? 1 : 0)
}
}

View File

@ -9610,6 +9610,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
currentIsVideo = !videoRepresentations.isEmpty
emojiMarkup = emojiMarkupValue
}
let _ = emojiMarkup
let peerId = self.peerId
let _ = (self.context.engine.data.get(
@ -9685,31 +9686,31 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasDeleteButton, hasViewButton: false, personalPhoto: strongSelf.isSettings || strongSelf.isMyProfile, isVideo: currentIsVideo, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: isForum, title: title, isSuggesting: [.custom, .suggest].contains(mode))!
mixin.stickersContext = LegacyPaintStickersContext(context: strongSelf.context)
let _ = strongSelf.currentAvatarMixin.swap(mixin)
var isFromEditor = false
mixin.requestAvatarEditor = { [weak self, weak parentController] imageCompletion, videoCompletion in
guard let strongSelf = self, let imageCompletion, let videoCompletion else {
return
}
let peerType: AvatarEditorScreen.PeerType
if mode == .suggest {
peerType = .suggest
} else if case .legacyGroup = peer {
peerType = .group
} else if case let .channel(channel) = peer {
if case .group = channel.info {
peerType = channel.flags.contains(.isForum) ? .forum : .group
} else {
peerType = .channel
}
} else {
peerType = .user
}
let controller = AvatarEditorScreen(context: strongSelf.context, inputData: keyboardInputData.get(), peerType: peerType, markup: emojiMarkup)
controller.imageCompletion = imageCompletion
controller.videoCompletion = videoCompletion
parentController?.push(controller)
isFromEditor = true
}
let isFromEditor = !"".isEmpty
// mixin.requestAvatarEditor = { [weak self, weak parentController] imageCompletion, videoCompletion in
// guard let strongSelf = self, let imageCompletion, let videoCompletion else {
// return
// }
// let peerType: AvatarEditorScreen.PeerType
// if mode == .suggest {
// peerType = .suggest
// } else if case .legacyGroup = peer {
// peerType = .group
// } else if case let .channel(channel) = peer {
// if case .group = channel.info {
// peerType = channel.flags.contains(.isForum) ? .forum : .group
// } else {
// peerType = .channel
// }
// } else {
// peerType = .user
// }
// let controller = AvatarEditorScreen(context: strongSelf.context, inputData: keyboardInputData.get(), peerType: peerType, markup: emojiMarkup)
// controller.imageCompletion = imageCompletion
// controller.videoCompletion = videoCompletion
// parentController?.push(controller)
// isFromEditor = true
// }
if let confirmationTextPhoto, let confirmationAction {
mixin.willFinishWithImage = { [weak self, weak parentController] image, commit in
@ -9737,12 +9738,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self?.controller?.updateProfilePhoto(image, mode: mode, uploadStatus: nil)
}
}
mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in
if let image = image, let asset = asset {
completion(image)
self?.controller?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: mode)
}
}
// mixin.didFinishWithVideo = { [weak self] image, asset, adjustments, _ in
// if let image = image, let asset = asset {
// completion(image)
// self?.controller?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: mode)
// }
// }
mixin.didFinishWithDelete = {
guard let strongSelf = self else {
return
@ -12238,12 +12239,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
completion()
})
}
galleryController.avatarPhotoEditCompletion = { [weak self] image in
self?.controller?.updateProfilePhoto(image, mode: .generic, uploadStatus: nil)
}
galleryController.avatarVideoEditCompletion = { [weak self] image, asset, adjustments in
self?.controller?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: .generic)
}
galleryController.removedEntry = { [weak self] entry in
if let item = PeerInfoAvatarListItem(entry: entry) {
let _ = self?.headerNode.avatarListNode.listContainerNode.deleteItem(item)

View File

@ -69,7 +69,32 @@ extension PeerInfoScreenImpl {
}
}
let _ = hasDeleteButton
struct ConfirmationAlert {
let title: String
let photoText: String
let videoText: String
let action: String
}
let confirmationAlert: ConfirmationAlert?
switch mode {
case .suggest:
confirmationAlert = ConfirmationAlert(
title: self.presentationData.strings.UserInfo_SuggestPhotoTitle(peer.compactDisplayTitle).string,
photoText: self.presentationData.strings.UserInfo_SuggestPhoto_AlertPhotoText(peer.compactDisplayTitle).string,
videoText: self.presentationData.strings.UserInfo_SuggestPhoto_AlertVideoText(peer.compactDisplayTitle).string,
action: self.presentationData.strings.UserInfo_SuggestPhoto_AlertSuggest
)
case .custom:
confirmationAlert = ConfirmationAlert(
title: self.presentationData.strings.UserInfo_SetCustomPhotoTitle(peer.compactDisplayTitle).string,
photoText: self.presentationData.strings.UserInfo_SetCustomPhoto_AlertPhotoText(peer.compactDisplayTitle, peer.compactDisplayTitle).string,
videoText: self.presentationData.strings.UserInfo_SetCustomPhoto_AlertVideoText(peer.compactDisplayTitle, peer.compactDisplayTitle).string,
action: self.presentationData.strings.UserInfo_SetCustomPhoto_AlertSet
)
default:
confirmationAlert = nil
}
let parentController = (self.context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController as? ViewController
@ -77,6 +102,9 @@ extension PeerInfoScreenImpl {
let mainController = self.context.sharedContext.makeAvatarMediaPickerScreen(context: self.context, getSourceRect: { return nil }, canDelete: hasDeleteButton, performDelete: { [weak self] in
self?.openAvatarRemoval(mode: mode, peer: peer, item: item)
}, completion: { result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in
var resultImage: UIImage?
let uploadStatusPromise = Promise<PeerInfoAvatarUploadStatus>(.progress(0.0))
let subject: Signal<MediaEditorScreenImpl.Subject?, NoError>
if let asset = result as? PHAsset {
subject = .single(.asset(asset))
@ -112,15 +140,21 @@ extension PeerInfoScreenImpl {
peerType = .user
}
let controller = AvatarEditorScreen(context: self.context, inputData: keyboardInputData.get(), peerType: peerType, markup: emojiMarkup)
//controller.imageCompletion = imageCompletion
//controller.videoCompletion = videoCompletion
controller.imageCompletion = { [weak self] image, commit in
resultImage = image
self?.updateProfilePhoto(image, mode: mode, uploadStatus: uploadStatusPromise)
commit()
}
controller.videoCompletion = { [weak self] image, url, values, markup, commit in
resultImage = image
self?.updateProfileVideo(image, asset: url, values: values, markup: markup, mode: mode, uploadStatus: uploadStatusPromise)
commit()
}
parentController?.push(controller)
//isFromEditor = true
return
}
var resultImage: UIImage?
let uploadStatusPromise = Promise<PeerInfoAvatarUploadStatus>(.progress(0.0))
let editorController = MediaEditorScreenImpl(
context: self.context,
mode: .avatarEditor,
@ -153,7 +187,18 @@ extension PeerInfoScreenImpl {
)
}
return nil
}, completion: { [weak self] result, commit in
},
willComplete: { [weak self, weak parentController] image, isVideo, commit in
if let self, let confirmationAlert, let image {
let controller = photoUpdateConfirmationController(context: self.context, peer: peer, image: image, text: isVideo ? confirmationAlert.videoText : confirmationAlert.photoText, doneTitle: confirmationAlert.action, commit: {
commit()
})
parentController?.presentInGlobalOverlay(controller)
} else {
commit()
}
},
completion: { [weak self] result, commit in
switch result.media {
case let .image(image, _):
resultImage = image
@ -161,10 +206,8 @@ extension PeerInfoScreenImpl {
commit({})
case let .video(video, coverImage, values, _, _):
if let coverImage {
let _ = values
//TODO:release
resultImage = coverImage
self?.updateProfileVideo(coverImage, asset: video, adjustments: nil, mode: mode)
self?.updateProfileVideo(coverImage, asset: video, values: values, markup: nil, mode: mode, uploadStatus: uploadStatusPromise)
}
commit({})
default:
@ -415,228 +458,136 @@ extension PeerInfoScreenImpl {
}
}))
}
public func updateProfileVideo(_ image: UIImage, asset: Any?, values: MediaEditorValues?, markup: UploadPeerPhotoMarkup?, mode: PeerInfoAvatarEditingMode, uploadStatus: Promise<PeerInfoAvatarUploadStatus>?) {
var uploadVideo = true
if let _ = markup {
if let data = self.context.currentAppConfiguration.with({ $0 }).data, let uploadVideoValue = data["upload_markup_video"] as? Bool, uploadVideoValue {
uploadVideo = true
} else {
uploadVideo = false
}
}
guard let photoResource = self.setupProfilePhotoUpload(image: image, mode: mode, indefiniteProgress: !uploadVideo) else {
uploadStatus?.set(.single(.done))
return
}
var videoStartTimestamp: Double? = nil
if let values, let coverImageTimestamp = values.coverImageTimestamp, coverImageTimestamp > 0.0 {
videoStartTimestamp = coverImageTimestamp - (values.videoTrimRange?.lowerBound ?? 0.0)
}
// 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
// }
// }
// })
// }
// }))
// }
let account = self.context.account
let context = self.context
let videoResource: Signal<TelegramMediaResource?, UploadPeerPhotoError>
if uploadVideo {
videoResource = Signal { subscriber in
return EmptyDisposable
}
} 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:
uploadStatus?.set(.single(.done))
strongSelf.controllerNode.state = strongSelf.controllerNode.state.withUpdatingAvatar(nil).withAvatarUploadProgress(nil)
case let .progress(value):
uploadStatus?.set(.single(.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 oldUpdateProfileVideo(_ image: UIImage, asset: Any?, adjustments: TGVideoEditAdjustments?, 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 {

View File

@ -411,7 +411,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(CheckComponent(
theme: checkTheme,
size: CGSize(width: 22.0, height: 22.0),
selected: self.notify
selected: self.profileGifts.currentState?.notificationsEnabled ?? false
))),
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))
@ -421,20 +421,23 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
)),
effectAlignment: .center,
action: { [weak self] in
guard let self else {
guard let self, let currentState = self.profileGifts.currentState else {
return
}
self.notify = !self.notify
let enabled = !(currentState.notificationsEnabled ?? false)
self.profileGifts.toggleStarGiftsNotifications(enabled: enabled)
if self.notify {
let controller = UndoOverlayController(
presentationData: presentationData,
content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.PeerInfo_Gifts_ChannelNotifyTooltip, customUndoText: nil, timeout: nil),
appearance: UndoOverlayController.Appearance(bottomInset: 53.0),
action: { _ in return true }
)
self.chatControllerInteraction.presentController(controller, nil)
}
let animation = enabled ? "anim_profileunmute" : "anim_profilemute"
let text = enabled ? presentationData.strings.PeerInfo_Gifts_ChannelNotifyTooltip : presentationData.strings.PeerInfo_Gifts_ChannelNotifyDisabledTooltip
let controller = UndoOverlayController(
presentationData: presentationData,
content: .universal(animation: animation, scale: 0.075, colors: ["__allcolors__": UIColor.white], title: nil, text: text, customUndoText: nil, timeout: nil),
appearance: UndoOverlayController.Appearance(bottomInset: 53.0),
action: { _ in return true }
)
self.chatControllerInteraction.presentController(controller, nil)
self.updateScrolling(transition: .immediate)
},
animateAlpha: false,

View File

@ -1233,10 +1233,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
}
controller.videoCompletion = { [weak self] image, url, adjustments, commit in
controller.videoCompletion = { [weak self] image, url, values, markup, commit in
if let strongSelf = self {
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
settingsController.updateProfileVideo(image, asset: AVURLAsset(url: url), adjustments: adjustments, mode: .accept)
settingsController.updateProfileVideo(image, asset: AVURLAsset(url: url), values: values, markup: markup, mode: .accept, uploadStatus: nil)
commit()
}
}
@ -1271,7 +1271,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}, videoCompletion: { [weak self] image, url, adjustments in
if let strongSelf = self {
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
settingsController.updateProfileVideo(image, asset: AVURLAsset(url: url), adjustments: adjustments, mode: .accept)
settingsController.oldUpdateProfileVideo(image, asset: AVURLAsset(url: url), adjustments: adjustments, mode: .accept)
}
}
})

View File

@ -642,15 +642,15 @@ public func createChannelController(context: AccountContext, mode: CreateChannel
}))
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}
mixin.requestAvatarEditor = { imageCompletion, videoCompletion in
guard let imageCompletion, let videoCompletion else {
return
}
let controller = AvatarEditorScreen(context: context, inputData: keyboardInputData.get(), peerType: .channel, markup: nil)
controller.imageCompletion = imageCompletion
controller.videoCompletion = videoCompletion
pushControllerImpl?(controller)
}
// mixin.requestAvatarEditor = { imageCompletion, videoCompletion in
// guard let imageCompletion, let videoCompletion else {
// return
// }
// let controller = AvatarEditorScreen(context: context, inputData: keyboardInputData.get(), peerType: .channel, markup: nil)
// controller.imageCompletion = imageCompletion
// controller.videoCompletion = videoCompletion
// pushControllerImpl?(controller)
// }
mixin.didFinishWithImage = { image in
if let image = image {
completedChannelPhotoImpl(image)

View File

@ -1061,15 +1061,15 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
}))
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}
mixin.requestAvatarEditor = { imageCompletion, videoCompletion in
guard let imageCompletion, let videoCompletion else {
return
}
let controller = AvatarEditorScreen(context: context, inputData: keyboardInputData.get(), peerType: .group, markup: nil)
controller.imageCompletion = imageCompletion
controller.videoCompletion = videoCompletion
pushImpl?(controller)
}
// mixin.requestAvatarEditor = { imageCompletion, videoCompletion in
// guard let imageCompletion, let videoCompletion else {
// return
// }
// let controller = AvatarEditorScreen(context: context, inputData: keyboardInputData.get(), peerType: .group, markup: nil)
// controller.imageCompletion = imageCompletion
// controller.videoCompletion = videoCompletion
// pushImpl?(controller)
// }
mixin.didFinishWithImage = { image in
if let image = image {
completedGroupPhotoImpl(image)