Various fixes

This commit is contained in:
Ilya Laktyushin 2023-11-26 22:22:35 +04:00
parent 6ec58eb9b4
commit fb50102b06
9 changed files with 198 additions and 144 deletions

View file

@ -10489,6 +10489,7 @@ Sorry for the inconvenience.";
"Chat.SimilarChannels" = "Similar Channels";
"Chat.SimilarChannels.Join" = "Join";
"Chat.SimilarChannels.JoinedChannel" = "You joined channel **%@**.";
"Chat.SimilarChannels.MoreChannels" = "More Channels";
"Wallpaper.ApplyForMe" = "Apply for Me";
"Wallpaper.ApplyForBoth" = "Apply for Me and %@";

View file

@ -117,6 +117,7 @@ private final class CameraContext {
private var invalidated = false
private let detectedCodesPipe = ValuePipe<[CameraCode]>()
private let audioLevelPipe = ValuePipe<Float>()
fileprivate let modeChangePromise = ValuePromise<Camera.ModeChange>(.none)
var previewView: CameraPreviewView?
@ -281,6 +282,10 @@ private final class CameraContext {
}
}
private var micLevelPeak: Int16 = 0
private var micLevelPeakCount = 0
private var isDualCameraEnabled: Bool?
public func setDualCameraEnabled(_ enabled: Bool, change: Bool = true) {
guard enabled != self.isDualCameraEnabled else {
@ -352,6 +357,48 @@ private final class CameraContext {
self.lastSnapshotTimestamp = timestamp
}
}
if self.initialConfiguration.reportAudioLevel {
self.mainDeviceContext?.output.processAudioBuffer = { [weak self] sampleBuffer in
guard let self else {
return
}
var blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer)
let numSamplesInBuffer = CMSampleBufferGetNumSamples(sampleBuffer)
var audioBufferList = AudioBufferList()
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, bufferListSizeNeededOut: nil, bufferListOut: &audioBufferList, bufferListSize: MemoryLayout<AudioBufferList>.size, blockBufferAllocator: nil, blockBufferMemoryAllocator: nil, flags: kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, blockBufferOut: &blockBuffer)
// for bufferCount in 0..<Int(audioBufferList.mNumberBuffers) {
let buffer = audioBufferList.mBuffers.mData
let size = audioBufferList.mBuffers.mDataByteSize
if let data = buffer?.bindMemory(to: Int16.self, capacity: Int(size)) {
processWaveformPreview(samples: data, count: numSamplesInBuffer)
}
// }
func processWaveformPreview(samples: UnsafePointer<Int16>, count: Int) {
for i in 0..<count {
var sample = samples[i]
if sample < 0 {
sample = -sample
}
if self.micLevelPeak < sample {
self.micLevelPeak = sample
}
self.micLevelPeakCount += 1
if self.micLevelPeakCount >= 1200 {
let level = Float(self.micLevelPeak) / 4000.0
self.audioLevelPipe.putNext(level)
self.micLevelPeak = 0
self.micLevelPeakCount = 0
}
}
}
}
}
self.mainDeviceContext?.output.processCodes = { [weak self] codes in
self?.detectedCodesPipe.putNext(codes)
}
@ -526,6 +573,10 @@ private final class CameraContext {
return self.detectedCodesPipe.signal()
}
var audioLevel: Signal<Float, NoError> {
return self.audioLevelPipe.signal()
}
@objc private func sessionInterruptionEnded(notification: NSNotification) {
}
@ -564,8 +615,9 @@ public final class Camera {
let metadata: Bool
let preferredFps: Double
let preferWide: Bool
let reportAudioLevel: Bool
public init(preset: Preset, position: Position, isDualEnabled: Bool = false, audio: Bool, photo: Bool, metadata: Bool, preferredFps: Double, preferWide: Bool = false) {
public init(preset: Preset, position: Position, isDualEnabled: Bool = false, audio: Bool, photo: Bool, metadata: Bool, preferredFps: Double, preferWide: Bool = false, reportAudioLevel: Bool = false) {
self.preset = preset
self.position = position
self.isDualEnabled = isDualEnabled
@ -574,6 +626,7 @@ public final class Camera {
self.metadata = metadata
self.preferredFps = preferredFps
self.preferWide = preferWide
self.reportAudioLevel = reportAudioLevel
}
}
@ -865,6 +918,20 @@ public final class Camera {
}
}
public var audioLevel: Signal<Float, NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.queue.async {
if let context = self.contextRef?.takeUnretainedValue() {
disposable.set(context.audioLevel.start(next: { codes in
subscriber.putNext(codes)
}))
}
}
return disposable
}
}
public enum ModeChange: Equatable {
case none
case position

View file

@ -96,6 +96,7 @@ final class CameraOutput: NSObject {
private var videoRecorder: VideoRecorder?
var processSampleBuffer: ((CMSampleBuffer, CVImageBuffer, AVCaptureConnection) -> Void)?
var processAudioBuffer: ((CMSampleBuffer) -> Void)?
var processCodes: (([CameraCode]) -> Void)?
init(exclusive: Bool) {
@ -379,6 +380,8 @@ extension CameraOutput: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureA
if let videoPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
self.processSampleBuffer?(sampleBuffer, videoPixelBuffer, connection)
} else {
self.processAudioBuffer?(sampleBuffer)
}
if let videoRecorder = self.videoRecorder, videoRecorder.isRecording {

View file

@ -47,7 +47,8 @@ public final class EntityVideoRecorder {
photo: false,
metadata: false,
preferredFps: 60.0,
preferWide: true
preferWide: true,
reportAudioLevel: true
),
previewView: self.previewView,
secondaryPreviewView: nil
@ -73,7 +74,7 @@ public final class EntityVideoRecorder {
}
}
self.micLevelPromise.set(.single(0.0))
self.micLevelPromise.set(camera.audioLevel)
let start = mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0
mediaEditor.stop()

View file

@ -544,11 +544,6 @@ private class MessageBackgroundNode: ASDisplayNode {
private let itemSize = CGSize(width: 84.0, height: 90.0)
private final class ChannelItemComponent: Component {
class ExternalState {
var cachedPlaceholderImage: UIImage?
}
let externalState: ExternalState
let context: AccountContext
let theme: PresentationTheme
let strings: PresentationStrings
@ -561,7 +556,6 @@ private final class ChannelItemComponent: Component {
let contextAction: ((EnginePeer, UIView, ContextGesture?) -> Void)?
init(
externalState: ExternalState,
context: AccountContext,
theme: PresentationTheme,
strings: PresentationStrings,
@ -573,7 +567,6 @@ private final class ChannelItemComponent: Component {
openMore: @escaping () -> Void,
contextAction: ((EnginePeer, UIView, ContextGesture?) -> Void)?
) {
self.externalState = externalState
self.context = context
self.theme = theme
self.strings = strings
@ -677,8 +670,11 @@ private final class ChannelItemComponent: Component {
}
func update(component: ChannelItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
let previousComponent = self.component
self.component = component
self.state = state
let themeUpdated = previousComponent?.theme !== component.theme
self.contextContainer.isGestureEnabled = true
@ -776,69 +772,43 @@ private final class ChannelItemComponent: Component {
}
self.circlesView.isHidden = false
if self.circlesView.image == nil {
if let current = component.externalState.cachedPlaceholderImage {
self.circlesView.image = current
} else {
let image = generateImage(CGSize(width: 50.0, height: avatarSize.height), rotatedContext: { size, context in
context.clear(CGRect(origin: .zero, size: size))
let randomColors: [(UInt32, UInt32)] = [
(0x4493de, 0x52d5d9),
(0xfcc418, 0xf6774a),
(0xffc9a2, 0xfbedb2),
(0x133e88, 0x131925),
(0x63c7f0, 0xf6c506),
(0x88a5cb, 0x162639),
(0xd669ed, 0xe0a2f3),
(0x54cb68, 0xa0de7e)
]
context.saveGState()
let rect1 = CGRect(origin: CGPoint(x: size.width - avatarSize.width, y: 0.0), size: avatarSize)
context.addEllipse(in: rect1)
context.clip()
var firstColors: NSArray = []
if let random = randomColors.randomElement() {
firstColors = [UIColor(rgb: random.0).cgColor, UIColor(rgb: random.1).cgColor]
}
var locations: [CGFloat] = [1.0, 0.0]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let firstGradient = CGGradient(colorsSpace: colorSpace, colors: firstColors as CFArray, locations: &locations)!
context.drawLinearGradient(firstGradient, start: CGPoint(x: rect1.minX, y: rect1.minY), end: CGPoint(x: rect1.maxX, y: rect1.maxY), options: CGGradientDrawingOptions())
context.restoreGState()
context.setBlendMode(.clear)
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - avatarSize.width - 12.0, y: -2.0), size: CGSize(width: avatarSize.width + 4.0, height: avatarSize.height + 4.0)))
context.setBlendMode(.normal)
context.saveGState()
let rect2 = CGRect(origin: CGPoint(x: size.width - avatarSize.width - 10.0, y: 0.0), size: avatarSize)
context.addEllipse(in: rect2)
context.clip()
var secondColors: NSArray = []
if let random = randomColors.randomElement() {
secondColors = [UIColor(rgb: random.0).cgColor, UIColor(rgb: random.1).cgColor]
}
let secondGradient = CGGradient(colorsSpace: colorSpace, colors: secondColors as CFArray, locations: &locations)!
context.drawLinearGradient(secondGradient, start: CGPoint(x: rect2.minX, y: rect2.minY), end: CGPoint(x: rect2.minX, y: rect2.maxY), options: CGGradientDrawingOptions())
context.restoreGState()
context.setBlendMode(.clear)
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - avatarSize.width - 22.0, y: -2.0), size: CGSize(width: avatarSize.width + 4.0, height: avatarSize.height + 4.0)))
})
component.externalState.cachedPlaceholderImage = image
self.circlesView.image = image
}
if self.circlesView.image == nil || themeUpdated {
let image = generateImage(CGSize(width: 50.0, height: avatarSize.height), rotatedContext: { size, context in
context.clear(CGRect(origin: .zero, size: size))
let color = component.theme.chat.message.incoming.secondaryTextColor.withMultipliedAlpha(0.35)
context.saveGState()
let rect1 = CGRect(origin: CGPoint(x: size.width - avatarSize.width, y: 0.0), size: avatarSize)
context.addEllipse(in: rect1)
context.clip()
context.setFillColor(color.cgColor)
context.fill(rect1)
context.restoreGState()
context.setBlendMode(.clear)
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - avatarSize.width - 12.0, y: -2.0), size: CGSize(width: avatarSize.width + 4.0, height: avatarSize.height + 4.0)))
context.setBlendMode(.normal)
context.saveGState()
let rect2 = CGRect(origin: CGPoint(x: size.width - avatarSize.width - 10.0, y: 0.0), size: avatarSize)
context.addEllipse(in: rect2)
context.clip()
context.setFillColor(color.cgColor)
context.fill(rect2)
context.restoreGState()
context.setBlendMode(.clear)
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - avatarSize.width - 22.0, y: -2.0), size: CGSize(width: avatarSize.width + 4.0, height: avatarSize.height + 4.0)))
})
self.circlesView.image = image
}
self.circlesView.frame = CGRect(origin: CGPoint(x: avatarFrame.midX, y: 0.0), size: CGSize(width: 50.0, height: 60.0))
} else {
@ -991,7 +961,6 @@ final class ChannelListPanelComponent: Component {
private let measureItem = ComponentView<Empty>()
private var visibleItems: [EnginePeer.Id: ComponentView<Empty>] = [:]
private var externalState = ChannelItemComponent.ExternalState()
private var ignoreScrolling: Bool = false
@ -1072,7 +1041,7 @@ final class ChannelListPanelComponent: Component {
if !component.context.isPremium {
isLocked = true
}
title = isLocked ? "Unlock More Channels" : "View More Channels"
title = component.strings.Chat_SimilarChannels_MoreChannels
subtitle = "+\(component.peers.count - channelsLimit)"
isLast = true
} else {
@ -1092,7 +1061,6 @@ final class ChannelListPanelComponent: Component {
let _ = itemView.update(
transition: itemTransition,
component: AnyComponent(ChannelItemComponent(
externalState: self.externalState,
context: component.context,
theme: component.theme,
strings: component.strings,

View file

@ -1,11 +1,14 @@
import Foundation
import UIKit
import Display
import SwiftSignalKit
import MediaEditor
import DrawingUI
import ChatPresentationInterfaceState
import PresentationDataUtils
import TelegramPresentationData
import DeviceAccess
import AccountContext
extension MediaEditorScreen {
final class Recording {
@ -13,10 +16,56 @@ extension MediaEditorScreen {
private var recorder: EntityVideoRecorder?
private let idleTimerExtensionDisposable = MetaDisposable()
private var authorizationStatusDisposables = DisposableSet()
private var cameraAuthorizationStatus: AccessType = .notDetermined
private var microphoneAuthorizationStatus: AccessType = .notDetermined
fileprivate var cameraIsActive = true {
didSet {
guard let context = self.controller?.context else {
return
}
if self.cameraIsActive {
self.idleTimerExtensionDisposable.set(context.sharedContext.applicationBindings.pushIdleTimerExtension())
} else {
self.idleTimerExtensionDisposable.set(nil)
}
}
}
var isLocked = false
init(controller: MediaEditorScreen) {
self.controller = controller
self.authorizationStatusDisposables.add((DeviceAccess.authorizationStatus(subject: .camera(.video))
|> deliverOnMainQueue).start(next: { [weak self] status in
if let self {
self.cameraAuthorizationStatus = status
}
}))
self.authorizationStatusDisposables.add((DeviceAccess.authorizationStatus(subject: .microphone(.video))
|> deliverOnMainQueue).start(next: { [weak self] status in
if let self {
self.microphoneAuthorizationStatus = status
}
}))
}
deinit {
self.idleTimerExtensionDisposable.dispose()
self.authorizationStatusDisposables.dispose()
}
func requestDeviceAccess() {
DeviceAccess.authorizeAccess(to: .camera(.video), { granted in
if granted {
DeviceAccess.authorizeAccess(to: .microphone(.video))
}
})
}
func setMediaRecordingActive(_ isActive: Bool, finished: Bool, sourceView: UIView?) {
@ -29,8 +78,8 @@ extension MediaEditorScreen {
}
if isActive {
if controller.cameraAuthorizationStatus != .allowed || controller.microphoneAuthorizationStatus != .allowed {
controller.requestDeviceAccess()
if self.cameraAuthorizationStatus != .allowed || self.microphoneAuthorizationStatus != .allowed {
self.requestDeviceAccess()
return
}
@ -53,6 +102,8 @@ extension MediaEditorScreen {
}
self.recorder = recorder
controller.node.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.2))
self.cameraIsActive = true
} else {
if let recorder = self.recorder {
recorder.stopRecording(save: finished, completion: { [weak self] in
@ -65,6 +116,8 @@ extension MediaEditorScreen {
})
controller.node.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.2))
self.cameraIsActive = false
} else {
guard self.tooltipController == nil, let sourceView else {
return

View file

@ -4043,11 +4043,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
private var audioSessionDisposable: Disposable?
private let postingAvailabilityPromise = Promise<StoriesUploadAvailability>()
private var postingAvailabilityDisposable: Disposable?
private var authorizationStatusDisposables = DisposableSet()
private(set) var cameraAuthorizationStatus: AccessType = .notDetermined
private(set) var microphoneAuthorizationStatus: AccessType = .notDetermined
public init(
context: AccountContext,
subject: Signal<Subject?, NoError>,
@ -4122,20 +4118,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
if let _ = forwardSource {
self.postingAvailabilityPromise.set(self.context.engine.messages.checkStoriesUploadAvailability(target: .myStories))
}
self.authorizationStatusDisposables.add((DeviceAccess.authorizationStatus(subject: .camera(.video))
|> deliverOnMainQueue).start(next: { [weak self] status in
if let self {
self.cameraAuthorizationStatus = status
}
}))
self.authorizationStatusDisposables.add((DeviceAccess.authorizationStatus(subject: .microphone(.video))
|> deliverOnMainQueue).start(next: { [weak self] status in
if let self {
self.microphoneAuthorizationStatus = status
}
}))
}
required public init(coder aDecoder: NSCoder) {
@ -4146,7 +4128,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
self.exportDisposable.dispose()
self.audioSessionDisposable?.dispose()
self.postingAvailabilityDisposable?.dispose()
self.authorizationStatusDisposables.dispose()
}
override public func loadDisplayNode() {
@ -4225,14 +4206,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
fileprivate var isEmbeddedEditor: Bool {
return self.isEditingStory || self.forwardSource != nil
}
func requestDeviceAccess() {
DeviceAccess.authorizeAccess(to: .camera(.video), { granted in
if granted {
DeviceAccess.authorizeAccess(to: .microphone(.video))
}
})
}
func openPrivacySettings(_ privacy: MediaEditorResultPrivacy? = nil, completion: @escaping () -> Void = {}) {
self.node.mediaEditor?.maybePauseVideo()

View file

@ -117,10 +117,11 @@ final class PeerInfoRecommendedChannelsPaneNode: ASDisplayNode, PeerInfoPaneNode
return self.ready.get()
}
private let statusPromise = Promise<PeerInfoStatusData?>(nil)
var status: Signal<PeerInfoStatusData?, NoError> {
return .single(nil)
self.statusPromise.get()
}
var tabBarOffsetUpdated: ((ContainedViewLayoutTransition) -> Void)?
var tabBarOffset: CGFloat {
return 0.0
@ -159,6 +160,16 @@ final class PeerInfoRecommendedChannelsPaneNode: ASDisplayNode, PeerInfoPaneNode
strongSelf.currentState = (recommendedChannels, isPremium)
strongSelf.updateState(recommendedChannels: recommendedChannels, isPremium: isPremium, presentationData: presentationData)
})
self.statusPromise.set(context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Peer.ParticipantCount(id: peerId)
)
|> map { count -> PeerInfoStatusData? in
if let count {
return PeerInfoStatusData(text: presentationData.strings.Conversation_StatusSubscribers(Int32(count)), isActivity: true, key: .recommended)
}
return nil
})
}
deinit {

View file

@ -116,7 +116,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var subtitleBackgroundButton: HighlightTrackingButtonNode?
var subtitleArrowNode: ASImageNode?
let panelSubtitleNode: MultiScaleTextNode
let nextPanelSubtitleNode: MultiScaleTextNode
let usernameNodeContainer: ASDisplayNode
let usernameNodeRawContainer: ASDisplayNode
let usernameNode: MultiScaleTextNode
@ -195,9 +194,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.panelSubtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded])
self.panelSubtitleNode.displaysAsynchronously = false
self.nextPanelSubtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded])
self.nextPanelSubtitleNode.displaysAsynchronously = false
self.usernameNodeContainer = ASDisplayNode()
self.usernameNodeRawContainer = ASDisplayNode()
self.usernameNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded])
@ -258,7 +254,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.titleNodeContainer.addSubnode(self.titleNode)
self.subtitleNodeContainer.addSubnode(self.subtitleNode)
self.subtitleNodeContainer.addSubnode(self.panelSubtitleNode)
// self.subtitleNodeContainer.addSubnode(self.nextPanelSubtitleNode)
self.usernameNodeContainer.addSubnode(self.usernameNode)
self.regularContentNode.addSubnode(self.avatarClippingNode)
@ -778,7 +773,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.titleNode.updateTintColor(color: navigationContentsPrimaryColor, transition: navigationTransition)
self.subtitleNode.updateTintColor(color: navigationContentsSecondaryColor, transition: navigationTransition)
self.panelSubtitleNode.updateTintColor(color: navigationContentsSecondaryColor, transition: navigationTransition)
self.nextPanelSubtitleNode.updateTintColor(color: navigationContentsSecondaryColor, transition: navigationTransition)
if let navigationBar = self.controller?.navigationBar {
if let mainContentNode = navigationBar.backButtonNode.mainContentNode {
navigationTransition.updateTintColor(layer: mainContentNode.layer, color: navigationContentsAccentColor)
@ -839,7 +833,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let subtitleAttributes: MultiScaleTextState.Attributes
var subtitleIsButton: Bool = false
var panelSubtitleString: (text: String, attributes: MultiScaleTextState.Attributes)?
var nextPanelSubtitleString: (text: String, attributes: MultiScaleTextState.Attributes)?
let usernameString: (text: String, attributes: MultiScaleTextState.Attributes)
if let peer = peer {
isPremium = peer.isPremium
@ -899,7 +892,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
subtitleIsButton = true
let (maybePanelStatusData, maybeNextPanelStatusData, _) = panelStatusData
let (maybePanelStatusData, _, _) = panelStatusData
if let panelStatusData = maybePanelStatusData {
let subtitleColor: UIColor
if panelStatusData.isActivity {
@ -909,9 +902,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
panelSubtitleString = (panelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor))
}
if let nextPanelStatusData = maybeNextPanelStatusData {
nextPanelSubtitleString = (nextPanelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: .white))
}
} else if let statusData = statusData {
let subtitleColor: UIColor
if statusData.isActivity {
@ -926,7 +916,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white))
let (maybePanelStatusData, maybeNextPanelStatusData, _) = panelStatusData
let (maybePanelStatusData, _, _) = panelStatusData
if let panelStatusData = maybePanelStatusData {
let subtitleColor: UIColor
if panelStatusData.isActivity {
@ -936,9 +926,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
panelSubtitleString = (panelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor))
}
if let nextPanelStatusData = maybeNextPanelStatusData {
nextPanelSubtitleString = (nextPanelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: .white))
}
} else {
subtitleStringText = " "
subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white)
@ -1071,14 +1058,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
], mainState: TitleNodeStateRegular)
self.panelSubtitleNode.accessibilityLabel = panelSubtitleString?.text ?? subtitleStringText
let nextPanelSubtitleNodeLayout = self.nextPanelSubtitleNode.updateLayout(text: nextPanelSubtitleString?.text ?? subtitleStringText, states: [
TitleNodeStateRegular: MultiScaleTextState(attributes: nextPanelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize),
TitleNodeStateExpanded: MultiScaleTextState(attributes: nextPanelSubtitleString?.attributes ?? subtitleAttributes, constrainedSize: titleConstrainedSize)
], mainState: TitleNodeStateRegular)
if let _ = nextPanelSubtitleString {
self.nextPanelSubtitleNode.isHidden = false
}
let usernameNodeLayout = self.usernameNode.updateLayout(text: usernameString.text, states: [
TitleNodeStateRegular: MultiScaleTextState(attributes: usernameString.attributes, constrainedSize: CGSize(width: titleConstrainedSize.width, height: titleConstrainedSize.height)),
TitleNodeStateExpanded: MultiScaleTextState(attributes: usernameString.attributes, constrainedSize: CGSize(width: width - titleNodeLayout[TitleNodeStateExpanded]!.size.width - 8.0, height: titleConstrainedSize.height))
@ -1096,7 +1075,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let titleExpandedSize = titleNodeLayout[TitleNodeStateExpanded]!.size
let subtitleSize = subtitleNodeLayout[TitleNodeStateRegular]!.size
let _ = panelSubtitleNodeLayout[TitleNodeStateRegular]!.size
let _ = nextPanelSubtitleNodeLayout[TitleNodeStateRegular]!.size
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
var titleHorizontalOffset: CGFloat = 0.0
@ -1203,11 +1181,17 @@ final class PeerInfoHeaderNode: ASDisplayNode {
if (panelSubtitleString?.text ?? subtitleStringText) != subtitleStringText {
subtitleAlpha = 1.0 - effectiveAreaExpansionFraction
panelSubtitleAlpha = effectiveAreaExpansionFraction
subtitleOffset = -effectiveAreaExpansionFraction * 5.0
panelSubtitleOffset = (1.0 - effectiveAreaExpansionFraction) * 5.0
} else {
subtitleAlpha = 1.0
panelSubtitleAlpha = 0.0
if effectiveAreaExpansionFraction == 1.0 {
subtitleAlpha = 0.0
panelSubtitleAlpha = 1.0
} else {
subtitleAlpha = 1.0
panelSubtitleAlpha = 0.0
}
}
}
self.subtitleNode.update(stateFractions: [
@ -1220,11 +1204,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
], alpha: panelSubtitleAlpha, transition: transition)
self.nextPanelSubtitleNode.update(stateFractions: [
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
], alpha: panelSubtitleAlpha, transition: transition)
self.usernameNode.update(stateFractions: [
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
@ -1501,8 +1480,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.subtitleNodeRawContainer.frame = rawSubtitleFrame
transition.updateFrameAdditiveToCenter(node: self.subtitleNodeContainer, frame: CGRect(origin: rawSubtitleFrame.center, size: CGSize()))
transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: subtitleOffset), size: CGSize()))
transition.updateFrame(node: self.panelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset), size: CGSize()))
transition.updateFrame(node: self.nextPanelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset), size: CGSize()))
transition.updateFrame(node: self.panelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset - 1.0), size: CGSize()))
transition.updateFrame(node: self.usernameNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
transition.updateSublayerTransformScale(node: self.titleNodeContainer, scale: titleScale)
transition.updateSublayerTransformScale(node: self.subtitleNodeContainer, scale: subtitleScale)
@ -1517,7 +1495,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} else {
titleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * titleMinScale
subtitleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * subtitleMinScale
subtitleOffset = titleCollapseFraction * -2.0
subtitleOffset = titleCollapseFraction * -1.0
}
let rawTitleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0)
@ -1544,8 +1522,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transition.updateFrameAdditiveToCenter(node: self.usernameNodeContainer, frame: CGRect(origin: usernameCenter, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset))
}
transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: subtitleOffset), size: CGSize()))
transition.updateFrame(node: self.panelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset), size: CGSize()))
transition.updateFrame(node: self.nextPanelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset), size: CGSize()))
transition.updateFrame(node: self.panelSubtitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelSubtitleOffset - 1.0), size: CGSize()))
transition.updateFrame(node: self.usernameNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
transition.updateSublayerTransformScaleAdditive(node: self.titleNodeContainer, scale: titleScale)
transition.updateSublayerTransformScaleAdditive(node: self.subtitleNodeContainer, scale: subtitleScale)