Merge commit '82ff76bb694e994f668a8c371f14167408246063'

This commit is contained in:
Ali 2020-11-26 02:55:01 +04:00
commit d1d533a443
13 changed files with 141 additions and 58 deletions

View File

@ -227,7 +227,11 @@ public protocol PresentationGroupCall: class {
func toggleIsMuted() func toggleIsMuted()
func setIsMuted(_ value: Bool) func setIsMuted(_ value: Bool)
func setCurrentAudioOutput(_ output: AudioSessionOutput) func setCurrentAudioOutput(_ output: AudioSessionOutput)
func updateMuteState(peerId: PeerId, isMuted: Bool) func updateMuteState(peerId: PeerId, isMuted: Bool)
func invitePeer(_ peerId: PeerId)
var invitedPeers: Signal<Set<PeerId>, NoError> { get }
} }
public protocol PresentationCallManager: class { public protocol PresentationCallManager: class {

View File

@ -1,4 +1,4 @@
#import <UIKit/UIKit.h> #import <Foundation/Foundation.h>
#import <FFMpegBinding/FFMpegGlobals.h> #import <FFMpegBinding/FFMpegGlobals.h>
#import <FFMpegBinding/FFMpegAVCodecContext.h> #import <FFMpegBinding/FFMpegAVCodecContext.h>

View File

@ -1,3 +1,4 @@
import UIKit
import CoreMedia import CoreMedia
import Accelerate import Accelerate
import FFMpegBinding import FFMpegBinding

View File

@ -1,4 +1,5 @@
import Foundation import Foundation
import UIKit
import SwiftSignalKit import SwiftSignalKit
import Postbox import Postbox
import TelegramCore import TelegramCore

View File

@ -1,4 +1,5 @@
import Foundation import Foundation
import UIKit
import SwiftSignalKit import SwiftSignalKit
import Postbox import Postbox
import TelegramCore import TelegramCore

View File

@ -667,8 +667,12 @@ final class MutableMessageHistoryView {
var updatedCachedPeerDataMessages = false var updatedCachedPeerDataMessages = false
var currentCachedPeerData: CachedPeerData? var currentCachedPeerData: CachedPeerData?
for i in 0 ..< self.additionalDatas.count {
switch self.additionalDatas[i] { let additionalDatas = self.additionalDatas
var updated = self.additionalDatas
for i in 0 ..< additionalDatas.count {
switch additionalDatas[i] {
case let .cachedPeerData(peerId, currentData): case let .cachedPeerData(peerId, currentData):
currentCachedPeerData = currentData currentCachedPeerData = currentData
if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] { if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] {
@ -676,7 +680,7 @@ final class MutableMessageHistoryView {
updatedCachedPeerDataMessages = true updatedCachedPeerDataMessages = true
} }
currentCachedPeerData = updatedData currentCachedPeerData = updatedData
self.additionalDatas[i] = .cachedPeerData(peerId, updatedData) updated[i] = .cachedPeerData(peerId, updatedData)
hasChanges = true hasChanges = true
} }
case .cachedPeerDataMessages: case .cachedPeerDataMessages:
@ -729,13 +733,13 @@ final class MutableMessageHistoryView {
} }
if updateMessage { if updateMessage {
let messages = postbox.getMessageGroup(at: id) ?? [] let messages = postbox.getMessageGroup(at: id) ?? []
self.additionalDatas[i] = .message(id, messages) updated[i] = .message(id, messages)
hasChanges = true hasChanges = true
} }
} }
case let .peerChatState(peerId, _): case let .peerChatState(peerId, _):
if transaction.currentUpdatedPeerChatStates.contains(peerId) { if transaction.currentUpdatedPeerChatStates.contains(peerId) {
self.additionalDatas[i] = .peerChatState(peerId, postbox.peerChatStateTable.get(peerId) as? PeerChatState) updated[i] = .peerChatState(peerId, postbox.peerChatStateTable.get(peerId) as? PeerChatState)
hasChanges = true hasChanges = true
} }
case .totalUnreadState: case .totalUnreadState:
@ -744,7 +748,7 @@ final class MutableMessageHistoryView {
break break
case let .cacheEntry(entryId, _): case let .cacheEntry(entryId, _):
if transaction.updatedCacheEntryKeys.contains(entryId) { if transaction.updatedCacheEntryKeys.contains(entryId) {
self.additionalDatas[i] = .cacheEntry(entryId, postbox.retrieveItemCacheEntry(id: entryId)) updated[i] = .cacheEntry(entryId, postbox.retrieveItemCacheEntry(id: entryId))
hasChanges = true hasChanges = true
} }
case .preferencesEntry: case .preferencesEntry:
@ -759,20 +763,20 @@ final class MutableMessageHistoryView {
} }
if value != updatedValue { if value != updatedValue {
self.additionalDatas[i] = .peerIsContact(peerId, value) updated[i] = .peerIsContact(peerId, value)
hasChanges = true hasChanges = true
} }
} }
case let .peer(peerId, _): case let .peer(peerId, _):
if let peer = transaction.currentUpdatedPeers[peerId] { if let peer = transaction.currentUpdatedPeers[peerId] {
self.additionalDatas[i] = .peer(peerId, peer) updated[i] = .peer(peerId, peer)
hasChanges = true hasChanges = true
} }
} }
} }
if let cachedData = currentCachedPeerData, !cachedData.messageIds.isEmpty { if let cachedData = currentCachedPeerData, !cachedData.messageIds.isEmpty {
for i in 0 ..< self.additionalDatas.count { for i in 0 ..< additionalDatas.count {
switch self.additionalDatas[i] { switch additionalDatas[i] {
case .cachedPeerDataMessages(_, _): case .cachedPeerDataMessages(_, _):
outer: for operationSet in operations { outer: for operationSet in operations {
for operation in operationSet { for operation in operationSet {
@ -802,8 +806,8 @@ final class MutableMessageHistoryView {
if updatedCachedPeerDataMessages { if updatedCachedPeerDataMessages {
hasChanges = true hasChanges = true
for i in 0 ..< self.additionalDatas.count { for i in 0 ..< additionalDatas.count {
switch self.additionalDatas[i] { switch additionalDatas[i] {
case let .cachedPeerDataMessages(peerId, _): case let .cachedPeerDataMessages(peerId, _):
var messages: [MessageId: Message] = [:] var messages: [MessageId: Message] = [:]
if let cachedData = currentCachedPeerData { if let cachedData = currentCachedPeerData {
@ -813,13 +817,15 @@ final class MutableMessageHistoryView {
} }
} }
} }
self.additionalDatas[i] = .cachedPeerDataMessages(peerId, messages) updated[i] = .cachedPeerDataMessages(peerId, messages)
default: default:
break break
} }
} }
} }
self.additionalDatas = updated
if !transaction.currentPeerHoleOperations.isEmpty { if !transaction.currentPeerHoleOperations.isEmpty {
var holePeerIdsSet: [PeerId] = [] var holePeerIdsSet: [PeerId] = []
switch self.peerIds { switch self.peerIds {

View File

@ -207,7 +207,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
encoder.encodeInt32(23, forKey: "_rawValue") encoder.encodeInt32(23, forKey: "_rawValue")
encoder.encodeInt64(callId, forKey: "callId") encoder.encodeInt64(callId, forKey: "callId")
encoder.encodeInt64(accessHash, forKey: "accessHash") encoder.encodeInt64(accessHash, forKey: "accessHash")
encoder.encodeInt64(peerId.toInt64(), forKey: "peerIdId") encoder.encodeInt64(peerId.toInt64(), forKey: "peerId")
} }
} }

View File

@ -158,6 +158,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
return self.membersPromise.get() return self.membersPromise.get()
} }
private var invitedPeersValue: Set<PeerId> = Set() {
didSet {
if self.invitedPeersValue != oldValue {
self.inivitedPeersPromise.set(self.invitedPeersValue)
}
}
}
private let inivitedPeersPromise = ValuePromise<Set<PeerId>>(Set())
public var invitedPeers: Signal<Set<PeerId>, NoError> {
return self.inivitedPeersPromise.get()
}
private let requestDisposable = MetaDisposable() private let requestDisposable = MetaDisposable()
private var groupCallParticipantUpdatesDisposable: Disposable? private var groupCallParticipantUpdatesDisposable: Disposable?
@ -641,4 +653,16 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl) strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl)
})) }))
} }
public func invitePeer(_ peerId: PeerId) {
guard case let .estabilished(callInfo, _, _, _) = self.internalState, !self.invitedPeersValue.contains(peerId) else {
return
}
var updatedInvitedPeers = self.invitedPeersValue
updatedInvitedPeers.insert(peerId)
self.invitedPeersValue = updatedInvitedPeers
let _ = inviteToGroupCall(account: self.account, callId: callInfo.id, accessHash: callInfo.accessHash, peerId: peerId).start()
}
} }

View File

@ -88,11 +88,11 @@ private final class Blob {
} }
} }
var currentShape: CGPath? var currentShape: UIBezierPath?
private var transition: CGFloat = 0 { private var transition: CGFloat = 0 {
didSet { didSet {
if let currentPoints = self.currentPoints { if let currentPoints = self.currentPoints {
self.currentShape = UIBezierPath.smoothCurve(through: currentPoints, length: size.width, smoothness: smoothness).cgPath self.currentShape = UIBezierPath.smoothCurve(through: currentPoints, length: size.width, smoothness: smoothness)
} }
} }
} }
@ -450,7 +450,6 @@ private class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
let buttonSize = CGSize(width: 144.0, height: 144.0) let buttonSize = CGSize(width: 144.0, height: 144.0)
let radius = buttonSize.width / 2.0 let radius = buttonSize.width / 2.0
let blue = UIColor(rgb: 0x0078ff) let blue = UIColor(rgb: 0x0078ff)
let lightBlue = UIColor(rgb: 0x59c7f8) let lightBlue = UIColor(rgb: 0x59c7f8)
let green = UIColor(rgb: 0x33c659) let green = UIColor(rgb: 0x33c659)
@ -511,8 +510,7 @@ private class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
context.saveGState() context.saveGState()
if let blobsState = parameters.state as? VoiceChatActionButtonBackgroundNodeBlobState { if let blobsState = parameters.state as? VoiceChatActionButtonBackgroundNodeBlobState {
for blob in blobsState.blobs { for blob in blobsState.blobs {
if let path = blob.currentShape { if let path = blob.currentShape, let uiPath = path.copy() as? UIBezierPath {
let uiPath = UIBezierPath(cgPath: path)
let toOrigin = CGAffineTransform(translationX: -bounds.size.width / 2.0, y: -bounds.size.height / 2.0) let toOrigin = CGAffineTransform(translationX: -bounds.size.width / 2.0, y: -bounds.size.height / 2.0)
let fromOrigin = CGAffineTransform(translationX: bounds.size.width / 2.0, y: bounds.size.height / 2.0) let fromOrigin = CGAffineTransform(translationX: bounds.size.width / 2.0, y: bounds.size.height / 2.0)

View File

@ -142,6 +142,7 @@ public final class VoiceChatController: ViewController {
var activityTimestamp: Int32 var activityTimestamp: Int32
var state: State var state: State
var muteState: GroupCallParticipantsContext.Participant.MuteState? var muteState: GroupCallParticipantsContext.Participant.MuteState?
var invited: Bool
var stableId: PeerId { var stableId: PeerId {
return self.peer.id return self.peer.id
@ -163,6 +164,9 @@ public final class VoiceChatController: ViewController {
if lhs.muteState != rhs.muteState { if lhs.muteState != rhs.muteState {
return false return false
} }
if lhs.invited != rhs.invited {
return false
}
return true return true
} }
@ -181,7 +185,7 @@ public final class VoiceChatController: ViewController {
switch self.state { switch self.state {
case .inactive: case .inactive:
text = .presence text = .presence
icon = .invite icon = .invite(self.invited)
case .listening: case .listening:
text = .text(presentationData.strings.VoiceChat_StatusListening, .accent) text = .text(presentationData.strings.VoiceChat_StatusListening, .accent)
let microphoneColor: UIColor let microphoneColor: UIColor
@ -241,6 +245,7 @@ public final class VoiceChatController: ViewController {
private var currentMembers: [RenderedChannelParticipant]? private var currentMembers: [RenderedChannelParticipant]?
private var currentMemberStates: [PeerId: PresentationGroupCallMemberState]? private var currentMemberStates: [PeerId: PresentationGroupCallMemberState]?
private var currentInvitedPeers: Set<PeerId>?
private var currentEntries: [PeerEntry] = [] private var currentEntries: [PeerEntry] = []
private var peersDisposable: Disposable? private var peersDisposable: Disposable?
@ -261,6 +266,7 @@ public final class VoiceChatController: ViewController {
private var audioLevelsDisposable: Disposable? private var audioLevelsDisposable: Disposable?
private var myAudioLevelDisposable: Disposable? private var myAudioLevelDisposable: Disposable?
private var memberStatesDisposable: Disposable? private var memberStatesDisposable: Disposable?
private var invitedPeersDisposable: Disposable?
private var itemInteraction: Interaction? private var itemInteraction: Interaction?
@ -295,6 +301,10 @@ public final class VoiceChatController: ViewController {
return return
} }
if let invitedPeers = strongSelf.currentInvitedPeers, invitedPeers.contains(peer.id) {
return
}
strongSelf.controller?.present( strongSelf.controller?.present(
UndoOverlayController( UndoOverlayController(
presentationData: strongSelf.presentationData, presentationData: strongSelf.presentationData,
@ -310,6 +320,7 @@ public final class VoiceChatController: ViewController {
), ),
in: .current in: .current
) )
strongSelf.call.invitePeer(peer.id)
} }
self.itemInteraction = Interaction( self.itemInteraction = Interaction(
@ -327,12 +338,15 @@ public final class VoiceChatController: ViewController {
var items: [ContextMenuItem] = [] var items: [ContextMenuItem] = []
switch entry.state { switch entry.state {
case .inactive: case .inactive:
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_InvitePeer, icon: { theme in if let invitedPeers = strongSelf.currentInvitedPeers, invitedPeers.contains(peer.id) {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.actionSheet.primaryTextColor) } else {
}, action: { _, f in items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_InvitePeer, icon: { theme in
invitePeer(peer) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.actionSheet.primaryTextColor)
f(.default) }, action: { _, f in
}))) invitePeer(peer)
f(.default)
})))
}
default: default:
if entry.muteState == nil { if entry.muteState == nil {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_MutePeer, icon: { theme in items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_MutePeer, icon: { theme in
@ -389,6 +403,10 @@ public final class VoiceChatController: ViewController {
}))) })))
} }
} }
guard !items.isEmpty else {
return
}
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .extracted(VoiceChatContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false)), items: .single(items), reactionItems: [], gesture: gesture) let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .extracted(VoiceChatContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false)), items: .single(items), reactionItems: [], gesture: gesture)
strongSelf.controller?.presentInGlobalOverlay(contextController) strongSelf.controller?.presentInGlobalOverlay(contextController)
@ -406,7 +424,7 @@ public final class VoiceChatController: ViewController {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.updateMembers(isMuted: strongSelf.callState?.isMuted ?? true, members: state.list, memberStates: strongSelf.currentMemberStates ?? [:]) strongSelf.updateMembers(isMuted: strongSelf.callState?.isMuted ?? true, members: state.list, memberStates: strongSelf.currentMemberStates ?? [:], invitedPeers: strongSelf.currentInvitedPeers ?? Set())
} }
}) })
@ -416,12 +434,24 @@ public final class VoiceChatController: ViewController {
return return
} }
if let members = strongSelf.currentMembers { if let members = strongSelf.currentMembers {
strongSelf.updateMembers(isMuted: strongSelf.callState?.isMuted ?? true, members: members, memberStates: memberStates) strongSelf.updateMembers(isMuted: strongSelf.callState?.isMuted ?? true, members: members, memberStates: memberStates, invitedPeers: strongSelf.currentInvitedPeers ?? Set())
} else { } else {
strongSelf.currentMemberStates = memberStates strongSelf.currentMemberStates = memberStates
} }
}) })
self.invitedPeersDisposable = (self.call.invitedPeers
|> deliverOnMainQueue).start(next: { [weak self] invitedPeers in
guard let strongSelf = self else {
return
}
if let members = strongSelf.currentMembers {
strongSelf.updateMembers(isMuted: strongSelf.callState?.isMuted ?? true, members: members, memberStates: strongSelf.currentMemberStates ?? [:], invitedPeers: invitedPeers)
} else {
strongSelf.currentInvitedPeers = invitedPeers
}
})
self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -470,7 +500,7 @@ public final class VoiceChatController: ViewController {
strongSelf.callState = state strongSelf.callState = state
if wasMuted != state.isMuted, let members = strongSelf.currentMembers { if wasMuted != state.isMuted, let members = strongSelf.currentMembers {
strongSelf.updateMembers(isMuted: state.isMuted, members: members, memberStates: strongSelf.currentMemberStates ?? [:]) strongSelf.updateMembers(isMuted: state.isMuted, members: members, memberStates: strongSelf.currentMemberStates ?? [:], invitedPeers: strongSelf.currentInvitedPeers ?? Set())
} }
if let (layout, navigationHeight) = strongSelf.validLayout { if let (layout, navigationHeight) = strongSelf.validLayout {
@ -568,6 +598,7 @@ public final class VoiceChatController: ViewController {
self.callStateDisposable?.dispose() self.callStateDisposable?.dispose()
self.audioOutputStateDisposable?.dispose() self.audioOutputStateDisposable?.dispose()
self.memberStatesDisposable?.dispose() self.memberStatesDisposable?.dispose()
self.invitedPeersDisposable?.dispose()
self.audioLevelsDisposable?.dispose() self.audioLevelsDisposable?.dispose()
self.myAudioLevelDisposable?.dispose() self.myAudioLevelDisposable?.dispose()
} }
@ -576,7 +607,7 @@ public final class VoiceChatController: ViewController {
super.didLoad() super.didLoad()
let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.actionButtonPressGesture(_:))) let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.actionButtonPressGesture(_:)))
longTapRecognizer.minimumPressDuration = 0.01 longTapRecognizer.minimumPressDuration = 0.1
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(_:)))
@ -716,6 +747,7 @@ public final class VoiceChatController: ViewController {
let actionButtonTitle: String let actionButtonTitle: String
let actionButtonSubtitle: String let actionButtonSubtitle: String
let audioButtonAppearance: CallControllerButtonItemNode.Content.Appearance let audioButtonAppearance: CallControllerButtonItemNode.Content.Appearance
var actionButtonEnabled = true
if let callState = callState { if let callState = callState {
isMicOn = !callState.isMuted isMicOn = !callState.isMuted
@ -725,6 +757,7 @@ public final class VoiceChatController: ViewController {
actionButtonTitle = self.presentationData.strings.VoiceChat_Connecting actionButtonTitle = self.presentationData.strings.VoiceChat_Connecting
actionButtonSubtitle = "" actionButtonSubtitle = ""
audioButtonAppearance = .color(.custom(0x1c1c1e)) audioButtonAppearance = .color(.custom(0x1c1c1e))
actionButtonEnabled = false
case .connected: case .connected:
actionButtonState = .active(state: isMicOn ? .on : .muted) actionButtonState = .active(state: isMicOn ? .on : .muted)
if isMicOn { if isMicOn {
@ -742,8 +775,10 @@ public final class VoiceChatController: ViewController {
actionButtonTitle = self.presentationData.strings.VoiceChat_Connecting actionButtonTitle = self.presentationData.strings.VoiceChat_Connecting
actionButtonSubtitle = "" actionButtonSubtitle = ""
audioButtonAppearance = .color(.custom(0x1c1c1e)) audioButtonAppearance = .color(.custom(0x1c1c1e))
actionButtonEnabled = false
} }
self.actionButton.isUserInteractionEnabled = actionButtonEnabled
self.actionButton.update(size: centralButtonSize, buttonSize: CGSize(width: 144.0, height: 144.0), state: actionButtonState, title: actionButtonTitle, subtitle: actionButtonSubtitle, animated: true) self.actionButton.update(size: centralButtonSize, buttonSize: CGSize(width: 144.0, height: 144.0), state: actionButtonState, title: actionButtonTitle, subtitle: actionButtonSubtitle, animated: true)
transition.updateFrame(node: self.actionButton, frame: actionButtonFrame) transition.updateFrame(node: self.actionButton, frame: actionButtonFrame)
@ -780,7 +815,7 @@ public final class VoiceChatController: ViewController {
soundImage = .speaker soundImage = .speaker
case .speaker: case .speaker:
soundImage = .speaker soundImage = .speaker
// soundAppearance = .blurred(isFilled: false) soundAppearance = .blurred(isFilled: true)
case .headphones: case .headphones:
soundImage = .bluetooth soundImage = .bluetooth
case let .bluetooth(type): case let .bluetooth(type):
@ -881,7 +916,7 @@ public final class VoiceChatController: ViewController {
}) })
} }
private func updateMembers(isMuted: Bool, members: [RenderedChannelParticipant], memberStates: [PeerId: PresentationGroupCallMemberState]) { private func updateMembers(isMuted: Bool, members: [RenderedChannelParticipant], memberStates: [PeerId: PresentationGroupCallMemberState], invitedPeers: Set<PeerId>) {
var members = members var members = members
members.sort(by: { lhs, rhs in members.sort(by: { lhs, rhs in
if lhs.peer.id == self.context.account.peerId { if lhs.peer.id == self.context.account.peerId {
@ -903,6 +938,7 @@ public final class VoiceChatController: ViewController {
self.currentMembers = members self.currentMembers = members
self.currentMemberStates = memberStates self.currentMemberStates = memberStates
self.currentInvitedPeers = invitedPeers
let previousEntries = self.currentEntries let previousEntries = self.currentEntries
var entries: [PeerEntry] = [] var entries: [PeerEntry] = []
@ -931,9 +967,11 @@ public final class VoiceChatController: ViewController {
entries.append(PeerEntry( entries.append(PeerEntry(
peer: member.peer, peer: member.peer,
presence: member.presences[member.peer.id] as? TelegramUserPresence,
activityTimestamp: Int32.max - 1 - index, activityTimestamp: Int32.max - 1 - index,
state: memberState, state: memberState,
muteState: memberMuteState muteState: memberMuteState,
invited: invitedPeers.contains(member.peer.id)
)) ))
index += 1 index += 1
} }

View File

@ -34,7 +34,7 @@ public final class VoiceChatParticipantItem: ListViewItem {
public enum Icon { public enum Icon {
case none case none
case microphone(Bool, UIColor) case microphone(Bool, UIColor)
case invite case invite(Bool)
} }
let presentationData: ItemListPresentationData let presentationData: ItemListPresentationData
@ -134,7 +134,8 @@ public class VoiceChatParticipantItemNode: ListViewItemNode {
private let actionContainerNode: ASDisplayNode private let actionContainerNode: ASDisplayNode
private var animationNode: VoiceChatMicrophoneNode? private var animationNode: VoiceChatMicrophoneNode?
private var actionButtonNode: HighlightableButtonNode? private var iconNode: ASImageNode?
private var actionButtonNode: HighlightTrackingButtonNode
private var audioLevelView: VoiceBlobView? private var audioLevelView: VoiceBlobView?
private let audioLevelDisposable = MetaDisposable() private let audioLevelDisposable = MetaDisposable()
@ -181,6 +182,7 @@ public class VoiceChatParticipantItemNode: ListViewItemNode {
self.statusNode.contentsScale = UIScreen.main.scale self.statusNode.contentsScale = UIScreen.main.scale
self.actionContainerNode = ASDisplayNode() self.actionContainerNode = ASDisplayNode()
self.actionButtonNode = HighlightTrackingButtonNode()
self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.isLayerBacked = true self.highlightedBackgroundNode.isLayerBacked = true
@ -199,8 +201,11 @@ public class VoiceChatParticipantItemNode: ListViewItemNode {
self.offsetContainerNode.addSubnode(self.titleNode) self.offsetContainerNode.addSubnode(self.titleNode)
self.offsetContainerNode.addSubnode(self.statusNode) self.offsetContainerNode.addSubnode(self.statusNode)
self.offsetContainerNode.addSubnode(self.actionContainerNode) self.offsetContainerNode.addSubnode(self.actionContainerNode)
self.actionContainerNode.addSubnode(self.actionButtonNode)
self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode
self.actionButtonNode.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside)
self.peerPresenceManager = PeerPresenceStatusManager(update: { [weak self] in self.peerPresenceManager = PeerPresenceStatusManager(update: { [weak self] in
if let strongSelf = self, let layoutParams = strongSelf.layoutParams { if let strongSelf = self, let layoutParams = strongSelf.layoutParams {
let (_, apply) = strongSelf.asyncLayout()(layoutParams.0, layoutParams.1, layoutParams.2, layoutParams.3) let (_, apply) = strongSelf.asyncLayout()(layoutParams.0, layoutParams.1, layoutParams.2, layoutParams.3)
@ -212,7 +217,7 @@ public class VoiceChatParticipantItemNode: ListViewItemNode {
guard let strongSelf = self else { guard let strongSelf = self else {
return false return false
} }
if let actionButtonNode = strongSelf.actionButtonNode, actionButtonNode.frame.contains(location) { if strongSelf.actionButtonNode.frame.contains(location) {
return false return false
} }
return true return true
@ -345,8 +350,8 @@ public class VoiceChatParticipantItemNode: ListViewItemNode {
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, 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 - 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, 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 insets = UIEdgeInsets() let insets = UIEdgeInsets()
@ -547,7 +552,7 @@ public class VoiceChatParticipantItemNode: ListViewItemNode {
} else { } else {
animationNode = VoiceChatMicrophoneNode() animationNode = VoiceChatMicrophoneNode()
strongSelf.animationNode = animationNode strongSelf.animationNode = animationNode
strongSelf.actionContainerNode.addSubnode(animationNode) strongSelf.actionButtonNode.addSubnode(animationNode)
} }
animationNode.update(state: VoiceChatMicrophoneNode.State(muted: muted, color: color), animated: true) animationNode.update(state: VoiceChatMicrophoneNode.State(muted: muted, color: color), animated: true)
} else if let animationNode = strongSelf.animationNode { } else if let animationNode = strongSelf.animationNode {
@ -557,29 +562,34 @@ public class VoiceChatParticipantItemNode: ListViewItemNode {
}) })
} }
if case .invite = item.icon { if case let .invite(invited) = item.icon {
let actionButtonNode: HighlightableButtonNode let iconNode: ASImageNode
if let current = strongSelf.actionButtonNode { if let current = strongSelf.iconNode {
actionButtonNode = current iconNode = current
} else { } else {
actionButtonNode = HighlightableButtonNode() iconNode = ASImageNode()
actionButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: item.presentationData.theme.list.itemAccentColor), for: .normal) iconNode.contentMode = .center
actionButtonNode.addTarget(strongSelf, action: #selector(strongSelf.actionButtonPressed), forControlEvents: .touchUpInside) strongSelf.iconNode = iconNode
strongSelf.actionButtonNode.addSubnode(iconNode)
strongSelf.actionButtonNode = actionButtonNode
strongSelf.actionContainerNode.addSubnode(actionButtonNode)
} }
} else if let actionButtonNode = strongSelf.actionButtonNode {
strongSelf.actionButtonNode = nil if invited {
actionButtonNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false, completion: { [weak actionButtonNode] _ in iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Call/Context Menu/Invited"), color: UIColor(rgb: 0x979797))
actionButtonNode?.removeFromSupernode() } else {
iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: item.presentationData.theme.list.itemAccentColor)
}
} else if let iconNode = strongSelf.iconNode {
strongSelf.iconNode = nil
iconNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false, completion: { [weak iconNode] _ in
iconNode?.removeFromSupernode()
}) })
} }
let animationSize = CGSize(width: 36.0, height: 36.0) let animationSize = CGSize(width: 36.0, height: 36.0)
strongSelf.animationNode?.frame = CGRect(x: params.width - animationSize.width - 6.0, y: floor((layout.contentSize.height - animationSize.height) / 2.0) + 1.0, width: animationSize.width, height: animationSize.height) strongSelf.iconNode?.frame = CGRect(origin: CGPoint(), size: animationSize)
strongSelf.animationNode?.frame = CGRect(origin: CGPoint(), size: animationSize)
strongSelf.actionButtonNode?.frame = CGRect(x: params.width - animationSize.width - 6.0, y: floor((layout.contentSize.height - animationSize.height) / 2.0) + 1.0, width: animationSize.width, height: animationSize.height) strongSelf.actionButtonNode.frame = CGRect(x: params.width - animationSize.width - 6.0, y: floor((layout.contentSize.height - animationSize.height) / 2.0) + 1.0, width: animationSize.width, height: animationSize.height)
if let presence = item.presence as? TelegramUserPresence { if let presence = item.presence as? TelegramUserPresence {
strongSelf.peerPresenceManager?.reset(presence: presence) strongSelf.peerPresenceManager?.reset(presence: presence)

View File

@ -614,7 +614,7 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId
for peerId in fetchedChats.chatPeerIds { for peerId in fetchedChats.chatPeerIds {
if let peer = transaction.getPeer(peerId) { if let peer = transaction.getPeer(peerId) {
transaction.updatePeerChatListInclusion(peerId, inclusion: .ifHasMessagesOrOneOf(groupId: groupId, pinningIndex: nil, minTimestamp: minTimestampForPeerInclusion(peer))) transaction.updatePeerChatListInclusion(peerId, inclusion: .ifHasMessagesOrOneOf(groupId: groupId, pinningIndex: transaction.getPeerChatListIndex(peerId)?.1.pinningIndex, minTimestamp: minTimestampForPeerInclusion(peer)))
} else { } else {
assertionFailure() assertionFailure()
} }

@ -1 +1 @@
Subproject commit b245c575f350e13186d34b9ae38f6af555c6fe14 Subproject commit aeaa63b502a1ee88feef282af739980001138993