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
62e92134e0
commit
4853623813
10 changed files with 83 additions and 44 deletions
|
|
@ -895,6 +895,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
invite: nil,
|
||||
joinAsPeerId: nil,
|
||||
isStream: false,
|
||||
streamPeerId: nil,
|
||||
keyPair: keyPair,
|
||||
conferenceSourceId: self.internalId,
|
||||
isConference: true,
|
||||
|
|
@ -1081,6 +1082,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||
invite: nil,
|
||||
joinAsPeerId: nil,
|
||||
isStream: false,
|
||||
streamPeerId: nil,
|
||||
keyPair: keyPair,
|
||||
conferenceSourceId: self.internalId,
|
||||
isConference: true,
|
||||
|
|
|
|||
|
|
@ -893,6 +893,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
invite: nil,
|
||||
joinAsPeerId: nil,
|
||||
isStream: false,
|
||||
streamPeerId: nil,
|
||||
keyPair: nil,
|
||||
conferenceSourceId: nil,
|
||||
isConference: false,
|
||||
|
|
@ -1116,6 +1117,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
invite: invite,
|
||||
joinAsPeerId: joinAsPeerId,
|
||||
isStream: initialCall.isStream ?? false,
|
||||
streamPeerId: nil,
|
||||
keyPair: nil,
|
||||
conferenceSourceId: nil,
|
||||
isConference: false,
|
||||
|
|
@ -1157,6 +1159,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
invite: nil,
|
||||
joinAsPeerId: nil,
|
||||
isStream: false,
|
||||
streamPeerId: nil,
|
||||
keyPair: keyPair,
|
||||
conferenceSourceId: nil,
|
||||
isConference: true,
|
||||
|
|
|
|||
|
|
@ -807,6 +807,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
private var screencastStateDisposable: Disposable?
|
||||
|
||||
public let isStream: Bool
|
||||
private let streamPeerId: EnginePeer.Id?
|
||||
private let sharedAudioContext: SharedCallAudioContext?
|
||||
|
||||
public let isConference: Bool
|
||||
|
|
@ -854,6 +855,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
invite: String?,
|
||||
joinAsPeerId: EnginePeer.Id?,
|
||||
isStream: Bool,
|
||||
streamPeerId: EnginePeer.Id?,
|
||||
keyPair: TelegramKeyPair?,
|
||||
conferenceSourceId: CallSessionInternalId?,
|
||||
isConference: Bool,
|
||||
|
|
@ -888,6 +890,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
self.hasVideo = false
|
||||
self.hasScreencast = false
|
||||
self.isStream = isStream
|
||||
self.streamPeerId = streamPeerId
|
||||
self.conferenceSourceId = conferenceSourceId
|
||||
self.isConference = isConference
|
||||
self.beginWithVideo = beginWithVideo
|
||||
|
|
@ -2051,6 +2054,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
callId: callInfo.id,
|
||||
reference: reference,
|
||||
isStream: self.isStream,
|
||||
streamPeerId: self.streamPeerId,
|
||||
preferMuted: isEffectivelyMuted,
|
||||
joinPayload: joinPayload,
|
||||
peerAdminIds: peerAdminIds,
|
||||
|
|
@ -2306,7 +2310,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
let accountContext = self.accountContext
|
||||
let peerId = self.peerId
|
||||
let rawAdminIds: Signal<Set<PeerId>, NoError>
|
||||
if let peerId {
|
||||
if let peerId = peerId ?? self.streamPeerId {
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
rawAdminIds = Signal { subscriber in
|
||||
let (disposable, _) = accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: accountContext.engine, postbox: accountContext.account.postbox, network: accountContext.account.network, accountPeerId: accountContext.account.peerId, peerId: peerId, updated: { list in
|
||||
|
|
@ -2346,7 +2350,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
}
|
||||
|
||||
let peer: Signal<EnginePeer?, NoError>
|
||||
if let peerId {
|
||||
if let peerId = peerId ?? self.streamPeerId {
|
||||
peer = accountContext.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
} else {
|
||||
peer = .single(nil)
|
||||
|
|
|
|||
|
|
@ -644,7 +644,7 @@ public class JoinGroupCallE2E {
|
|||
}
|
||||
}
|
||||
|
||||
func _internal_joinGroupCall(account: Account, 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<JoinGroupCallResult, JoinGroupCallError> {
|
||||
func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, callId: Int64, reference: InternalGroupCallReference, isStream: Bool, streamPeerId: PeerId?, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil, generateE2E: ((Data?) -> JoinGroupCallE2E?)?) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
|
||||
enum InternalJoinError {
|
||||
case error(JoinGroupCallError)
|
||||
case restart
|
||||
|
|
@ -750,7 +750,7 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||
return joinRequest
|
||||
|> mapToSignal { updates -> Signal<JoinGroupCallResult, InternalJoinError> in
|
||||
let peer = account.postbox.transaction { transaction -> Peer? in
|
||||
return peerId.flatMap(transaction.getPeer)
|
||||
return (peerId ?? streamPeerId).flatMap(transaction.getPeer)
|
||||
}
|
||||
|> castError(InternalJoinError.self)
|
||||
|
||||
|
|
@ -820,6 +820,9 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||
state.sortAscending = parsedCall.sortAscending
|
||||
|
||||
state.adminIds = Set(peerAdminIds)
|
||||
if isStream, let channel = peer as? TelegramChannel, channel.hasPermission(.manageCalls) {
|
||||
state.adminIds.insert(account.peerId)
|
||||
}
|
||||
|
||||
let connectionMode: JoinGroupCallResult.ConnectionMode
|
||||
if let clientParamsData = parsedClientParams.data(using: .utf8), let dict = (try? JSONSerialization.jsonObject(with: clientParamsData, options: [])) as? [String: Any] {
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ public extension TelegramEngine {
|
|||
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<JoinGroupCallResult, JoinGroupCallError> {
|
||||
return _internal_joinGroupCall(account: self.account, peerId: peerId, joinAs: joinAs, callId: callId, reference: reference, isStream: isStream, preferMuted: preferMuted, joinPayload: joinPayload, peerAdminIds: peerAdminIds, inviteHash: inviteHash, generateE2E: generateE2E)
|
||||
public func joinGroupCall(peerId: PeerId?, joinAs: PeerId?, callId: Int64, reference: InternalGroupCallReference, isStream: Bool, streamPeerId: PeerId?, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil, generateE2E: ((Data?) -> JoinGroupCallE2E?)?) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
|
||||
return _internal_joinGroupCall(account: self.account, peerId: peerId, joinAs: joinAs, callId: callId, reference: reference, isStream: isStream, streamPeerId: streamPeerId, preferMuted: preferMuted, joinPayload: joinPayload, peerAdminIds: peerAdminIds, inviteHash: inviteHash, generateE2E: generateE2E)
|
||||
}
|
||||
|
||||
public func joinGroupCallAsScreencast(callId: Int64, accessHash: Int64, joinPayload: String) -> Signal<JoinGroupCallAsScreencastResult, JoinGroupCallError> {
|
||||
|
|
|
|||
|
|
@ -945,25 +945,13 @@ public final class MessageInputPanelComponent: Component {
|
|||
|
||||
var inlineActions: [ChatTextInputPanelComponent.InlineAction] = []
|
||||
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?()
|
||||
}
|
||||
))
|
||||
} else if let inputMode {
|
||||
if let inputMode {
|
||||
let mappedInputMode: ChatTextInputPanelComponent.InputMode
|
||||
switch inputMode {
|
||||
case .text:
|
||||
mappedInputMode = .text
|
||||
case .emoji:
|
||||
case .emoji, .stickers:
|
||||
mappedInputMode = .emoji
|
||||
case .stickers:
|
||||
mappedInputMode = .stickers
|
||||
}
|
||||
inlineActions.append(ChatTextInputPanelComponent.InlineAction(
|
||||
kind: .inputMode(mappedInputMode),
|
||||
|
|
@ -975,6 +963,17 @@ public final class MessageInputPanelComponent: Component {
|
|||
}
|
||||
))
|
||||
}
|
||||
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?()
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
let placeholder: String
|
||||
|
|
@ -1106,15 +1105,17 @@ public final class MessageInputPanelComponent: Component {
|
|||
containerSize: availableSize
|
||||
)
|
||||
let inputPanelFrame = CGRect(origin: CGPoint(), size: inputPanelSize)
|
||||
if let inputPanelView = inputPanel.view {
|
||||
var inputPanelViewIsActive = false
|
||||
if let inputPanelView = inputPanel.view as? ChatTextInputPanelComponent.View {
|
||||
if inputPanelView.superview == nil {
|
||||
inputPanel.parentState = state
|
||||
self.addSubview(inputPanelView)
|
||||
}
|
||||
transition.setFrame(view: inputPanelView, frame: inputPanelFrame)
|
||||
inputPanelViewIsActive = inputPanelView.isActive
|
||||
}
|
||||
|
||||
component.externalState.isEditing = self.textInputPanelExternalState.isEditing
|
||||
component.externalState.isEditing = self.textInputPanelExternalState.isEditing || inputPanelViewIsActive
|
||||
component.externalState.hasText = self.textInputPanelExternalState.textInputState.inputText.length != 0
|
||||
component.externalState.isKeyboardHidden = component.hideKeyboard
|
||||
component.externalState.insertText = { [weak self] text in
|
||||
|
|
|
|||
|
|
@ -124,7 +124,18 @@ public final class PeerNameTextComponent: Component {
|
|||
containerSize: availableSize
|
||||
)
|
||||
|
||||
var titleX: CGFloat = 0.0
|
||||
let titleFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
titleView.layer.anchorPoint = CGPoint()
|
||||
self.addSubview(titleView)
|
||||
}
|
||||
transition.setPosition(view: titleView, position: titleFrame.origin)
|
||||
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
|
||||
}
|
||||
|
||||
var size = CGSize(width: titleFrame.maxX, height: titleSize.height)
|
||||
|
||||
if let iconContent {
|
||||
let icon: ComponentView<Empty>
|
||||
if let current = self.icon {
|
||||
|
|
@ -148,30 +159,21 @@ public final class PeerNameTextComponent: Component {
|
|||
environment: {},
|
||||
containerSize: containerSize
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: titleX, y: floorToScreenPixels((titleSize.height - iconSize.height) * 0.5)), size: iconSize)
|
||||
titleX += iconSize.width + 4.0
|
||||
size.width += 3.0
|
||||
let iconFrame = CGRect(origin: CGPoint(x: size.width, y: floorToScreenPixels((titleSize.height - iconSize.height) * 0.5)), size: iconSize)
|
||||
if let iconView = icon.view {
|
||||
if iconView.superview == nil {
|
||||
self.addSubview(iconView)
|
||||
}
|
||||
transition.setFrame(view: iconView, frame: iconFrame)
|
||||
}
|
||||
size.width += iconSize.width
|
||||
} else if let icon = self.icon {
|
||||
self.icon = nil
|
||||
icon.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: titleX, y: 0.0), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
titleView.layer.anchorPoint = CGPoint()
|
||||
self.addSubview(titleView)
|
||||
}
|
||||
transition.setPosition(view: titleView, position: titleFrame.origin)
|
||||
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
|
||||
}
|
||||
|
||||
return CGSize(width: titleFrame.maxX, height: titleSize.height)
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ final class StoryContentLiveChatComponent: Component {
|
|||
let storyPeerId: EnginePeer.Id
|
||||
let insets: UIEdgeInsets
|
||||
let isEmbeddedInCamera: Bool
|
||||
let minPaidStars: Int?
|
||||
let controller: () -> ViewController?
|
||||
|
||||
init(
|
||||
|
|
@ -47,6 +48,7 @@ final class StoryContentLiveChatComponent: Component {
|
|||
storyPeerId: EnginePeer.Id,
|
||||
insets: UIEdgeInsets,
|
||||
isEmbeddedInCamera: Bool,
|
||||
minPaidStars: Int?,
|
||||
controller: @escaping () -> ViewController?
|
||||
) {
|
||||
self.external = external
|
||||
|
|
@ -57,6 +59,7 @@ final class StoryContentLiveChatComponent: Component {
|
|||
self.storyPeerId = storyPeerId
|
||||
self.insets = insets
|
||||
self.isEmbeddedInCamera = isEmbeddedInCamera
|
||||
self.minPaidStars = minPaidStars
|
||||
self.controller = controller
|
||||
}
|
||||
|
||||
|
|
@ -85,6 +88,9 @@ final class StoryContentLiveChatComponent: Component {
|
|||
if lhs.isEmbeddedInCamera != rhs.isEmbeddedInCamera {
|
||||
return false
|
||||
}
|
||||
if lhs.minPaidStars != rhs.minPaidStars {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -436,6 +442,12 @@ final class StoryContentLiveChatComponent: Component {
|
|||
isMyMessage = true
|
||||
canDelete = true
|
||||
}
|
||||
var isMessageFromAdmin = false
|
||||
if message.isFromAdmin {
|
||||
isMessageFromAdmin = true
|
||||
} else if message.author?.id == component.storyPeerId {
|
||||
isMessageFromAdmin = true
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
if !isMyMessage, let author = message.author {
|
||||
|
|
@ -467,13 +479,6 @@ final class StoryContentLiveChatComponent: Component {
|
|||
})))
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if "".isEmpty {
|
||||
isAdmin = true
|
||||
canDelete = true
|
||||
}
|
||||
#endif
|
||||
|
||||
if canDelete {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
|
||||
guard let self else {
|
||||
|
|
@ -484,7 +489,7 @@ final class StoryContentLiveChatComponent: Component {
|
|||
guard let self else {
|
||||
return
|
||||
}
|
||||
if isAdmin && !isMyMessage {
|
||||
if isAdmin && !isMyMessage && !isMessageFromAdmin {
|
||||
self.displayDeleteMessageAndBan(id: id)
|
||||
} else {
|
||||
self.displayDeleteMessageConfirmation(id: id)
|
||||
|
|
@ -616,6 +621,12 @@ final class StoryContentLiveChatComponent: Component {
|
|||
|
||||
for message in messagesState.pinnedMessages.reversed() {
|
||||
if let author = message.author, let paidStars = message.paidStars {
|
||||
if let minPaidStars = component.minPaidStars {
|
||||
if Int(paidStars) < minPaidStars {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if let current = topMessageByPeerId[author.id] {
|
||||
if let currentPaidStars = current.paidStars, currentPaidStars < paidStars {
|
||||
topMessageByPeerId[author.id] = message
|
||||
|
|
|
|||
|
|
@ -296,6 +296,9 @@ final class StoryItemContentComponent: Component {
|
|||
if let _ = self.overlaysView.hitTest(self.convert(self.convert(point, to: self.overlaysView), to: self.overlaysView), with: nil) {
|
||||
return false
|
||||
}
|
||||
if self.liveChat != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -452,6 +455,7 @@ final class StoryItemContentComponent: Component {
|
|||
invite: nil,
|
||||
joinAsPeerId: nil,
|
||||
isStream: !(component.isEmbeddedInCamera && liveStream.kind == .rtc),
|
||||
streamPeerId: component.peer.id,
|
||||
keyPair: nil,
|
||||
conferenceSourceId: nil,
|
||||
isConference: false,
|
||||
|
|
@ -938,6 +942,11 @@ final class StoryItemContentComponent: Component {
|
|||
self.liveChat = liveChat
|
||||
}
|
||||
|
||||
var minPaidStars: Int?
|
||||
if let mediaStreamCallState = self.mediaStreamCallState {
|
||||
minPaidStars = mediaStreamCallState.minMessagePrice.flatMap(Int.init)
|
||||
}
|
||||
|
||||
let _ = liveChat.update(
|
||||
transition: mediaStreamTransition,
|
||||
component: AnyComponent(StoryContentLiveChatComponent(
|
||||
|
|
@ -949,6 +958,7 @@ final class StoryItemContentComponent: Component {
|
|||
storyPeerId: component.peer.id,
|
||||
insets: environment.containerInsets,
|
||||
isEmbeddedInCamera: component.isEmbeddedInCamera,
|
||||
minPaidStars: minPaidStars,
|
||||
controller: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -949,6 +949,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
self.sendMessageContext.currentInputMode = .text
|
||||
if hasFirstResponder(self) {
|
||||
view.deactivateInput()
|
||||
if self.sendMessageContext.inputMediaNode != nil {
|
||||
self.state?.updated(transition: .spring(duration: 0.4).withUserData(TextFieldComponent.AnimationHint(view: nil, kind: .textFocusChanged(isFocused: false))))
|
||||
}
|
||||
} else {
|
||||
self.state?.updated(transition: .spring(duration: 0.4).withUserData(TextFieldComponent.AnimationHint(view: nil, kind: .textFocusChanged(isFocused: false))))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue