mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
0284687897
18 changed files with 227 additions and 76 deletions
|
|
@ -233,6 +233,7 @@ public struct PresentationGroupCallState: Equatable {
|
|||
public var isVideoEnabled: Bool
|
||||
public var isVideoWatchersLimitReached: Bool
|
||||
public var isMyVideoActive: Bool
|
||||
public var isUnifiedStream: Bool
|
||||
|
||||
public init(
|
||||
myPeerId: EnginePeer.Id,
|
||||
|
|
@ -251,7 +252,8 @@ public struct PresentationGroupCallState: Equatable {
|
|||
subscribedToScheduled: Bool,
|
||||
isVideoEnabled: Bool,
|
||||
isVideoWatchersLimitReached: Bool,
|
||||
isMyVideoActive: Bool
|
||||
isMyVideoActive: Bool,
|
||||
isUnifiedStream: Bool
|
||||
) {
|
||||
self.myPeerId = myPeerId
|
||||
self.networkState = networkState
|
||||
|
|
@ -270,6 +272,7 @@ public struct PresentationGroupCallState: Equatable {
|
|||
self.isVideoEnabled = isVideoEnabled
|
||||
self.isVideoWatchersLimitReached = isVideoWatchersLimitReached
|
||||
self.isMyVideoActive = isMyVideoActive
|
||||
self.isUnifiedStream = isUnifiedStream
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ private extension PresentationGroupCallState {
|
|||
subscribedToScheduled: subscribedToScheduled,
|
||||
isVideoEnabled: false,
|
||||
isVideoWatchersLimitReached: false,
|
||||
isMyVideoActive: false
|
||||
isMyVideoActive: false,
|
||||
isUnifiedStream: false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2664,6 +2665,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
self.stateValue.scheduleTimestamp = state.scheduleTimestamp
|
||||
self.stateValue.isVideoEnabled = state.isVideoEnabled && otherParticipantsWithVideo < state.unmutedVideoLimit
|
||||
self.stateValue.isVideoWatchersLimitReached = videoWatchingParticipants >= configuration.videoParticipantsMaxCount
|
||||
self.stateValue.isUnifiedStream = state.isStream
|
||||
|
||||
self.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo(
|
||||
id: callInfo.id,
|
||||
|
|
@ -4050,15 +4052,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
})
|
||||
}
|
||||
|
||||
public func sendMessage(fromId: PeerId?, randomId: Int64? = nil, text: String, entities: [MessageTextEntity], paidStars: Int64?) {
|
||||
public func sendMessage(fromId: PeerId?, isAdmin: Bool, randomId: Int64? = nil, text: String, entities: [MessageTextEntity], paidStars: Int64?) {
|
||||
if let messagesContext = self.messagesContext {
|
||||
messagesContext.send(fromId: fromId ?? self.joinAsPeerId, randomId: randomId, text: text, entities: entities, paidStars: paidStars)
|
||||
messagesContext.send(fromId: fromId ?? self.joinAsPeerId, isAdmin: isAdmin, randomId: randomId, text: text, entities: entities, paidStars: paidStars)
|
||||
}
|
||||
}
|
||||
|
||||
public func sendStars(fromId: PeerId?, amount: Int64, delay: Bool) {
|
||||
public func sendStars(fromId: PeerId?, isAdmin: Bool, amount: Int64, delay: Bool) {
|
||||
if let messagesContext = self.messagesContext {
|
||||
messagesContext.sendStars(fromId: fromId ?? self.joinAsPeerId, amount: amount, delay: delay)
|
||||
messagesContext.sendStars(fromId: fromId ?? self.joinAsPeerId, isAdmin: isAdmin, amount: amount, delay: delay)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1218,7 +1218,8 @@ final class VideoChatScreenComponent: Component {
|
|||
subscribedToScheduled: false,
|
||||
isVideoEnabled: true,
|
||||
isVideoWatchersLimitReached: false,
|
||||
isMyVideoActive: false
|
||||
isMyVideoActive: false,
|
||||
isUnifiedStream: false
|
||||
)
|
||||
|
||||
return .single((callState, invitedPeers.compactMap({ peer -> VideoChatScreenComponent.InvitedPeer? in
|
||||
|
|
@ -1416,7 +1417,7 @@ final class VideoChatScreenComponent: Component {
|
|||
return
|
||||
}
|
||||
let entities = generateTextEntities(text.string, enabledTypes: [.mention, .hashtag], currentEntities: generateChatInputTextEntities(text))
|
||||
call.sendMessage(fromId: nil, randomId: randomId, text: text.string, entities: entities, paidStars: nil)
|
||||
call.sendMessage(fromId: nil, isAdmin: false, randomId: randomId, text: text.string, entities: entities, paidStars: nil)
|
||||
}
|
||||
|
||||
inputPanelView.clearSendMessageInput(updateState: true)
|
||||
|
|
@ -3836,7 +3837,7 @@ final class VideoChatScreenComponent: Component {
|
|||
guard case let .group(groupCall) = self.currentCall, let call = groupCall as? PresentationGroupCallImpl else {
|
||||
return
|
||||
}
|
||||
call.sendMessage(fromId: nil, text: text, entities: entities, paidStars: nil)
|
||||
call.sendMessage(fromId: nil, isAdmin: false, text: text, entities: entities, paidStars: nil)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ enum AccountStateMutationOperation {
|
|||
case UpdateGroupCallParticipants(id: Int64, accessHash: Int64, participants: [Api.GroupCallParticipant], version: Int32)
|
||||
case UpdateGroupCall(peerId: PeerId?, call: Api.GroupCall)
|
||||
case UpdateGroupCallChainBlocks(id: Int64, accessHash: Int64, subChainId: Int32, blocks: [Data], nextOffset: Int32)
|
||||
case UpdateGroupCallMessage(id: Int64, authorId: PeerId, messageId: Int32, text: Api.TextWithEntities, date: Int32, paidMessageStars: Int64?)
|
||||
case UpdateGroupCallMessage(id: Int64, authorId: PeerId, isFromAdmin: Bool, messageId: Int32, text: Api.TextWithEntities, date: Int32, paidMessageStars: Int64?)
|
||||
case UpdateGroupCallOpaqueMessage(id: Int64, authorId: PeerId, data: Data)
|
||||
case UpdateAutoremoveTimeout(peer: Api.Peer, value: CachedPeerAutoremoveTimeout.Value?)
|
||||
case UpdateAttachMenuBots
|
||||
|
|
@ -413,8 +413,8 @@ struct AccountMutableState {
|
|||
self.addOperation(.UpdateGroupCallChainBlocks(id: id, accessHash: accessHash, subChainId: subChainId, blocks: blocks, nextOffset: nextOffset))
|
||||
}
|
||||
|
||||
mutating func updateGroupCallMessage(id: Int64, authorId: PeerId, messageId: Int32, text: Api.TextWithEntities, date: Int32, paidMessageStars: Int64?) {
|
||||
self.addOperation(.UpdateGroupCallMessage(id: id, authorId: authorId, messageId: messageId, text: text, date: date, paidMessageStars: paidMessageStars))
|
||||
mutating func updateGroupCallMessage(id: Int64, authorId: PeerId, isFromAdmin: Bool, messageId: Int32, text: Api.TextWithEntities, date: Int32, paidMessageStars: Int64?) {
|
||||
self.addOperation(.UpdateGroupCallMessage(id: id, authorId: authorId, isFromAdmin: isFromAdmin, messageId: messageId, text: text, date: date, paidMessageStars: paidMessageStars))
|
||||
}
|
||||
|
||||
mutating func updateGroupCallOpaqueMessage(id: Int64, authorId: PeerId, data: Data) {
|
||||
|
|
|
|||
|
|
@ -1686,8 +1686,8 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
|||
case let .updateGroupCallMessage(call, message):
|
||||
if case let .inputGroupCall(id, _) = call {
|
||||
switch message {
|
||||
case let .groupCallMessage(_, messageId, fromId, date, message, paidMessageStars):
|
||||
updatedState.updateGroupCallMessage(id: id, authorId: fromId.peerId, messageId: messageId, text: message, date: date, paidMessageStars: paidMessageStars)
|
||||
case let .groupCallMessage(flags, messageId, fromId, date, message, paidMessageStars):
|
||||
updatedState.updateGroupCallMessage(id: id, authorId: fromId.peerId, isFromAdmin: (flags & (1 << 1)) != 0, messageId: messageId, text: message, date: date, paidMessageStars: paidMessageStars)
|
||||
}
|
||||
}
|
||||
case let .updateGroupCallEncryptedMessage(call, fromId, encryptedMessage):
|
||||
|
|
@ -4895,10 +4895,10 @@ func replayFinalState(
|
|||
callId,
|
||||
.state(update: GroupCallParticipantsContext.Update.StateUpdate(participants: participants, version: version))
|
||||
))
|
||||
case let .UpdateGroupCallMessage(callId, authorId, messageId, text, date, paidMessageStars):
|
||||
case let .UpdateGroupCallMessage(callId, authorId, isFromAdmin, messageId, text, date, paidMessageStars):
|
||||
switch text {
|
||||
case let .textWithEntities(text, entities):
|
||||
groupCallMessageUpdates.append(GroupCallMessageUpdate(callId: callId, update: .newPlaintextMessage(authorId: authorId, messageId: messageId, text: text, entities: messageTextEntitiesFromApiEntities(entities), timestamp: date, paidMessageStars: paidMessageStars)))
|
||||
groupCallMessageUpdates.append(GroupCallMessageUpdate(callId: callId, update: .newPlaintextMessage(authorId: authorId, isFromAdmin: isFromAdmin, messageId: messageId, text: text, entities: messageTextEntitiesFromApiEntities(entities), timestamp: date, paidMessageStars: paidMessageStars)))
|
||||
}
|
||||
case let .UpdateGroupCallOpaqueMessage(callId, authorId, data):
|
||||
groupCallMessageUpdates.append(GroupCallMessageUpdate(callId: callId, update: .newOpaqueMessage(authorId: authorId, data: data)))
|
||||
|
|
|
|||
|
|
@ -3462,7 +3462,7 @@ func _internal_refreshInlineGroupCall(account: Account, messageId: MessageId) ->
|
|||
|
||||
struct GroupCallMessageUpdate {
|
||||
enum Update {
|
||||
case newPlaintextMessage(authorId: PeerId, messageId: Int32, text: String, entities: [MessageTextEntity], timestamp: Int32, paidMessageStars: Int64?)
|
||||
case newPlaintextMessage(authorId: PeerId, isFromAdmin: Bool, messageId: Int32, text: String, entities: [MessageTextEntity], timestamp: Int32, paidMessageStars: Int64?)
|
||||
case newOpaqueMessage(authorId: PeerId, data: Data)
|
||||
}
|
||||
|
||||
|
|
@ -3703,17 +3703,19 @@ public final class GroupCallMessagesContext {
|
|||
public let stableId: Int
|
||||
public let isIncoming: Bool
|
||||
public let author: EnginePeer?
|
||||
public let isFromAdmin: Bool
|
||||
public let text: String
|
||||
public let entities: [MessageTextEntity]
|
||||
public let date: Int32
|
||||
public let lifetime: Int32
|
||||
public let paidStars: Int64?
|
||||
|
||||
public init(id: Id, stableId: Int, isIncoming: Bool, author: EnginePeer?, text: String, entities: [MessageTextEntity], date: Int32, lifetime: Int32, paidStars: Int64?) {
|
||||
public init(id: Id, stableId: Int, isIncoming: Bool, author: EnginePeer?, isFromAdmin: Bool, text: String, entities: [MessageTextEntity], date: Int32, lifetime: Int32, paidStars: Int64?) {
|
||||
self.id = id
|
||||
self.stableId = stableId
|
||||
self.isIncoming = isIncoming
|
||||
self.author = author
|
||||
self.isFromAdmin = isFromAdmin
|
||||
self.text = text
|
||||
self.entities = entities
|
||||
self.date = date
|
||||
|
|
@ -3727,6 +3729,7 @@ public final class GroupCallMessagesContext {
|
|||
stableId: self.stableId,
|
||||
isIncoming: self.isIncoming,
|
||||
author: self.author,
|
||||
isFromAdmin: self.isFromAdmin,
|
||||
text: self.text,
|
||||
entities: self.entities,
|
||||
date: self.date,
|
||||
|
|
@ -3751,6 +3754,9 @@ public final class GroupCallMessagesContext {
|
|||
if lhs.author != rhs.author {
|
||||
return false
|
||||
}
|
||||
if lhs.isFromAdmin != rhs.isFromAdmin {
|
||||
return false
|
||||
}
|
||||
if lhs.text != rhs.text {
|
||||
return false
|
||||
}
|
||||
|
|
@ -3874,15 +3880,15 @@ public final class GroupCallMessagesContext {
|
|||
return
|
||||
}
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
var addedMessages: [(authorId: PeerId, messageId: Int32, text: String, entities: [MessageTextEntity], timestamp: Int32, paidMessageStars: Int64?)] = []
|
||||
var addedMessages: [(authorId: PeerId, isFromAdmin: Bool, messageId: Int32, text: String, entities: [MessageTextEntity], timestamp: Int32, paidMessageStars: Int64?)] = []
|
||||
var addedOpaqueMessages: [(authorId: PeerId, data: Data)] = []
|
||||
for update in updates {
|
||||
if update.callId != self.callId {
|
||||
continue
|
||||
}
|
||||
switch update.update {
|
||||
case let .newPlaintextMessage(authorId, messageId, text, entities, timestamp, paidMessageStars):
|
||||
addedMessages.append((authorId, messageId, text, entities, timestamp, paidMessageStars))
|
||||
case let .newPlaintextMessage(authorId, isFromAdmin, messageId, text, entities, timestamp, paidMessageStars):
|
||||
addedMessages.append((authorId, isFromAdmin, messageId, text, entities, timestamp, paidMessageStars))
|
||||
case let .newOpaqueMessage(authorId, data):
|
||||
if authorId != self.account.peerId {
|
||||
addedOpaqueMessages.append((authorId, data))
|
||||
|
|
@ -3933,6 +3939,7 @@ public final class GroupCallMessagesContext {
|
|||
stableId: allocatedStableIds[messages.count],
|
||||
isIncoming: addedOpaqueMessage.authorId != accountPeerId,
|
||||
author: transaction.getPeer(addedOpaqueMessage.authorId).flatMap(EnginePeer.init),
|
||||
isFromAdmin: false,
|
||||
text: text,
|
||||
entities: entities,
|
||||
date: currentTime,
|
||||
|
|
@ -3963,6 +3970,7 @@ public final class GroupCallMessagesContext {
|
|||
stableId: allocatedStableIds[messages.count],
|
||||
isIncoming: addedMessage.authorId != accountPeerId,
|
||||
author: transaction.getPeer(addedMessage.authorId).flatMap(EnginePeer.init),
|
||||
isFromAdmin: addedMessage.isFromAdmin,
|
||||
text: addedMessage.text,
|
||||
entities: addedMessage.entities,
|
||||
date: addedMessage.timestamp,
|
||||
|
|
@ -4212,7 +4220,7 @@ public final class GroupCallMessagesContext {
|
|||
}
|
||||
}
|
||||
|
||||
func send(fromId: EnginePeer.Id, randomId requestedRandomId: Int64?, text: String, entities: [MessageTextEntity], paidStars: Int64?) {
|
||||
func send(fromId: EnginePeer.Id, isAdmin: Bool, randomId requestedRandomId: Int64?, text: String, entities: [MessageTextEntity], paidStars: Int64?) {
|
||||
let _ = (self.account.postbox.transaction { transaction -> Peer? in
|
||||
return transaction.getPeer(fromId)
|
||||
}
|
||||
|
|
@ -4245,6 +4253,7 @@ public final class GroupCallMessagesContext {
|
|||
stableId: stableId,
|
||||
isIncoming: false,
|
||||
author: fromPeer.flatMap(EnginePeer.init),
|
||||
isFromAdmin: isAdmin,
|
||||
text: text,
|
||||
entities: entities,
|
||||
date: currentTime,
|
||||
|
|
@ -4417,7 +4426,7 @@ public final class GroupCallMessagesContext {
|
|||
}
|
||||
}
|
||||
|
||||
func sendStars(fromId: EnginePeer.Id, amount: Int64, delay: Bool) {
|
||||
func sendStars(fromId: EnginePeer.Id, isAdmin: Bool, amount: Int64, delay: Bool) {
|
||||
let _ = (self.account.postbox.transaction { transaction -> Peer? in
|
||||
return transaction.getPeer(fromId)
|
||||
}
|
||||
|
|
@ -4464,6 +4473,7 @@ public final class GroupCallMessagesContext {
|
|||
stableId: message.stableId,
|
||||
isIncoming: false,
|
||||
author: message.author,
|
||||
isFromAdmin: isAdmin,
|
||||
text: message.text,
|
||||
entities: message.entities,
|
||||
date: currentTime,
|
||||
|
|
@ -4478,6 +4488,7 @@ public final class GroupCallMessagesContext {
|
|||
stableId: stableId,
|
||||
isIncoming: false,
|
||||
author: EnginePeer(fromPeer),
|
||||
isFromAdmin: isAdmin,
|
||||
text: "",
|
||||
entities: [],
|
||||
date: currentTime,
|
||||
|
|
@ -4493,6 +4504,7 @@ public final class GroupCallMessagesContext {
|
|||
stableId: message.stableId,
|
||||
isIncoming: message.isIncoming,
|
||||
author: message.author,
|
||||
isFromAdmin: isAdmin,
|
||||
text: message.text,
|
||||
entities: message.entities,
|
||||
date: currentTime,
|
||||
|
|
@ -4507,6 +4519,7 @@ public final class GroupCallMessagesContext {
|
|||
stableId: stableId,
|
||||
isIncoming: false,
|
||||
author: EnginePeer(fromPeer),
|
||||
isFromAdmin: isAdmin,
|
||||
text: "",
|
||||
entities: [],
|
||||
date: currentTime,
|
||||
|
|
@ -4614,15 +4627,15 @@ public final class GroupCallMessagesContext {
|
|||
})
|
||||
}
|
||||
|
||||
public func send(fromId: EnginePeer.Id, randomId: Int64?, text: String, entities: [MessageTextEntity], paidStars: Int64?) {
|
||||
public func send(fromId: EnginePeer.Id, isAdmin: Bool, randomId: Int64?, text: String, entities: [MessageTextEntity], paidStars: Int64?) {
|
||||
self.impl.with { impl in
|
||||
impl.send(fromId: fromId, randomId: randomId, text: text, entities: entities, paidStars: paidStars)
|
||||
impl.send(fromId: fromId, isAdmin: isAdmin, randomId: randomId, text: text, entities: entities, paidStars: paidStars)
|
||||
}
|
||||
}
|
||||
|
||||
public func sendStars(fromId: EnginePeer.Id, amount: Int64, delay: Bool) {
|
||||
public func sendStars(fromId: EnginePeer.Id, isAdmin: Bool, amount: Int64, delay: Bool) {
|
||||
self.impl.with { impl in
|
||||
impl.sendStars(fromId: fromId, amount: amount, delay: delay)
|
||||
impl.sendStars(fromId: fromId, isAdmin: isAdmin, amount: amount, delay: delay)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2017,6 +2017,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||
stableId: 0,
|
||||
isIncoming: false,
|
||||
author: liveStreamMessage.myPeer,
|
||||
isFromAdmin: false,
|
||||
text: liveStreamMessage.text.string,
|
||||
entities: entities,
|
||||
date: 0,
|
||||
|
|
@ -2032,6 +2033,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||
stableId: 0,
|
||||
isIncoming: false,
|
||||
author: reactData.myPeer,
|
||||
isFromAdmin: false,
|
||||
text: "",
|
||||
entities: [],
|
||||
date: 0,
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@ public final class ChatTextInputPanelComponent: Component {
|
|||
let rightAction: RightAction?
|
||||
let sendAsConfiguration: SendAsConfiguration?
|
||||
let placeholder: String
|
||||
let isEnabled: Bool
|
||||
let paidMessagePrice: StarsAmount?
|
||||
let sendColor: UIColor?
|
||||
let isSendDisabled: Bool
|
||||
|
|
@ -166,6 +167,7 @@ public final class ChatTextInputPanelComponent: Component {
|
|||
rightAction: RightAction?,
|
||||
sendAsConfiguration: SendAsConfiguration?,
|
||||
placeholder: String,
|
||||
isEnabled: Bool,
|
||||
paidMessagePrice: StarsAmount?,
|
||||
sendColor: UIColor?,
|
||||
isSendDisabled: Bool,
|
||||
|
|
@ -186,6 +188,7 @@ public final class ChatTextInputPanelComponent: Component {
|
|||
self.rightAction = rightAction
|
||||
self.sendAsConfiguration = sendAsConfiguration
|
||||
self.placeholder = placeholder
|
||||
self.isEnabled = isEnabled
|
||||
self.paidMessagePrice = paidMessagePrice
|
||||
self.sendColor = sendColor
|
||||
self.isSendDisabled = isSendDisabled
|
||||
|
|
@ -228,6 +231,9 @@ public final class ChatTextInputPanelComponent: Component {
|
|||
if lhs.placeholder != rhs.placeholder {
|
||||
return false
|
||||
}
|
||||
if lhs.isEnabled != rhs.isEnabled {
|
||||
return false
|
||||
}
|
||||
if lhs.paidMessagePrice != rhs.paidMessagePrice {
|
||||
return false
|
||||
}
|
||||
|
|
@ -829,6 +835,7 @@ public final class ChatTextInputPanelComponent: Component {
|
|||
}
|
||||
|
||||
panelNode.customPlaceholder = component.placeholder
|
||||
panelNode.customIsDisabled = !component.isEnabled
|
||||
|
||||
if let leftAction = component.leftAction {
|
||||
switch leftAction.kind {
|
||||
|
|
|
|||
|
|
@ -264,6 +264,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
public let attachmentButtonBackground: GlassBackgroundView
|
||||
public let attachmentButtonIcon: GlassBackgroundView.ContentImageView
|
||||
private var commentsButtonIcon: RasterizedCompositionMonochromeLayer?
|
||||
private var commentsButtonCenterIcon: UIImageView?
|
||||
private var commentsButtonContentsLayer: RasterizedCompositionImageLayer?
|
||||
private var commentsButtonDotLayer: RasterizedCompositionImageLayer?
|
||||
private var attachmentButtonUnseenIcon: UIImageView?
|
||||
|
|
@ -393,6 +394,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
}
|
||||
|
||||
public var customPlaceholder: String?
|
||||
public var customIsDisabled: Bool = false
|
||||
public var customLeftAction: LeftAction?
|
||||
public var customRightAction: RightAction?
|
||||
public var customSendColor: UIColor?
|
||||
|
|
@ -1495,6 +1497,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
}
|
||||
}
|
||||
}
|
||||
if self.customIsDisabled {
|
||||
sendingTextDisabled = true
|
||||
}
|
||||
self.sendingTextDisabled = sendingTextDisabled
|
||||
|
||||
self.textInputNode?.isUserInteractionEnabled = !sendingTextDisabled
|
||||
|
|
@ -1941,7 +1946,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
if let customLeftAction = self.customLeftAction {
|
||||
switch customLeftAction {
|
||||
case let .toggleExpanded(_, isExpanded, hasUnseen):
|
||||
let _ = isExpanded
|
||||
let commentsButtonIcon: RasterizedCompositionMonochromeLayer
|
||||
if let current = self.commentsButtonIcon {
|
||||
commentsButtonIcon = current
|
||||
|
|
@ -1951,6 +1955,17 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
self.attachmentButtonBackground.contentView.layer.addSublayer(commentsButtonIcon)
|
||||
}
|
||||
|
||||
let commentsButtonCenterIcon: UIImageView
|
||||
if let current = self.commentsButtonCenterIcon {
|
||||
commentsButtonCenterIcon = current
|
||||
} else {
|
||||
commentsButtonCenterIcon = UIImageView()
|
||||
self.commentsButtonCenterIcon = commentsButtonCenterIcon
|
||||
self.attachmentButtonBackground.contentView.addSubview(commentsButtonCenterIcon)
|
||||
commentsButtonCenterIcon.image = UIImage(bundleImageName: "Chat/Input/Text/CommensCross")?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
commentsButtonCenterIcon.tintColor = interfaceState.theme.chat.inputPanel.panelControlColor
|
||||
|
||||
let commentsButtonContentsLayer: RasterizedCompositionImageLayer
|
||||
if let current = self.commentsButtonContentsLayer {
|
||||
commentsButtonContentsLayer = current
|
||||
|
|
@ -1975,6 +1990,12 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
commentsButtonIcon.position = iconFrame.center
|
||||
commentsButtonIcon.bounds = CGRect(origin: CGPoint(), size: iconFrame.size)
|
||||
|
||||
commentsButtonCenterIcon.center = iconFrame.center
|
||||
if let image = commentsButtonCenterIcon.image {
|
||||
commentsButtonCenterIcon.bounds = image.size.centered(in: iconFrame)
|
||||
}
|
||||
transition.updateTransformRotation(view: commentsButtonCenterIcon, angle: !isExpanded ? (CGFloat.pi * 3.0 / 4.0) : 0.0)
|
||||
|
||||
commentsButtonIcon.contentsLayer.position = CGRect(origin: CGPoint(), size: iconFrame.size).center
|
||||
commentsButtonIcon.contentsLayer.bounds = CGRect(origin: CGPoint(), size: iconFrame.size)
|
||||
|
||||
|
|
@ -2036,6 +2057,13 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
attachmentButtonUnseenIcon?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
if let commentsButtonCenterIcon = self.commentsButtonCenterIcon {
|
||||
self.commentsButtonCenterIcon = nil
|
||||
transition.updateTransformScale(layer: commentsButtonCenterIcon.layer, scale: 0.001)
|
||||
transition.updateAlpha(layer: commentsButtonCenterIcon.layer, alpha: 0.0, completion: { [weak commentsButtonCenterIcon] _ in
|
||||
commentsButtonCenterIcon?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var textFieldMinHeight: CGFloat = 33.0
|
||||
|
|
|
|||
|
|
@ -174,11 +174,15 @@ public final class MessageInputPanelComponent: Component {
|
|||
}
|
||||
|
||||
public struct LiveChatState: Equatable {
|
||||
public var isEnabled: Bool
|
||||
public var isExpanded: Bool
|
||||
public var isEmpty: Bool
|
||||
public var hasUnseenMessages: Bool
|
||||
|
||||
public init(isExpanded: Bool, hasUnseenMessages: Bool) {
|
||||
public init(isEnabled: Bool, isExpanded: Bool, isEmpty: Bool, hasUnseenMessages: Bool) {
|
||||
self.isEnabled = isEnabled
|
||||
self.isExpanded = isExpanded
|
||||
self.isEmpty = isEmpty
|
||||
self.hasUnseenMessages = hasUnseenMessages
|
||||
}
|
||||
}
|
||||
|
|
@ -929,35 +933,37 @@ public final class MessageInputPanelComponent: Component {
|
|||
self.currentInputMode = inputMode
|
||||
|
||||
var inlineActions: [ChatTextInputPanelComponent.InlineAction] = []
|
||||
if component.paidMessageAction != nil && self.textInputPanelExternalState.textInputState.inputText.length == 0 {
|
||||
inlineActions.append(ChatTextInputPanelComponent.InlineAction(
|
||||
kind: .paidMessage,
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
if component.liveChatState?.isEnabled == true {
|
||||
if component.paidMessageAction != nil && self.textInputPanelExternalState.textInputState.inputText.length == 0 {
|
||||
inlineActions.append(ChatTextInputPanelComponent.InlineAction(
|
||||
kind: .paidMessage,
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.component?.paidMessageAction?()
|
||||
}
|
||||
self.component?.paidMessageAction?()
|
||||
))
|
||||
} else if let inputMode {
|
||||
let mappedInputMode: ChatTextInputPanelComponent.InputMode
|
||||
switch inputMode {
|
||||
case .text:
|
||||
mappedInputMode = .text
|
||||
case .emoji:
|
||||
mappedInputMode = .emoji
|
||||
case .stickers:
|
||||
mappedInputMode = .stickers
|
||||
}
|
||||
))
|
||||
} else if let inputMode {
|
||||
let mappedInputMode: ChatTextInputPanelComponent.InputMode
|
||||
switch inputMode {
|
||||
case .text:
|
||||
mappedInputMode = .text
|
||||
case .emoji:
|
||||
mappedInputMode = .emoji
|
||||
case .stickers:
|
||||
mappedInputMode = .stickers
|
||||
inlineActions.append(ChatTextInputPanelComponent.InlineAction(
|
||||
kind: .inputMode(mappedInputMode),
|
||||
action: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.inputModeAction?()
|
||||
}
|
||||
))
|
||||
}
|
||||
inlineActions.append(ChatTextInputPanelComponent.InlineAction(
|
||||
kind: .inputMode(mappedInputMode),
|
||||
action: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.inputModeAction?()
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
let placeholder: String
|
||||
|
|
@ -1026,11 +1032,18 @@ public final class MessageInputPanelComponent: Component {
|
|||
strings: component.strings,
|
||||
chatPeerId: component.chatLocation?.peerId ?? component.context.account.peerId,
|
||||
inlineActions: inlineActions,
|
||||
leftAction: ChatTextInputPanelComponent.LeftAction(kind: .toggleExpanded(isVisible: component.liveChatState != nil, isExpanded: component.liveChatState?.isExpanded ?? true, hasUnseen: component.liveChatState?.hasUnseenMessages ?? false), action: { [weak self] in
|
||||
leftAction: ChatTextInputPanelComponent.LeftAction(kind: .toggleExpanded(isVisible: component.liveChatState?.isEnabled == true, isExpanded: component.liveChatState?.isExpanded ?? true, hasUnseen: component.liveChatState?.hasUnseenMessages ?? false), action: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.toggleLiveChatExpanded?()
|
||||
guard let inputPanelView = self.inputPanel?.view as? ChatTextInputPanelComponent.View else {
|
||||
return
|
||||
}
|
||||
if let liveChatState = component.liveChatState, liveChatState.isEmpty {
|
||||
inputPanelView.activateInput()
|
||||
} else {
|
||||
component.toggleLiveChatExpanded?()
|
||||
}
|
||||
}),
|
||||
rightAction: ChatTextInputPanelComponent.RightAction(kind: .stars(count: Int(component.starStars?.totalStars ?? 0), isFilled: component.starStars?.hasOutgoingStars ?? false), action: { [weak self] sourceView in
|
||||
guard let self, let component = self.component else {
|
||||
|
|
@ -1043,8 +1056,10 @@ public final class MessageInputPanelComponent: Component {
|
|||
}
|
||||
component.sendStarsAction?(sourceView, true)
|
||||
}),
|
||||
sendAsConfiguration: sendAsConfiguration,
|
||||
placeholder: placeholder,
|
||||
sendAsConfiguration: component.liveChatState?.isEnabled == true ? sendAsConfiguration : nil,
|
||||
//TODO:localize
|
||||
placeholder: component.liveChatState?.isEnabled == true ? placeholder : "Comments are disabled",
|
||||
isEnabled: component.liveChatState?.isEnabled == true,
|
||||
paidMessagePrice: component.sendPaidMessageStars,
|
||||
sendColor: component.sendPaidMessageStars.flatMap { value in
|
||||
let params = LiveChatMessageParams(appConfig: component.context.currentAppConfiguration.with({ $0 }))
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import AdminUserActionsSheet
|
|||
|
||||
final class StoryContentLiveChatComponent: Component {
|
||||
final class External {
|
||||
fileprivate(set) var isEmpty: Bool = false
|
||||
fileprivate(set) var hasUnseenMessages: Bool = false
|
||||
|
||||
init() {
|
||||
|
|
@ -268,6 +269,35 @@ final class StoryContentLiveChatComponent: Component {
|
|||
self.state?.updated(transition: .spring(duration: 0.4))
|
||||
}
|
||||
|
||||
private func displayDeleteMessageConfirmation(id: GroupCallMessagesContext.Message.Id) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
|
||||
actionSheet.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: component.strings.Chat_DeleteMessagesConfirmation(1), color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
guard let self, let component = self.component, let call = component.call as? PresentationGroupCallImpl else {
|
||||
return
|
||||
}
|
||||
call.deleteMessage(id: id, reportSpam: false)
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])
|
||||
])
|
||||
|
||||
component.controller()?.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
|
||||
private func displayDeleteMessageAndBan(id: GroupCallMessagesContext.Message.Id) {
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
|
|
@ -281,6 +311,10 @@ final class StoryContentLiveChatComponent: Component {
|
|||
guard let messagesState = self.messagesState, let message = messagesState.messages.first(where: { $0.id == id }) else {
|
||||
return
|
||||
}
|
||||
if message.isFromAdmin {
|
||||
self.displayDeleteMessageConfirmation(id: id)
|
||||
return
|
||||
}
|
||||
guard let author = message.author else {
|
||||
return
|
||||
}
|
||||
|
|
@ -311,10 +345,23 @@ final class StoryContentLiveChatComponent: Component {
|
|||
messageCount: 1,
|
||||
deleteAllMessageCount: totalCount,
|
||||
completion: { [weak self] result in
|
||||
guard let self else {
|
||||
guard let self, let component = self.component, let call = component.call as? PresentationGroupCallImpl else {
|
||||
return
|
||||
}
|
||||
let _ = self
|
||||
|
||||
if result.deleteAll {
|
||||
call.deleteAllMessages(authorId: author.id, reportSpam: result.reportSpam)
|
||||
} else {
|
||||
call.deleteMessage(id: id, reportSpam: result.reportSpam)
|
||||
}
|
||||
|
||||
if result.ban {
|
||||
if component.storyPeerId == component.context.account.peerId {
|
||||
let _ = component.context.engine.privacy.requestUpdatePeerIsBlocked(peerId: author.id, isBlocked: true).startStandalone()
|
||||
} else {
|
||||
let _ = component.context.engine.peers.updateChannelMemberBannedRights(peerId: component.storyPeerId, memberId: author.id, rights: TelegramChatBannedRights(flags: .banReadMessages, untilDate: Int32.max)).startStandalone()
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
customTheme: defaultDarkColorPresentationTheme
|
||||
|
|
@ -414,15 +461,13 @@ final class StoryContentLiveChatComponent: Component {
|
|||
}
|
||||
|
||||
c?.dismiss(completion: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let call = component.call as? PresentationGroupCallImpl {
|
||||
if isAdmin && !isMyMessage {
|
||||
self.displayDeleteMessageAndBan(id: id)
|
||||
} else {
|
||||
call.deleteMessage(id: id, reportSpam: false)
|
||||
}
|
||||
if isAdmin && !isMyMessage {
|
||||
self.displayDeleteMessageAndBan(id: id)
|
||||
} else {
|
||||
self.displayDeleteMessageConfirmation(id: id)
|
||||
}
|
||||
})
|
||||
})))
|
||||
|
|
@ -500,6 +545,7 @@ final class StoryContentLiveChatComponent: Component {
|
|||
self.reactionStreamView?.add(peer: peer, count: Int(state.pendingMyStars - previousMessagesState.pendingMyStars))
|
||||
}
|
||||
}
|
||||
component.external.isEmpty = state.messages.isEmpty
|
||||
self.messagesState = state
|
||||
|
||||
if !self.isUpdating {
|
||||
|
|
|
|||
|
|
@ -112,27 +112,35 @@ final class StoryItemContentComponent: Component {
|
|||
|
||||
struct LiveChatState {
|
||||
var isExpanded: Bool
|
||||
var isEmpty: Bool
|
||||
var hasUnseenMessages: Bool
|
||||
var areMessagesEnabled: Bool
|
||||
var minMessagePrice: Int64?
|
||||
var starStats: StarStats?
|
||||
var isAdmin: Bool
|
||||
|
||||
init(isExpanded: Bool, hasUnseenMessages: Bool, areMessagesEnabled: Bool, minMessagePrice: Int64?, starStats: StarStats?) {
|
||||
init(isExpanded: Bool, isEmpty: Bool, hasUnseenMessages: Bool, areMessagesEnabled: Bool, minMessagePrice: Int64?, starStats: StarStats?, isAdmin: Bool) {
|
||||
self.isExpanded = isExpanded
|
||||
self.isEmpty = isEmpty
|
||||
self.hasUnseenMessages = hasUnseenMessages
|
||||
self.areMessagesEnabled = areMessagesEnabled
|
||||
self.minMessagePrice = minMessagePrice
|
||||
self.starStats = starStats
|
||||
self.isAdmin = isAdmin
|
||||
}
|
||||
}
|
||||
|
||||
private struct MediaStreamCallState: Equatable {
|
||||
var areMessagesEnabled: Bool
|
||||
var minMessagePrice: Int64?
|
||||
var isAdmin: Bool
|
||||
var isUnifiedStream: Bool
|
||||
|
||||
init(areMessagesEnabled: Bool, minMessagePrice: Int64?) {
|
||||
init(areMessagesEnabled: Bool, minMessagePrice: Int64?, isAdmin: Bool, isUnifiedStream: Bool) {
|
||||
self.areMessagesEnabled = areMessagesEnabled
|
||||
self.minMessagePrice = minMessagePrice
|
||||
self.isAdmin = isAdmin
|
||||
self.isUnifiedStream = isUnifiedStream
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,10 +224,12 @@ final class StoryItemContentComponent: Component {
|
|||
|
||||
return LiveChatState(
|
||||
isExpanded: currentInfo.isChatExpanded,
|
||||
isEmpty: self.liveChatExternal.isEmpty,
|
||||
hasUnseenMessages: self.liveChatExternal.hasUnseenMessages,
|
||||
areMessagesEnabled: mediaStreamCallState?.areMessagesEnabled ?? false,
|
||||
minMessagePrice: mediaStreamCallState?.minMessagePrice,
|
||||
starStats: starStats
|
||||
starStats: starStats,
|
||||
isAdmin: mediaStreamCallState?.isAdmin ?? false
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -932,7 +942,9 @@ final class StoryItemContentComponent: Component {
|
|||
if case .rtc = liveStream.kind, component.isEmbeddedInCamera {
|
||||
} else {
|
||||
var videoEndpointId: String?
|
||||
if let mediaStreamCallVideoState = self.mediaStreamCallVideoState {
|
||||
if let mediaStreamCallState = self.mediaStreamCallState, mediaStreamCallState.isUnifiedStream {
|
||||
videoEndpointId = "unified"
|
||||
} else if let mediaStreamCallVideoState = self.mediaStreamCallVideoState {
|
||||
videoEndpointId = mediaStreamCallVideoState.videoEndpointId
|
||||
}
|
||||
let _ = mediaStream.update(
|
||||
|
|
@ -1076,7 +1088,9 @@ final class StoryItemContentComponent: Component {
|
|||
|
||||
let mappedState = MediaStreamCallState(
|
||||
areMessagesEnabled: state.messagesAreEnabled,
|
||||
minMessagePrice: state.sendPaidMessageStars
|
||||
minMessagePrice: state.sendPaidMessageStars,
|
||||
isAdmin: state.canManageCall,
|
||||
isUnifiedStream: state.isUnifiedStream
|
||||
)
|
||||
if self.mediaStreamCallState != mappedState {
|
||||
self.mediaStreamCallState = mappedState
|
||||
|
|
|
|||
|
|
@ -2968,7 +2968,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
if let visibleItemView = self.visibleItems[component.slice.item.id]?.view.view as? StoryItemContentComponent.View {
|
||||
if let liveChatStateValue = visibleItemView.liveChatState {
|
||||
liveChatState = MessageInputPanelComponent.LiveChatState(
|
||||
isEnabled: liveChatStateValue.areMessagesEnabled,
|
||||
isExpanded: liveChatStateValue.isExpanded,
|
||||
isEmpty: liveChatStateValue.isEmpty,
|
||||
hasUnseenMessages: liveChatStateValue.hasUnseenMessages
|
||||
)
|
||||
starStats = liveChatStateValue.starStats.flatMap { starStats in
|
||||
|
|
|
|||
|
|
@ -683,6 +683,7 @@ final class StoryItemSetContainerSendMessage {
|
|||
if let visibleItem = view.visibleItems[component.slice.item.id], let itemView = visibleItem.view.view as? StoryItemContentComponent.View {
|
||||
|
||||
var sendPaidMessageStars = self.currentLiveStreamMessageStars
|
||||
var isAdmin = false
|
||||
if let visibleItemView = view.visibleItems[component.slice.item.id]?.view.view as? StoryItemContentComponent.View {
|
||||
if let liveChatStateValue = visibleItemView.liveChatState {
|
||||
if let minMessagePrice = liveChatStateValue.minMessagePrice {
|
||||
|
|
@ -694,6 +695,7 @@ final class StoryItemSetContainerSendMessage {
|
|||
sendPaidMessageStars = StarsAmount(value: minMessagePrice, nanos: 0)
|
||||
}
|
||||
}
|
||||
isAdmin = liveChatStateValue.isAdmin
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -756,7 +758,7 @@ final class StoryItemSetContainerSendMessage {
|
|||
|
||||
let entities = generateChatInputTextEntities(text)
|
||||
|
||||
call.sendMessage(fromId: self.currentSendAsPeer?.peer.id, text: text.string, entities: entities, paidStars: sendPaidMessageStars?.value)
|
||||
call.sendMessage(fromId: self.currentSendAsPeer?.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)
|
||||
|
|
@ -4207,14 +4209,18 @@ final class StoryItemSetContainerSendMessage {
|
|||
guard case .liveStream = component.slice.item.storyItem.media else {
|
||||
return
|
||||
}
|
||||
var isAdmin = false
|
||||
guard let visibleItem = view.visibleItems[component.slice.item.id], let itemView = visibleItem.view.view as? StoryItemContentComponent.View else {
|
||||
return
|
||||
}
|
||||
guard let call = itemView.mediaStreamCall else {
|
||||
return
|
||||
}
|
||||
if let liveChatStateValue = itemView.liveChatState {
|
||||
isAdmin = liveChatStateValue.isAdmin
|
||||
}
|
||||
|
||||
call.sendStars(fromId: self.currentSendAsPeer?.peer.id, amount: Int64(count), delay: delay)
|
||||
call.sendStars(fromId: self.currentSendAsPeer?.peer.id, isAdmin: isAdmin, amount: Int64(count), delay: delay)
|
||||
}
|
||||
|
||||
func openSendAsSelection(view: StoryItemSetContainerComponent.View, sourceView: UIView, gesture: ContextGesture?) {
|
||||
|
|
|
|||
12
submodules/TelegramUI/Images.xcassets/Chat/Input/Text/CommensCross.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Input/Text/CommensCross.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "comments2.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Chat/Input/Text/CommensCross.imageset/comments2.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Input/Text/CommensCross.imageset/comments2.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "hidecomments.pdf",
|
||||
"filename" : "comments1.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue