Merge commit 'dfe1dbd825ea321903a7d63e19577109c2cfa35b'

This commit is contained in:
Ali 2021-04-05 20:01:19 +04:00
commit 5d3e3b9ebe
8 changed files with 4977 additions and 4791 deletions

View File

@ -6339,3 +6339,25 @@ Sorry for the inconvenience.";
"VoiceChat.UnpinVideo" = "Unpin Video";
"Notification.VoiceChatScheduled" = "Voice chat scheduled";
"VoiceChat.EditStartTime" = "Edit Start Time";
"VoiceChat.StartsIn" = "Starts in";
"VoiceChat.SetReminder" = "Set Reminder";
"VoiceChat.CancelReminder" = "Cancel Reminder";
"VoiceChat.ShareShort" = "share";
"ChannelInfo.ScheduleVoiceChat" = "Schedule Voice Chat";
"ScheduleVoiceChat.Title" = "Schedule Voice Chat";
"ScheduleVoiceChat.GroupText" = "The members of the group will be notified that the voice chat will start in %@.";
"ScheduleVoiceChat.ChannelText" = "The members of the channel will be notified that the voice chat will start in %@.";
"ScheduleVoiceChat.ScheduleToday" = "Remind today at %@";
"ScheduleVoiceChat.ScheduleTomorrow" = "Remind tomorrow at %@";
"ScheduleVoiceChat.ScheduleOn" = "Remind on %@ at %@";
"Conversation.ScheduledVoiceChat" = "Scheduled Voice Chat";
"Conversation.ScheduledVoiceChatStartsInShort" = "Voice chat starts %@";
"Conversation.ScheduledVoiceChatStartsInShort" = "Starts %@";

View File

@ -12,8 +12,10 @@ import AlertUI
import PresentationDataUtils
import CountrySelectionUI
import PhoneNumberFormat
import CoreTelephony
import MessageUI
final class ChangePhoneNumberController: ViewController {
final class ChangePhoneNumberController: ViewController, MFMailComposeViewControllerDelegate {
private var controllerNode: ChangePhoneNumberControllerNode {
return self.displayNode as! ChangePhoneNumberControllerNode
}
@ -133,6 +135,9 @@ final class ChangePhoneNumberController: ViewController {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let text: String
var actions: [TextAlertAction] = [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
]
switch error {
case .limitExceeded:
text = presentationData.strings.Login_CodeFloodError
@ -140,11 +145,26 @@ final class ChangePhoneNumberController: ViewController {
text = presentationData.strings.Login_InvalidPhoneError
case .phoneNumberOccupied:
text = presentationData.strings.ChangePhone_ErrorOccupied(formatPhoneNumber(phoneNumber)).0
case .phoneBanned:
text = presentationData.strings.Login_PhoneBannedError
actions.append(TextAlertAction(type: .defaultAction, title: presentationData.strings.Login_PhoneNumberHelp, action: { [weak self] in
guard let strongSelf = self else {
return
}
let formattedNumber = formatPhoneNumber(number)
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
let systemVersion = UIDevice.current.systemVersion
let locale = Locale.current.identifier
let carrier = CTCarrier()
let mnc = carrier.mobileNetworkCode ?? "none"
strongSelf.presentEmailComposeController(address: "login@stel.com", subject: presentationData.strings.Login_PhoneBannedEmailSubject(formattedNumber).0, body: presentationData.strings.Login_PhoneBannedEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).0)
}))
case .generic:
text = presentationData.strings.Login_UnknownError
}
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: actions), in: .window(.root))
}
}))
} else {
@ -152,4 +172,22 @@ final class ChangePhoneNumberController: ViewController {
self.controllerNode.animateError()
}
}
private func presentEmailComposeController(address: String, subject: String, body: String) {
if MFMailComposeViewController.canSendMail() {
let composeController = MFMailComposeViewController()
composeController.setToRecipients([address])
composeController.setSubject(subject)
composeController.setMessageBody(body, isHTML: false)
composeController.mailComposeDelegate = self
self.view.window?.rootViewController?.present(composeController, animated: true, completion: nil)
} else {
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
}

View File

@ -1322,7 +1322,7 @@ public final class VoiceChatController: ViewController {
maxBioLength = 100
}
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_EditBioTitle, text: presentationData.strings.VoiceChat_EditBioText, placeholder: presentationData.strings.VoiceChat_EditBioPlaceholder, doneButtonTitle: presentationData.strings.VoiceChat_EditBioSave, value: entry.about, maxLength: maxBioLength, apply: { bio in
if let strongSelf = self {
if let strongSelf = self, let bio = bio {
let _ = (updateAbout(account: strongSelf.context.account, about: bio)
|> `catch` { _ -> Signal<Void, NoError> in
return .complete()
@ -1345,8 +1345,8 @@ public final class VoiceChatController: ViewController {
f(.default)
Queue.mainQueue().after(0.1) {
let controller = voiceChatUserNameController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_ChangeNameTitle, firstNamePlaceholder: presentationData.strings.UserInfo_FirstNamePlaceholder, lastNamePlaceholder: presentationData.strings.UserInfo_LastNamePlaceholder, doneButtonTitle: presentationData.strings.VoiceChat_EditBioSave, firstName: peer.firstName, lastName: peer.lastName, maxLength: 128, apply: { firstName, lastName in
if let strongSelf = self {
let controller = voiceChatUserNameController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_ChangeNameTitle, firstNamePlaceholder: presentationData.strings.UserInfo_FirstNamePlaceholder, lastNamePlaceholder: presentationData.strings.UserInfo_LastNamePlaceholder, doneButtonTitle: presentationData.strings.VoiceChat_EditBioSave, firstName: peer.firstName, lastName: peer.lastName, maxLength: 128, apply: { firstAndLastName in
if let strongSelf = self, let (firstName, lastName) = firstAndLastName {
let _ = updateAccountPeerName(account: context.account, firstName: firstName, lastName: lastName).start()
strongSelf.presentUndoOverlay(content: .info(text: strongSelf.presentationData.strings.VoiceChat_EditNameSuccess), action: { _ in return false })
@ -1995,6 +1995,15 @@ public final class VoiceChatController: ViewController {
})))
}
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_EditPermissions, icon: { theme -> UIImage? in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.actionSheet.primaryTextColor)
}, action: { c, _ in
guard let strongSelf = self else {
return
}
c.setItems(strongSelf.contextMenuPermissionItems())
})))
if let inviteLinks = inviteLinks {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_Share, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.actionSheet.primaryTextColor)
@ -3748,7 +3757,7 @@ public final class VoiceChatController: ViewController {
mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in
if let image = image, let asset = asset {
completion()
// self?.updateProfileVideo(image, asset: asset, adjustments: adjustments)
self?.updateProfileVideo(image, asset: asset, adjustments: adjustments)
}
}
mixin.didFinishWithDelete = {
@ -3756,55 +3765,42 @@ public final class VoiceChatController: ViewController {
return
}
// let proceed = {
// if let item = item {
// strongSelf.deleteAvatar(item, remove: false)
// }
//
// let _ = strongSelf.currentAvatarMixin.swap(nil)
// if let _ = peer.smallProfileImage {
// strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
// if let (layout, navigationHeight) = strongSelf.validLayout {
// strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
// }
// }
// let postbox = strongSelf.context.account.postbox
// strongSelf.updateAvatarDisposable.set((updatePeerPhoto(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, stateManager: strongSelf.context.account.stateManager, accountPeerId: strongSelf.context.account.peerId, peerId: strongSelf.peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in
// return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
// })
// |> deliverOnMainQueue).start(next: { result in
// guard let strongSelf = self else {
// return
// }
// switch result {
// case .complete:
// strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
// if let (layout, navigationHeight) = strongSelf.validLayout {
// strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
// }
// case .progress:
// break
// }
// }))
// }
//
// let actionSheet = ActionSheetController(presentationData: presentationData)
// let items: [ActionSheetItem] = [
// ActionSheetButtonItem(title: presentationData.strings.Settings_RemoveConfirmation, color: .destructive, action: { [weak actionSheet] in
// actionSheet?.dismissAnimated()
// proceed()
// })
// ]
//
// actionSheet.setItemGroups([
// ActionSheetItemGroup(items: items),
// ActionSheetItemGroup(items: [
// ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
// actionSheet?.dismissAnimated()
// })
// ])
// ])
// strongSelf.controller?.present(actionSheet, in: .window(.root))
let proceed = {
let _ = strongSelf.currentAvatarMixin.swap(nil)
let postbox = strongSelf.context.account.postbox
strongSelf.updateAvatarDisposable.set((updatePeerPhoto(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, stateManager: strongSelf.context.account.stateManager, accountPeerId: strongSelf.context.account.peerId, peerId: peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
})
|> deliverOnMainQueue).start(next: { result in
guard let strongSelf = self else {
return
}
switch result {
case .complete:
break
case .progress:
break
}
}))
}
let actionSheet = ActionSheetController(presentationData: presentationData)
let items: [ActionSheetItem] = [
ActionSheetButtonItem(title: presentationData.strings.Settings_RemoveConfirmation, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
proceed()
})
]
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
])
strongSelf.controller?.present(actionSheet, in: .window(.root))
}
mixin.didDismiss = { [weak legacyController] in
guard let strongSelf = self else {
@ -3856,6 +3852,125 @@ public final class VoiceChatController: ViewController {
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? ([], nil), invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
}
private func updateProfileVideo(_ image: UIImage, asset: Any?, adjustments: TGVideoEditAdjustments?) {
guard let data = image.jpegData(compressionQuality: 0.6), let peerId = self.callState?.myPeerId else {
return
}
let photoResource = LocalFileMediaResource(fileId: arc4random64())
self.context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil)
self.currentUpdatingAvatar = representation
self.updateAvatarPromise.set(.single((representation, 0.0)))
var videoStartTimestamp: Double? = nil
if let adjustments = adjustments, adjustments.videoStartValue > 0.0 {
videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue
}
let account = self.context.account
let signal = Signal<TelegramMediaResource, UploadPeerPhotoError> { [weak self] subscriber in
let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in
if let paintingData = adjustments.paintingData, paintingData.hasAnimation {
return LegacyPaintEntityRenderer(account: account, adjustments: adjustments)
} else {
return nil
}
}
let uploadInterface = LegacyLiveUploadInterface(account: account)
let signal: SSignal
if let asset = asset as? AVAsset {
signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)!
} else if let url = asset as? URL, 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, watcher: nil, entityRenderer: entityRenderer)!
} else {
return SSignal.single(nil)
}
})
} 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: arc4random64())
}
account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
subscriber.putNext(resource)
}
}
subscriber.putCompletion()
} else if let strongSelf = self, let progress = next as? NSNumber {
Queue.mainQueue().async {
strongSelf.updateAvatarPromise.set(.single((representation, Float(truncating: progress) * 0.25)))
}
}
}, error: { _ in
}, completed: nil)
let disposable = ActionDisposable {
signalDisposable?.dispose()
}
return ActionDisposable {
disposable.dispose()
}
}
self.updateAvatarDisposable.set((signal
|> mapToSignal { videoResource -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> in
if peerId.namespace == Namespaces.Peer.CloudUser {
return updateAccountPhoto(account: account, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
})
} else {
return updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: peerId, photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: photoResource), video: uploadedPeerVideo(postbox: account.postbox, network: account.network, messageMediaPreuploadManager: account.messageMediaPreuploadManager, resource: videoResource) |> map(Optional.init), videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
})
}
}
|> deliverOnMainQueue).start(next: { [weak self] result in
guard let strongSelf = self else {
return
}
switch result {
case .complete:
strongSelf.updateAvatarPromise.set(.single(nil))
case let .progress(value):
strongSelf.updateAvatarPromise.set(.single((representation, 0.25 + value * 0.75)))
}
}))
}
}
private let sharedContext: SharedAccountContext

View File

@ -710,7 +710,7 @@ private final class VoiceChatUserNameEditAlertContentNode: AlertContentNode {
}
}
func voiceChatUserNameController(sharedContext: SharedAccountContext, account: Account, forceTheme: PresentationTheme?, title: String, firstNamePlaceholder: String, lastNamePlaceholder: String, doneButtonTitle: String? = nil, firstName: String?, lastName: String?, maxLength: Int, apply: @escaping (String, String) -> Void) -> AlertController {
func voiceChatUserNameController(sharedContext: SharedAccountContext, account: Account, forceTheme: PresentationTheme?, title: String, firstNamePlaceholder: String, lastNamePlaceholder: String, doneButtonTitle: String? = nil, firstName: String?, lastName: String?, maxLength: Int, apply: @escaping ((String, String)?) -> Void) -> AlertController {
var presentationData = sharedContext.currentPresentationData.with { $0 }
if let forceTheme = forceTheme {
presentationData = presentationData.withUpdated(theme: forceTheme)
@ -733,14 +733,24 @@ func voiceChatUserNameController(sharedContext: SharedAccountContext, account: A
guard let contentNode = contentNode else {
return
}
dismissImpl?(true)
let previousFirstName = firstName ?? ""
let previousLastName = firstName ?? ""
let previousLastName = lastName ?? ""
let newFirstName = contentNode.firstName.trimmingCharacters(in: .whitespacesAndNewlines)
let newLastName = contentNode.lastName.trimmingCharacters(in: .whitespacesAndNewlines)
apply(newFirstName, newLastName)
if newFirstName.isEmpty {
contentNode.animateError()
return
}
dismissImpl?(true)
if previousFirstName != newFirstName || previousLastName != newLastName {
apply((newFirstName, newLastName))
} else {
apply(nil)
}
}
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)

View File

@ -33,6 +33,7 @@ public enum RequestChangeAccountPhoneNumberVerificationError {
case invalidPhoneNumber
case limitExceeded
case phoneNumberOccupied
case phoneBanned
case generic
}
@ -45,6 +46,8 @@ public func requestChangeAccountPhoneNumberVerification(account: Account, phoneN
return .invalidPhoneNumber
} else if error.errorDescription == "PHONE_NUMBER_OCCUPIED" {
return .phoneNumberOccupied
} else if error.errorDescription == "PHONE_NUMBER_BANNED" {
return .phoneBanned
} else {
return .generic
}

View File

@ -54,7 +54,7 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
let referenceNode: ContextReferenceContentNode
let containerNode: ContextControllerSourceNode
private let backgroundNode: ASImageNode
private let backgroundWithIconNode: ASImageNode
private let iconNode: ASImageNode
private let textNode: ImmediateTextNode
private var animationNode: AnimationNode?
@ -73,11 +73,10 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
self.backgroundNode = ASImageNode()
self.backgroundNode.displaysAsynchronously = false
self.backgroundNode.displayWithoutProcessing = true
self.backgroundNode.isHidden = true
self.backgroundWithIconNode = ASImageNode()
self.backgroundWithIconNode.displaysAsynchronously = false
self.backgroundWithIconNode.displayWithoutProcessing = true
self.iconNode = ASImageNode()
self.iconNode.displaysAsynchronously = false
self.iconNode.displayWithoutProcessing = true
self.textNode = ImmediateTextNode()
self.textNode.displaysAsynchronously = false
@ -88,7 +87,7 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
self.containerNode.addSubnode(self.referenceNode)
self.referenceNode.addSubnode(self.backgroundNode)
self.referenceNode.addSubnode(self.backgroundWithIconNode)
self.referenceNode.addSubnode(self.iconNode)
self.addSubnode(self.containerNode)
self.addSubnode(self.textNode)
@ -126,13 +125,12 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
func update(size: CGSize, text: String, icon: PeerInfoHeaderButtonIcon, isActive: Bool, isExpanded: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
let previousIcon = self.icon
let iconUpdated = self.icon != icon
if self.theme != presentationData.theme || self.icon != icon || self.isActive != isActive {
let isActiveUpdated = self.isActive != isActive
self.isActive = isActive
if self.theme != presentationData.theme || self.icon != icon {
self.theme = presentationData.theme
self.icon = icon
let isActiveUpdated = self.isActive != isActive
self.isActive = isActive
var isGestureEnabled = false
if [.mute, .voiceChat, .more].contains(icon) {
isGestureEnabled = true
@ -195,17 +193,13 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
}
} else {
animationNode = AnimationNode(animation: animationName, colors: colors, scale: 1.0)
self.addSubnode(animationNode)
self.referenceNode.addSubnode(animationNode)
self.animationNode = animationNode
}
animationNode.frame = CGRect(origin: CGPoint(), size: size)
self.backgroundWithIconNode.isHidden = true
self.backgroundNode.isHidden = false
} else if let animationNode = self.animationNode {
self.animationNode = nil
animationNode.removeFromSupernode()
self.backgroundWithIconNode.isHidden = false
self.backgroundNode.isHidden = true
}
if playOnce {
@ -214,34 +208,9 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
self.animationNode?.seekToEnd()
}
if isActiveUpdated, !self.containerNode.alpha.isZero {
let backgroundNode = !self.backgroundNode.isHidden ? self.backgroundNode : self.backgroundWithIconNode
if let snapshotView = backgroundNode.view.snapshotContentTree() {
snapshotView.frame = backgroundNode.view.frame
if let animationNode = self.animationNode {
self.view.insertSubview(snapshotView, belowSubview: animationNode.view)
} else {
self.view.addSubview(snapshotView)
}
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
if !isExpanded, let snapshotView = self.textNode.view.snapshotContentTree() {
snapshotView.frame = self.textNode.view.frame
self.view.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
self.backgroundNode.image = generateFilledCircleImage(diameter: 40.0, color: isActive ? presentationData.theme.list.itemAccentColor : presentationData.theme.list.itemDisabledTextColor)
self.backgroundWithIconNode.image = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in
self.backgroundNode.image = generateFilledCircleImage(diameter: 40.0, color: presentationData.theme.list.itemAccentColor)
self.iconNode.image = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(isActive ? presentationData.theme.list.itemAccentColor.cgColor : presentationData.theme.list.itemDisabledTextColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.normal)
context.setFillColor(presentationData.theme.list.itemCheckColors.foregroundColor.cgColor)
let imageName: String?
@ -275,15 +244,22 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
})
}
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(12.0), textColor: isActive ? presentationData.theme.list.itemAccentColor : presentationData.theme.list.itemDisabledTextColor)
let alpha: CGFloat = isActive ? 1.0 : 0.3
if isActiveUpdated, !self.containerNode.alpha.isZero {
let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
alphaTransition.updateAlpha(node: self.backgroundNode, alpha: isActive ? 1.0 : 0.3)
alphaTransition.updateAlpha(node: self.textNode, alpha: isActive ? 1.0 : 0.3)
}
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(12.0), textColor: presentationData.theme.list.itemAccentColor)
self.accessibilityLabel = text
let titleSize = self.textNode.updateLayout(CGSize(width: 120.0, height: .greatestFiniteMagnitude))
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size))
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
transition.updateFrame(node: self.backgroundWithIconNode, frame: CGRect(origin: CGPoint(), size: size))
transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(), size: size))
transition.updateFrameAdditiveToCenter(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: size.height + 6.0), size: titleSize))
transition.updateAlpha(node: self.textNode, alpha: isExpanded ? 0.0 : 1.0)
transition.updateAlpha(node: self.textNode, alpha: isExpanded ? 0.0 : alpha)
self.referenceNode.frame = self.containerNode.bounds
}