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 setIsMuted(_ value: Bool)
func setCurrentAudioOutput(_ output: AudioSessionOutput)
func updateMuteState(peerId: PeerId, isMuted: Bool)
func invitePeer(_ peerId: PeerId)
var invitedPeers: Signal<Set<PeerId>, NoError> { get }
}
public protocol PresentationCallManager: class {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -207,7 +207,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
encoder.encodeInt32(23, forKey: "_rawValue")
encoder.encodeInt64(callId, forKey: "callId")
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()
}
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 var groupCallParticipantUpdatesDisposable: Disposable?
@ -641,4 +653,16 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
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 {
didSet {
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 radius = buttonSize.width / 2.0
let blue = UIColor(rgb: 0x0078ff)
let lightBlue = UIColor(rgb: 0x59c7f8)
let green = UIColor(rgb: 0x33c659)
@ -511,8 +510,7 @@ private class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
context.saveGState()
if let blobsState = parameters.state as? VoiceChatActionButtonBackgroundNodeBlobState {
for blob in blobsState.blobs {
if let path = blob.currentShape {
let uiPath = UIBezierPath(cgPath: path)
if let path = blob.currentShape, let uiPath = path.copy() as? UIBezierPath {
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)

View File

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

View File

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

View File

@ -614,7 +614,7 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId
for peerId in fetchedChats.chatPeerIds {
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 {
assertionFailure()
}

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