mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '82ff76bb694e994f668a8c371f14167408246063'
This commit is contained in:
commit
d1d533a443
@ -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 {
|
||||||
|
@ -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>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import UIKit
|
||||||
import CoreMedia
|
import CoreMedia
|
||||||
import Accelerate
|
import Accelerate
|
||||||
import FFMpegBinding
|
import FFMpegBinding
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import UIKit
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import Postbox
|
import Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import UIKit
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import Postbox
|
import Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
|
@ -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 {
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user