mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-04 10:30:42 +00:00
Merge commit '8ec300703d18093504b95aee79c37d561c0fafbc'
This commit is contained in:
commit
6e5e365d40
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -841,9 +841,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> take(1)
|
let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> take(1)
|
||||||
|
|
||||||
let foundLocalPeers: Signal<(peers: [RenderedPeer], unread: [PeerId: (Int32, Bool)]), NoError>
|
let foundLocalPeers: Signal<(peers: [RenderedPeer], unread: [PeerId: (Int32, Bool)]), NoError>
|
||||||
|
|
||||||
if let query = query {
|
if let query = query {
|
||||||
foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased())
|
foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased())
|
||||||
|> mapToSignal { local -> Signal<([PeerView], [RenderedPeer]), NoError> in
|
|> mapToSignal { local -> Signal<([PeerView], [RenderedPeer]), NoError> in
|
||||||
@ -1279,7 +1277,6 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
}, openUrl: { url in
|
}, openUrl: { url in
|
||||||
interaction.openUrl(url)
|
interaction.openUrl(url)
|
||||||
}, openPeer: { peer, navigation in
|
}, openPeer: { peer, navigation in
|
||||||
// interaction.openPeer(peer.id, navigation)
|
|
||||||
}, callPeer: { _, _ in
|
}, callPeer: { _, _ in
|
||||||
}, enqueueMessage: { _ in
|
}, enqueueMessage: { _ in
|
||||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: .custom(messages: foundMessages, at: message.id, loadMore: {
|
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: .custom(messages: foundMessages, at: message.id, loadMore: {
|
||||||
|
@ -320,21 +320,6 @@ private final class VisualMediaItemNode: ASDisplayNode {
|
|||||||
|
|
||||||
func updateIsVisible(_ isVisible: Bool) {
|
func updateIsVisible(_ isVisible: Bool) {
|
||||||
self.hasVisibility = isVisible
|
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
|
self.displayLink?.isPaused = !self.hasVisibility || self.isHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,8 +407,8 @@ private final class VisualMediaItem {
|
|||||||
let dimensions: CGSize
|
let dimensions: CGSize
|
||||||
let aspectRatio: CGFloat
|
let aspectRatio: CGFloat
|
||||||
|
|
||||||
init(message: Message) {
|
init(message: Message, index: UInt32?) {
|
||||||
self.index = nil
|
self.index = index
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
var aspectRatio: CGFloat = 1.0
|
var aspectRatio: CGFloat = 1.0
|
||||||
@ -441,10 +426,10 @@ private final class VisualMediaItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var stableId: UInt32 {
|
var stableId: UInt32 {
|
||||||
if let message = self.message {
|
if let index = self.index {
|
||||||
return message.stableId
|
|
||||||
} else if let index = self.index {
|
|
||||||
return index
|
return index
|
||||||
|
} else if let message = self.message {
|
||||||
|
return message.stableId
|
||||||
} else {
|
} else {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -708,7 +693,6 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.animationTimer?.invalidate()
|
self.animationTimer?.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func updateHistory(entries: [ChatListSearchEntry]?, totalCount: Int32, updateType: ViewUpdateType) {
|
func updateHistory(entries: [ChatListSearchEntry]?, totalCount: Int32, updateType: ViewUpdateType) {
|
||||||
switch updateType {
|
switch updateType {
|
||||||
case .FillHole:
|
case .FillHole:
|
||||||
@ -716,11 +700,13 @@ final class ChatListSearchMediaNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
default:
|
default:
|
||||||
self.mediaItems.removeAll()
|
self.mediaItems.removeAll()
|
||||||
|
|
||||||
|
var index: UInt32 = 0
|
||||||
if let entries = entries {
|
if let entries = entries {
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
if case let .message(message, _, _, _, _, _, _) = entry {
|
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
|
self.itemsLayout = nil
|
||||||
|
@ -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<Set<PeerId>, 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<PeerId>()
|
||||||
|
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<PeerId> 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<PeerId> 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?) {
|
private func updateSessionState(internalState: InternalState, audioSessionControl: ManagedAudioSessionControl?) {
|
||||||
let previousControl = self.audioSessionControl
|
let previousControl = self.audioSessionControl
|
||||||
self.audioSessionControl = audioSessionControl
|
self.audioSessionControl = audioSessionControl
|
||||||
@ -1725,179 +1904,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
self.startCheckingCallIfNeeded()
|
self.startCheckingCallIfNeeded()
|
||||||
}
|
}
|
||||||
} else if case let .active(callInfo) = internalState, callInfo.scheduleTimestamp != nil {
|
} else if case let .active(callInfo) = internalState, callInfo.scheduleTimestamp != nil {
|
||||||
let accountContext = self.accountContext
|
self.switchToTemporaryScheduledParticipantsContext()
|
||||||
let peerId = self.peerId
|
|
||||||
let rawAdminIds: Signal<Set<PeerId>, 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<PeerId>()
|
|
||||||
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<PeerId> 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<PeerId> 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()
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2086,6 +2093,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
if strongSelf.stateValue.scheduleTimestamp != nil {
|
if strongSelf.stateValue.scheduleTimestamp != nil {
|
||||||
strongSelf.stateValue.myPeerId = peerId
|
strongSelf.stateValue.myPeerId = peerId
|
||||||
strongSelf.reconnectedAsEventsPipe.putNext(myPeer)
|
strongSelf.reconnectedAsEventsPipe.putNext(myPeer)
|
||||||
|
strongSelf.switchToTemporaryScheduledParticipantsContext()
|
||||||
} else {
|
} else {
|
||||||
strongSelf.reconnectingAsPeer = myPeer
|
strongSelf.reconnectingAsPeer = myPeer
|
||||||
|
|
||||||
|
@ -1723,7 +1723,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.listNode.updateFloatingHeaderOffset = { [weak self] offset, transition in
|
self.listNode.updateFloatingHeaderOffset = { [weak self] offset, transition in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.currentContentOffset = offset
|
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)
|
strongSelf.updateFloatingHeaderOffset(offset: offset, transition: transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2876,9 +2876,24 @@ public final class VoiceChatController: ViewController {
|
|||||||
topInset = listSize.height
|
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
|
self.floatingHeaderOffset = offset
|
||||||
|
|
||||||
|
if bottomEdge.isZero {
|
||||||
|
bottomEdge = self.listNode.frame.minY + 46.0 + 56.0
|
||||||
|
}
|
||||||
|
|
||||||
let rawPanelOffset = offset + listTopInset - topPanelHeight
|
let rawPanelOffset = offset + listTopInset - topPanelHeight
|
||||||
let panelOffset = max(layoutTopInset, rawPanelOffset)
|
let panelOffset = max(layoutTopInset, rawPanelOffset)
|
||||||
let topPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: panelOffset), size: CGSize(width: size.width, height: topPanelHeight))
|
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)
|
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 listMaxY = listTopInset + listSize.height
|
||||||
let bottomOffset: CGFloat = min(0.0, bottomEdge - listMaxY)
|
let bottomOffset: CGFloat = min(0.0, bottomEdge - listMaxY)
|
||||||
|
|
||||||
@ -3388,16 +3393,20 @@ public final class VoiceChatController: ViewController {
|
|||||||
guard let (layout, navigationHeight) = self.validLayout else {
|
guard let (layout, navigationHeight) = self.validLayout else {
|
||||||
return
|
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 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.bounds = initialBounds.offsetBy(dx: 0.0, dy: -(layout.size.height - topPanelFrame.minY))
|
||||||
self.contentContainer.isHidden = false
|
self.contentContainer.isHidden = false
|
||||||
|
|
||||||
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
|
||||||
transition.animateView({
|
transition.animateView({
|
||||||
self.contentContainer.view.bounds = initialBounds
|
self.contentContainer.view.bounds = initialBounds
|
||||||
}, completion: { _ in
|
}, completion: { _ in
|
||||||
|
self.animatingAppearance = false
|
||||||
if self.actionButton.supernode !== self.bottomPanelNode {
|
if self.actionButton.supernode !== self.bottomPanelNode {
|
||||||
self.actionButton.ignoreHierarchyChanges = true
|
self.actionButton.ignoreHierarchyChanges = true
|
||||||
self.audioButton.isHidden = false
|
self.audioButton.isHidden = false
|
||||||
@ -3731,6 +3740,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
private var animatingInsertion = false
|
private var animatingInsertion = false
|
||||||
private var animatingExpansion = false
|
private var animatingExpansion = false
|
||||||
|
private var animatingAppearance = false
|
||||||
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat)?
|
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat)?
|
||||||
|
|
||||||
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||||
|
@ -132,7 +132,7 @@ final class VoiceChatTimerNode: ASDisplayNode {
|
|||||||
let elapsedTime = scheduleTime - currentTime
|
let elapsedTime = scheduleTime - currentTime
|
||||||
let timerText: String
|
let timerText: String
|
||||||
if elapsedTime >= 86400 {
|
if elapsedTime >= 86400 {
|
||||||
timerText = timeIntervalString(strings: self.strings, value: elapsedTime)
|
timerText = timeIntervalString(strings: self.strings, value: elapsedTime).uppercased()
|
||||||
} else {
|
} else {
|
||||||
timerText = textForTimeout(value: abs(elapsedTime))
|
timerText = textForTimeout(value: abs(elapsedTime))
|
||||||
}
|
}
|
||||||
|
@ -188,8 +188,11 @@ func locallyRenderedMessage(message: StoreMessage, peers: [PeerId: Peer]) -> Mes
|
|||||||
var hasher = Hasher()
|
var hasher = Hasher()
|
||||||
hasher.combine(id.id)
|
hasher.combine(id.id)
|
||||||
hasher.combine(id.peerId)
|
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: [])
|
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: [])
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user