mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Stories
This commit is contained in:
parent
a05ef5e022
commit
4616f0833c
9 changed files with 321 additions and 221 deletions
|
|
@ -240,6 +240,7 @@ public struct PresentationGroupCallState: Equatable {
|
|||
public var isVideoWatchersLimitReached: Bool
|
||||
public var isMyVideoActive: Bool
|
||||
public var isUnifiedStream: Bool
|
||||
public var defaultSendAs: EnginePeer.Id?
|
||||
|
||||
public init(
|
||||
myPeerId: EnginePeer.Id,
|
||||
|
|
@ -260,7 +261,8 @@ public struct PresentationGroupCallState: Equatable {
|
|||
isVideoEnabled: Bool,
|
||||
isVideoWatchersLimitReached: Bool,
|
||||
isMyVideoActive: Bool,
|
||||
isUnifiedStream: Bool
|
||||
isUnifiedStream: Bool,
|
||||
defaultSendAs: EnginePeer.Id?
|
||||
) {
|
||||
self.myPeerId = myPeerId
|
||||
self.networkState = networkState
|
||||
|
|
@ -281,6 +283,7 @@ public struct PresentationGroupCallState: Equatable {
|
|||
self.isVideoWatchersLimitReached = isVideoWatchersLimitReached
|
||||
self.isMyVideoActive = isMyVideoActive
|
||||
self.isUnifiedStream = isUnifiedStream
|
||||
self.defaultSendAs = defaultSendAs
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ private extension PresentationGroupCallState {
|
|||
isVideoEnabled: false,
|
||||
isVideoWatchersLimitReached: false,
|
||||
isMyVideoActive: false,
|
||||
isUnifiedStream: false
|
||||
isUnifiedStream: false,
|
||||
defaultSendAs: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1565,6 +1566,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
isVideoEnabled: callInfo.isVideoEnabled,
|
||||
unmutedVideoLimit: callInfo.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream,
|
||||
sendPaidMessagesStars: self.stateValue.sendPaidMessageStars,
|
||||
defaultSendAs: self.stateValue.defaultSendAs,
|
||||
version: 0
|
||||
),
|
||||
previousServiceState: nil,
|
||||
|
|
@ -2282,19 +2285,23 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
if case let .established(callInfo, _, _, _, initialState) = internalState {
|
||||
self.summaryInfoState.set(.single(SummaryInfoState(info: callInfo)))
|
||||
|
||||
self.stateValue.canManageCall = initialState.isCreator || initialState.adminIds.contains(self.accountContext.account.peerId)
|
||||
if self.stateValue.canManageCall && initialState.defaultParticipantsAreMuted.canChange {
|
||||
self.stateValue.defaultParticipantMuteState = initialState.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted
|
||||
var stateValue = self.stateValue
|
||||
|
||||
stateValue.canManageCall = initialState.isCreator || initialState.adminIds.contains(self.accountContext.account.peerId)
|
||||
if stateValue.canManageCall && initialState.defaultParticipantsAreMuted.canChange {
|
||||
stateValue.defaultParticipantMuteState = initialState.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted
|
||||
}
|
||||
if self.stateValue.recordingStartTimestamp != initialState.recordingStartTimestamp {
|
||||
self.stateValue.recordingStartTimestamp = initialState.recordingStartTimestamp
|
||||
if stateValue.recordingStartTimestamp != initialState.recordingStartTimestamp {
|
||||
stateValue.recordingStartTimestamp = initialState.recordingStartTimestamp
|
||||
}
|
||||
if self.stateValue.title != initialState.title {
|
||||
self.stateValue.title = initialState.title
|
||||
if stateValue.title != initialState.title {
|
||||
stateValue.title = initialState.title
|
||||
}
|
||||
if self.stateValue.scheduleTimestamp != initialState.scheduleTimestamp {
|
||||
self.stateValue.scheduleTimestamp = initialState.scheduleTimestamp
|
||||
if stateValue.scheduleTimestamp != initialState.scheduleTimestamp {
|
||||
stateValue.scheduleTimestamp = initialState.scheduleTimestamp
|
||||
}
|
||||
stateValue.defaultSendAs = initialState.defaultSendAs
|
||||
self.stateValue = stateValue
|
||||
|
||||
let accountContext = self.accountContext
|
||||
let peerId = self.peerId
|
||||
|
|
@ -2667,21 +2674,24 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
|
||||
self.membersValue = members
|
||||
|
||||
self.stateValue.adminIds = adminIds
|
||||
var stateValue = self.stateValue
|
||||
stateValue.adminIds = adminIds
|
||||
|
||||
self.stateValue.canManageCall = state.isCreator || adminIds.contains(self.accountContext.account.peerId)
|
||||
if (state.isCreator || self.stateValue.adminIds.contains(self.accountContext.account.peerId)) && state.defaultParticipantsAreMuted.canChange {
|
||||
self.stateValue.defaultParticipantMuteState = state.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted
|
||||
stateValue.canManageCall = state.isCreator || adminIds.contains(self.accountContext.account.peerId)
|
||||
if (state.isCreator || stateValue.adminIds.contains(self.accountContext.account.peerId)) && state.defaultParticipantsAreMuted.canChange {
|
||||
stateValue.defaultParticipantMuteState = state.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted
|
||||
}
|
||||
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
|
||||
self.stateValue.isVideoEnabled = state.isVideoEnabled && otherParticipantsWithVideo < state.unmutedVideoLimit
|
||||
self.stateValue.isVideoWatchersLimitReached = videoWatchingParticipants >= configuration.videoParticipantsMaxCount
|
||||
self.stateValue.isUnifiedStream = state.isStream
|
||||
stateValue.messagesAreEnabled = state.messagesAreEnabled.isEnabled
|
||||
stateValue.canEnableMessages = state.messagesAreEnabled.canChange
|
||||
stateValue.sendPaidMessageStars = state.messagesAreEnabled.sendPaidMessagesStars
|
||||
stateValue.recordingStartTimestamp = state.recordingStartTimestamp
|
||||
stateValue.title = state.title
|
||||
stateValue.scheduleTimestamp = state.scheduleTimestamp
|
||||
stateValue.isVideoEnabled = state.isVideoEnabled && otherParticipantsWithVideo < state.unmutedVideoLimit
|
||||
stateValue.isVideoWatchersLimitReached = videoWatchingParticipants >= configuration.videoParticipantsMaxCount
|
||||
stateValue.isUnifiedStream = state.isStream
|
||||
stateValue.defaultSendAs = state.defaultSendAs
|
||||
self.stateValue = stateValue
|
||||
|
||||
self.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo(
|
||||
id: callInfo.id,
|
||||
|
|
|
|||
|
|
@ -1220,7 +1220,8 @@ final class VideoChatScreenComponent: Component {
|
|||
isVideoEnabled: true,
|
||||
isVideoWatchersLimitReached: false,
|
||||
isMyVideoActive: false,
|
||||
isUnifiedStream: false
|
||||
isUnifiedStream: false,
|
||||
defaultSendAs: nil
|
||||
)
|
||||
|
||||
return .single((callState, invitedPeers.compactMap({ peer -> VideoChatScreenComponent.InvitedPeer? in
|
||||
|
|
|
|||
|
|
@ -595,6 +595,8 @@ func _internal_getGroupCallParticipants(account: Account, reference: InternalGro
|
|||
isVideoEnabled: isVideoEnabled,
|
||||
unmutedVideoLimit: unmutedVideoLimit,
|
||||
isStream: isStream,
|
||||
sendPaidMessagesStars: nil,
|
||||
defaultSendAs: nil,
|
||||
version: version
|
||||
)
|
||||
}
|
||||
|
|
@ -1479,6 +1481,8 @@ public final class GroupCallParticipantsContext {
|
|||
isVideoEnabled: Bool,
|
||||
unmutedVideoLimit: Int,
|
||||
isStream: Bool,
|
||||
sendPaidMessagesStars: Int64?,
|
||||
defaultSendAs: PeerId?,
|
||||
version: Int32
|
||||
) {
|
||||
self.participants = participants
|
||||
|
|
@ -1496,6 +1500,8 @@ public final class GroupCallParticipantsContext {
|
|||
self.isVideoEnabled = isVideoEnabled
|
||||
self.unmutedVideoLimit = unmutedVideoLimit
|
||||
self.isStream = isStream
|
||||
self.sendPaidMessagesStars = sendPaidMessagesStars
|
||||
self.defaultSendAs = defaultSendAs
|
||||
self.version = version
|
||||
}
|
||||
}
|
||||
|
|
@ -1871,6 +1877,8 @@ public final class GroupCallParticipantsContext {
|
|||
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
||||
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
||||
isStream: strongSelf.stateValue.state.isStream,
|
||||
sendPaidMessagesStars: strongSelf.stateValue.state.sendPaidMessagesStars,
|
||||
defaultSendAs: strongSelf.stateValue.state.defaultSendAs,
|
||||
version: strongSelf.stateValue.state.version
|
||||
),
|
||||
overlayState: strongSelf.stateValue.overlayState,
|
||||
|
|
@ -2102,6 +2110,8 @@ public final class GroupCallParticipantsContext {
|
|||
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
||||
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
||||
isStream: strongSelf.stateValue.state.isStream,
|
||||
sendPaidMessagesStars: strongSelf.stateValue.state.sendPaidMessagesStars,
|
||||
defaultSendAs: strongSelf.stateValue.state.defaultSendAs,
|
||||
version: strongSelf.stateValue.state.version
|
||||
),
|
||||
overlayState: strongSelf.stateValue.overlayState,
|
||||
|
|
@ -2328,6 +2338,8 @@ public final class GroupCallParticipantsContext {
|
|||
let isVideoEnabled = strongSelf.stateValue.state.isVideoEnabled
|
||||
let isStream = strongSelf.stateValue.state.isStream
|
||||
let unmutedVideoLimit = strongSelf.stateValue.state.unmutedVideoLimit
|
||||
let sendPaidMessagesStars = strongSelf.stateValue.state.sendPaidMessagesStars
|
||||
let defaultSendAs = strongSelf.stateValue.state.defaultSendAs
|
||||
|
||||
updatedParticipants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: strongSelf.stateValue.state.sortAscending) })
|
||||
|
||||
|
|
@ -2348,6 +2360,8 @@ public final class GroupCallParticipantsContext {
|
|||
isVideoEnabled: isVideoEnabled,
|
||||
unmutedVideoLimit: unmutedVideoLimit,
|
||||
isStream: isStream,
|
||||
sendPaidMessagesStars: sendPaidMessagesStars,
|
||||
defaultSendAs: defaultSendAs,
|
||||
version: update.version
|
||||
),
|
||||
overlayState: updatedOverlayState,
|
||||
|
|
@ -4300,7 +4314,7 @@ public final class GroupCallMessagesContext {
|
|||
flags |= 1 << 0
|
||||
}
|
||||
var sendAs: Api.InputPeer?
|
||||
if fromId != self.account.peerId {
|
||||
if fromId != self.account.peerId || self.isLiveStream {
|
||||
guard let fromPeer else {
|
||||
return
|
||||
}
|
||||
|
|
@ -4364,7 +4378,7 @@ public final class GroupCallMessagesContext {
|
|||
var flags: Int32 = 0
|
||||
flags |= 1 << 0
|
||||
var sendAs: Api.InputPeer?
|
||||
if pendingSendStars.fromPeer.id != self.account.peerId {
|
||||
if pendingSendStars.fromPeer.id != self.account.peerId || self.isLiveStream {
|
||||
sendAs = apiInputPeer(pendingSendStars.fromPeer)
|
||||
}
|
||||
if sendAs != nil {
|
||||
|
|
|
|||
|
|
@ -1531,10 +1531,11 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
shouldDisplayMenuButton = true
|
||||
}
|
||||
|
||||
var displaySendAsAvatarButton = false
|
||||
let mediaRecordingState = interfaceState.inputTextPanelState.mediaRecordingState
|
||||
if let sendAsPeers = interfaceState.sendAsPeers, !sendAsPeers.isEmpty && interfaceState.editMessageState == nil {
|
||||
menuButtonExpanded = false
|
||||
self.sendAsAvatarButtonNode.isHidden = false
|
||||
displaySendAsAvatarButton = true
|
||||
|
||||
var currentPeer = sendAsPeers.first(where: { $0.peer.id == interfaceState.currentSendAsPeerId})?.peer
|
||||
if currentPeer == nil {
|
||||
|
|
@ -1554,9 +1555,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
break
|
||||
}
|
||||
}
|
||||
self.sendAsAvatarButtonNode.isHidden = true
|
||||
} else {
|
||||
self.sendAsAvatarButtonNode.isHidden = true
|
||||
}
|
||||
if mediaRecordingState != nil || interfaceState.interfaceState.mediaDraftState != nil {
|
||||
hasMenuButton = false
|
||||
|
|
@ -2968,11 +2966,13 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
}
|
||||
transition.updateFrame(node: self.textPlaceholderNode, frame: textPlaceholderFrame)
|
||||
|
||||
|
||||
let sendAsButtonFrame = CGRect(origin: CGPoint(x: 3.0, y: textInputContainerBackgroundFrame.height - 3.0 - 34.0), size: CGSize(width: 34.0, height: 34.0))
|
||||
let sendAsAvatarButtonAlpha: CGFloat = audioRecordingItemsAlpha * (displaySendAsAvatarButton ? 1.0 : 0.0)
|
||||
transition.updatePosition(node: self.sendAsAvatarButtonNode, position: sendAsButtonFrame.center)
|
||||
transition.updateBounds(node: self.sendAsAvatarButtonNode, bounds: CGRect(origin: CGPoint(), size: sendAsButtonFrame.size))
|
||||
transition.updateAlpha(layer: self.sendAsAvatarButtonNode.layer, alpha: audioRecordingItemsAlpha)
|
||||
transition.updateTransformScale(layer: self.sendAsAvatarButtonNode.layer, scale: audioRecordingItemsAlpha == 0.0 ? 0.001 : 1.0)
|
||||
transition.updateAlpha(layer: self.sendAsAvatarButtonNode.layer, alpha: sendAsAvatarButtonAlpha)
|
||||
transition.updateTransformScale(layer: self.sendAsAvatarButtonNode.layer, scale: sendAsAvatarButtonAlpha == 0.0 ? 0.001 : 1.0)
|
||||
transition.updateFrame(node: self.sendAsAvatarContainerNode, frame: CGRect(origin: CGPoint(), size: sendAsButtonFrame.size))
|
||||
transition.updateFrame(node: self.sendAsAvatarReferenceNode, frame: CGRect(origin: CGPoint(), size: sendAsButtonFrame.size))
|
||||
transition.updatePosition(node: self.sendAsAvatarNode, position: CGRect(origin: CGPoint(), size: sendAsButtonFrame.size).center)
|
||||
|
|
|
|||
|
|
@ -118,8 +118,9 @@ final class StoryItemContentComponent: Component {
|
|||
var minMessagePrice: Int64?
|
||||
var starStats: StarStats?
|
||||
var isAdmin: Bool
|
||||
var defaultSendAs: EnginePeer.Id?
|
||||
|
||||
init(isExpanded: Bool, isEmpty: Bool, hasUnseenMessages: Bool, areMessagesEnabled: Bool, minMessagePrice: Int64?, starStats: StarStats?, isAdmin: Bool) {
|
||||
init(isExpanded: Bool, isEmpty: Bool, hasUnseenMessages: Bool, areMessagesEnabled: Bool, minMessagePrice: Int64?, starStats: StarStats?, isAdmin: Bool, defaultSendAs: EnginePeer.Id?) {
|
||||
self.isExpanded = isExpanded
|
||||
self.isEmpty = isEmpty
|
||||
self.hasUnseenMessages = hasUnseenMessages
|
||||
|
|
@ -127,6 +128,7 @@ final class StoryItemContentComponent: Component {
|
|||
self.minMessagePrice = minMessagePrice
|
||||
self.starStats = starStats
|
||||
self.isAdmin = isAdmin
|
||||
self.defaultSendAs = defaultSendAs
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -134,12 +136,14 @@ final class StoryItemContentComponent: Component {
|
|||
var areMessagesEnabled: Bool
|
||||
var minMessagePrice: Int64?
|
||||
var isAdmin: Bool
|
||||
var defaultSendAs: EnginePeer.Id?
|
||||
var isUnifiedStream: Bool
|
||||
|
||||
init(areMessagesEnabled: Bool, minMessagePrice: Int64?, isAdmin: Bool, isUnifiedStream: Bool) {
|
||||
init(areMessagesEnabled: Bool, minMessagePrice: Int64?, isAdmin: Bool, defaultSendAs: EnginePeer.Id?, isUnifiedStream: Bool) {
|
||||
self.areMessagesEnabled = areMessagesEnabled
|
||||
self.minMessagePrice = minMessagePrice
|
||||
self.isAdmin = isAdmin
|
||||
self.defaultSendAs = defaultSendAs
|
||||
self.isUnifiedStream = isUnifiedStream
|
||||
}
|
||||
}
|
||||
|
|
@ -229,7 +233,8 @@ final class StoryItemContentComponent: Component {
|
|||
areMessagesEnabled: mediaStreamCallState?.areMessagesEnabled ?? false,
|
||||
minMessagePrice: mediaStreamCallState?.minMessagePrice,
|
||||
starStats: starStats,
|
||||
isAdmin: mediaStreamCallState?.isAdmin ?? false
|
||||
isAdmin: mediaStreamCallState?.isAdmin ?? false,
|
||||
defaultSendAs: mediaStreamCallState?.defaultSendAs
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1116,6 +1121,7 @@ final class StoryItemContentComponent: Component {
|
|||
areMessagesEnabled: state.messagesAreEnabled,
|
||||
minMessagePrice: state.sendPaidMessageStars,
|
||||
isAdmin: state.canManageCall,
|
||||
defaultSendAs: state.defaultSendAs,
|
||||
isUnifiedStream: state.isUnifiedStream
|
||||
)
|
||||
if self.mediaStreamCallState != mappedState {
|
||||
|
|
|
|||
|
|
@ -103,7 +103,9 @@ final class StoryItemLoadingEffectView: UIView {
|
|||
}
|
||||
|
||||
func update(size: CGSize, transition: ComponentTransition) {
|
||||
if self.backgroundView.bounds.size != size {
|
||||
let backgroundSize = CGSize(width: self.gradientWidth, height: size.height)
|
||||
|
||||
if self.backgroundView.bounds.size != backgroundSize {
|
||||
self.backgroundView.layer.removeAllAnimations()
|
||||
|
||||
if !self.hasCustomBorder {
|
||||
|
|
@ -115,7 +117,7 @@ final class StoryItemLoadingEffectView: UIView {
|
|||
}
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: CGSize(width: self.gradientWidth, height: size.height)))
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: backgroundSize))
|
||||
|
||||
transition.setFrame(view: self.borderContainerView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.setFrame(view: self.borderGradientView, frame: CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: CGSize(width: self.gradientWidth, height: size.height)))
|
||||
|
|
|
|||
|
|
@ -3004,7 +3004,16 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
sendAsConfiguration = self.sendMessageContext.currentSendAsPeer.flatMap { value in
|
||||
var sendAsPeer: SendAsPeer?
|
||||
if let currentSendAsPeer = self.sendMessageContext.currentSendAsPeer {
|
||||
sendAsPeer = currentSendAsPeer
|
||||
} else {
|
||||
sendAsPeer = liveChatStateValue.defaultSendAs.flatMap { defaultSendAs in
|
||||
return self.sendMessageContext.sendAsData?.availablePeers.first(where: { $0.peer.id == defaultSendAs })
|
||||
}
|
||||
}
|
||||
|
||||
sendAsConfiguration = sendAsPeer.flatMap { value in
|
||||
return MessageInputPanelComponent.SendAsConfiguration(
|
||||
currentPeer: EnginePeer(value.peer),
|
||||
subscriberCount: value.subscribers.flatMap(Int.init),
|
||||
|
|
@ -6307,102 +6316,109 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
return .complete()
|
||||
}
|
||||
|
||||
var isLiveStream = false
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
isLiveStream = true
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Stories_MenuAddToAlbum, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddToFolder"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
|
||||
guard let self, let c else {
|
||||
f(.default)
|
||||
return
|
||||
}
|
||||
|
||||
Task { @MainActor [weak self, weak c] in
|
||||
guard let self, let component = self.component, let peerId = component.slice.item.peerId, let c else {
|
||||
if !isLiveStream {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Stories_MenuAddToAlbum, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddToFolder"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
|
||||
guard let self, let c else {
|
||||
f(.default)
|
||||
return
|
||||
}
|
||||
|
||||
let (peerReference, folderPreviews) = await PeerStoryListContext.folderPreviews(peerId: peerId, account: component.context.account).get()
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor)
|
||||
}, iconPosition: .left, action: { c ,f in
|
||||
c?.popItems()
|
||||
})))
|
||||
items.append(.separator)
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Stories_MenuNewAlbum, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddFolder"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { [weak self] c, f in
|
||||
guard let self else {
|
||||
f(.default)
|
||||
Task { @MainActor [weak self, weak c] in
|
||||
guard let self, let component = self.component, let peerId = component.slice.item.peerId, let c else {
|
||||
return
|
||||
}
|
||||
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
self.presentAddStoryFolder(addItems: [component.slice.item.storyItem])
|
||||
})
|
||||
})))
|
||||
|
||||
for folderPreview in folderPreviews {
|
||||
var iconSource: ContextMenuActionItemIconSource?
|
||||
if let story = folderPreview.item {
|
||||
var imageSignal: Signal<UIImage?, NoError>?
|
||||
|
||||
var selectedMedia: Media?
|
||||
if let image = story.media._asMedia() as? TelegramMediaImage {
|
||||
selectedMedia = image
|
||||
} else if let file = story.media._asMedia() as? TelegramMediaFile {
|
||||
selectedMedia = file
|
||||
}
|
||||
|
||||
if let selectedMedia {
|
||||
let directMediaImageCache = DirectMediaImageCache(account: component.context.account)
|
||||
if let result = directMediaImageCache.getImage(peer: peerReference, story: story, media: selectedMedia, width: 48, aspectRatio: 1.0, possibleWidths: [48], includeBlurred: false, synchronous: true) {
|
||||
if let loadSignal = result.loadSignal {
|
||||
imageSignal = .single(result.image) |> then(loadSignal)
|
||||
} else {
|
||||
imageSignal = .single(result.image)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let imageSignal {
|
||||
iconSource = ContextMenuActionItemIconSource(
|
||||
size: CGSize(width: 24.0, height: 24.0),
|
||||
cornerRadius: 5.0,
|
||||
signal: imageSignal
|
||||
)
|
||||
}
|
||||
}
|
||||
let (peerReference, folderPreviews) = await PeerStoryListContext.folderPreviews(peerId: peerId, account: component.context.account).get()
|
||||
|
||||
var icon: (PresentationTheme) -> UIImage? = { _ in nil }
|
||||
if iconSource == nil {
|
||||
icon = { theme in
|
||||
return generateImage(CGSize(width: 24.0, height: 24.0), opaque: false, scale: nil, rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(theme.contextMenu.primaryColor.withMultipliedAlpha(0.1).cgColor)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: 5.0).cgPath)
|
||||
context.fillPath()
|
||||
})
|
||||
}
|
||||
}
|
||||
var items: [ContextMenuItem] = []
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Back, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor)
|
||||
}, iconPosition: .left, action: { c ,f in
|
||||
c?.popItems()
|
||||
})))
|
||||
items.append(.separator)
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: folderPreview.folder.title, icon: icon, iconSource: iconSource, iconPosition: .left, action: { [weak self] c, f in
|
||||
guard let self, let component = self.component else {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Stories_MenuNewAlbum, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddFolder"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { [weak self] c, f in
|
||||
guard let self else {
|
||||
f(.default)
|
||||
return
|
||||
}
|
||||
|
||||
c?.dismiss(completion: {})
|
||||
|
||||
component.addToFolder(folderPreview.folder.id)
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
self.presentAddStoryFolder(addItems: [component.slice.item.storyItem])
|
||||
})
|
||||
})))
|
||||
|
||||
for folderPreview in folderPreviews {
|
||||
var iconSource: ContextMenuActionItemIconSource?
|
||||
if let story = folderPreview.item {
|
||||
var imageSignal: Signal<UIImage?, NoError>?
|
||||
|
||||
var selectedMedia: Media?
|
||||
if let image = story.media._asMedia() as? TelegramMediaImage {
|
||||
selectedMedia = image
|
||||
} else if let file = story.media._asMedia() as? TelegramMediaFile {
|
||||
selectedMedia = file
|
||||
}
|
||||
|
||||
if let selectedMedia {
|
||||
let directMediaImageCache = DirectMediaImageCache(account: component.context.account)
|
||||
if let result = directMediaImageCache.getImage(peer: peerReference, story: story, media: selectedMedia, width: 48, aspectRatio: 1.0, possibleWidths: [48], includeBlurred: false, synchronous: true) {
|
||||
if let loadSignal = result.loadSignal {
|
||||
imageSignal = .single(result.image) |> then(loadSignal)
|
||||
} else {
|
||||
imageSignal = .single(result.image)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let imageSignal {
|
||||
iconSource = ContextMenuActionItemIconSource(
|
||||
size: CGSize(width: 24.0, height: 24.0),
|
||||
cornerRadius: 5.0,
|
||||
signal: imageSignal
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var icon: (PresentationTheme) -> UIImage? = { _ in nil }
|
||||
if iconSource == nil {
|
||||
icon = { theme in
|
||||
return generateImage(CGSize(width: 24.0, height: 24.0), opaque: false, scale: nil, rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(theme.contextMenu.primaryColor.withMultipliedAlpha(0.1).cgColor)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: 5.0).cgPath)
|
||||
context.fillPath()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: folderPreview.folder.title, icon: icon, iconSource: iconSource, iconPosition: .left, action: { [weak self] c, f in
|
||||
guard let self, let component = self.component else {
|
||||
f(.default)
|
||||
return
|
||||
}
|
||||
|
||||
c?.dismiss(completion: {})
|
||||
|
||||
component.addToFolder(folderPreview.folder.id)
|
||||
})))
|
||||
}
|
||||
|
||||
c.pushItems(items: .single(ContextController.Items(content: .list(items))))
|
||||
}
|
||||
|
||||
c.pushItems(items: .single(ContextController.Items(content: .list(items))))
|
||||
}
|
||||
})))
|
||||
})))
|
||||
}
|
||||
|
||||
if case .file = component.slice.item.storyItem.media {
|
||||
var speedValue: String = presentationData.strings.PlaybackSpeed_Normal
|
||||
|
|
@ -6466,92 +6482,94 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
self.openItemPrivacySettings()
|
||||
})))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_Edit, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openStoryEditing()
|
||||
})))
|
||||
|
||||
if case .file = component.slice.item.storyItem.media, component.slice.item.storyItem.isPinned {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_EditCover, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Stories/Context Menu/EditCover"), color: theme.contextMenu.primaryColor)
|
||||
if !isLiveStream {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_Edit, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openStoryEditing(cover: true)
|
||||
self.openStoryEditing()
|
||||
})))
|
||||
}
|
||||
|
||||
items.append(.separator)
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: component.slice.item.storyItem.isPinned ? component.strings.Story_Context_RemoveFromProfile : component.strings.Story_Context_SaveToProfile, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: component.slice.item.storyItem.isPinned ? "Stories/Context Menu/Unpin" : "Stories/Context Menu/Pin"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
if case .file = component.slice.item.storyItem.media, component.slice.item.storyItem.isPinned {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_EditCover, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Stories/Context Menu/EditCover"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openStoryEditing(cover: true)
|
||||
})))
|
||||
}
|
||||
|
||||
let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.slice.effectivePeer.id, ids: [component.slice.item.storyItem.id: component.slice.item.storyItem], isPinned: !component.slice.item.storyItem.isPinned).startStandalone()
|
||||
items.append(.separator)
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||
if component.slice.item.storyItem.isPinned {
|
||||
self.component?.presentController(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: nil, text: component.strings.Story_ToastRemovedFromProfileText, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
} else {
|
||||
self.component?.presentController(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: component.strings.Story_ToastSavedToProfileTitle, text: component.strings.Story_ToastSavedToProfileText, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
}
|
||||
})))
|
||||
|
||||
let saveText: String = component.strings.Story_Context_SaveToGallery
|
||||
items.append(.action(ContextMenuActionItem(text: saveText, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.requestSave()
|
||||
})))
|
||||
|
||||
if case let .user(accountUser) = component.slice.effectivePeer {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_ContextStealthMode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: accountUser.isPremium ? "Chat/Context Menu/Eye" : "Chat/Context Menu/EyeLocked"), color: theme.contextMenu.primaryColor)
|
||||
items.append(.action(ContextMenuActionItem(text: component.slice.item.storyItem.isPinned ? component.strings.Story_Context_RemoveFromProfile : component.strings.Story_Context_SaveToProfile, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: component.slice.item.storyItem.isPinned ? "Stories/Context Menu/Unpin" : "Stories/Context Menu/Pin"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
if accountUser.isPremium {
|
||||
self.sendMessageContext.requestStealthMode(view: self)
|
||||
|
||||
let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.slice.effectivePeer.id, ids: [component.slice.item.storyItem.id: component.slice.item.storyItem], isPinned: !component.slice.item.storyItem.isPinned).startStandalone()
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||
if component.slice.item.storyItem.isPinned {
|
||||
self.component?.presentController(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: nil, text: component.strings.Story_ToastRemovedFromProfileText, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
} else {
|
||||
self.presentStealthModeUpgradeScreen()
|
||||
self.component?.presentController(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(title: component.strings.Story_ToastSavedToProfileTitle, text: component.strings.Story_ToastSavedToProfileText, timeout: nil, customUndoText: nil),
|
||||
elevatedLayout: false,
|
||||
animateInAsReplacement: false,
|
||||
appearance: UndoOverlayController.Appearance(isBlurred: true),
|
||||
action: { _ in return false }
|
||||
), nil)
|
||||
}
|
||||
})))
|
||||
|
||||
let saveText: String = component.strings.Story_Context_SaveToGallery
|
||||
items.append(.action(ContextMenuActionItem(text: saveText, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.requestSave()
|
||||
})))
|
||||
|
||||
if case let .user(accountUser) = component.slice.effectivePeer {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_ContextStealthMode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: accountUser.isPremium ? "Chat/Context Menu/Eye" : "Chat/Context Menu/EyeLocked"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if accountUser.isPremium {
|
||||
self.sendMessageContext.requestStealthMode(view: self)
|
||||
} else {
|
||||
self.presentStealthModeUpgradeScreen()
|
||||
}
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
if component.slice.item.storyItem.isPublic && (component.slice.effectivePeer.addressName != nil || !component.slice.effectivePeer._asPeer().usernames.isEmpty) && (component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) || component.slice.item.storyItem.isPinned) {
|
||||
|
|
@ -6644,6 +6662,11 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
guard let self, let component else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
var isLiveStream = false
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
isLiveStream = true
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
if case .file = component.slice.item.storyItem.media {
|
||||
|
|
@ -6676,7 +6699,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
items.append(.separator)
|
||||
}
|
||||
|
||||
if (component.slice.item.storyItem.isMy && channel.hasPermission(.postStories)) || channel.hasPermission(.editStories) {
|
||||
if !isLiveStream && ((component.slice.item.storyItem.isMy && channel.hasPermission(.postStories)) || channel.hasPermission(.editStories)) {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_Edit, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
|
|
@ -6706,7 +6729,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
items.append(.separator)
|
||||
}
|
||||
|
||||
if channel.hasPermission(.editStories) {
|
||||
if !isLiveStream && channel.hasPermission(.editStories) {
|
||||
items.append(.action(ContextMenuActionItem(text: component.slice.item.storyItem.isPinned ? component.strings.Story_Context_RemoveFromChannel : component.strings.Story_Context_SaveToChannel, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: component.slice.item.storyItem.isPinned ? "Stories/Context Menu/Unpin" : "Stories/Context Menu/Pin"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
|
|
@ -6746,7 +6769,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
})))
|
||||
}
|
||||
|
||||
if component.slice.additionalPeerData.canViewStats {
|
||||
if !isLiveStream && component.slice.additionalPeerData.canViewStats {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_ViewStats, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
|
|
@ -6768,17 +6791,19 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
})))
|
||||
}
|
||||
|
||||
let saveText: String = component.strings.Story_Context_SaveToGallery
|
||||
items.append(.action(ContextMenuActionItem(text: saveText, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.requestSave()
|
||||
})))
|
||||
if !isLiveStream {
|
||||
let saveText: String = component.strings.Story_Context_SaveToGallery
|
||||
items.append(.action(ContextMenuActionItem(text: saveText, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.requestSave()
|
||||
})))
|
||||
}
|
||||
|
||||
if component.slice.item.storyItem.isPublic && (component.slice.effectivePeer.addressName != nil || !component.slice.effectivePeer._asPeer().usernames.isEmpty) && (component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) || component.slice.item.storyItem.isPinned) {
|
||||
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_CopyLink, icon: { theme in
|
||||
|
|
|
|||
|
|
@ -107,7 +107,17 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) {
|
|||
var currentLiveStreamStarsIsActive: Bool = false
|
||||
var currentLiveStreamStarsIsActiveTimer: Foundation.Timer?
|
||||
|
||||
var sendAsData: (isPremium: Bool, availablePeers: [SendAsPeer])?
|
||||
struct SendAsData: Equatable {
|
||||
var isPremium: Bool
|
||||
var availablePeers: [SendAsPeer]
|
||||
|
||||
init(isPremium: Bool, availablePeers: [SendAsPeer]) {
|
||||
self.isPremium = isPremium
|
||||
self.availablePeers = availablePeers
|
||||
}
|
||||
}
|
||||
|
||||
var sendAsData: SendAsData?
|
||||
var currentSendAsPeer: SendAsPeer?
|
||||
var isSelectingSendAsPeer: Bool = false
|
||||
var sendAsDisposable: Disposable?
|
||||
|
|
@ -244,24 +254,14 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) {
|
|||
availablePeers.append(peer)
|
||||
}
|
||||
|
||||
self.sendAsData = (
|
||||
let sendAsData = SendAsData(
|
||||
isPremium: isPremium,
|
||||
availablePeers: availablePeers
|
||||
)
|
||||
|
||||
if availablePeers.count > 1 {
|
||||
if self.currentSendAsPeer == nil {
|
||||
self.currentSendAsPeer = availablePeers.first
|
||||
if !view.isUpdatingComponent {
|
||||
view.state?.updated(transition: .spring(duration: 0.4))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if self.currentSendAsPeer != nil {
|
||||
self.currentSendAsPeer = nil
|
||||
if !view.isUpdatingComponent {
|
||||
view.state?.updated(transition: .spring(duration: 0.4))
|
||||
}
|
||||
if self.sendAsData != sendAsData {
|
||||
self.sendAsData = sendAsData
|
||||
if !view.isUpdatingComponent {
|
||||
view.state?.updated(transition: .spring(duration: 0.4))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -684,6 +684,7 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) {
|
|||
|
||||
var sendPaidMessageStars = self.currentLiveStreamMessageStars
|
||||
var isAdmin = false
|
||||
var sendAsPeer: SendAsPeer?
|
||||
if let visibleItemView = view.visibleItems[component.slice.item.id]?.view.view as? StoryItemContentComponent.View {
|
||||
if let liveChatStateValue = visibleItemView.liveChatState {
|
||||
if let minMessagePrice = liveChatStateValue.minMessagePrice {
|
||||
|
|
@ -696,6 +697,14 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) {
|
|||
}
|
||||
}
|
||||
isAdmin = liveChatStateValue.isAdmin
|
||||
|
||||
if let currentSendAsPeer = self.currentSendAsPeer {
|
||||
sendAsPeer = currentSendAsPeer
|
||||
} else {
|
||||
sendAsPeer = liveChatStateValue.defaultSendAs.flatMap { defaultSendAs in
|
||||
return self.sendAsData?.availablePeers.first(where: { $0.peer.id == defaultSendAs })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -758,7 +767,7 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) {
|
|||
|
||||
let entities = generateChatInputTextEntities(text)
|
||||
|
||||
call.sendMessage(fromId: self.currentSendAsPeer?.peer.id, isAdmin: isAdmin, text: text.string, entities: entities, paidStars: sendPaidMessageStars?.value)
|
||||
call.sendMessage(fromId: sendAsPeer?.peer.id, isAdmin: isAdmin, text: text.string, entities: entities, paidStars: sendPaidMessageStars?.value)
|
||||
|
||||
component.storyItemSharedState.replyDrafts.removeValue(forKey: StoryId(peerId: peerId, id: focusedItem.storyItem.id))
|
||||
inputPanelView.clearSendMessageInput(updateState: true)
|
||||
|
|
@ -3991,6 +4000,7 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) {
|
|||
|
||||
var topPeers: [ReactionsMessageAttribute.TopPeer] = []
|
||||
var minAmount: Int64 = 1
|
||||
var sendAsPeer: SendAsPeer?
|
||||
if let visibleItemView = view.visibleItems[component.slice.item.id]?.view.view as? StoryItemContentComponent.View {
|
||||
if let topItems = visibleItemView.liveChatState?.starStats?.topItems {
|
||||
topPeers = topItems.map { item -> ReactionsMessageAttribute.TopPeer in
|
||||
|
|
@ -4007,12 +4017,20 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) {
|
|||
if let minMessagePrice = visibleItemView.liveChatState?.minMessagePrice {
|
||||
minAmount = minMessagePrice
|
||||
}
|
||||
|
||||
if let currentSendAsPeer = self.currentSendAsPeer {
|
||||
sendAsPeer = currentSendAsPeer
|
||||
} else {
|
||||
sendAsPeer = visibleItemView.liveChatState?.defaultSendAs.flatMap { defaultSendAs in
|
||||
return self.sendAsData?.availablePeers.first(where: { $0.peer.id == defaultSendAs })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let initialData = await ChatSendStarsScreen.initialData(
|
||||
context: component.context,
|
||||
peerId: peerId,
|
||||
myPeer: (self.currentSendAsPeer?.peer).flatMap(EnginePeer.init),
|
||||
myPeer: (sendAsPeer?.peer).flatMap(EnginePeer.init),
|
||||
reactSubject: .liveStream(peerId: peerId, storyId: focusedItem.storyItem.id, minAmount: Int(minAmount), liveChatMessageParams: LiveChatMessageParams(appConfig: component.context.currentAppConfiguration.with({ $0 }))),
|
||||
topPeers: topPeers,
|
||||
completion: { [weak self, weak view] amount, _, _, _ in
|
||||
|
|
@ -4245,17 +4263,38 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) {
|
|||
guard let call = itemView.mediaStreamCall else {
|
||||
return
|
||||
}
|
||||
var sendAsPeer: SendAsPeer?
|
||||
if let liveChatStateValue = itemView.liveChatState {
|
||||
isAdmin = liveChatStateValue.isAdmin
|
||||
|
||||
if let currentSendAsPeer = self.currentSendAsPeer {
|
||||
sendAsPeer = currentSendAsPeer
|
||||
} else {
|
||||
sendAsPeer = liveChatStateValue.defaultSendAs.flatMap { defaultSendAs in
|
||||
return self.sendAsData?.availablePeers.first(where: { $0.peer.id == defaultSendAs })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call.sendStars(fromId: self.currentSendAsPeer?.peer.id, isAdmin: isAdmin, amount: Int64(count), delay: delay)
|
||||
call.sendStars(fromId: sendAsPeer?.peer.id, isAdmin: isAdmin, amount: Int64(count), delay: delay)
|
||||
}
|
||||
|
||||
func openSendAsSelection(view: StoryItemSetContainerComponent.View, sourceView: UIView, gesture: ContextGesture?) {
|
||||
guard let component = view.component, let sendAsData = self.sendAsData, let currentSendAsPeer = self.currentSendAsPeer, let controller = component.controller() else {
|
||||
guard let component = view.component, let sendAsData = self.sendAsData, let controller = component.controller() else {
|
||||
return
|
||||
}
|
||||
guard let visibleItemView = view.visibleItems[component.slice.item.id]?.view.view as? StoryItemContentComponent.View else {
|
||||
return
|
||||
}
|
||||
|
||||
var currentSendAsPeer: SendAsPeer?
|
||||
if let currentSendAsPeerValue = self.currentSendAsPeer {
|
||||
currentSendAsPeer = currentSendAsPeerValue
|
||||
} else {
|
||||
currentSendAsPeer = visibleItemView.liveChatState?.defaultSendAs.flatMap { defaultSendAs in
|
||||
return self.sendAsData?.availablePeers.first(where: { $0.peer.id == defaultSendAs })
|
||||
}
|
||||
}
|
||||
|
||||
let focusedItem = component.slice.item
|
||||
guard let peerId = focusedItem.peerId else {
|
||||
|
|
@ -4269,7 +4308,7 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) {
|
|||
context: component.context,
|
||||
chatPeerId: peerId,
|
||||
peers: sendAsData.availablePeers,
|
||||
selectedPeerId: currentSendAsPeer.peer.id,
|
||||
selectedPeerId: currentSendAsPeer?.peer.id,
|
||||
isPremium: isPremium,
|
||||
action: { [weak self, weak view] peer in
|
||||
guard let self, let view else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue