Merge commit '790343efa815556bca3c2d09609fbe3eb562faf4' into beta

This commit is contained in:
Ali 2023-04-13 21:16:29 +04:00
commit b00726be03
10 changed files with 142 additions and 27 deletions

View File

@ -9100,7 +9100,7 @@ Sorry for the inconvenience.";
"TextFormat.EditLinkTitle" = "Edit Link"; "TextFormat.EditLinkTitle" = "Edit Link";
"PeerInfo.Username" = "Username"; "PeerInfo.BotLinks" = "Public Links";
"Username.BotLinksOrderInfo" = "Drag and drop links to change the order in which they will be displayed on the bot info page."; "Username.BotLinksOrderInfo" = "Drag and drop links to change the order in which they will be displayed on the bot info page.";
"Wallpaper.ApplyForAll" = "Apply For All Chats"; "Wallpaper.ApplyForAll" = "Apply For All Chats";
@ -9140,6 +9140,10 @@ Sorry for the inconvenience.";
"Username.BotLinkHint" = "This username cannot be edited."; "Username.BotLinkHint" = "This username cannot be edited.";
"Username.BotLinkHintExtended" = "This username cannot be edited. You can acquire additional usernames on [Fragment]()."; "Username.BotLinkHintExtended" = "This username cannot be edited. You can acquire additional usernames on [Fragment]().";
"Username.BotActivateAlertText" = "Do you want to show this link on the bot's info page?";
"Username.BotDeactivateAlertText" = "Do you want to hide this link from the bot's info page?";
"Username.BotActiveLimitReachedError" = "Sorry, you have too many active public links already. Please hide one of the bot's active public links first.";
"PeerInfo.Bot.EditIntro" = "Edit Intro"; "PeerInfo.Bot.EditIntro" = "Edit Intro";
"PeerInfo.Bot.EditCommands" = "Edit Commands"; "PeerInfo.Bot.EditCommands" = "Edit Commands";
"PeerInfo.Bot.ChangeSettings" = "Change Bot Settings"; "PeerInfo.Bot.ChangeSettings" = "Change Bot Settings";
@ -9150,6 +9154,7 @@ Sorry for the inconvenience.";
"WallpaperPreview.ChatBottomText" = "Enjoy the view."; "WallpaperPreview.ChatBottomText" = "Enjoy the view.";
"Conversation.Theme.SetPhotoWallpaper" = "Choose Wallpaper from Photos"; "Conversation.Theme.SetPhotoWallpaper" = "Choose Wallpaper from Photos";
"Conversation.Theme.SetNewPhotoWallpaper" = "Choose a New Wallpaper";
"Conversation.Theme.SetColorWallpaper" = "Set a Color as Wallpaper"; "Conversation.Theme.SetColorWallpaper" = "Set a Color as Wallpaper";
"Conversation.Theme.ChooseWallpaperTitle" = "Choose Wallpaper"; "Conversation.Theme.ChooseWallpaperTitle" = "Choose Wallpaper";
@ -9327,3 +9332,5 @@ Sorry for the inconvenience.";
"ChatList.EmptyListContactsHeader" = "YOUR CONTACTS ON TELEGRAM"; "ChatList.EmptyListContactsHeader" = "YOUR CONTACTS ON TELEGRAM";
"ChatList.EmptyListContactsHeaderHide" = "hide"; "ChatList.EmptyListContactsHeaderHide" = "hide";
"ChatList.EmptyListTooltip" = "Send a message or\nstart a group here."; "ChatList.EmptyListTooltip" = "Send a message or\nstart a group here.";
"Username.BotTitle" = "Public Links";

View File

@ -117,6 +117,12 @@ public final class AnimationNode: ASDisplayNode {
self.animationView()?.currentProgress = progress self.animationView()?.currentProgress = progress
} }
public func animate(from: CGFloat, to: CGFloat, completion: @escaping () -> Void) {
self.animationView()?.play(fromProgress: from, toProgress: to, completion: { _ in
completion()
})
}
public func setAnimation(name: String, colors: [String: UIColor]? = nil) { public func setAnimation(name: String, colors: [String: UIColor]? = nil) {
self.currentParams = (name, colors) self.currentParams = (name, colors)
if let url = getAppBundle().url(forResource: name, withExtension: "json"), let animation = Animation.filepath(url.path) { if let url = getAppBundle().url(forResource: name, withExtension: "json"), let animation = Animation.filepath(url.path) {

View File

@ -392,6 +392,16 @@ public extension CALayer {
self.animate(from: NSValue(cgPoint: from), to: NSValue(cgPoint: to), keyPath: "position", timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion) self.animate(from: NSValue(cgPoint: from), to: NSValue(cgPoint: to), keyPath: "position", timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion)
} }
func animateAnchorPoint(from: CGPoint, to: CGPoint, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
if from == to && !force {
if let completion = completion {
completion(true)
}
return
}
self.animate(from: NSValue(cgPoint: from), to: NSValue(cgPoint: to), keyPath: "anchorPoint", timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion)
}
func animateBounds(from: CGRect, to: CGRect, duration: Double, delay: Double = 0.0, timingFunction: String, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) { func animateBounds(from: CGRect, to: CGRect, duration: Double, delay: Double = 0.0, timingFunction: String, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
if from == to && !force { if from == to && !force {
if let completion = completion { if let completion = completion {

View File

@ -423,6 +423,29 @@ public extension ContainedViewLayoutTransition {
} }
} }
func updateAnchorPoint(layer: CALayer, anchorPoint: CGPoint, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
if layer.anchorPoint.equalTo(anchorPoint) && !force {
completion?(true)
} else {
switch self {
case .immediate:
layer.removeAnimation(forKey: "anchorPoint")
layer.anchorPoint = anchorPoint
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousAnchorPoint = layer.anchorPoint
layer.anchorPoint = anchorPoint
layer.animateAnchorPoint(from: previousAnchorPoint, to: anchorPoint, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func animatePosition(layer: CALayer, from fromValue: CGPoint, to toValue: CGPoint, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { func animatePosition(layer: CALayer, from fromValue: CGPoint, to toValue: CGPoint, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
switch self { switch self {
case .immediate: case .immediate:

View File

@ -219,6 +219,11 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode {
self.animationNode?.setAnimation(name: !isNight ? "anim_sun_reverse" : "anim_sun", colors: [:]) self.animationNode?.setAnimation(name: !isNight ? "anim_sun_reverse" : "anim_sun", colors: [:])
self.animationNode?.speed = 1.66 self.animationNode?.speed = 1.66
self.animationNode?.playOnce() self.animationNode?.playOnce()
self.isUserInteractionEnabled = false
Queue.mainQueue().after(0.4) {
self.isUserInteractionEnabled = true
}
} }
var buttonColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.3) { var buttonColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.3) {

View File

@ -488,13 +488,23 @@ public func usernameSetupController(context: AccountContext, mode: UsernameSetup
}, activateLink: { name in }, activateLink: { name in
dismissInputImpl?() dismissInputImpl?()
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Username_ActivateAlertTitle, text: presentationData.strings.Username_ActivateAlertText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Username_ActivateAlertShow, action: { let alertText: String
if case .bot = mode {
alertText = presentationData.strings.Username_BotActivateAlertText
} else {
alertText = presentationData.strings.Username_ActivateAlertText
}
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Username_ActivateAlertTitle, text: alertText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Username_ActivateAlertShow, action: {
let _ = (context.engine.peers.toggleAddressNameActive(domain: domain, name: name, active: true) let _ = (context.engine.peers.toggleAddressNameActive(domain: domain, name: name, active: true)
|> deliverOnMainQueue).start(error: { error in |> deliverOnMainQueue).start(error: { error in
let errorText: String let errorText: String
switch error { switch error {
case .activeLimitReached: case .activeLimitReached:
if case .bot = mode {
errorText = presentationData.strings.Username_BotActiveLimitReachedError
} else {
errorText = presentationData.strings.Username_ActiveLimitReachedError errorText = presentationData.strings.Username_ActiveLimitReachedError
}
default: default:
errorText = presentationData.strings.Login_UnknownError errorText = presentationData.strings.Login_UnknownError
} }
@ -504,7 +514,13 @@ public func usernameSetupController(context: AccountContext, mode: UsernameSetup
}, deactivateLink: { name in }, deactivateLink: { name in
dismissInputImpl?() dismissInputImpl?()
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Username_DeactivateAlertTitle, text: presentationData.strings.Username_DeactivateAlertText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Username_DeactivateAlertHide, action: { let alertText: String
if case .bot = mode {
alertText = presentationData.strings.Username_BotDeactivateAlertText
} else {
alertText = presentationData.strings.Username_DeactivateAlertText
}
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Username_DeactivateAlertTitle, text: alertText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Username_DeactivateAlertHide, action: {
let _ = context.engine.peers.toggleAddressNameActive(domain: domain, name: name, active: false).start() let _ = context.engine.peers.toggleAddressNameActive(domain: domain, name: name, active: false).start()
})]), nil) })]), nil)
}, openAuction: { username in }, openAuction: { username in
@ -577,7 +593,13 @@ public func usernameSetupController(context: AccountContext, mode: UsernameSetup
dismissImpl?() dismissImpl?()
}) })
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Username_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) let title: String
if case .bot = mode {
title = presentationData.strings.Username_BotTitle
} else {
title = presentationData.strings.Username_Title
}
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: usernameSetupControllerEntries(presentationData: presentationData, view: view, state: state, temporaryOrder: temporaryOrder, mode: mode), style: .blocks, focusItemTag: mode == .account ? UsernameEntryTag.username : nil, animateChanges: true) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: usernameSetupControllerEntries(presentationData: presentationData, view: view, state: state, temporaryOrder: temporaryOrder, mode: mode), style: .blocks, focusItemTag: mode == .account ? UsernameEntryTag.username : nil, animateChanges: true)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))

View File

@ -860,12 +860,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let wallpaperPreviewController = WallpaperGalleryController(context: strongSelf.context, source: .wallpaper(wallpaper, nil, [], nil, nil, nil), mode: .peer(EnginePeer(peer), true)) let wallpaperPreviewController = WallpaperGalleryController(context: strongSelf.context, source: .wallpaper(wallpaper, nil, [], nil, nil, nil), mode: .peer(EnginePeer(peer), true))
wallpaperPreviewController.apply = { [weak wallpaperPreviewController] entry, options, _, _, brightness in wallpaperPreviewController.apply = { [weak wallpaperPreviewController] entry, options, _, _, brightness in
var settings: WallpaperSettings? var settings: WallpaperSettings?
if case let .wallpaper(wallpaper, _) = entry, case let .file(file) = wallpaper, !file.isPattern { if case let .wallpaper(wallpaper, _) = entry {
var intensity: Int32? let baseSettings = wallpaper.settings
var intensity: Int32? = baseSettings?.intensity
if case let .file(file) = wallpaper, !file.isPattern {
if let brightness { if let brightness {
intensity = max(0, min(100, Int32(brightness * 100.0))) intensity = max(0, min(100, Int32(brightness * 100.0)))
} }
settings = WallpaperSettings(blur: options.contains(.blur), motion: options.contains(.motion), intensity: intensity) }
settings = WallpaperSettings(blur: options.contains(.blur), motion: options.contains(.motion), colors: baseSettings?.colors ?? [], intensity: intensity, rotation: baseSettings?.rotation)
} }
let _ = (strongSelf.context.engine.themes.setExistingChatWallpaper(messageId: message.id, settings: settings) let _ = (strongSelf.context.engine.themes.setExistingChatWallpaper(messageId: message.id, settings: settings)
|> deliverOnMainQueue).start() |> deliverOnMainQueue).start()

View File

@ -1015,8 +1015,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
var accentButtonTheme = true var accentButtonTheme = true
var otherIsEnabled = false var otherIsEnabled = false
if self.selectedEmoticon?.strippedEmoji == self.initiallySelectedEmoticon?.strippedEmoji { if self.selectedEmoticon?.strippedEmoji == self.initiallySelectedEmoticon?.strippedEmoji {
doneButtonTitle = self.presentationData.strings.Conversation_Theme_SetPhotoWallpaper
otherIsEnabled = self.controller?.canResetWallpaper == true otherIsEnabled = self.controller?.canResetWallpaper == true
doneButtonTitle = otherIsEnabled ? self.presentationData.strings.Conversation_Theme_SetNewPhotoWallpaper : self.presentationData.strings.Conversation_Theme_SetPhotoWallpaper
accentButtonTheme = false accentButtonTheme = false
} else if self.selectedEmoticon == nil && self.initiallySelectedEmoticon != nil { } else if self.selectedEmoticon == nil && self.initiallySelectedEmoticon != nil {
doneButtonTitle = self.presentationData.strings.Conversation_Theme_Reset doneButtonTitle = self.presentationData.strings.Conversation_Theme_Reset

View File

@ -1022,6 +1022,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
final class PeerInfoAvatarListNode: ASDisplayNode { final class PeerInfoAvatarListNode: ASDisplayNode {
private let isSettings: Bool private let isSettings: Bool
let containerNode: ASDisplayNode
let pinchSourceNode: PinchSourceContainerNode let pinchSourceNode: PinchSourceContainerNode
let bottomCoverNode: ASDisplayNode let bottomCoverNode: ASDisplayNode
fileprivate let maskNode: DynamicIslandMaskNode fileprivate let maskNode: DynamicIslandMaskNode
@ -1041,6 +1042,8 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
init(context: AccountContext, readyWhenGalleryLoads: Bool, isSettings: Bool) { init(context: AccountContext, readyWhenGalleryLoads: Bool, isSettings: Bool) {
self.isSettings = isSettings self.isSettings = isSettings
self.containerNode = ASDisplayNode()
self.bottomCoverNode = ASDisplayNode() self.bottomCoverNode = ASDisplayNode()
self.bottomCoverNode.backgroundColor = .black self.bottomCoverNode.backgroundColor = .black
@ -1057,12 +1060,13 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
super.init() super.init()
self.addSubnode(self.bottomCoverNode) self.addSubnode(self.containerNode)
self.addSubnode(self.pinchSourceNode) self.containerNode.addSubnode(self.bottomCoverNode)
self.containerNode.addSubnode(self.pinchSourceNode)
self.pinchSourceNode.contentNode.addSubnode(self.avatarContainerNode) self.pinchSourceNode.contentNode.addSubnode(self.avatarContainerNode)
self.listContainerTransformNode.addSubnode(self.listContainerNode) self.listContainerTransformNode.addSubnode(self.listContainerNode)
self.pinchSourceNode.contentNode.addSubnode(self.listContainerTransformNode) self.pinchSourceNode.contentNode.addSubnode(self.listContainerTransformNode)
self.addSubnode(self.topCoverNode) self.containerNode.addSubnode(self.topCoverNode)
let avatarReady = (self.avatarContainerNode.avatarNode.ready let avatarReady = (self.avatarContainerNode.avatarNode.ready
|> mapToSignal { _ -> Signal<Bool, NoError> in |> mapToSignal { _ -> Signal<Bool, NoError> in
@ -1129,6 +1133,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
self.arguments = (peer, threadId, threadInfo, theme, avatarSize, isExpanded) self.arguments = (peer, threadId, threadInfo, theme, avatarSize, isExpanded)
self.maskNode.isForum = isForum self.maskNode.isForum = isForum
self.pinchSourceNode.update(size: size, transition: transition) self.pinchSourceNode.update(size: size, transition: transition)
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
self.pinchSourceNode.frame = CGRect(origin: CGPoint(), size: size) self.pinchSourceNode.frame = CGRect(origin: CGPoint(), size: size)
self.avatarContainerNode.update(peer: peer, threadId: threadId, threadInfo: threadInfo, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: self.isSettings) self.avatarContainerNode.update(peer: peer, threadId: threadId, threadInfo: threadInfo, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: self.isSettings)
} }
@ -2244,6 +2249,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var skipCollapseCompletion = false var skipCollapseCompletion = false
var ignoreCollapse = false var ignoreCollapse = false
let avatarClippingNode: SparseNode
let avatarListNode: PeerInfoAvatarListNode let avatarListNode: PeerInfoAvatarListNode
let buttonsContainerNode: SparseNode let buttonsContainerNode: SparseNode
@ -2305,6 +2311,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var emojiStatusPackDisposable = MetaDisposable() var emojiStatusPackDisposable = MetaDisposable()
var emojiStatusFileAndPackTitle = Promise<(TelegramMediaFile, LoadedStickerPack)?>() var emojiStatusFileAndPackTitle = Promise<(TelegramMediaFile, LoadedStickerPack)?>()
private var validWidth: CGFloat?
init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool, forumTopicThreadId: Int64?, chatLocation: ChatLocation) { init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool, forumTopicThreadId: Int64?, chatLocation: ChatLocation) {
self.context = context self.context = context
self.isAvatarExpanded = avatarInitiallyExpanded self.isAvatarExpanded = avatarInitiallyExpanded
@ -2314,6 +2322,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.forumTopicThreadId = forumTopicThreadId self.forumTopicThreadId = forumTopicThreadId
self.chatLocation = chatLocation self.chatLocation = chatLocation
self.avatarClippingNode = SparseNode()
self.avatarClippingNode.clipsToBounds = true
self.avatarListNode = PeerInfoAvatarListNode(context: context, readyWhenGalleryLoads: avatarInitiallyExpanded, isSettings: isSettings) self.avatarListNode = PeerInfoAvatarListNode(context: context, readyWhenGalleryLoads: avatarInitiallyExpanded, isSettings: isSettings)
self.titleNodeContainer = ASDisplayNode() self.titleNodeContainer = ASDisplayNode()
@ -2388,7 +2398,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self?.requestUpdateLayout?(false) self?.requestUpdateLayout?(false)
} }
if !isMediaOnly { if !isMediaOnly {
self.addSubnode(self.buttonsContainerNode) self.addSubnode(self.buttonsContainerNode)
} }
@ -2400,7 +2409,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
// self.subtitleNodeContainer.addSubnode(self.nextPanelSubtitleNode) // self.subtitleNodeContainer.addSubnode(self.nextPanelSubtitleNode)
self.usernameNodeContainer.addSubnode(self.usernameNode) self.usernameNodeContainer.addSubnode(self.usernameNode)
self.regularContentNode.addSubnode(self.avatarListNode) self.regularContentNode.addSubnode(self.avatarClippingNode)
self.avatarClippingNode.addSubnode(self.avatarListNode)
self.regularContentNode.addSubnode(self.avatarListNode.listContainerNode.controlsClippingOffsetNode) self.regularContentNode.addSubnode(self.avatarListNode.listContainerNode.controlsClippingOffsetNode)
self.regularContentNode.addSubnode(self.titleNodeContainer) self.regularContentNode.addSubnode(self.titleNodeContainer)
self.regularContentNode.addSubnode(self.subtitleNodeContainer) self.regularContentNode.addSubnode(self.subtitleNodeContainer)
@ -2567,6 +2577,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.peer = peer self.peer = peer
self.threadData = threadData self.threadData = threadData
self.avatarListNode.listContainerNode.peer = peer self.avatarListNode.listContainerNode.peer = peer
self.validWidth = width
let previousPanelStatusData = self.currentPanelStatusData let previousPanelStatusData = self.currentPanelStatusData
self.currentPanelStatusData = panelStatusData.0 self.currentPanelStatusData = panelStatusData.0
@ -3277,6 +3288,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
apparentAvatarFrame = CGRect(origin: CGPoint(x: avatarCenter.x - avatarFrame.width / 2.0, y: -contentOffset + avatarOffset + avatarCenter.y - avatarFrame.height / 2.0), size: avatarFrame.size) apparentAvatarFrame = CGRect(origin: CGPoint(x: avatarCenter.x - avatarFrame.width / 2.0, y: -contentOffset + avatarOffset + avatarCenter.y - avatarFrame.height / 2.0), size: avatarFrame.size)
controlsClippingFrame = apparentAvatarFrame controlsClippingFrame = apparentAvatarFrame
} }
let avatarClipOffset: CGFloat = !self.isAvatarExpanded && deviceMetrics.hasDynamicIsland ? 48.0 : 0.0
let clippingNodeTransition = ContainedViewLayoutTransition.immediate
clippingNodeTransition.updateFrame(layer: self.avatarClippingNode.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: avatarClipOffset), size: CGSize(width: width, height: 1000.0)))
clippingNodeTransition.updateSublayerTransformOffset(layer: self.avatarClippingNode.layer, offset: CGPoint(x: 0.0, y: -avatarClipOffset))
let clippingNodeRadiusTransition = ContainedViewLayoutTransition.animated(duration: 0.15, curve: .easeInOut)
clippingNodeRadiusTransition.updateCornerRadius(node: self.avatarClippingNode, cornerRadius: avatarClipOffset > 0.0 ? width / 2.5 : 0.0)
transition.updateFrameAdditive(node: self.avatarListNode, frame: CGRect(origin: apparentAvatarFrame.center, size: CGSize())) transition.updateFrameAdditive(node: self.avatarListNode, frame: CGRect(origin: apparentAvatarFrame.center, size: CGSize()))
transition.updateFrameAdditive(node: self.avatarOverlayNode, frame: CGRect(origin: apparentAvatarFrame.center, size: CGSize())) transition.updateFrameAdditive(node: self.avatarOverlayNode, frame: CGRect(origin: apparentAvatarFrame.center, size: CGSize()))
@ -3318,26 +3337,34 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} }
if deviceMetrics.hasDynamicIsland && self.forumTopicThreadId == nil { if deviceMetrics.hasDynamicIsland && self.forumTopicThreadId == nil {
self.avatarListNode.maskNode.frame = CGRect(origin: CGPoint(x: -85.5, y: -self.avatarListNode.frame.minY + 48.0), size: CGSize(width: 171.0, height: 171.0))
self.avatarListNode.bottomCoverNode.frame = self.avatarListNode.maskNode.frame
self.avatarListNode.topCoverNode.frame = self.avatarListNode.maskNode.frame
let maskValue = max(0.0, min(1.0, contentOffset / 120.0)) let maskValue = max(0.0, min(1.0, contentOffset / 120.0))
self.avatarListNode.containerNode.view.mask = self.avatarListNode.maskNode.view
if maskValue > 0.03 { if maskValue > 0.03 {
self.avatarListNode.bottomCoverNode.isHidden = false self.avatarListNode.bottomCoverNode.isHidden = false
self.avatarListNode.topCoverNode.isHidden = false self.avatarListNode.topCoverNode.isHidden = false
self.avatarListNode.view.mask = self.avatarListNode.maskNode.view self.avatarListNode.maskNode.backgroundColor = .clear
} else { } else {
self.avatarListNode.bottomCoverNode.isHidden = true self.avatarListNode.bottomCoverNode.isHidden = true
self.avatarListNode.topCoverNode.isHidden = true self.avatarListNode.topCoverNode.isHidden = true
self.avatarListNode.view.mask = nil self.avatarListNode.maskNode.backgroundColor = .white
} }
self.avatarListNode.maskNode.update(maskValue)
self.avatarListNode.topCoverNode.update(maskValue) self.avatarListNode.topCoverNode.update(maskValue)
self.avatarListNode.maskNode.update(maskValue)
self.avatarListNode.listContainerNode.topShadowNode.isHidden = !self.isAvatarExpanded
self.avatarListNode.maskNode.position = CGPoint(x: 0.0, y: -self.avatarListNode.frame.minY + 48.0 + 85.5)
self.avatarListNode.maskNode.bounds = CGRect(origin: .zero, size: CGSize(width: 171.0, height: 171.0))
self.avatarListNode.bottomCoverNode.position = self.avatarListNode.maskNode.position
self.avatarListNode.bottomCoverNode.bounds = self.avatarListNode.maskNode.bounds
self.avatarListNode.topCoverNode.position = self.avatarListNode.maskNode.position
self.avatarListNode.topCoverNode.bounds = self.avatarListNode.maskNode.bounds
} else { } else {
self.avatarListNode.bottomCoverNode.isHidden = true self.avatarListNode.bottomCoverNode.isHidden = true
self.avatarListNode.topCoverNode.isHidden = true self.avatarListNode.topCoverNode.isHidden = true
self.avatarListNode.view.mask = nil self.avatarListNode.containerNode.view.mask = nil
} }
self.avatarListNode.listContainerNode.update(size: expandedAvatarListSize, peer: peer, isExpanded: self.isAvatarExpanded, transition: transition) self.avatarListNode.listContainerNode.update(size: expandedAvatarListSize, peer: peer, isExpanded: self.isAvatarExpanded, transition: transition)
@ -3662,12 +3689,22 @@ final class PeerInfoHeaderNode: ASDisplayNode {
if case .animated = transition, !isAvatarExpanded { if case .animated = transition, !isAvatarExpanded {
self.avatarListNode.animateAvatarCollapse(transition: transition) self.avatarListNode.animateAvatarCollapse(transition: transition)
} }
if let width = self.validWidth {
let maskScale: CGFloat = isAvatarExpanded ? width / 100.0 : 1.0
transition.updateTransformScale(layer: self.avatarListNode.maskNode.layer, scale: maskScale)
transition.updateTransformScale(layer: self.avatarListNode.bottomCoverNode.layer, scale: maskScale)
transition.updateTransformScale(layer: self.avatarListNode.topCoverNode.layer, scale: maskScale)
let maskAnchorPoint = CGPoint(x: 0.5, y: isAvatarExpanded ? 0.37 : 0.5)
transition.updateAnchorPoint(layer: self.avatarListNode.maskNode.layer, anchorPoint: maskAnchorPoint)
}
} }
} }
} }
private class DynamicIslandMaskNode: ASDisplayNode { private class DynamicIslandMaskNode: ASDisplayNode {
private var animationNode: AnimationNode? var animationNode: AnimationNode?
var isForum = false { var isForum = false {
didSet { didSet {
@ -3693,6 +3730,8 @@ private class DynamicIslandMaskNode: ASDisplayNode {
self.animationNode?.setProgress(value) self.animationNode?.setProgress(value)
} }
var animating = false
override func layout() { override func layout() {
self.animationNode?.frame = self.bounds self.animationNode?.frame = self.bounds
} }
@ -3701,7 +3740,7 @@ private class DynamicIslandMaskNode: ASDisplayNode {
private class DynamicIslandBlurNode: ASDisplayNode { private class DynamicIslandBlurNode: ASDisplayNode {
private var effectView: UIVisualEffectView? private var effectView: UIVisualEffectView?
private let fadeNode = ASDisplayNode() private let fadeNode = ASDisplayNode()
private let gradientNode = ASImageNode() let gradientNode = ASImageNode()
private var hierarchyTrackingNode: HierarchyTrackingNode? private var hierarchyTrackingNode: HierarchyTrackingNode?

View File

@ -1401,7 +1401,7 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
let ItemBotInfo = 9 let ItemBotInfo = 9
if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) {
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text("@\(user.addressName ?? "")"), text: presentationData.strings.PeerInfo_Username, icon: nil, action: { items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text("@\(user.addressName ?? "")"), text: presentationData.strings.PeerInfo_BotLinks, icon: nil, action: {
interaction.editingOpenPublicLinkSetup() interaction.editingOpenPublicLinkSetup()
})) }))