diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index b2a55dc231..28b2fb36a7 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -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 %@"; diff --git a/submodules/Camera/Sources/Camera.swift b/submodules/Camera/Sources/Camera.swift index 0f56597b9a..6e5b470df1 100644 --- a/submodules/Camera/Sources/Camera.swift +++ b/submodules/Camera/Sources/Camera.swift @@ -117,6 +117,7 @@ private final class CameraContext { private var invalidated = false private let detectedCodesPipe = ValuePipe<[CameraCode]>() + private let audioLevelPipe = ValuePipe() fileprivate let modeChangePromise = ValuePromise(.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.size, blockBufferAllocator: nil, blockBufferMemoryAllocator: nil, flags: kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, blockBufferOut: &blockBuffer) + +// for bufferCount in 0.., count: Int) { + for i in 0..= 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 { + 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 { + 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 diff --git a/submodules/Camera/Sources/CameraOutput.swift b/submodules/Camera/Sources/CameraOutput.swift index 80daa60410..b48219f156 100644 --- a/submodules/Camera/Sources/CameraOutput.swift +++ b/submodules/Camera/Sources/CameraOutput.swift @@ -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 { diff --git a/submodules/DrawingUI/Sources/VideoRecorder.swift b/submodules/DrawingUI/Sources/VideoRecorder.swift index 03c30a9f15..b835582b5f 100644 --- a/submodules/DrawingUI/Sources/VideoRecorder.swift +++ b/submodules/DrawingUI/Sources/VideoRecorder.swift @@ -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() diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/Sources/ChatMessageJoinedChannelBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/Sources/ChatMessageJoinedChannelBubbleContentNode.swift index 064658edfa..3130bc9361 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/Sources/ChatMessageJoinedChannelBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageJoinedChannelBubbleContentNode/Sources/ChatMessageJoinedChannelBubbleContentNode.swift @@ -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, 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() private var visibleItems: [EnginePeer.Id: ComponentView] = [:] - 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, diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorRecording.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorRecording.swift index 595de405f2..d2d30465c1 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorRecording.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorRecording.swift @@ -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 diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index c4f089e5b2..32eff31e10 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -4043,11 +4043,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate private var audioSessionDisposable: Disposable? private let postingAvailabilityPromise = Promise() 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, @@ -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() diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedChannelsPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedChannelsPane.swift index b38ab21e95..d36445ea24 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedChannelsPane.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoRecommendedChannelsPane.swift @@ -117,10 +117,11 @@ final class PeerInfoRecommendedChannelsPaneNode: ASDisplayNode, PeerInfoPaneNode return self.ready.get() } + private let statusPromise = Promise(nil) var status: Signal { - 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 { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 834ccd76c5..6fc70332b1 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -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)