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 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: {
|
||||
|
@ -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
|
||||
|
@ -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?) {
|
||||
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<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()
|
||||
)))
|
||||
}
|
||||
}))
|
||||
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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
let topPanelFrame = self.topPanelNode.view.convert(self.topPanelNode.bounds, to: self.view)
|
||||
self.animatingAppearance = true
|
||||
|
||||
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) {
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -189,7 +189,10 @@ func locallyRenderedMessage(message: StoreMessage, peers: [PeerId: Peer]) -> Mes
|
||||
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: [])
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user