mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Voice Chat UI improvements
This commit is contained in:
parent
572bc95254
commit
41bcbd14f0
@ -1353,7 +1353,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
case let .peer(_, renderedPeer, _, _, presence, _ ,_ ,_, _, _, displayAsMessage, _):
|
case let .peer(_, renderedPeer, _, _, presence, _ ,_ ,_, _, _, displayAsMessage, _):
|
||||||
if !displayAsMessage {
|
if !displayAsMessage {
|
||||||
if let peer = renderedPeer.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId {
|
if let peer = renderedPeer.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId {
|
||||||
var updatedPresence = TelegramUserPresence(status: presence.status, lastActivity: 0)
|
let updatedPresence = TelegramUserPresence(status: presence.status, lastActivity: 0)
|
||||||
let relativeStatus = relativeUserPresenceStatus(updatedPresence, relativeTo: timestamp)
|
let relativeStatus = relativeUserPresenceStatus(updatedPresence, relativeTo: timestamp)
|
||||||
if case .online = relativeStatus {
|
if case .online = relativeStatus {
|
||||||
online = true
|
online = true
|
||||||
@ -1361,10 +1361,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
animateOnline = true
|
animateOnline = true
|
||||||
} else if let channel = renderedPeer.peer as? TelegramChannel {
|
} else if let channel = renderedPeer.peer as? TelegramChannel {
|
||||||
onlineIsVoiceChat = true
|
onlineIsVoiceChat = true
|
||||||
if channel.flags.contains(.hasVoiceChat) {
|
if channel.flags.contains(.hasVoiceChat) && item.interaction.searchTextHighightState == nil {
|
||||||
online = true
|
online = true
|
||||||
animateOnline = true
|
|
||||||
}
|
}
|
||||||
|
animateOnline = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,15 @@ private final class Curve {
|
|||||||
let minSpeed: CGFloat
|
let minSpeed: CGFloat
|
||||||
let maxSpeed: CGFloat
|
let maxSpeed: CGFloat
|
||||||
|
|
||||||
var size: CGSize
|
var size: CGSize {
|
||||||
|
didSet {
|
||||||
|
if self.size != oldValue {
|
||||||
|
self.fromPoints = nil
|
||||||
|
self.toPoints = nil
|
||||||
|
self.animateToNewShape()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let alpha: CGFloat
|
let alpha: CGFloat
|
||||||
var currentOffset: CGFloat = 1.0
|
var currentOffset: CGFloat = 1.0
|
||||||
var minOffset: CGFloat = 0.0
|
var minOffset: CGFloat = 0.0
|
||||||
@ -177,7 +185,7 @@ private final class Curve {
|
|||||||
private func generateNextCurve(for size: CGSize) -> [CGPoint] {
|
private func generateNextCurve(for size: CGSize) -> [CGPoint] {
|
||||||
let randomness = minRandomness + (maxRandomness - minRandomness) * speedLevel
|
let randomness = minRandomness + (maxRandomness - minRandomness) * speedLevel
|
||||||
return curve(pointsCount: pointsCount, randomness: randomness).map {
|
return curve(pointsCount: pointsCount, randomness: randomness).map {
|
||||||
return CGPoint(x: $0.x * CGFloat(size.width), y: size.height - 14.0 + $0.y * 12.0)
|
return CGPoint(x: $0.x * CGFloat(size.width), y: size.height - 17.0 + $0.y * 12.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,6 +553,6 @@ public class CallStatusBarNodeImpl: CallStatusBarNode {
|
|||||||
self.subtitleNode.frame = CGRect(origin: CGPoint(x: horizontalOrigin + animationSize + iconSpacing + titleSize.width + spacing, y: verticalOrigin + floor((contentHeight - subtitleSize.height) / 2.0)), size: subtitleSize)
|
self.subtitleNode.frame = CGRect(origin: CGPoint(x: horizontalOrigin + animationSize + iconSpacing + titleSize.width + spacing, y: verticalOrigin + floor((contentHeight - subtitleSize.height) / 2.0)), size: subtitleSize)
|
||||||
|
|
||||||
self.backgroundNode.speaking = !self.currentIsMuted
|
self.backgroundNode.speaking = !self.currentIsMuted
|
||||||
self.backgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + 14.0))
|
self.backgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + 17.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
|||||||
|
|
||||||
let titleNode: ImmediateTextNode
|
let titleNode: ImmediateTextNode
|
||||||
let textNode: ImmediateTextNode
|
let textNode: ImmediateTextNode
|
||||||
|
private var textIsActive = false
|
||||||
private let muteIconNode: ASImageNode
|
private let muteIconNode: ASImageNode
|
||||||
|
|
||||||
private let avatarsContext: AnimatedAvatarSetContext
|
private let avatarsContext: AnimatedAvatarSetContext
|
||||||
@ -229,6 +230,22 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
|||||||
self.muteIconNode.image = PresentationResourcesChat.chatTitleMuteIcon(presentationData.theme)
|
self.muteIconNode.image = PresentationResourcesChat.chatTitleMuteIcon(presentationData.theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func animateTextChange() {
|
||||||
|
if let snapshotView = self.textNode.view.snapshotContentTree() {
|
||||||
|
let offset: CGFloat = self.textIsActive ? -7.0 : 7.0
|
||||||
|
self.textNode.view.superview?.insertSubview(snapshotView, belowSubview: self.textNode.view)
|
||||||
|
|
||||||
|
snapshotView.frame = self.textNode.frame
|
||||||
|
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||||
|
snapshotView?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -offset), duration: 0.2, removeOnCompletion: false, additive: true)
|
||||||
|
|
||||||
|
self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
self.textNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: 0.2, additive: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func update(data: GroupCallPanelData) {
|
public func update(data: GroupCallPanelData) {
|
||||||
let previousData = self.currentData
|
let previousData = self.currentData
|
||||||
self.currentData = data
|
self.currentData = data
|
||||||
@ -271,6 +288,11 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
|||||||
membersTextIsActive = false
|
membersTextIsActive = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strongSelf.textIsActive != membersTextIsActive {
|
||||||
|
strongSelf.textIsActive = membersTextIsActive
|
||||||
|
strongSelf.animateTextChange()
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: membersTextIsActive ? strongSelf.theme.chat.inputPanel.panelControlAccentColor : strongSelf.theme.chat.inputPanel.secondaryTextColor)
|
strongSelf.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: membersTextIsActive ? strongSelf.theme.chat.inputPanel.panelControlAccentColor : strongSelf.theme.chat.inputPanel.secondaryTextColor)
|
||||||
|
|
||||||
strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { $0.peer }, animated: false)
|
strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { $0.peer }, animated: false)
|
||||||
@ -368,6 +390,11 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
|||||||
membersTextIsActive = false
|
membersTextIsActive = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.textIsActive != membersTextIsActive {
|
||||||
|
self.textIsActive = membersTextIsActive
|
||||||
|
self.animateTextChange()
|
||||||
|
}
|
||||||
|
|
||||||
self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: membersTextIsActive ? self.theme.chat.inputPanel.panelControlAccentColor : self.theme.chat.inputPanel.secondaryTextColor)
|
self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: membersTextIsActive ? self.theme.chat.inputPanel.panelControlAccentColor : self.theme.chat.inputPanel.secondaryTextColor)
|
||||||
|
|
||||||
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }, animated: false)
|
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }, animated: false)
|
||||||
@ -481,7 +508,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
|||||||
let foregroundFrame = self.micButtonForegroundNode.view.convert(self.micButtonForegroundNode.bounds, to: nil)
|
let foregroundFrame = self.micButtonForegroundNode.view.convert(self.micButtonForegroundNode.bounds, to: nil)
|
||||||
|
|
||||||
let backgroundView = UIView()
|
let backgroundView = UIView()
|
||||||
backgroundView.backgroundColor = UIColor(rgb: 0x30b251)
|
backgroundView.backgroundColor = (self.micButtonBackgroundNodeIsMuted ?? true) ? UIColor(rgb: 0xb6b6bb) : UIColor(rgb: 0x30b251)
|
||||||
backgroundView.frame = backgroundFrame
|
backgroundView.frame = backgroundFrame
|
||||||
backgroundView.layer.cornerRadius = backgroundFrame.height / 2.0
|
backgroundView.layer.cornerRadius = backgroundFrame.height / 2.0
|
||||||
|
|
||||||
|
@ -576,7 +576,9 @@ private class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
|||||||
self.animator = animator
|
self.animator = animator
|
||||||
}
|
}
|
||||||
animator.isPaused = false
|
animator.isPaused = false
|
||||||
animator.frameInterval = state.frameInterval
|
if self.transition == nil {
|
||||||
|
animator.frameInterval = state.frameInterval
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.animator?.isPaused = true
|
self.animator?.isPaused = true
|
||||||
}
|
}
|
||||||
@ -619,7 +621,7 @@ private class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
|||||||
|
|
||||||
var appearanceProgress: CGFloat = 1.0
|
var appearanceProgress: CGFloat = 1.0
|
||||||
var glowScale: CGFloat = 0.75
|
var glowScale: CGFloat = 0.75
|
||||||
if let transition = parameters.transition, transition.previousState == .connecting {
|
if let transition = parameters.transition, transition.previousState == .connecting || transition.previousState == .disabled {
|
||||||
appearanceProgress = transition.transition
|
appearanceProgress = transition.transition
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -751,15 +753,18 @@ private class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var clearInside: CGFloat?
|
var clearInsideTransition: CGFloat?
|
||||||
if parameters.state is VoiceChatActionButtonBackgroundNodeBlobState {
|
if parameters.state is VoiceChatActionButtonBackgroundNodeBlobState {
|
||||||
let path = CGMutablePath()
|
let path = CGMutablePath()
|
||||||
path.addEllipse(in: buttonRect.insetBy(dx: -lineWidth / 2.0, dy: -lineWidth / 2.0))
|
path.addEllipse(in: buttonRect.insetBy(dx: -lineWidth / 2.0, dy: -lineWidth / 2.0))
|
||||||
context.addPath(path)
|
context.addPath(path)
|
||||||
context.clip()
|
context.clip()
|
||||||
if let transition = parameters.transition, transition.previousState == .connecting || transition.previousState == .disabled, transition.transition > 0.5 {
|
if let transition = parameters.transition {
|
||||||
let progress = (transition.transition - 0.5) / 0.5
|
if transition.previousState == .connecting, transition.transition > 0.5 {
|
||||||
clearInside = progress
|
clearInsideTransition = (transition.transition - 0.5) / 0.5
|
||||||
|
} else if transition.previousState == .disabled {
|
||||||
|
clearInsideTransition = transition.transition
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawGradient = true
|
drawGradient = true
|
||||||
@ -774,9 +779,9 @@ private class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let clearInside = clearInside {
|
if let transition = clearInsideTransition {
|
||||||
context.setFillColor(greyColor.cgColor)
|
context.setFillColor(greyColor.cgColor)
|
||||||
context.fillEllipse(in: buttonRect.insetBy(dx: clearInside * radius, dy: clearInside * radius))
|
context.fillEllipse(in: buttonRect.insetBy(dx: transition * radius, dy: transition * radius))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ private final class VoiceChatControllerTitleView: UIView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class VoiceChatController: ViewController {
|
public final class VoiceChatController: ViewController {
|
||||||
private final class Node: ViewControllerTracingNode {
|
private final class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate {
|
||||||
private struct ListTransition {
|
private struct ListTransition {
|
||||||
let deletions: [ListViewDeleteItem]
|
let deletions: [ListViewDeleteItem]
|
||||||
let insertions: [ListViewInsertItem]
|
let insertions: [ListViewInsertItem]
|
||||||
@ -412,7 +412,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let callState = strongSelf.callState, (callState.canManageCall && !callState.adminIds.contains(strongSelf.context.account.peerId)) {
|
if let callState = strongSelf.callState, (callState.canManageCall && !callState.adminIds.contains(peer.id)) {
|
||||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_RemovePeer, textColor: .destructive, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_RemovePeer, textColor: .destructive, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.destructiveActionTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.destructiveActionTextColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
@ -523,15 +523,19 @@ public final class VoiceChatController: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let peer = peerViewMainPeer(view), let channel = peer as? TelegramChannel {
|
|
||||||
if !(channel.addressName ?? "").isEmpty || (channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers)) {
|
|
||||||
strongSelf.optionsButton.isHidden = false
|
|
||||||
} else {
|
|
||||||
strongSelf.optionsButton.isHidden = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strongSelf.didSetDataReady {
|
if !strongSelf.didSetDataReady {
|
||||||
|
if let peer = peerViewMainPeer(view), let channel = peer as? TelegramChannel {
|
||||||
|
let addressName = channel.addressName ?? ""
|
||||||
|
if !addressName.isEmpty || (channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers)) {
|
||||||
|
if addressName.isEmpty {
|
||||||
|
let _ = ensuredExistingPeerExportedInvitation(account: strongSelf.context.account, peerId: call.peerId).start()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
strongSelf.optionsButton.isUserInteractionEnabled = false
|
||||||
|
strongSelf.optionsButton.alpha = 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.didSetDataReady = true
|
strongSelf.didSetDataReady = true
|
||||||
strongSelf.controller?.dataReady.set(true)
|
strongSelf.controller?.dataReady.set(true)
|
||||||
}
|
}
|
||||||
@ -675,8 +679,6 @@ public final class VoiceChatController: ViewController {
|
|||||||
optionsButtonItem.target = self
|
optionsButtonItem.target = self
|
||||||
optionsButtonItem.action = #selector(self.rightNavigationButtonAction)
|
optionsButtonItem.action = #selector(self.rightNavigationButtonAction)
|
||||||
self.controller?.navigationItem.setRightBarButton(optionsButtonItem, animated: false)
|
self.controller?.navigationItem.setRightBarButton(optionsButtonItem, animated: false)
|
||||||
|
|
||||||
let _ = ensuredExistingPeerExportedInvitation(account: self.context.account, peerId: call.peerId).start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -701,6 +703,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.actionButtonPressGesture(_:)))
|
let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.actionButtonPressGesture(_:)))
|
||||||
longTapRecognizer.minimumPressDuration = 0.001
|
longTapRecognizer.minimumPressDuration = 0.001
|
||||||
|
longTapRecognizer.delegate = self
|
||||||
self.actionButton.view.addGestureRecognizer(longTapRecognizer)
|
self.actionButton.view.addGestureRecognizer(longTapRecognizer)
|
||||||
|
|
||||||
let panRecognizer = CallPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
let panRecognizer = CallPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
||||||
@ -714,7 +717,9 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func rightNavigationButtonAction() {
|
@objc private func rightNavigationButtonAction() {
|
||||||
self.optionsButton.contextAction?(self.optionsButton.containerNode, nil)
|
if self.optionsButton.isUserInteractionEnabled {
|
||||||
|
self.optionsButton.contextAction?(self.optionsButton.containerNode, nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func leavePressed() {
|
@objc private func leavePressed() {
|
||||||
@ -726,6 +731,14 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
private var actionButtonPressGestureStartTime: Double = 0.0
|
private var actionButtonPressGestureStartTime: Double = 0.0
|
||||||
|
|
||||||
|
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
if let callState = self.callState, case .connected = callState.networkState, let muteState = callState.muteState, !muteState.canUnmute {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func actionButtonPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) {
|
@objc private func actionButtonPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) {
|
||||||
guard let callState = self.callState else {
|
guard let callState = self.callState else {
|
||||||
return
|
return
|
||||||
@ -1010,7 +1023,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (backgroundView, foregroundView) = sourcePanel.rightButtonSnapshotViews(), !self.optionsButton.isHidden {
|
if let (backgroundView, foregroundView) = sourcePanel.rightButtonSnapshotViews(), self.optionsButton.isUserInteractionEnabled {
|
||||||
self.view.addSubview(backgroundView)
|
self.view.addSubview(backgroundView)
|
||||||
self.view.addSubview(foregroundView)
|
self.view.addSubview(foregroundView)
|
||||||
|
|
||||||
@ -1190,6 +1203,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
memberState = .speaking
|
memberState = .speaking
|
||||||
} else {
|
} else {
|
||||||
memberState = .listening
|
memberState = .listening
|
||||||
|
memberMuteState = member.muteState
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
memberState = speakingPeers.contains(member.peer.id) ? .speaking : .listening
|
memberState = speakingPeers.contains(member.peer.id) ? .speaking : .listening
|
||||||
|
@ -14,14 +14,14 @@ func optionsButtonImage() -> UIImage? {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
final class VoiceChatOptionsButton: HighlightableButtonNode {
|
final class VoiceChatOptionsButton: ASDisplayNode {
|
||||||
let extractedContainerNode: ContextExtractedContentContainingNode
|
let extractedContainerNode: ContextExtractedContentContainingNode
|
||||||
let containerNode: ContextControllerSourceNode
|
let containerNode: ContextControllerSourceNode
|
||||||
private let iconNode: ASImageNode
|
private let iconNode: ASImageNode
|
||||||
|
|
||||||
var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
|
|
||||||
init() {
|
override init() {
|
||||||
self.extractedContainerNode = ContextExtractedContentContainingNode()
|
self.extractedContainerNode = ContextExtractedContentContainingNode()
|
||||||
self.containerNode = ContextControllerSourceNode()
|
self.containerNode = ContextControllerSourceNode()
|
||||||
self.containerNode.isGestureEnabled = false
|
self.containerNode.isGestureEnabled = false
|
||||||
|
@ -379,8 +379,8 @@ public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
|||||||
let verticalOffset: CGFloat = 0.0
|
let verticalOffset: CGFloat = 0.0
|
||||||
let avatarSize: CGFloat = 40.0
|
let avatarSize: CGFloat = 40.0
|
||||||
|
|
||||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 12.0 - rightInset - 25.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 12.0 - rightInset - 30.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
let (statusLayout, statusApply) = makeStatusLayout(TextNodeLayoutArguments(attributedString: statusAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - rightInset - 25.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (statusLayout, statusApply) = makeStatusLayout(TextNodeLayoutArguments(attributedString: statusAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - rightInset - 30.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
let insets = UIEdgeInsets()
|
let insets = UIEdgeInsets()
|
||||||
|
|
||||||
@ -622,11 +622,16 @@ public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
|||||||
animationNode = VoiceChatMicrophoneNode()
|
animationNode = VoiceChatMicrophoneNode()
|
||||||
strongSelf.animationNode = animationNode
|
strongSelf.animationNode = animationNode
|
||||||
strongSelf.actionButtonNode.addSubnode(animationNode)
|
strongSelf.actionButtonNode.addSubnode(animationNode)
|
||||||
|
if let _ = strongSelf.iconNode {
|
||||||
|
animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
animationNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
animationNode.update(state: VoiceChatMicrophoneNode.State(muted: muted, color: color), animated: true)
|
animationNode.update(state: VoiceChatMicrophoneNode.State(muted: muted, color: color), animated: true)
|
||||||
strongSelf.actionButtonNode.isUserInteractionEnabled = false
|
strongSelf.actionButtonNode.isUserInteractionEnabled = false
|
||||||
} else if let animationNode = strongSelf.animationNode {
|
} else if let animationNode = strongSelf.animationNode {
|
||||||
strongSelf.animationNode = nil
|
strongSelf.animationNode = nil
|
||||||
|
animationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
animationNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false, completion: { [weak animationNode] _ in
|
animationNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false, completion: { [weak animationNode] _ in
|
||||||
animationNode?.removeFromSupernode()
|
animationNode?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
@ -641,6 +646,11 @@ public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
|||||||
iconNode.contentMode = .center
|
iconNode.contentMode = .center
|
||||||
strongSelf.iconNode = iconNode
|
strongSelf.iconNode = iconNode
|
||||||
strongSelf.actionButtonNode.addSubnode(iconNode)
|
strongSelf.actionButtonNode.addSubnode(iconNode)
|
||||||
|
|
||||||
|
if let _ = strongSelf.animationNode {
|
||||||
|
iconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
iconNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if invited {
|
if invited {
|
||||||
@ -651,6 +661,7 @@ public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
|||||||
strongSelf.actionButtonNode.isUserInteractionEnabled = !invited
|
strongSelf.actionButtonNode.isUserInteractionEnabled = !invited
|
||||||
} else if let iconNode = strongSelf.iconNode {
|
} else if let iconNode = strongSelf.iconNode {
|
||||||
strongSelf.iconNode = nil
|
strongSelf.iconNode = nil
|
||||||
|
iconNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
iconNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false, completion: { [weak iconNode] _ in
|
iconNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false, completion: { [weak iconNode] _ in
|
||||||
iconNode?.removeFromSupernode()
|
iconNode?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
|
@ -496,37 +496,35 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .groupPhoneCall(callId, accessHash, duration):
|
case let .groupPhoneCall(callId, accessHash, nil), let .inviteToGroupPhoneCall(callId, accessHash, _):
|
||||||
let peerId = message.id.peerId
|
let peerId = message.id.peerId
|
||||||
if duration == nil {
|
let callResult = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: callId, accessHash: accessHash), endCurrentIfAny: false, sourcePanel: nil)
|
||||||
let callResult = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: callId, accessHash: accessHash), endCurrentIfAny: false, sourcePanel: nil)
|
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
|
||||||
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
|
if currentPeerId == peerId {
|
||||||
if currentPeerId == peerId {
|
strongSelf.context.sharedContext.navigateToCurrentCall(sourcePanel: nil)
|
||||||
strongSelf.context.sharedContext.navigateToCurrentCall(sourcePanel: nil)
|
} else {
|
||||||
} else {
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
let _ = (strongSelf.context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
|
||||||
let _ = (strongSelf.context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
|
return (transaction.getPeer(peerId), currentPeerId.flatMap(transaction.getPeer))
|
||||||
return (transaction.getPeer(peerId), currentPeerId.flatMap(transaction.getPeer))
|
|
||||||
}
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] peer, current in
|
|
||||||
guard let strongSelf = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let peer = peer else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if let current = current {
|
|
||||||
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
|
|
||||||
if let strongSelf = self {
|
|
||||||
let _ = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: callId, accessHash: accessHash), endCurrentIfAny: true, sourcePanel: nil)
|
|
||||||
}
|
|
||||||
})]), in: .window(.root))
|
|
||||||
} else {
|
|
||||||
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_ExternalCallInProgressMessage, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
|
|
||||||
})]), in: .window(.root))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] peer, current in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let peer = peer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let current = current {
|
||||||
|
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
|
||||||
|
if let strongSelf = self {
|
||||||
|
let _ = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: callId, accessHash: accessHash), endCurrentIfAny: true, sourcePanel: nil)
|
||||||
|
}
|
||||||
|
})]), in: .window(.root))
|
||||||
|
} else {
|
||||||
|
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_ExternalCallInProgressMessage, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
|
||||||
|
})]), in: .window(.root))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
Loading…
x
Reference in New Issue
Block a user