From 84171b89f45d8092c1cf9556debd52098057e09e Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 27 Oct 2025 00:22:14 +0400 Subject: [PATCH] Update --- .../Sources/PresentationGroupCall.swift | 1 + .../State/ConferenceCallE2EContext.swift | 3 +- .../TelegramEngine/Calls/GroupCalls.swift | 93 ++++++++---------- .../Calls/TelegramEngineCalls.swift | 2 +- .../Sources/ChatSendStarsScreen.swift | 53 +++++++--- .../StoryContentLiveChatComponent.swift | 65 +++++++++---- .../Sources/StoryItemContentComponent.swift | 97 ++++++++++++++++--- .../StoryItemSetContainerComponent.swift | 32 +++--- ...StoryItemSetContainerViewSendMessage.swift | 22 ++++- 9 files changed, 251 insertions(+), 117 deletions(-) diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 90c815b381..8860268561 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -2669,6 +2669,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } self.stateValue.messagesAreEnabled = state.messagesAreEnabled.isEnabled self.stateValue.canEnableMessages = state.messagesAreEnabled.canChange + self.stateValue.sendPaidMessageStars = state.messagesAreEnabled.sendPaidMessagesStars self.stateValue.recordingStartTimestamp = state.recordingStartTimestamp self.stateValue.title = state.title self.stateValue.scheduleTimestamp = state.scheduleTimestamp diff --git a/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift b/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift index c5ed1209c2..c371b612ba 100644 --- a/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift +++ b/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift @@ -371,7 +371,8 @@ public final class ConferenceCallE2EContext { offset: "", ssrcs: [], limit: 100, - sortAscending: true + sortAscending: true, + isStream: false ) |> map(Optional.init) |> `catch` { _ -> Signal in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift index f6166bb8ac..06bd09fe43 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift @@ -507,7 +507,7 @@ public enum GetGroupCallParticipantsError { case generic } -func _internal_getGroupCallParticipants(account: Account, reference: InternalGroupCallReference, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?) -> Signal { +func _internal_getGroupCallParticipants(account: Account, reference: InternalGroupCallReference, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?, isStream: Bool) -> Signal { let accountPeerId = account.peerId let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, GroupCallParticipantsContext.State.MessagesAreEnabled?, Bool, Int, Bool, Bool), GetGroupCallParticipantsError> @@ -525,8 +525,15 @@ func _internal_getGroupCallParticipants(account: Account, reference: InternalGro return combineLatest( account.network.request(Api.functions.phone.getGroupParticipants(call: reference.apiInputGroupCall, ids: [], sources: ssrcs.map { Int32(bitPattern: $0) }, offset: offset, limit: limit)) + |> map(Optional.init) |> mapError { _ -> GetGroupCallParticipantsError in return .generic + } + |> `catch` { error -> Signal in + if isStream { + return .single(nil) + } + return .fail(error) }, sortAscendingValue ) @@ -539,21 +546,27 @@ func _internal_getGroupCallParticipants(account: Account, reference: InternalGro let (sortAscendingValue, scheduleTimestamp, subscribedToScheduled, defaultParticipantsAreMuted, messagesAreEnabled, isVideoEnabled, unmutedVideoLimit, isStream, isCreator) = sortAscendingAndScheduleTimestamp - switch result { - case let .groupParticipants(count, participants, nextOffset, chats, users, apiVersion): - totalCount = Int(count) - version = apiVersion - - if participants.count != 0 && !nextOffset.isEmpty { - nextParticipantsFetchOffset = nextOffset - } else { - nextParticipantsFetchOffset = nil + if let result { + switch result { + case let .groupParticipants(count, participants, nextOffset, chats, users, apiVersion): + totalCount = Int(count) + version = apiVersion + + if participants.count != 0 && !nextOffset.isEmpty { + nextParticipantsFetchOffset = nextOffset + } else { + nextParticipantsFetchOffset = nil + } + + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + + parsedParticipants = participants.compactMap { GroupCallParticipantsContext.Participant($0, transaction: transaction) } } - - let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) - updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) - - parsedParticipants = participants.compactMap { GroupCallParticipantsContext.Participant($0, transaction: transaction) } + } else { + totalCount = 0 + version = 0 + nextParticipantsFetchOffset = nil } parsedParticipants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: sortAscendingValue) }) @@ -645,7 +658,7 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, |> castError(InternalJoinError.self) |> mapToSignal { e2eData -> Signal in return account.postbox.transaction { transaction -> Api.InputPeer? in - if let joinAs = joinAs { + if let joinAs { return transaction.getPeer(joinAs).flatMap(apiInputPeer) } else { return .inputPeerSelf @@ -653,7 +666,7 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, } |> castError(InternalJoinError.self) |> mapToSignal { inputJoinAs -> Signal in - guard let inputJoinAs = inputJoinAs else { + guard let inputJoinAs else { return .fail(.error(.generic)) } @@ -719,38 +732,13 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, } } - let getParticipantsRequest: Signal - if isStream { - getParticipantsRequest = .single(GroupCallParticipantsContext.State( - participants: [], - nextParticipantsFetchOffset: nil, - adminIds: Set(), - isCreator: false, - defaultParticipantsAreMuted: .init(isMuted: true, canChange: false), - messagesAreEnabled: .init(isEnabled: true, canChange: false, sendPaidMessagesStars: nil), - sortAscending: true, - recordingStartTimestamp: nil, - title: nil, - scheduleTimestamp: nil, - subscribedToScheduled: false, - totalCount: 0, - isVideoEnabled: false, - unmutedVideoLimit: 0, - isStream: true, - version: 0 - )) - } else { - getParticipantsRequest = _internal_getGroupCallParticipants(account: account, reference: reference, offset: "", ssrcs: [], limit: 100, sortAscending: true) - |> mapError { _ -> InternalJoinError in - return .error(.generic) - } + let getParticipantsRequest = _internal_getGroupCallParticipants(account: account, reference: reference, offset: "", ssrcs: [], limit: 100, sortAscending: true, isStream: isStream) + |> mapError { _ -> InternalJoinError in + return .error(.generic) } - return combineLatest( - joinRequest, - getParticipantsRequest - ) - |> mapToSignal { updates, participantsState -> Signal in + return joinRequest + |> mapToSignal { updates -> Signal in let peer = account.postbox.transaction { transaction -> Peer? in return peerId.flatMap(transaction.getPeer) } @@ -758,9 +746,10 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, return combineLatest( peerAdminIds |> castError(InternalJoinError.self) |> take(1), - peer + peer, + getParticipantsRequest ) - |> mapToSignal { peerAdminIds, peer -> Signal in + |> mapToSignal { peerAdminIds, peer, participantsState -> Signal in var state = participantsState if let peer { if let channel = peer as? TelegramChannel { @@ -2149,7 +2138,7 @@ public final class GroupCallParticipantsContext { Logger.shared.log("GroupCallParticipantsContext", "will request ssrcs=\(ssrcs)") - self.disposable.set((_internal_getGroupCallParticipants(account: self.account, reference: self.reference, offset: "", ssrcs: Array(ssrcs), limit: 100, sortAscending: true) + self.disposable.set((_internal_getGroupCallParticipants(account: self.account, reference: self.reference, offset: "", ssrcs: Array(ssrcs), limit: 100, sortAscending: true, isStream: false) |> deliverOnMainQueue).start(next: { [weak self] state in guard let strongSelf = self else { return @@ -2367,7 +2356,7 @@ public final class GroupCallParticipantsContext { self.updateQueue.removeAll() - self.disposable.set((_internal_getGroupCallParticipants(account: self.account, reference: self.reference, offset: "", ssrcs: [], limit: 100, sortAscending: self.stateValue.state.sortAscending) + self.disposable.set((_internal_getGroupCallParticipants(account: self.account, reference: self.reference, offset: "", ssrcs: [], limit: 100, sortAscending: self.stateValue.state.sortAscending, isStream: false) |> deliverOnMainQueue).start(next: { [weak self] state in guard let strongSelf = self else { return @@ -2672,7 +2661,7 @@ public final class GroupCallParticipantsContext { } self.isLoadingMore = true - self.disposable.set((_internal_getGroupCallParticipants(account: self.account, reference: self.reference, offset: token, ssrcs: [], limit: 100, sortAscending: self.stateValue.state.sortAscending) + self.disposable.set((_internal_getGroupCallParticipants(account: self.account, reference: self.reference, offset: token, ssrcs: [], limit: 100, sortAscending: self.stateValue.state.sortAscending, isStream: false) |> deliverOnMainQueue).start(next: { [weak self] state in guard let strongSelf = self else { return diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift index 93e4ede2ac..380d126e4c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift @@ -55,7 +55,7 @@ public extension TelegramEngine { } public func getGroupCallParticipants(reference: InternalGroupCallReference, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?) -> Signal { - return _internal_getGroupCallParticipants(account: self.account, reference: reference, offset: offset, ssrcs: ssrcs, limit: limit, sortAscending: sortAscending) + return _internal_getGroupCallParticipants(account: self.account, reference: reference, offset: offset, ssrcs: ssrcs, limit: limit, sortAscending: sortAscending, isStream: false) } public func joinGroupCall(peerId: PeerId?, joinAs: PeerId?, callId: Int64, reference: InternalGroupCallReference, isStream: Bool, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil, generateE2E: ((Data?) -> JoinGroupCallE2E?)?) -> Signal { diff --git a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift index 00f065cabb..aec891ec35 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift @@ -788,6 +788,7 @@ private final class ChatSendStarsScreenComponent: Component { private struct Amount: Equatable { private let sliderSteps: [Int] + private let minRealValue: Int private let maxRealValue: Int let maxSliderValue: Int private let isLogarithmic: Bool @@ -795,14 +796,16 @@ private final class ChatSendStarsScreenComponent: Component { private(set) var realValue: Int private(set) var sliderValue: Int - private static func makeSliderSteps(maxRealValue: Int, isLogarithmic: Bool) -> [Int] { + private static func makeSliderSteps(minRealValue: Int, maxRealValue: Int, isLogarithmic: Bool) -> [Int] { if isLogarithmic { - var sliderSteps: [Int] = [ 1, 10, 50, 100, 500, 1_000, 2_000, 5_000, 7_500, 10_000 ] + var sliderSteps: [Int] = [1, 10, 50, 100, 500, 1_000, 2_000, 5_000, 7_500, 10_000 ] + sliderSteps.removeAll(where: { $0 <= minRealValue }) + sliderSteps.insert(minRealValue, at: 0) sliderSteps.removeAll(where: { $0 >= maxRealValue }) sliderSteps.append(maxRealValue) return sliderSteps } else { - return [1, maxRealValue] + return [minRealValue, maxRealValue] } } @@ -834,8 +837,9 @@ private final class ChatSendStarsScreenComponent: Component { } } - init(realValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) { - self.sliderSteps = Amount.makeSliderSteps(maxRealValue: maxRealValue, isLogarithmic: isLogarithmic) + init(realValue: Int, minRealValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) { + self.sliderSteps = Amount.makeSliderSteps(minRealValue: minRealValue, maxRealValue: maxRealValue, isLogarithmic: isLogarithmic) + self.minRealValue = minRealValue self.maxRealValue = maxRealValue self.maxSliderValue = maxSliderValue self.isLogarithmic = isLogarithmic @@ -844,8 +848,9 @@ private final class ChatSendStarsScreenComponent: Component { self.sliderValue = Amount.remapValueToSlider(realValue: self.realValue, maxSliderValue: self.maxSliderValue, steps: self.sliderSteps) } - init(sliderValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) { - self.sliderSteps = Amount.makeSliderSteps(maxRealValue: maxRealValue, isLogarithmic: isLogarithmic) + init(sliderValue: Int, minRealValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) { + self.sliderSteps = Amount.makeSliderSteps(minRealValue: minRealValue, maxRealValue: maxRealValue, isLogarithmic: isLogarithmic) + self.minRealValue = minRealValue self.maxRealValue = maxRealValue self.maxSliderValue = maxSliderValue self.isLogarithmic = isLogarithmic @@ -855,11 +860,11 @@ private final class ChatSendStarsScreenComponent: Component { } func withRealValue(_ realValue: Int) -> Amount { - return Amount(realValue: realValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic) + return Amount(realValue: realValue, minRealValue: self.minRealValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic) } func withSliderValue(_ sliderValue: Int) -> Amount { - return Amount(sliderValue: sliderValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic) + return Amount(sliderValue: sliderValue, minRealValue: self.minRealValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic) } } @@ -926,7 +931,7 @@ private final class ChatSendStarsScreenComponent: Component { private var balance: StarsAmount? - private var amount: Amount = Amount(realValue: 1, maxRealValue: 1000, maxSliderValue: 1000, isLogarithmic: true) + private var amount: Amount = Amount(realValue: 1, minRealValue: 1, maxRealValue: 1000, maxSliderValue: 1000, isLogarithmic: true) private var didChangeAmount: Bool = false private var privacyPeer: PrivacyPeer = .account @@ -1423,7 +1428,7 @@ private final class ChatSendStarsScreenComponent: Component { switch component.initialData.subjectInitialData { case let .react(reactData): self.currentMyPeer = reactData.myPeer - self.amount = Amount(realValue: 50, maxRealValue: reactData.maxAmount, maxSliderValue: 999, isLogarithmic: true) + self.amount = Amount(realValue: max(reactData.minAmount, 50), minRealValue: reactData.minAmount, maxRealValue: reactData.maxAmount, maxSliderValue: 999, isLogarithmic: true) if let myTopPeer = reactData.myTopPeer { if myTopPeer.isAnonymous { @@ -1460,7 +1465,9 @@ private final class ChatSendStarsScreenComponent: Component { }) case let .liveStreamMessage(liveStreamMessageData): self.currentMyPeer = nil - self.amount = Amount(realValue: 50, maxRealValue: liveStreamMessageData.maxAmount, maxSliderValue: 999, isLogarithmic: true) + + let currentValue: Int = liveStreamMessageData.currentAmount ?? 50 + self.amount = Amount(realValue: max(currentValue, liveStreamMessageData.minAmount), minRealValue: liveStreamMessageData.minAmount, maxRealValue: liveStreamMessageData.maxAmount, maxSliderValue: 999, isLogarithmic: true) self.privacyPeer = .account } @@ -2614,7 +2621,7 @@ private final class ChatSendStarsScreenComponent: Component { public class ChatSendStarsScreen: ViewControllerComponentContainer { public enum ReactSubject { case message(EngineMessage.Id) - case liveStream(peerId: EnginePeer.Id, storyId: Int32) + case liveStream(peerId: EnginePeer.Id, storyId: Int32, minAmount: Int) } fileprivate enum SubjectInitialData { @@ -2627,10 +2634,11 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { let currentSentAmount: Int? let topPeers: [ChatSendStarsScreen.TopPeer] let myTopPeer: ChatSendStarsScreen.TopPeer? + let minAmount: Int let maxAmount: Int let completion: (Int64, TelegramPaidReactionPrivacy, Bool, ChatSendStarsScreen.TransitionOut) -> Void - init(peer: EnginePeer, myPeer: EnginePeer, defaultPrivacyPeer: ChatSendStarsScreenComponent.PrivacyPeer, channelsForPublicReaction: [EnginePeer], reactSubject: ReactSubject, currentSentAmount: Int?, topPeers: [ChatSendStarsScreen.TopPeer], myTopPeer: ChatSendStarsScreen.TopPeer?, maxAmount: Int, completion: @escaping (Int64, TelegramPaidReactionPrivacy, Bool, ChatSendStarsScreen.TransitionOut) -> Void) { + init(peer: EnginePeer, myPeer: EnginePeer, defaultPrivacyPeer: ChatSendStarsScreenComponent.PrivacyPeer, channelsForPublicReaction: [EnginePeer], reactSubject: ReactSubject, currentSentAmount: Int?, topPeers: [ChatSendStarsScreen.TopPeer], myTopPeer: ChatSendStarsScreen.TopPeer?, minAmount: Int, maxAmount: Int, completion: @escaping (Int64, TelegramPaidReactionPrivacy, Bool, ChatSendStarsScreen.TransitionOut) -> Void) { self.peer = peer self.myPeer = myPeer self.defaultPrivacyPeer = defaultPrivacyPeer @@ -2639,6 +2647,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { self.currentSentAmount = currentSentAmount self.topPeers = topPeers self.myTopPeer = myTopPeer + self.minAmount = minAmount self.maxAmount = maxAmount self.completion = completion } @@ -2647,20 +2656,26 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { final class LiveStreamMessage { let peer: EnginePeer let myPeer: EnginePeer + let minAmount: Int let maxAmount: Int + let currentAmount: Int? let text: NSAttributedString let completion: (Int64, ChatSendStarsScreen.TransitionOut) -> Void init( peer: EnginePeer, myPeer: EnginePeer, + minAmount: Int, maxAmount: Int, + currentAmount: Int?, text: NSAttributedString, completion: @escaping (Int64, ChatSendStarsScreen.TransitionOut) -> Void ) { self.peer = peer self.myPeer = myPeer + self.minAmount = minAmount self.maxAmount = maxAmount + self.currentAmount = currentAmount self.text = text self.completion = completion } @@ -2838,6 +2853,11 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { maxAmount = Int(value) } + var minAmount = 1 + if case let .liveStream(_, _, minAmountValue) = reactSubject { + minAmount = minAmountValue + } + return combineLatest( context.engine.data.get( TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), @@ -2911,6 +2931,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { count: Int(topPeer.count) ) }, + minAmount: minAmount, maxAmount: maxAmount, completion: completion )), @@ -2923,6 +2944,8 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { context: AccountContext, peerId: EnginePeer.Id, text: NSAttributedString, + minAmount: Int, + currentAmount: Int?, completion: @escaping (Int64, TransitionOut) -> Void ) -> Signal { let balance: Signal @@ -2958,7 +2981,9 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { subjectInitialData: .liveStreamMessage(SubjectInitialData.LiveStreamMessage( peer: peer, myPeer: myPeer, + minAmount: minAmount, maxAmount: maxAmount, + currentAmount: currentAmount, text: text, completion: completion )), diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift index 6938faed7e..26a2254daf 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift @@ -421,6 +421,32 @@ final class StoryContentLiveChatComponent: Component { } return true } + + struct StarStats { + var myStars: Int64 + var pendingMyStars: Int64 + var totalStars: Int64 + var topItems: [GroupCallMessagesContext.TopStarsItem] + + init(myStars: Int64, pendingMyStars: Int64, totalStars: Int64, topItems: [GroupCallMessagesContext.TopStarsItem]) { + self.myStars = myStars + self.pendingMyStars = pendingMyStars + self.totalStars = totalStars + self.topItems = topItems + } + } + + struct Info { + var starStats: StarStats? + var isChatEmpty: Bool + var isChatExpanded: Bool + + init(starStats: StarStats?, isChatEmpty: Bool, isChatExpanded: Bool) { + self.starStats = starStats + self.isChatEmpty = isChatEmpty + self.isChatExpanded = isChatExpanded + } + } final class View: UIView { private let listContainer: UIView @@ -443,23 +469,26 @@ final class StoryContentLiveChatComponent: Component { private var currentListIsEmpty: Bool = true private var isMessageContextMenuOpen: Bool = false - public var isChatEmpty: Bool { - guard let messagesState = self.messagesState else { - return true - } - return messagesState.messages.isEmpty - } - private(set) var isChatExpanded: Bool = false + private var isChatExpanded: Bool = false - public var starStars: (myStars: Int64, pendingMyStars: Int64, totalStars: Int64, topItems: [GroupCallMessagesContext.TopStarsItem])? { - guard let messagesState = self.messagesState else { - return nil + public var currentInfo: Info { + var starStats: StoryContentLiveChatComponent.StarStats? + var isChatEmpty = true + if let messagesState = self.messagesState { + isChatEmpty = messagesState.messages.isEmpty + + var myStars: Int64 = 0 + if let item = messagesState.topStars.first(where: { $0.isMy }) { + myStars = item.amount + } + starStats = StoryContentLiveChatComponent.StarStats(myStars: myStars + messagesState.pendingMyStars, pendingMyStars: messagesState.pendingMyStars, totalStars: messagesState.totalStars + messagesState.pendingMyStars, topItems: messagesState.topStars) } - var myStars: Int64 = 0 - if let item = messagesState.topStars.first(where: { $0.isMy }) { - myStars = item.amount - } - return (myStars + messagesState.pendingMyStars, pendingMyStars: messagesState.pendingMyStars, messagesState.totalStars + messagesState.pendingMyStars, messagesState.topStars) + + return Info( + starStats: starStats, + isChatEmpty: isChatEmpty, + isChatExpanded: self.isChatExpanded + ) } override init(frame: CGRect) { @@ -643,10 +672,8 @@ final class StoryContentLiveChatComponent: Component { let state = await (component.call.state |> take(1)).get() - var isAdmin = state.canManageCall - if component.storyPeerId == component.context.account.peerId { - isAdmin = true - } + var isAdmin = false + isAdmin = state.canManageCall var canDelete = isAdmin var isMyMessage = false guard let messagesState = self.messagesState, let message = messagesState.messages.first(where: { $0.id == id }) else { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index ddbdc7d3c5..c5a601a61a 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -96,13 +96,43 @@ final class StoryItemContentComponent: Component { return true } + struct StarStats { + var myStars: Int64 + var pendingMyStars: Int64 + var totalStars: Int64 + var topItems: [GroupCallMessagesContext.TopStarsItem] + + init(myStars: Int64, pendingMyStars: Int64, totalStars: Int64, topItems: [GroupCallMessagesContext.TopStarsItem]) { + self.myStars = myStars + self.pendingMyStars = pendingMyStars + self.totalStars = totalStars + self.topItems = topItems + } + } + struct LiveChatState { var isExpanded: Bool var hasUnseenMessages: Bool + var areMessagesEnabled: Bool + var minMessagePrice: Int64? + var starStats: StarStats? - init(isExpanded: Bool, hasUnseenMessages: Bool) { + init(isExpanded: Bool, hasUnseenMessages: Bool, areMessagesEnabled: Bool, minMessagePrice: Int64?, starStats: StarStats?) { self.isExpanded = isExpanded self.hasUnseenMessages = hasUnseenMessages + self.areMessagesEnabled = areMessagesEnabled + self.minMessagePrice = minMessagePrice + self.starStats = starStats + } + } + + private struct MediaStreamCallState: Equatable { + var areMessagesEnabled: Bool + var minMessagePrice: Int64? + + init(areMessagesEnabled: Bool, minMessagePrice: Int64?) { + self.areMessagesEnabled = areMessagesEnabled + self.minMessagePrice = minMessagePrice } } @@ -111,7 +141,9 @@ final class StoryItemContentComponent: Component { private let overlaysView: StoryItemOverlaysView private var videoNode: UniversalVideoNode? private(set) var mediaStreamCall: PresentationGroupCallImpl? + private var mediaStreamCallState: MediaStreamCallState? private var liveCallStateDisposable: Disposable? + private var liveCallStatsDisposable: Disposable? private var mediaStream: ComponentView? private var loadingEffectView: StoryItemLoadingEffectView? private var loadingEffectAppearanceTimer: SwiftSignalKit.Timer? @@ -160,22 +192,28 @@ final class StoryItemContentComponent: Component { guard let liveChatView = self.liveChat?.view as? StoryContentLiveChatComponent.View else { return nil } - if liveChatView.isChatEmpty { - return nil + + let currentInfo = liveChatView.currentInfo + let mediaStreamCallState = self.mediaStreamCallState + + let starStats = currentInfo.starStats.flatMap { starStats in + return StarStats( + myStars: starStats.myStars, + pendingMyStars: starStats.pendingMyStars, + totalStars: starStats.totalStars, + topItems: starStats.topItems + ) } + return LiveChatState( - isExpanded: liveChatView.isChatExpanded, - hasUnseenMessages: self.liveChatExternal.hasUnseenMessages + isExpanded: currentInfo.isChatExpanded, + hasUnseenMessages: self.liveChatExternal.hasUnseenMessages, + areMessagesEnabled: mediaStreamCallState?.areMessagesEnabled ?? false, + minMessagePrice: mediaStreamCallState?.minMessagePrice, + starStats: starStats ) } - public var starStars: (myStars: Int64, pendingMyStars: Int64, totalStars: Int64, topItems: [GroupCallMessagesContext.TopStarsItem])? { - guard let liveChatView = self.liveChat?.view as? StoryContentLiveChatComponent.View else { - return nil - } - return liveChatView.starStars - } - public func toggleLiveChatExpanded() { guard let liveChatView = self.liveChat?.view as? StoryContentLiveChatComponent.View else { return @@ -227,6 +265,7 @@ final class StoryItemContentComponent: Component { self.videoProgressDisposable?.dispose() self.currentFetchPriority?.disposable.dispose() self.liveCallStateDisposable?.dispose() + self.liveCallStatsDisposable?.dispose() } func allowsInstantPauseOnTouch(point: CGPoint) -> Bool { @@ -1015,7 +1054,27 @@ final class StoryItemContentComponent: Component { if let mediaStreamCall = self.mediaStreamCall { if self.liveCallStateDisposable == nil { - self.liveCallStateDisposable = (mediaStreamCall.members + self.liveCallStateDisposable = (mediaStreamCall.state + |> deliverOnMainQueue).startStrict(next: { [weak self] state in + guard let self else { + return + } + + let mappedState = MediaStreamCallState( + areMessagesEnabled: state.messagesAreEnabled, + minMessagePrice: state.sendPaidMessageStars + ) + if self.mediaStreamCallState != mappedState { + self.mediaStreamCallState = mappedState + if !self.isUpdating { + self.state?.updated(transition: .spring(duration: 0.4)) + } + } + }) + } + + if self.liveCallStatsDisposable == nil { + self.liveCallStatsDisposable = (mediaStreamCall.members |> deliverOnMainQueue).startStandalone(next: { [weak self] members in guard let self, let environment = self.environment else { return @@ -1033,9 +1092,15 @@ final class StoryItemContentComponent: Component { } }) } - } else if let liveCallStateDisposable = self.liveCallStateDisposable { - self.liveCallStateDisposable = nil - liveCallStateDisposable.dispose() + } else { + if let liveCallStateDisposable = self.liveCallStateDisposable { + self.liveCallStateDisposable = nil + liveCallStateDisposable.dispose() + } + if let liveCallStatsDisposable = self.liveCallStatsDisposable { + self.liveCallStatsDisposable = nil + liveCallStatsDisposable.dispose() + } } switch selectedMedia { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 14660949ec..3adc4d568e 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -2971,18 +2971,28 @@ public final class StoryItemSetContainerComponent: Component { var liveChatState: MessageInputPanelComponent.LiveChatState? var starStats: MessageInputPanelComponent.StarStats? + var sendPaidMessageStars = isLiveStream ? self.sendMessageContext.currentLiveStreamMessageStars : component.slice.additionalPeerData.sendPaidMessageStars if let visibleItemView = self.visibleItems[component.slice.item.id]?.view.view as? StoryItemContentComponent.View { - liveChatState = visibleItemView.liveChatState.flatMap { liveChatState in - return MessageInputPanelComponent.LiveChatState( - isExpanded: liveChatState.isExpanded, - hasUnseenMessages: liveChatState.hasUnseenMessages - ) - } - starStats = visibleItemView.starStars.flatMap { starStats in - return MessageInputPanelComponent.StarStats( - myStars: starStats.myStars, - totalStars: starStats.totalStars + if let liveChatStateValue = visibleItemView.liveChatState { + liveChatState = MessageInputPanelComponent.LiveChatState( + isExpanded: liveChatStateValue.isExpanded, + hasUnseenMessages: liveChatStateValue.hasUnseenMessages ) + starStats = liveChatStateValue.starStats.flatMap { starStats in + return MessageInputPanelComponent.StarStats( + myStars: starStats.myStars, + totalStars: starStats.totalStars + ) + } + if let minMessagePrice = liveChatStateValue.minMessagePrice { + if let current = sendPaidMessageStars { + if current < StarsAmount(value: minMessagePrice, nanos: 0) { + sendPaidMessageStars = StarsAmount(value: minMessagePrice, nanos: 0) + } + } else { + sendPaidMessageStars = StarsAmount(value: minMessagePrice, nanos: 0) + } + } } } @@ -2995,7 +3005,7 @@ public final class StoryItemSetContainerComponent: Component { strings: component.strings, style: .story, placeholder: inputPlaceholder, - sendPaidMessageStars: isLiveStream ? self.sendMessageContext.currentLiveStreamMessageStars : component.slice.additionalPeerData.sendPaidMessageStars, + sendPaidMessageStars: sendPaidMessageStars, maxLength: maxInputLength, maxEmojiCount: maxEmojiCount, queryTypes: [.mention, .hashtag, .emoji], diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index 28a7bb6837..06b72247d4 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -1335,10 +1335,21 @@ final class StoryItemSetContainerSendMessage { inputText = text } + guard let visibleItemView = view.visibleItems[component.slice.item.id]?.view.view as? StoryItemContentComponent.View else { + return + } + + var minAmount: Int64 = 1 + if let minMessagePrice = visibleItemView.liveChatState?.minMessagePrice { + minAmount = minMessagePrice + } + let initialData = await ChatSendStarsScreen.initialDataLiveStreamMessage( context: component.context, peerId: peerId, text: inputText, + minAmount: Int(minAmount), + currentAmount: (self.currentLiveStreamMessageStars?.value).flatMap { Int($0) }, completion: { [weak self, weak view] amount, _ in guard let self, let view else { return @@ -3893,8 +3904,9 @@ final class StoryItemSetContainerSendMessage { } var topPeers: [ReactionsMessageAttribute.TopPeer] = [] + var minAmount: Int64 = 1 if let visibleItemView = view.visibleItems[component.slice.item.id]?.view.view as? StoryItemContentComponent.View { - if let topItems = visibleItemView.starStars?.topItems { + if let topItems = visibleItemView.liveChatState?.starStats?.topItems { topPeers = topItems.map { item -> ReactionsMessageAttribute.TopPeer in return ReactionsMessageAttribute.TopPeer( peerId: item.peerId, @@ -3905,12 +3917,16 @@ final class StoryItemSetContainerSendMessage { ) } } + + if let minMessagePrice = visibleItemView.liveChatState?.minMessagePrice { + minAmount = minMessagePrice + } } let initialData = await ChatSendStarsScreen.initialData( context: component.context, peerId: peerId, - reactSubject: .liveStream(peerId: peerId, storyId: focusedItem.storyItem.id), + reactSubject: .liveStream(peerId: peerId, storyId: focusedItem.storyItem.id, minAmount: Int(minAmount)), topPeers: topPeers, completion: { [weak view] amount, privacy, isBecomingTop, transitionOut in guard let view, let component = view.component else { @@ -4024,7 +4040,7 @@ final class StoryItemSetContainerSendMessage { var totalStars = count if let visibleItemView = view.visibleItems[component.slice.item.id]?.view.view as? StoryItemContentComponent.View { - if let pendingMyStars = visibleItemView.starStars?.pendingMyStars { + if let pendingMyStars = visibleItemView.liveChatState?.starStats?.pendingMyStars { totalStars += Int(pendingMyStars) } }