Voice Chat UI improvements

This commit is contained in:
Ilya Laktyushin 2020-12-04 20:30:51 +04:00
parent 4c27869b04
commit e0aacc155c
7 changed files with 66 additions and 11 deletions

View File

@ -697,7 +697,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
transition.updateAlpha(layer: self.highlightedBackgroundNode.layer, alpha: highlightProgress) transition.updateAlpha(layer: self.highlightedBackgroundNode.layer, alpha: highlightProgress)
if let item = self.item { if let item = self.item {
self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted, voiceChat: self.onlineIsVoiceChat), color: nil) self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted, voiceChat: self.onlineIsVoiceChat), color: nil, transition: transition)
} }
} else { } else {
if self.highlightedBackgroundNode.supernode != nil { if self.highlightedBackgroundNode.supernode != nil {
@ -717,7 +717,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else { } else {
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: self.onlineIsVoiceChat) onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: self.onlineIsVoiceChat)
} }
self.onlineNode.setImage(onlineIcon, color: nil) self.onlineNode.setImage(onlineIcon, color: nil, transition: transition)
} }
} }
} }
@ -1543,7 +1543,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else { } else {
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: onlineIsVoiceChat) onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: onlineIsVoiceChat)
} }
strongSelf.onlineNode.setImage(onlineIcon, color: item.presentationData.theme.list.itemCheckColors.foregroundColor) strongSelf.onlineNode.setImage(onlineIcon, color: item.presentationData.theme.list.itemCheckColors.foregroundColor, transition: .immediate)
let _ = measureApply() let _ = measureApply()
let _ = dateApply() let _ = dateApply()

View File

@ -1426,11 +1426,16 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
} }
if self.stackFromBottom { if self.stackFromBottom {
let previousCompleteHeight = completeHeight
let updatedCompleteHeight = max(completeHeight, self.visibleSize.height) let updatedCompleteHeight = max(completeHeight, self.visibleSize.height)
let deltaCompleteHeight = updatedCompleteHeight - completeHeight let deltaCompleteHeight = updatedCompleteHeight - completeHeight
topItemEdge -= deltaCompleteHeight topItemEdge -= deltaCompleteHeight
bottomItemEdge -= deltaCompleteHeight bottomItemEdge -= deltaCompleteHeight
completeHeight = updatedCompleteHeight completeHeight = updatedCompleteHeight
if let _ = self.keepMinimalScrollHeightWithTopInset {
completeHeight += effectiveInsets.top + previousCompleteHeight
}
} }
} }
} }

View File

@ -209,7 +209,7 @@ public final class HorizontalPeerItemNode: ListViewItemNode {
strongSelf.badgeBackgroundNode.isHidden = true strongSelf.badgeBackgroundNode.isHidden = true
} }
strongSelf.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.theme, state: .regular), color: nil) strongSelf.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.theme, state: .regular), color: nil, transition: .immediate)
strongSelf.onlineNode.frame = CGRect(x: itemLayout.size.width - onlineLayout.width - 18.0, y: itemLayout.size.height - onlineLayout.height - 18.0, width: onlineLayout.width, height: onlineLayout.height) strongSelf.onlineNode.frame = CGRect(x: itemLayout.size.width - onlineLayout.width - 18.0, y: itemLayout.size.height - onlineLayout.height - 18.0, width: onlineLayout.width, height: onlineLayout.height)
let _ = badgeApply() let _ = badgeApply()

View File

@ -123,7 +123,17 @@ public final class PeerOnlineMarkerNode: ASDisplayNode {
self.addSubnode(self.iconNode) self.addSubnode(self.iconNode)
} }
public func setImage(_ image: UIImage?, color: UIColor?) { public func setImage(_ image: UIImage?, color: UIColor?, transition: ContainedViewLayoutTransition) {
if case let .animated(duration, curve) = transition {
if let snapshotLayer = self.iconNode.layer.snapshotContentTree() {
snapshotLayer.frame = self.iconNode.frame
self.iconNode.layer.insertSublayer(snapshotLayer, at: 0)
snapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: false, completion: { [weak snapshotLayer] _ in
snapshotLayer?.removeFromSuperlayer()
})
}
}
self.iconNode.image = image self.iconNode.image = image
if let color = color { if let color = color {
self.color = color self.color = color

View File

@ -166,7 +166,7 @@ public final class SelectablePeerNode: ASDisplayNode {
let (onlineSize, onlineApply) = onlineLayout(online, false) let (onlineSize, onlineApply) = onlineLayout(online, false)
let _ = onlineApply(false) let _ = onlineApply(false)
self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(theme, state: .panel), color: nil) self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(theme, state: .panel), color: nil, transition: .immediate)
self.onlineNode.frame = CGRect(origin: CGPoint(), size: onlineSize) self.onlineNode.frame = CGRect(origin: CGPoint(), size: onlineSize)
self.setNeedsLayout() self.setNeedsLayout()

View File

@ -411,6 +411,12 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
self.maskView.addSubview(self.maskBlobView) self.maskView.addSubview(self.maskBlobView)
self.maskView.layer.addSublayer(self.maskCircleLayer) self.maskView.layer.addSublayer(self.maskCircleLayer)
self.maskBlobView.scaleUpdated = { [weak self] scale in
if let strongSelf = self {
strongSelf.updateGlowScale(strongSelf.isActive ? scale : nil)
}
}
} }
private func setupGradientAnimations() { private func setupGradientAnimations() {
@ -483,6 +489,18 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
} }
} }
func updateGlowScale(_ scale: CGFloat?) {
return
if let scale = scale {
self.maskGradientLayer.transform = CATransform3DMakeScale(0.89 + 0.11 * scale, 0.89 + 0.11 * scale, 1.0)
} else {
// let initialScale: CGFloat = ((self.maskGradientLayer.value(forKeyPath: "presentationLayer.transform.scale.x") as? NSNumber)?.floatValue).flatMap({ CGFloat($0) }) ?? (((self.maskGradientLayer.value(forKeyPath: "transform.scale.x") as? NSNumber)?.floatValue).flatMap({ CGFloat($0) }) ?? (effectivePreviousActive ? 0.95 : 0.8))
// let targetScale: CGFloat = self.isActive ? 0.89 : 0.85
// self.maskGradientLayer.transform = CATransform3DMakeScale(targetScale, targetScale, 1.0)
// self.maskGradientLayer.animateScale(from: initialScale, to: targetScale, duration: 0.3)
}
}
func updateGlowAndGradientAnimations(active: Bool?, previousActive: Bool? = nil) { func updateGlowAndGradientAnimations(active: Bool?, previousActive: Bool? = nil) {
let effectivePreviousActive = previousActive ?? false let effectivePreviousActive = previousActive ?? false
@ -587,6 +605,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
CATransaction.commit() CATransaction.commit()
} }
var isActive = false
func updateAnimations() { func updateAnimations() {
if !self.isCurrentlyInHierarchy { if !self.isCurrentlyInHierarchy {
self.foregroundGradientLayer.removeAllAnimations() self.foregroundGradientLayer.removeAllAnimations()
@ -601,10 +620,12 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
case .connecting: case .connecting:
self.updatedActive?(false) self.updatedActive?(false)
self.setupProgressAnimations() self.setupProgressAnimations()
self.isActive = false
case let .blob(newActive): case let .blob(newActive):
if let transition = self.transition { if let transition = self.transition {
if transition == .connecting { if transition == .connecting {
self.playConnectionAnimation(active: newActive) { [weak self] in self.playConnectionAnimation(active: newActive) { [weak self] in
self?.isActive = newActive
self?.transition = nil self?.transition = nil
} }
} else if transition == .disabled { } else if transition == .disabled {
@ -612,11 +633,14 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
} else if case let .blob(previousActive) = transition { } else if case let .blob(previousActive) = transition {
updateGlowAndGradientAnimations(active: newActive, previousActive: previousActive) updateGlowAndGradientAnimations(active: newActive, previousActive: previousActive)
self.transition = nil self.transition = nil
self.isActive = newActive
} }
} else { } else {
self.maskBlobView.startAnimating() self.maskBlobView.startAnimating()
} }
case .disabled: case .disabled:
self.isActive = false
self.updateGlowScale(nil)
break break
} }
} }
@ -684,6 +708,12 @@ private final class VoiceBlobView: UIView {
private var audioLevel: CGFloat = 0.0 private var audioLevel: CGFloat = 0.0
var presentationAudioLevel: CGFloat = 0.0 var presentationAudioLevel: CGFloat = 0.0
var scaleUpdated: ((CGFloat) -> Void)? {
didSet {
self.bigBlob.scaleUpdated = self.scaleUpdated
}
}
private(set) var isAnimating = false private(set) var isAnimating = false
public typealias BlobRange = (min: CGFloat, max: CGFloat) public typealias BlobRange = (min: CGFloat, max: CGFloat)
@ -805,12 +835,15 @@ final class BlobView: UIView {
let minScale: CGFloat let minScale: CGFloat
let maxScale: CGFloat let maxScale: CGFloat
var scaleUpdated: ((CGFloat) -> Void)?
var level: CGFloat = 0 { var level: CGFloat = 0 {
didSet { didSet {
CATransaction.begin() CATransaction.begin()
CATransaction.setDisableActions(true) CATransaction.setDisableActions(true)
let lv = minScale + (maxScale - minScale) * level let lv = minScale + (maxScale - minScale) * level
shapeLayer.transform = CATransform3DMakeScale(lv, lv, 1) shapeLayer.transform = CATransform3DMakeScale(lv, lv, 1)
self.scaleUpdated?(level)
CATransaction.commit() CATransaction.commit()
} }
} }

View File

@ -456,6 +456,7 @@ public final class VoiceChatController: ViewController {
self.listNode.verticalScrollIndicatorColor = UIColor(white: 1.0, alpha: 0.3) self.listNode.verticalScrollIndicatorColor = UIColor(white: 1.0, alpha: 0.3)
self.listNode.clipsToBounds = true self.listNode.clipsToBounds = true
self.listNode.stackFromBottom = true self.listNode.stackFromBottom = true
self.listNode.keepMinimalScrollHeightWithTopInset = 0
self.topPanelNode = ASDisplayNode() self.topPanelNode = ASDisplayNode()
self.topPanelNode.backgroundColor = panelBackgroundColor self.topPanelNode.backgroundColor = panelBackgroundColor
@ -1360,7 +1361,7 @@ public final class VoiceChatController: ViewController {
actionButtonState = .connecting actionButtonState = .connecting
actionButtonTitle = self.presentationData.strings.VoiceChat_Connecting actionButtonTitle = self.presentationData.strings.VoiceChat_Connecting
actionButtonSubtitle = "" actionButtonSubtitle = ""
audioButtonAppearance = .color(.custom(0x1c1c1e)) audioButtonAppearance = .color(.custom(0x2c2c2e))
actionButtonEnabled = false actionButtonEnabled = false
case .connected: case .connected:
if let muteState = callState.muteState, !self.pushingToTalk { if let muteState = callState.muteState, !self.pushingToTalk {
@ -1506,6 +1507,7 @@ public final class VoiceChatController: ViewController {
} }
} }
private var isFirstTime = true
private func dequeueTransition() { private func dequeueTransition() {
guard let _ = self.validLayout, let transition = self.enqueuedTransitions.first else { guard let _ = self.validLayout, let transition = self.enqueuedTransitions.first else {
return return
@ -1522,7 +1524,14 @@ public final class VoiceChatController: ViewController {
options.insert(.LowLatency) options.insert(.LowLatency)
options.insert(.PreferSynchronousResourceLoading) options.insert(.PreferSynchronousResourceLoading)
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
var scrollToItem: ListViewScrollToItem?
if self.isFirstTime {
self.isFirstTime = false
scrollToItem = ListViewScrollToItem(index: 0, position: .bottom(0), animated: false, curve: .Default(duration: nil), directionHint: .Up)
}
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: scrollToItem, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
@ -1638,7 +1647,6 @@ public final class VoiceChatController: ViewController {
if let gestureRecognizers = view.gestureRecognizers, view != self.view { if let gestureRecognizers = view.gestureRecognizers, view != self.view {
for gestureRecognizer in gestureRecognizers { for gestureRecognizer in gestureRecognizers {
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer, gestureRecognizer.isEnabled { if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer, gestureRecognizer.isEnabled {
print(view)
if panGestureRecognizer.state != .began { if panGestureRecognizer.state != .began {
panGestureRecognizer.isEnabled = false panGestureRecognizer.isEnabled = false
panGestureRecognizer.isEnabled = true panGestureRecognizer.isEnabled = true
@ -1690,8 +1698,7 @@ public final class VoiceChatController: ViewController {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event) let result = super.hitTest(point, with: event)
print("actually hitting")
if result === self.topPanelNode.view || result === self.bottomPanelNode.view { if result === self.topPanelNode.view || result === self.bottomPanelNode.view {
return self.view return self.view
} }