From ef386a5a7bc42eea430bde77c3e0671dce920893 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 9 Apr 2021 14:40:38 +0300 Subject: [PATCH 1/2] Fix media search filter display --- .../Sources/ChatListSearchListPaneNode.swift | 3 -- .../Sources/ChatListSearchMediaNode.swift | 30 +++++-------------- .../TelegramCore/Sources/MessageUtils.swift | 7 +++-- 3 files changed, 13 insertions(+), 27 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index a8c34852be..93af51f808 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -841,9 +841,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> take(1) - let foundLocalPeers: Signal<(peers: [RenderedPeer], unread: [PeerId: (Int32, Bool)]), NoError> - if let query = query { foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased()) |> mapToSignal { local -> Signal<([PeerView], [RenderedPeer]), NoError> in @@ -1279,7 +1277,6 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { }, openUrl: { url in interaction.openUrl(url) }, openPeer: { peer, navigation in -// interaction.openPeer(peer.id, navigation) }, callPeer: { _, _ in }, enqueueMessage: { _ in }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: .custom(messages: foundMessages, at: message.id, loadMore: { diff --git a/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift b/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift index d74c1208e9..7979476472 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift @@ -320,21 +320,6 @@ private final class VisualMediaItemNode: ASDisplayNode { func updateIsVisible(_ isVisible: Bool) { self.hasVisibility = isVisible -// if let _ = self.videoLayerFrameManager { -// let displayLink: ConstantDisplayLinkAnimator -// if let current = self.displayLink { -// displayLink = current -// } else { -// displayLink = ConstantDisplayLinkAnimator { [weak self] in -// guard let strongSelf = self else { -// return -// } -// strongSelf.displayLinkTimestamp += 1.0 / 30.0 -// } -// displayLink.frameInterval = 2 -// self.displayLink = displayLink -// } -// } self.displayLink?.isPaused = !self.hasVisibility || self.isHidden } @@ -422,8 +407,8 @@ private final class VisualMediaItem { let dimensions: CGSize let aspectRatio: CGFloat - init(message: Message) { - self.index = nil + init(message: Message, index: UInt32?) { + self.index = index self.message = message var aspectRatio: CGFloat = 1.0 @@ -441,10 +426,10 @@ private final class VisualMediaItem { } var stableId: UInt32 { - if let message = self.message { - return message.stableId - } else if let index = self.index { + if let index = self.index { return index + } else if let message = self.message { + return message.stableId } else { return 0 } @@ -708,7 +693,6 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate { self.animationTimer?.invalidate() } - func updateHistory(entries: [ChatListSearchEntry]?, totalCount: Int32, updateType: ViewUpdateType) { switch updateType { case .FillHole: @@ -716,11 +700,13 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate { default: self.mediaItems.removeAll() + var index: UInt32 = 0 if let entries = entries { for entry in entries { if case let .message(message, _, _, _, _, _, _) = entry { - self.mediaItems.append(VisualMediaItem(message: message)) + self.mediaItems.append(VisualMediaItem(message: message, index: nil)) } + index += 1 } } self.itemsLayout = nil diff --git a/submodules/TelegramCore/Sources/MessageUtils.swift b/submodules/TelegramCore/Sources/MessageUtils.swift index ee98a74694..80f922b68b 100644 --- a/submodules/TelegramCore/Sources/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/MessageUtils.swift @@ -188,8 +188,11 @@ func locallyRenderedMessage(message: StoreMessage, peers: [PeerId: Peer]) -> Mes var hasher = Hasher() hasher.combine(id.id) hasher.combine(id.peerId) - - let stableId = UInt32(clamping: hasher.finalize()) + + let hashValue = Int64(hasher.finalize()) + let first = UInt32((hashValue >> 32) & 0xffffffff) + let second = UInt32(hashValue & 0xffffffff) + let stableId = first &+ second return Message(stableId: stableId, stableVersion: 0, id: id, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: message.threadId, timestamp: message.timestamp, flags: MessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: message.attributes, media: message.media, peers: messagePeers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) } From 8ec300703d18093504b95aee79c37d561c0fafbc Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 9 Apr 2021 15:46:13 +0300 Subject: [PATCH 2/2] Voice Chat Fixes --- .../Resources/VoiceCancelReminder.tgs | Bin 2307 -> 2319 bytes Telegram/Telegram-iOS/Resources/VoiceMute.tgs | Bin 2218 -> 2201 bytes .../Resources/VoiceSetReminder.tgs | Bin 2303 -> 2057 bytes .../Telegram-iOS/Resources/VoiceUnmute.tgs | Bin 2330 -> 1918 bytes .../Sources/PresentationGroupCall.swift | 354 +++++++++--------- .../Sources/VoiceChatController.swift | 42 ++- .../Sources/VoiceChatTimerNode.swift | 2 +- 7 files changed, 208 insertions(+), 190 deletions(-) diff --git a/Telegram/Telegram-iOS/Resources/VoiceCancelReminder.tgs b/Telegram/Telegram-iOS/Resources/VoiceCancelReminder.tgs index e70706ac8f0e7f36dd4f02da6fac0dc35fc4dd54..133d47e1e1a31dabe7208113bdb8fe43c8c839ab 100644 GIT binary patch delta 2235 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 diff --git a/Telegram/Telegram-iOS/Resources/VoiceSetReminder.tgs b/Telegram/Telegram-iOS/Resources/VoiceSetReminder.tgs index 1cea642acb13f24b38bc0676dfa341a67152cb5a..fe22c2c4ea35e5cf596226f872b985a2f0b87043 100644 GIT binary patch literal 2057 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 Z58fWe`1xdYQ+PGr{ujMrtkVrP001c%aHRkM diff --git a/Telegram/Telegram-iOS/Resources/VoiceUnmute.tgs b/Telegram/Telegram-iOS/Resources/VoiceUnmute.tgs index 16ba09aa4d96cdd58c6a3bc4d2717837b2537fe9..57d546d4d2064b43dc27b9f8ba70fcc48443c03d 100644 GIT binary patch literal 1918 zcmV-^2Z8t>iwFolMQ~sM16FToV`WutZFO{IE^2dcZUF6CYj4{&6#XlPK2rqWr1Gu9 zx?w;E6nkMn5DZ?TG+tsmklk%X;QzjJFXe|E+0F_lZBe7B8HrcqeefQhOX;EbyErSn z^fDHr7}xV*arRKW!@W1t`FvD=H@d55gIBKyN@{5oWFSzWxy{X0ZswQNE<|zN6lXz+ zVsu}ep_!u5X#b%G?M>TG#*qJYGJZd=X^^WcmLAS#^?X*Goj(*K8u)TLzW;;fyc>R~ zo7}OK9Zk?`%>1D^bM1g{Mw5E*+h+mwEWJjQLS&FEkJ zp`m@#>09B~dyVqZR5ROPU3{=dd8nHR}qJBJKZHC@g5WDND?5hWB-L z02y4oYhdAMo?SJ`9^sDJP0>!Vw0AWHBq4vUNDKbOJkv*a{-!&nHcEl zm>{&#G6qMI2>JE!ZdNbW#&ZS(Q|jnir)Rj&7WhIVYPprH`Cj?U*+$c;r-3%5;|w8{&3MU+)53#FCEkq zQeea0-h2$Z9T92J+rEd$MYCaFk6)~UIGoI`r_H#a1;V#@p|XD_u+kzzZPZ&Ywu#o` zyzK>k(SgKDP{8Sm2O@uVaLsga?d_<(d<<;vIe^V#BhXQ#C~c+pMY3P;(*s-SrHft| z>Ah`HO`KP(YiXo$)e824R0aSiZ89stGD)v0;bcf*3lnuEL)0QEG&HgevhpGV*saH< zCtS25Rnj|p)HrF4C#$7$vBk@I^ZuxCd3|>XHQerG@p)b7E+zn~?hYXW$(-)N02P5Uz0^@gV*~)g`xRVNQFYOtjUss3t%j^k(j?TLd;jG(*vu`$V)_-j{+hJJ_{KsigR?>RnO~9VW)X6ed2H>ml zYlMn2p=gvzRm{zdbfL;j3$>iD2$q6&E2si%^4bxL3YE)JR6bXD&JgJ;fn^NITu;A9 z6}lbPN8ojWt`l^9dg$_7M`885Rl#_Cv@#ytNjRlJN8+L5ujqt!koV!U0I;W=f2pO$ ze7_3(=QDxLDUCnhG=BL?FLcxRo9WeumpS-BrYhtz5*z#aft2nM{K85<)2+5v0MPw~ zi2$#BZBc^R*n=6Xqa!CPPh7_uIu9!I1CNi^!H|?jJ!a@MiaCu3r7~1z8ksyl>ndqt z!W{xw;z1f{WRvg~qiY^9`=5k^B03aY+B{_9vhpLHW1q!2F*sqDh3YQn_zF%W=V)1_ z#1Eo)jh6VPV?qdAPPjzLtzveJwUq#|G}b=1W86;1lp2w7J|7iunf z&Bs21yB=dJ-RKv)PZiA0v3BfYF`*Do%iOARa`9Z+FE2Vh>l6g+C0QvgES(wfI}^jj zy^|`El7KEcpfxgj-9oad$g={i9jFlXMCD#!_x&p4 zf&d^cI-i8GnStVBWl$d%m%V%a*XiTquKg3w_|kcX*~>G`GaLxS?twsl9r612S3kae z^ZL18N>D)&1b)-rPeBxk*w5qj#D19Sbawid$kc;FE4l3=Wpjr0$>CFQroSfXV%Ej6 zzZlv4Vshswoy6G_1nr18=^954F;E#eT3GqxvH4S^$uNv4qCT# zZ@yT?W%CIdCv9#rs}Z2pN@`>4BBc}SY}}SP8^g9UGA&)}(TZMl-lbz-2#Skp6{QR7 z0HRujceFByuFuzj&ysVnkpL9&99UZBv?F~vJJJz9P7Lk$+DUEgNz7ahe1p9V+1sWr>AP)Ktf{Ym=zPe1 z{5{Ge{r5)IfQ^|0@1DS!MgHmCpZ-B0wBfJUx!wG@{jsy)@?mqEeRZ+;4-~ThKw&Qc E0G;2()&Kwi literal 2330 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 AsQ>@~ diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 35e91c9cec..6679d24bf1 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -1028,6 +1028,185 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } + private func switchToTemporaryScheduledParticipantsContext() { + guard let callInfo = self.internalState.callInfo, callInfo.scheduleTimestamp != nil else { + return + } + let accountContext = self.accountContext + let peerId = self.peerId + let rawAdminIds: Signal, NoError> + if peerId.namespace == Namespaces.Peer.CloudChannel { + rawAdminIds = Signal { subscriber in + let (disposable, _) = accountContext.peerChannelMemberCategoriesContextsManager.admins(postbox: accountContext.account.postbox, network: accountContext.account.network, accountPeerId: accountContext.account.peerId, peerId: peerId, updated: { list in + var peerIds = Set() + for item in list.list { + if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) { + peerIds.insert(item.peer.id) + } + } + subscriber.putNext(peerIds) + }) + return disposable + } + |> distinctUntilChanged + |> runOn(.mainQueue()) + } else { + rawAdminIds = accountContext.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peerId)]) + |> map { views -> Set in + guard let view = views.views[.cachedPeerData(peerId: peerId)] as? CachedPeerDataView else { + return Set() + } + guard let cachedData = view.cachedPeerData as? CachedGroupData, let participants = cachedData.participants else { + return Set() + } + return Set(participants.participants.compactMap { item -> PeerId? in + switch item { + case .creator, .admin: + return item.peerId + default: + return nil + } + }) + } + |> distinctUntilChanged + } + + let adminIds = combineLatest(queue: .mainQueue(), + rawAdminIds, + accountContext.account.postbox.combinedView(keys: [.basicPeer(peerId)]) + ) + |> map { rawAdminIds, view -> Set in + var rawAdminIds = rawAdminIds + if let peerView = view.views[.basicPeer(peerId)] as? BasicPeerView, let peer = peerView.peer as? TelegramChannel { + if peer.hasPermission(.manageCalls) { + rawAdminIds.insert(accountContext.account.peerId) + } else { + rawAdminIds.remove(accountContext.account.peerId) + } + } + return rawAdminIds + } + |> distinctUntilChanged + + let participantsContext = GroupCallParticipantsContext( + account: self.accountContext.account, + peerId: self.peerId, + myPeerId: self.joinAsPeerId, + id: callInfo.id, + accessHash: callInfo.accessHash, + state: GroupCallParticipantsContext.State( + participants: [], + nextParticipantsFetchOffset: nil, + adminIds: Set(), + isCreator: false, + defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: self.stateValue.defaultParticipantMuteState == .muted, canChange: false), + sortAscending: true, + recordingStartTimestamp: nil, + title: self.stateValue.title, + scheduleTimestamp: self.stateValue.scheduleTimestamp, + subscribedToScheduled: self.stateValue.subscribedToScheduled, + totalCount: 0, + version: 0 + ), + previousServiceState: nil + ) + self.temporaryParticipantsContext = nil + self.participantsContext = participantsContext + + let myPeerId = self.joinAsPeerId + let myPeer = self.accountContext.account.postbox.transaction { transaction -> (Peer, CachedPeerData?)? in + if let peer = transaction.getPeer(myPeerId) { + return (peer, transaction.getPeerCachedData(peerId: myPeerId)) + } else { + return nil + } + } + self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(), + participantsContext.state, + adminIds, + myPeer, + accountContext.account.postbox.peerView(id: peerId) + ).start(next: { [weak self] state, adminIds, myPeerAndCachedData, view in + guard let strongSelf = self else { + return + } + + var members = PresentationGroupCallMembers( + participants: [], + speakingParticipants: Set(), + totalCount: state.totalCount, + loadMoreToken: state.nextParticipantsFetchOffset + ) + + var participants: [GroupCallParticipantsContext.Participant] = [] + var topParticipants: [GroupCallParticipantsContext.Participant] = [] + if let (myPeer, cachedData) = myPeerAndCachedData { + let about: String? + if let cachedData = cachedData as? CachedUserData { + about = cachedData.about + } else if let cachedData = cachedData as? CachedUserData { + about = cachedData.about + } else { + about = nil + } + participants.append(GroupCallParticipantsContext.Participant( + peer: myPeer, + ssrc: nil, + jsonParams: nil, + joinTimestamp: strongSelf.temporaryJoinTimestamp, + raiseHandRating: strongSelf.temporaryRaiseHandRating, + hasRaiseHand: strongSelf.temporaryHasRaiseHand, + activityTimestamp: strongSelf.temporaryActivityTimestamp, + activityRank: strongSelf.temporaryActivityRank, + muteState: strongSelf.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), + volume: nil, + about: about + )) + } + + for participant in participants { + members.participants.append(participant) + + if topParticipants.count < 3 { + topParticipants.append(participant) + } + } + + strongSelf.membersValue = members + strongSelf.stateValue.adminIds = adminIds + strongSelf.stateValue.canManageCall = state.isCreator || adminIds.contains(strongSelf.accountContext.account.peerId) + if (state.isCreator || strongSelf.stateValue.adminIds.contains(strongSelf.accountContext.account.peerId)) && state.defaultParticipantsAreMuted.canChange { + strongSelf.stateValue.defaultParticipantMuteState = state.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted + } + strongSelf.stateValue.recordingStartTimestamp = state.recordingStartTimestamp + strongSelf.stateValue.title = state.title + + strongSelf.stateValue.scheduleTimestamp = strongSelf.isScheduledStarted ? nil : state.scheduleTimestamp + if state.scheduleTimestamp == nil && !strongSelf.isScheduledStarted { + strongSelf.updateSessionState(internalState: .active(GroupCallInfo(id: callInfo.id, accessHash: callInfo.accessHash, participantCount: state.totalCount, clientParams: callInfo.clientParams, streamDcId: callInfo.streamDcId, title: state.title, scheduleTimestamp: nil, subscribedToScheduled: false, recordingStartTimestamp: nil, sortAscending: true)), audioSessionControl: strongSelf.audioSessionControl) + } else { + strongSelf.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo( + id: callInfo.id, + accessHash: callInfo.accessHash, + participantCount: state.totalCount, + clientParams: nil, + streamDcId: nil, + title: state.title, + scheduleTimestamp: state.scheduleTimestamp, + subscribedToScheduled: false, + recordingStartTimestamp: state.recordingStartTimestamp, + sortAscending: state.sortAscending + )))) + + strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState( + participantCount: state.totalCount, + topParticipants: topParticipants, + activeSpeakers: Set() + ))) + } + })) + } + private func updateSessionState(internalState: InternalState, audioSessionControl: ManagedAudioSessionControl?) { let previousControl = self.audioSessionControl self.audioSessionControl = audioSessionControl @@ -1725,179 +1904,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.startCheckingCallIfNeeded() } } else if case let .active(callInfo) = internalState, callInfo.scheduleTimestamp != nil { - let accountContext = self.accountContext - let peerId = self.peerId - let rawAdminIds: Signal, NoError> - if peerId.namespace == Namespaces.Peer.CloudChannel { - rawAdminIds = Signal { subscriber in - let (disposable, _) = accountContext.peerChannelMemberCategoriesContextsManager.admins(postbox: accountContext.account.postbox, network: accountContext.account.network, accountPeerId: accountContext.account.peerId, peerId: peerId, updated: { list in - var peerIds = Set() - for item in list.list { - if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) { - peerIds.insert(item.peer.id) - } - } - subscriber.putNext(peerIds) - }) - return disposable - } - |> distinctUntilChanged - |> runOn(.mainQueue()) - } else { - rawAdminIds = accountContext.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peerId)]) - |> map { views -> Set in - guard let view = views.views[.cachedPeerData(peerId: peerId)] as? CachedPeerDataView else { - return Set() - } - guard let cachedData = view.cachedPeerData as? CachedGroupData, let participants = cachedData.participants else { - return Set() - } - return Set(participants.participants.compactMap { item -> PeerId? in - switch item { - case .creator, .admin: - return item.peerId - default: - return nil - } - }) - } - |> distinctUntilChanged - } - - let adminIds = combineLatest(queue: .mainQueue(), - rawAdminIds, - accountContext.account.postbox.combinedView(keys: [.basicPeer(peerId)]) - ) - |> map { rawAdminIds, view -> Set in - var rawAdminIds = rawAdminIds - if let peerView = view.views[.basicPeer(peerId)] as? BasicPeerView, let peer = peerView.peer as? TelegramChannel { - if peer.hasPermission(.manageCalls) { - rawAdminIds.insert(accountContext.account.peerId) - } else { - rawAdminIds.remove(accountContext.account.peerId) - } - } - return rawAdminIds - } - |> distinctUntilChanged - - let participantsContext = GroupCallParticipantsContext( - account: self.accountContext.account, - peerId: self.peerId, - myPeerId: self.joinAsPeerId, - id: callInfo.id, - accessHash: callInfo.accessHash, - state: GroupCallParticipantsContext.State( - participants: [], - nextParticipantsFetchOffset: nil, - adminIds: Set(), - isCreator: false, - defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: self.stateValue.defaultParticipantMuteState == .muted, canChange: false), - sortAscending: true, - recordingStartTimestamp: nil, - title: self.stateValue.title, - scheduleTimestamp: self.stateValue.scheduleTimestamp, - subscribedToScheduled: self.stateValue.subscribedToScheduled, - totalCount: 0, - version: 0 - ), - previousServiceState: nil - ) - self.temporaryParticipantsContext = nil - self.participantsContext = participantsContext - - let myPeerId = self.joinAsPeerId - let myPeer = self.accountContext.account.postbox.transaction { transaction -> (Peer, CachedPeerData?)? in - if let peer = transaction.getPeer(myPeerId) { - return (peer, transaction.getPeerCachedData(peerId: myPeerId)) - } else { - return nil - } - } - self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(), - participantsContext.state, - adminIds, - myPeer, - accountContext.account.postbox.peerView(id: peerId) - ).start(next: { [weak self] state, adminIds, myPeerAndCachedData, view in - guard let strongSelf = self else { - return - } - - var members = PresentationGroupCallMembers( - participants: [], - speakingParticipants: Set(), - totalCount: state.totalCount, - loadMoreToken: state.nextParticipantsFetchOffset - ) - - var participants: [GroupCallParticipantsContext.Participant] = [] - var topParticipants: [GroupCallParticipantsContext.Participant] = [] - if let (myPeer, cachedData) = myPeerAndCachedData { - let about: String? - if let cachedData = cachedData as? CachedUserData { - about = cachedData.about - } else if let cachedData = cachedData as? CachedUserData { - about = cachedData.about - } else { - about = nil - } - participants.append(GroupCallParticipantsContext.Participant( - peer: myPeer, - ssrc: nil, - jsonParams: nil, - joinTimestamp: strongSelf.temporaryJoinTimestamp, - raiseHandRating: strongSelf.temporaryRaiseHandRating, - hasRaiseHand: strongSelf.temporaryHasRaiseHand, - activityTimestamp: strongSelf.temporaryActivityTimestamp, - activityRank: strongSelf.temporaryActivityRank, - muteState: strongSelf.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), - volume: nil, - about: about - )) - } - - for participant in participants { - members.participants.append(participant) - - if topParticipants.count < 3 { - topParticipants.append(participant) - } - } - - strongSelf.membersValue = members - strongSelf.stateValue.adminIds = adminIds - strongSelf.stateValue.canManageCall = state.isCreator || adminIds.contains(strongSelf.accountContext.account.peerId) - if (state.isCreator || strongSelf.stateValue.adminIds.contains(strongSelf.accountContext.account.peerId)) && state.defaultParticipantsAreMuted.canChange { - strongSelf.stateValue.defaultParticipantMuteState = state.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted - } - strongSelf.stateValue.recordingStartTimestamp = state.recordingStartTimestamp - strongSelf.stateValue.title = state.title - - strongSelf.stateValue.scheduleTimestamp = strongSelf.isScheduledStarted ? nil : state.scheduleTimestamp - if state.scheduleTimestamp == nil && !strongSelf.isScheduledStarted { - strongSelf.updateSessionState(internalState: .active(GroupCallInfo(id: callInfo.id, accessHash: callInfo.accessHash, participantCount: state.totalCount, clientParams: callInfo.clientParams, streamDcId: callInfo.streamDcId, title: state.title, scheduleTimestamp: nil, subscribedToScheduled: false, recordingStartTimestamp: nil, sortAscending: true)), audioSessionControl: strongSelf.audioSessionControl) - } else { - strongSelf.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo( - id: callInfo.id, - accessHash: callInfo.accessHash, - participantCount: state.totalCount, - clientParams: nil, - streamDcId: nil, - title: state.title, - scheduleTimestamp: state.scheduleTimestamp, - subscribedToScheduled: false, - recordingStartTimestamp: state.recordingStartTimestamp, - sortAscending: state.sortAscending - )))) - - strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState( - participantCount: state.totalCount, - topParticipants: topParticipants, - activeSpeakers: Set() - ))) - } - })) + self.switchToTemporaryScheduledParticipantsContext() } } } @@ -2086,6 +2093,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if strongSelf.stateValue.scheduleTimestamp != nil { strongSelf.stateValue.myPeerId = peerId strongSelf.reconnectedAsEventsPipe.putNext(myPeer) + strongSelf.switchToTemporaryScheduledParticipantsContext() } else { strongSelf.reconnectingAsPeer = myPeer diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 4e23ad8ae5..27c7472a94 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -1723,7 +1723,7 @@ public final class VoiceChatController: ViewController { self.listNode.updateFloatingHeaderOffset = { [weak self] offset, transition in if let strongSelf = self { strongSelf.currentContentOffset = offset - if !strongSelf.animatingExpansion && !strongSelf.animatingInsertion && strongSelf.panGestureArguments == nil { + if !strongSelf.animatingExpansion && !strongSelf.animatingInsertion && strongSelf.panGestureArguments == nil && !strongSelf.animatingAppearance { strongSelf.updateFloatingHeaderOffset(offset: offset, transition: transition) } } @@ -2876,9 +2876,24 @@ public final class VoiceChatController: ViewController { topInset = listSize.height } - let offset = offset + topInset + var bottomEdge: CGFloat = 0.0 + self.listNode.forEachItemNode { itemNode in + if let itemNode = itemNode as? ListViewItemNode { + let convertedFrame = self.listNode.view.convert(itemNode.frame, to: self.contentContainer.view) + if convertedFrame.maxY > bottomEdge { + bottomEdge = convertedFrame.maxY + } + } + } + + + let offset = (bottomEdge.isZero ? 0.0 : offset) + topInset self.floatingHeaderOffset = offset - + + if bottomEdge.isZero { + bottomEdge = self.listNode.frame.minY + 46.0 + 56.0 + } + let rawPanelOffset = offset + listTopInset - topPanelHeight let panelOffset = max(layoutTopInset, rawPanelOffset) let topPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: panelOffset), size: CGSize(width: size.width, height: topPanelHeight)) @@ -2920,16 +2935,6 @@ public final class VoiceChatController: ViewController { } self.topPanelBackgroundNode.frame = CGRect(x: 0.0, y: topPanelHeight - 24.0, width: size.width, height: 24.0) - var bottomEdge: CGFloat = 0.0 - self.listNode.forEachItemNode { itemNode in - if let itemNode = itemNode as? ListViewItemNode { - let convertedFrame = self.listNode.view.convert(itemNode.frame, to: self.contentContainer.view) - if convertedFrame.maxY > bottomEdge { - bottomEdge = convertedFrame.maxY - } - } - } - let listMaxY = listTopInset + listSize.height let bottomOffset: CGFloat = min(0.0, bottomEdge - listMaxY) @@ -3388,16 +3393,20 @@ public final class VoiceChatController: ViewController { guard let (layout, navigationHeight) = self.validLayout else { return } - let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring) + self.updateFloatingHeaderOffset(offset: 0.0, transition: .immediate) + + self.animatingAppearance = true - let topPanelFrame = self.topPanelNode.view.convert(self.topPanelNode.bounds, to: self.view) - let initialBounds = self.contentContainer.bounds + let topPanelFrame = self.topPanelNode.view.convert(self.topPanelNode.bounds, to: self.view) self.contentContainer.bounds = initialBounds.offsetBy(dx: 0.0, dy: -(layout.size.height - topPanelFrame.minY)) self.contentContainer.isHidden = false + + let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring) transition.animateView({ self.contentContainer.view.bounds = initialBounds }, completion: { _ in + self.animatingAppearance = false if self.actionButton.supernode !== self.bottomPanelNode { self.actionButton.ignoreHierarchyChanges = true self.audioButton.isHidden = false @@ -3731,6 +3740,7 @@ public final class VoiceChatController: ViewController { private var animatingInsertion = false private var animatingExpansion = false + private var animatingAppearance = false private var panGestureArguments: (topInset: CGFloat, offset: CGFloat)? @objc func panGesture(_ recognizer: UIPanGestureRecognizer) { diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatTimerNode.swift b/submodules/TelegramCallsUI/Sources/VoiceChatTimerNode.swift index 6d586d3946..0994a2880a 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatTimerNode.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatTimerNode.swift @@ -132,7 +132,7 @@ final class VoiceChatTimerNode: ASDisplayNode { let elapsedTime = scheduleTime - currentTime let timerText: String if elapsedTime >= 86400 { - timerText = timeIntervalString(strings: self.strings, value: elapsedTime) + timerText = timeIntervalString(strings: self.strings, value: elapsedTime).uppercased() } else { timerText = textForTimeout(value: abs(elapsedTime)) }