Various improvements
This commit is contained in:
parent
e3ee0126b2
commit
b9b38e5fbf
87 changed files with 1337 additions and 498 deletions
|
|
@ -1134,6 +1134,51 @@ public enum StarsWithdrawalScreenSubject {
|
|||
case postSuggestionModification(current: CurrencyAmount, timestamp: Int32?, completion: (CurrencyAmount, Int32?) -> Void)
|
||||
}
|
||||
|
||||
public enum ChannelMembersSearchControllerMode {
|
||||
case promote
|
||||
case ban
|
||||
case inviteToCall
|
||||
}
|
||||
|
||||
public enum ChannelMembersSearchFilter {
|
||||
case exclude([EnginePeer.Id])
|
||||
case disable([EnginePeer.Id])
|
||||
case excludeNonMembers
|
||||
case excludeBots
|
||||
}
|
||||
|
||||
public final class ChannelMembersSearchControllerParams {
|
||||
public let context: AccountContext
|
||||
public let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||
public let peerId: EnginePeer.Id
|
||||
public let forceTheme: PresentationTheme?
|
||||
public let mode: ChannelMembersSearchControllerMode
|
||||
public let filters: [ChannelMembersSearchFilter]
|
||||
public let openPeer: (EnginePeer, RenderedChannelParticipant?) -> Void
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||
peerId: EnginePeer.Id,
|
||||
forceTheme: PresentationTheme? = nil,
|
||||
mode: ChannelMembersSearchControllerMode,
|
||||
filters: [ChannelMembersSearchFilter] = [],
|
||||
openPeer: @escaping (EnginePeer, RenderedChannelParticipant?) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.peerId = peerId
|
||||
self.forceTheme = forceTheme
|
||||
self.mode = mode
|
||||
self.filters = filters
|
||||
self.openPeer = openPeer
|
||||
}
|
||||
}
|
||||
|
||||
public protocol ChannelMembersSearchController: ViewController {
|
||||
var copyInviteLink: (() -> Void)? { get set }
|
||||
}
|
||||
|
||||
public protocol SharedAccountContext: AnyObject {
|
||||
var sharedContainerPath: String { get }
|
||||
var basePath: String { get }
|
||||
|
|
@ -1365,6 +1410,8 @@ public protocol SharedAccountContext: AnyObject {
|
|||
func makeBirthdaySuggestionScreen(context: AccountContext, peerId: EnginePeer.Id, completion: @escaping (TelegramBirthday) -> Void) -> ViewController
|
||||
func makeBirthdayAcceptSuggestionScreen(context: AccountContext, birthday: TelegramBirthday, settings: Promise<AccountPrivacySettings?>, openSettings: @escaping () -> Void, completion: @escaping (TelegramBirthday) -> Void) -> ViewController
|
||||
|
||||
func makeChannelMembersSearchController(params: ChannelMembersSearchControllerParams) -> ChannelMembersSearchController
|
||||
|
||||
func makeDebugSettingsController(context: AccountContext?) -> ViewController?
|
||||
|
||||
func openCreateGroupCallUI(context: AccountContext, peerIds: [EnginePeer.Id], parentController: ViewController)
|
||||
|
|
|
|||
|
|
@ -1156,6 +1156,11 @@ public final class AvatarNode: ASDisplayNode {
|
|||
|
||||
public let contentNode: ContentNode
|
||||
private var storyIndicator: ComponentView<Empty>?
|
||||
private var contentMaskView: UIView?
|
||||
private var storyIndicatorMaskView: UIView?
|
||||
private var liveBadgeMaskView: UIImageView?
|
||||
private var liveBadgeStoryIndicatorMaskView: UIImageView?
|
||||
private var liveBadgeView: UIImageView?
|
||||
public private(set) var storyPresentationParams: StoryPresentationParams?
|
||||
|
||||
private var loadingStatuses = Bag<Disposable>()
|
||||
|
|
@ -1164,22 +1169,26 @@ public final class AvatarNode: ASDisplayNode {
|
|||
public var totalCount: Int
|
||||
public var unseenCount: Int
|
||||
public var hasUnseenCloseFriendsItems: Bool
|
||||
public var hasLiveItems: Bool
|
||||
public var progress: Float?
|
||||
|
||||
public init(
|
||||
totalCount: Int,
|
||||
unseenCount: Int,
|
||||
hasUnseenCloseFriendsItems: Bool,
|
||||
hasLiveItems: Bool,
|
||||
progress: Float? = nil
|
||||
) {
|
||||
self.totalCount = totalCount
|
||||
self.unseenCount = unseenCount
|
||||
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
|
||||
self.hasLiveItems = hasLiveItems
|
||||
self.progress = progress
|
||||
}
|
||||
}
|
||||
|
||||
public private(set) var storyStats: StoryStats?
|
||||
public var displayLiveBadge: Bool = false
|
||||
|
||||
public var font: UIFont {
|
||||
get {
|
||||
|
|
@ -1459,6 +1468,7 @@ public final class AvatarNode: ASDisplayNode {
|
|||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||
hasUnseen: storyStats.unseenCount != 0,
|
||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriendsItems,
|
||||
hasLiveItems: storyStats.hasLiveItems,
|
||||
colors: AvatarStoryIndicatorComponent.Colors(
|
||||
unseenColors: storyPresentationParams.colors.unseenColors,
|
||||
unseenCloseFriendsColors: storyPresentationParams.colors.unseenCloseFriendsColors,
|
||||
|
|
@ -1494,6 +1504,124 @@ public final class AvatarNode: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.displayLiveBadge, let storyStats = self.storyStats, storyStats.hasLiveItems {
|
||||
let contentMaskView: UIView
|
||||
let storyIndicatorMaskView: UIView
|
||||
let liveBadgeMaskView: UIImageView
|
||||
let liveBadgeStoryIndicatorMaskView: UIImageView
|
||||
let liveBadgeView: UIImageView
|
||||
|
||||
var liveBadgeTransition = transition
|
||||
|
||||
if let current = self.contentMaskView {
|
||||
contentMaskView = current
|
||||
} else {
|
||||
liveBadgeTransition = liveBadgeTransition.withAnimation(.none)
|
||||
contentMaskView = UIView()
|
||||
contentMaskView.backgroundColor = .white
|
||||
if let filter = CALayer.luminanceToAlpha() {
|
||||
contentMaskView.layer.filters = [filter]
|
||||
}
|
||||
self.contentMaskView = contentMaskView
|
||||
self.contentNode.view.mask = contentMaskView
|
||||
}
|
||||
|
||||
if let current = self.storyIndicatorMaskView {
|
||||
storyIndicatorMaskView = current
|
||||
} else {
|
||||
storyIndicatorMaskView = UIView()
|
||||
storyIndicatorMaskView.backgroundColor = .white
|
||||
if let filter = CALayer.luminanceToAlpha() {
|
||||
storyIndicatorMaskView.layer.filters = [filter]
|
||||
}
|
||||
self.storyIndicatorMaskView = storyIndicatorMaskView
|
||||
self.storyIndicator?.view?.mask = storyIndicatorMaskView
|
||||
}
|
||||
|
||||
if let current = self.liveBadgeView {
|
||||
liveBadgeView = current
|
||||
} else {
|
||||
liveBadgeView = UIImageView()
|
||||
|
||||
//TODO:localize
|
||||
let liveString = NSAttributedString(string: "LIVE", font: Font.semibold(10.0), textColor: .white)
|
||||
let liveStringBounds = liveString.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
|
||||
let liveBadgeSize = CGSize(width: ceil(liveStringBounds.width) + 4.0 * 2.0, height: ceil(liveStringBounds.height) + 2.0 * 2.0)
|
||||
liveBadgeView.image = generateImage(liveBadgeSize, rotatedContext: { size, context in
|
||||
UIGraphicsPushContext(context)
|
||||
defer {
|
||||
UIGraphicsPopContext()
|
||||
}
|
||||
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(UIColor(rgb: 0xFF2D55).cgColor)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: size.height * 0.5).cgPath)
|
||||
context.fillPath()
|
||||
|
||||
liveString.draw(at: CGPoint(x: floorToScreenPixels((size.width - liveStringBounds.width) * 0.5), y: floorToScreenPixels((size.height - liveStringBounds.height) * 0.5)))
|
||||
})
|
||||
|
||||
self.view.addSubview(liveBadgeView)
|
||||
self.liveBadgeView = liveBadgeView
|
||||
}
|
||||
|
||||
if let current = self.liveBadgeMaskView {
|
||||
liveBadgeMaskView = current
|
||||
} else {
|
||||
liveBadgeMaskView = UIImageView()
|
||||
self.liveBadgeMaskView = liveBadgeMaskView
|
||||
contentMaskView.addSubview(liveBadgeMaskView)
|
||||
|
||||
if let image = liveBadgeView.image {
|
||||
liveBadgeMaskView.image = generateStretchableFilledCircleImage(diameter: image.size.height + 2.0 * 2.0, color: .black)
|
||||
}
|
||||
}
|
||||
|
||||
if let current = self.liveBadgeStoryIndicatorMaskView {
|
||||
liveBadgeStoryIndicatorMaskView = current
|
||||
} else {
|
||||
liveBadgeStoryIndicatorMaskView = UIImageView()
|
||||
self.liveBadgeStoryIndicatorMaskView = liveBadgeStoryIndicatorMaskView
|
||||
storyIndicatorMaskView.addSubview(liveBadgeStoryIndicatorMaskView)
|
||||
|
||||
if let image = liveBadgeView.image {
|
||||
liveBadgeStoryIndicatorMaskView.image = generateStretchableFilledCircleImage(diameter: image.size.height + 2.0 * 2.0, color: .black)
|
||||
}
|
||||
}
|
||||
|
||||
liveBadgeTransition.setFrame(view: contentMaskView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
liveBadgeTransition.setFrame(view: storyIndicatorMaskView, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -6.0, dy: -6.0))
|
||||
if let image = liveBadgeView.image {
|
||||
let badgeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - image.size.width) * 0.5), y: size.height + 5.0 - image.size.height), size: image.size)
|
||||
liveBadgeTransition.setFrame(view: liveBadgeView, frame: badgeFrame)
|
||||
liveBadgeTransition.setFrame(view: liveBadgeMaskView, frame: self.contentNode.view.convert(badgeFrame.insetBy(dx: -2.0, dy: -2.0), from: self.view))
|
||||
liveBadgeTransition.setFrame(view: liveBadgeStoryIndicatorMaskView, frame: badgeFrame.insetBy(dx: -2.0, dy: -2.0).offsetBy(dx: 1.0, dy: 0.0))
|
||||
}
|
||||
} else {
|
||||
if let contentMaskView = self.contentMaskView {
|
||||
self.contentMaskView = nil
|
||||
contentMaskView.removeFromSuperview()
|
||||
self.contentNode.view.mask = nil
|
||||
}
|
||||
if let storyIndicatorMaskView = self.storyIndicatorMaskView {
|
||||
self.storyIndicatorMaskView = nil
|
||||
storyIndicatorMaskView.removeFromSuperview()
|
||||
self.storyIndicator?.view?.mask = nil
|
||||
}
|
||||
if let liveBadgeMaskView = self.liveBadgeMaskView {
|
||||
self.liveBadgeMaskView = nil
|
||||
liveBadgeMaskView.removeFromSuperview()
|
||||
}
|
||||
if let liveBadgeStoryIndicatorMaskView = self.liveBadgeStoryIndicatorMaskView {
|
||||
self.liveBadgeStoryIndicatorMaskView = nil
|
||||
liveBadgeStoryIndicatorMaskView.removeFromSuperview()
|
||||
}
|
||||
if let liveBadgeView = self.liveBadgeView {
|
||||
self.liveBadgeView = nil
|
||||
liveBadgeView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func cancelLoading() {
|
||||
|
|
|
|||
|
|
@ -2251,7 +2251,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
stats: EngineChatList.StoryStats(
|
||||
totalCount: rawStoryArchiveSubscriptions.items.count,
|
||||
unseenCount: unseenCount,
|
||||
hasUnseenCloseFriends: hasUnseenCloseFriends
|
||||
hasUnseenCloseFriends: hasUnseenCloseFriends,
|
||||
hasLiveItems: false
|
||||
),
|
||||
hasUnseenCloseFriends: hasUnseenCloseFriends
|
||||
)
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
|||
animationCache: animationCache,
|
||||
animationRenderer: animationRenderer,
|
||||
storyStats: storyStats.flatMap { stats in
|
||||
return (stats.totalCount, unseen: stats.unseenCount, stats.hasUnseenCloseFriends)
|
||||
return (stats.totalCount, unseen: stats.unseenCount, stats.hasUnseenCloseFriends, hasLiveItems: stats.hasLiveItems)
|
||||
},
|
||||
openStories: { itemPeer, sourceNode in
|
||||
guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else {
|
||||
|
|
@ -829,7 +829,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
|||
}
|
||||
}
|
||||
}, arrowAction: nil, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer, storyStats: storyStats.flatMap { stats in
|
||||
return (stats.totalCount, stats.unseenCount, stats.hasUnseenCloseFriends)
|
||||
return (stats.totalCount, stats.unseenCount, stats.hasUnseenCloseFriends, stats.hasLiveItems)
|
||||
}, openStories: { itemPeer, sourceNode in
|
||||
guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else {
|
||||
return
|
||||
|
|
@ -1002,7 +1002,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
|||
}
|
||||
}
|
||||
}, arrowAction: nil, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer, storyStats: storyStats.flatMap { stats in
|
||||
return (stats.totalCount, stats.unseenCount, stats.hasUnseenCloseFriends)
|
||||
return (stats.totalCount, stats.unseenCount, stats.hasUnseenCloseFriends, stats.hasLiveItems)
|
||||
}, openStories: { itemPeer, sourceNode in
|
||||
guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else {
|
||||
return
|
||||
|
|
@ -1080,7 +1080,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
|||
peerContextAction(EnginePeer(peer.peer), .search(nil), node, gesture, location)
|
||||
}
|
||||
}, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer, storyStats: storyStats.flatMap { stats in
|
||||
return (stats.totalCount, stats.unseenCount, stats.hasUnseenCloseFriends)
|
||||
return (stats.totalCount, stats.unseenCount, stats.hasUnseenCloseFriends, stats.hasLiveItems)
|
||||
}, openStories: { itemPeer, sourceNode in
|
||||
guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else {
|
||||
return
|
||||
|
|
@ -1205,7 +1205,8 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
|||
stats: EngineChatList.StoryStats(
|
||||
totalCount: stats.totalCount,
|
||||
unseenCount: stats.unseenCount,
|
||||
hasUnseenCloseFriends: stats.hasUnseenCloseFriends
|
||||
hasUnseenCloseFriends: stats.hasUnseenCloseFriends,
|
||||
hasLiveItems: stats.hasLiveItems
|
||||
),
|
||||
hasUnseenCloseFriends: stats.hasUnseenCloseFriends
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1601,6 +1601,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
|
||||
self.avatarContainerNode = ASDisplayNode()
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||
self.avatarNode.displayLiveBadge = true
|
||||
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.isLayerBacked = true
|
||||
|
|
@ -1791,7 +1792,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||
return AvatarNode.StoryStats(
|
||||
totalCount: storyState.stats.totalCount,
|
||||
unseenCount: storyState.stats.unseenCount,
|
||||
hasUnseenCloseFriendsItems: storyState.hasUnseenCloseFriends
|
||||
hasUnseenCloseFriendsItems: storyState.hasUnseenCloseFriends,
|
||||
hasLiveItems: storyState.stats.hasLiveItems
|
||||
)
|
||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||
colors: AvatarNode.Colors(theme: item.presentationData.theme),
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
|
|||
let arrowAction: (() -> Void)?
|
||||
let animationCache: AnimationCache?
|
||||
let animationRenderer: MultiAnimationRenderer?
|
||||
let storyStats: (total: Int, unseen: Int, hasUnseenCloseFriends: Bool)?
|
||||
let storyStats: (total: Int, unseen: Int, hasUnseenCloseFriends: Bool, hasLiveItems: Bool)?
|
||||
let openStories: ((ContactsPeerItemPeer, ASDisplayNode) -> Void)?
|
||||
let adButtonAction: ((ASDisplayNode) -> Void)?
|
||||
let visibilityUpdated: ((Bool) -> Void)?
|
||||
|
|
@ -253,7 +253,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
|
|||
contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil, arrowAction: (() -> Void)? = nil,
|
||||
animationCache: AnimationCache? = nil,
|
||||
animationRenderer: MultiAnimationRenderer? = nil,
|
||||
storyStats: (total: Int, unseen: Int, hasUnseenCloseFriends: Bool)? = nil,
|
||||
storyStats: (total: Int, unseen: Int, hasUnseenCloseFriends: Bool, hasLiveItems: Bool)? = nil,
|
||||
openStories: ((ContactsPeerItemPeer, ASDisplayNode) -> Void)? = nil,
|
||||
adButtonAction: ((ASDisplayNode) -> Void)? = nil,
|
||||
visibilityUpdated: ((Bool) -> Void)? = nil
|
||||
|
|
@ -1286,7 +1286,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||
return AvatarNode.StoryStats(
|
||||
totalCount: stats.total,
|
||||
unseenCount: stats.unseen,
|
||||
hasUnseenCloseFriendsItems: stats.hasUnseenCloseFriends
|
||||
hasUnseenCloseFriendsItems: stats.hasUnseenCloseFriends,
|
||||
hasLiveItems: stats.hasLiveItems
|
||||
)
|
||||
},
|
||||
presentationParams: AvatarNode.StoryPresentationParams(
|
||||
|
|
|
|||
|
|
@ -1233,6 +1233,9 @@ public class Window1 {
|
|||
let updatedInputOffset = inputHeightOffsetForLayout(self.windowLayout)
|
||||
if !previousInputOffset.isEqual(to: updatedInputOffset) {
|
||||
let hide = updatingLayout.transition.isAnimated && updatingLayout.layout.upperKeyboardInputPositionBound == updatingLayout.layout.size.height
|
||||
if hide {
|
||||
print("hide with \(updatingLayout.transition)")
|
||||
}
|
||||
self.keyboardManager?.updateInteractiveInputOffset(updatedInputOffset, transition: updatingLayout.transition, completion: { [weak self] in
|
||||
if let strongSelf = self, hide {
|
||||
strongSelf.updateLayout {
|
||||
|
|
@ -1370,8 +1373,14 @@ public class Window1 {
|
|||
}
|
||||
|
||||
if canDismiss, let inputHeight = self.windowLayout.inputHeight, currentLocation.y + (self.keyboardGestureAccessoryHeight ?? 0.0) > self.windowLayout.size.height - inputHeight {
|
||||
let springDuration: CGFloat
|
||||
if #available(iOS 26.0, *) {
|
||||
springDuration = 0.3832
|
||||
} else {
|
||||
springDuration = 0.25
|
||||
}
|
||||
self.updateLayout {
|
||||
$0.update(upperKeyboardInputPositionBound: self.windowLayout.size.height, transition: .animated(duration: 0.25, curve: .spring), overrideTransition: false)
|
||||
$0.update(upperKeyboardInputPositionBound: self.windowLayout.size.height, transition: .animated(duration: springDuration, curve: .spring), overrideTransition: false)
|
||||
}
|
||||
} else {
|
||||
self.updateLayout {
|
||||
|
|
|
|||
|
|
@ -1749,7 +1749,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||
return AvatarNode.StoryStats(
|
||||
totalCount: storyStats.totalCount,
|
||||
unseenCount: storyStats.unseenCount,
|
||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends,
|
||||
hasLiveItems: storyStats.hasLiveItems
|
||||
)
|
||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||
colors: AvatarNode.Colors(theme: item.presentationData.theme),
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ private final class CreateExternalMediaStreamScreenComponent: CombinedComponent
|
|||
if let credentialsPromise = credentialsPromise {
|
||||
credentialsSignal = credentialsPromise.get()
|
||||
} else {
|
||||
credentialsSignal = context.engine.calls.getGroupCallStreamCredentials(peerId: peerId, revokePreviousCredentials: false)
|
||||
credentialsSignal = context.engine.calls.getGroupCallStreamCredentials(peerId: peerId, isLiveStream: false, revokePreviousCredentials: false)
|
||||
|> `catch` { _ -> Signal<GroupCallStreamCredentials, NoError> in
|
||||
return .never()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -754,7 +754,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
|||
|> deliverOnMainQueue).start(next: { peerView in
|
||||
updateState { current in
|
||||
var dismissController: (() -> Void)?
|
||||
let controller = ChannelMembersSearchController(context: context, peerId: peerId, mode: .promote, filters: [], openPeer: { peer, participant in
|
||||
let controller = ChannelMembersSearchControllerImpl(params: ChannelMembersSearchControllerParams(context: context, peerId: peerId, mode: .promote, filters: [], openPeer: { peer, participant in
|
||||
dismissController?()
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
if peer.id == context.account.peerId {
|
||||
|
|
@ -784,7 +784,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
|||
}
|
||||
pushControllerImpl?(channelAdminController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, adminId: peer.id, initialParticipant: participant?.participant, updated: { _ in
|
||||
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership))
|
||||
})
|
||||
}))
|
||||
dismissController = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ public func channelBlacklistController(context: AccountContext, updatedPresentat
|
|||
}
|
||||
}, addPeer: {
|
||||
var dismissController: (() -> Void)?
|
||||
let controller = ChannelMembersSearchController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, mode: .ban, openPeer: { peer, participant in
|
||||
let controller = ChannelMembersSearchControllerImpl(params: ChannelMembersSearchControllerParams(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, mode: .ban, openPeer: { peer, participant in
|
||||
if let participant = participant {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
switch participant.participant {
|
||||
|
|
@ -329,7 +329,7 @@ public func channelBlacklistController(context: AccountContext, updatedPresentat
|
|||
dismissController?()
|
||||
}))
|
||||
})
|
||||
})
|
||||
}))
|
||||
dismissController = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,20 +7,7 @@ import TelegramPresentationData
|
|||
import AccountContext
|
||||
import SearchUI
|
||||
|
||||
public enum ChannelMembersSearchControllerMode {
|
||||
case promote
|
||||
case ban
|
||||
case inviteToCall
|
||||
}
|
||||
|
||||
public enum ChannelMembersSearchFilter {
|
||||
case exclude([EnginePeer.Id])
|
||||
case disable([EnginePeer.Id])
|
||||
case excludeNonMembers
|
||||
case excludeBots
|
||||
}
|
||||
|
||||
public final class ChannelMembersSearchController: ViewController {
|
||||
public final class ChannelMembersSearchControllerImpl: ViewController, ChannelMembersSearchController {
|
||||
private let queue = Queue()
|
||||
|
||||
private let context: AccountContext
|
||||
|
|
@ -43,15 +30,15 @@ public final class ChannelMembersSearchController: ViewController {
|
|||
|
||||
private var searchContentNode: NavigationBarSearchContentNode?
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, forceTheme: PresentationTheme? = nil, mode: ChannelMembersSearchControllerMode, filters: [ChannelMembersSearchFilter] = [], openPeer: @escaping (EnginePeer, RenderedChannelParticipant?) -> Void) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.mode = mode
|
||||
self.openPeer = openPeer
|
||||
self.filters = filters
|
||||
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.forceTheme = forceTheme
|
||||
if let forceTheme = forceTheme {
|
||||
public init(params: ChannelMembersSearchControllerParams) {
|
||||
self.context = params.context
|
||||
self.peerId = params.peerId
|
||||
self.mode = params.mode
|
||||
self.openPeer = params.openPeer
|
||||
self.filters = params.filters
|
||||
self.presentationData = params.updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.forceTheme = params.forceTheme
|
||||
if let forceTheme = params.forceTheme {
|
||||
self.presentationData = self.presentationData.withUpdated(theme: forceTheme)
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +66,7 @@ public final class ChannelMembersSearchController: ViewController {
|
|||
})
|
||||
self.navigationBar?.setContentNode(self.searchContentNode, animated: false)
|
||||
|
||||
self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|
||||
self.presentationDataDisposable = ((params.updatedPresentationData?.signal ?? params.context.sharedContext.presentationData)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
|
@ -88,7 +75,7 @@ public final class ChannelMembersSearchController: ViewController {
|
|||
strongSelf.controllerNode.updatePresentationData(presentationData)
|
||||
})
|
||||
|
||||
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|
||||
let _ = (params.context.account.postbox.loadedPeerWithId(peerId)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self {
|
||||
|
|
|
|||
|
|
@ -1027,7 +1027,7 @@ public func channelPermissionsController(context: AccountContext, updatedPresent
|
|||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { peerId, _ in
|
||||
var dismissController: (() -> Void)?
|
||||
let controller = ChannelMembersSearchController(context: context, peerId: peerId, mode: .ban, filters: [.disable([context.account.peerId])], openPeer: { peer, participant in
|
||||
let controller = ChannelMembersSearchControllerImpl(params: ChannelMembersSearchControllerParams(context: context, peerId: peerId, mode: .ban, filters: [.disable([context.account.peerId])], openPeer: { peer, participant in
|
||||
if let participant = participant {
|
||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
switch participant.participant {
|
||||
|
|
@ -1048,7 +1048,7 @@ public func channelPermissionsController(context: AccountContext, updatedPresent
|
|||
upgradedToSupergroupImpl?(upgradedPeerId, f)
|
||||
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
})
|
||||
})
|
||||
}))
|
||||
dismissController = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -282,11 +282,13 @@ public struct PeerStoryStats: Equatable {
|
|||
public var totalCount: Int
|
||||
public var unseenCount: Int
|
||||
public var hasUnseenCloseFriends: Bool
|
||||
public var hasLiveItems: Bool
|
||||
|
||||
public init(totalCount: Int, unseenCount: Int, hasUnseenCloseFriends: Bool) {
|
||||
public init(totalCount: Int, unseenCount: Int, hasUnseenCloseFriends: Bool, hasLiveItems: Bool) {
|
||||
self.totalCount = totalCount
|
||||
self.unseenCount = unseenCount
|
||||
self.hasUnseenCloseFriends = hasUnseenCloseFriends
|
||||
self.hasLiveItems = hasLiveItems
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -305,9 +307,9 @@ func fetchPeerStoryStats(postbox: PostboxImpl, peerId: PeerId) -> PeerStoryStats
|
|||
|
||||
if topItems.isExact {
|
||||
let stats = postbox.storyItemsTable.getStats(peerId: peerId, maxSeenId: maxSeenId)
|
||||
return PeerStoryStats(totalCount: stats.total, unseenCount: stats.unseen, hasUnseenCloseFriends: stats.hasUnseenCloseFriends)
|
||||
return PeerStoryStats(totalCount: stats.total, unseenCount: stats.unseen, hasUnseenCloseFriends: stats.hasUnseenCloseFriends, hasLiveItems: stats.hasLiveItems)
|
||||
} else {
|
||||
return PeerStoryStats(totalCount: 1, unseenCount: topItems.id > maxSeenId ? 1 : 0, hasUnseenCloseFriends: false)
|
||||
return PeerStoryStats(totalCount: 1, unseenCount: topItems.id > maxSeenId ? 1 : 0, hasUnseenCloseFriends: false, hasLiveItems: false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,17 +5,20 @@ public final class StoryItemsTableEntry: Equatable {
|
|||
public let id: Int32
|
||||
public let expirationTimestamp: Int32?
|
||||
public let isCloseFriends: Bool
|
||||
public let isLiveStream: Bool
|
||||
|
||||
public init(
|
||||
value: CodableEntry,
|
||||
id: Int32,
|
||||
expirationTimestamp: Int32?,
|
||||
isCloseFriends: Bool
|
||||
isCloseFriends: Bool,
|
||||
isLiveStream: Bool
|
||||
) {
|
||||
self.value = value
|
||||
self.id = id
|
||||
self.expirationTimestamp = expirationTimestamp
|
||||
self.isCloseFriends = isCloseFriends
|
||||
self.isLiveStream = isLiveStream
|
||||
}
|
||||
|
||||
public static func ==(lhs: StoryItemsTableEntry, rhs: StoryItemsTableEntry) -> Bool {
|
||||
|
|
@ -34,6 +37,9 @@ public final class StoryItemsTableEntry: Equatable {
|
|||
if lhs.isCloseFriends != rhs.isCloseFriends {
|
||||
return false
|
||||
}
|
||||
if lhs.isLiveStream != rhs.isLiveStream {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -139,10 +145,11 @@ final class StoryItemsTable: Table {
|
|||
return key.successor
|
||||
}
|
||||
|
||||
public func getStats(peerId: PeerId, maxSeenId: Int32) -> (total: Int, unseen: Int, hasUnseenCloseFriends: Bool) {
|
||||
public func getStats(peerId: PeerId, maxSeenId: Int32) -> (total: Int, unseen: Int, hasUnseenCloseFriends: Bool, hasLiveItems: Bool) {
|
||||
var total = 0
|
||||
var unseen = 0
|
||||
var hasUnseenCloseFriends = false
|
||||
var hasLiveItems = false
|
||||
|
||||
self.valueBox.range(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId), values: { key, value in
|
||||
let id = key.getInt32(8)
|
||||
|
|
@ -152,6 +159,7 @@ final class StoryItemsTable: Table {
|
|||
unseen += 1
|
||||
|
||||
var isCloseFriends = false
|
||||
var isLiveStream = false
|
||||
let readBuffer = ReadBuffer(data: value.makeData())
|
||||
var magic: UInt32 = 0
|
||||
readBuffer.read(&magic, offset: 0, length: 4)
|
||||
|
|
@ -168,6 +176,7 @@ final class StoryItemsTable: Table {
|
|||
var flags: UInt8 = 0
|
||||
readBuffer.read(&flags, offset: 0, length: 1)
|
||||
isCloseFriends = (flags & (1 << 0)) != 0
|
||||
isLiveStream = (flags & (1 << 1)) != 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -180,12 +189,15 @@ final class StoryItemsTable: Table {
|
|||
if isCloseFriends {
|
||||
hasUnseenCloseFriends = true
|
||||
}
|
||||
if isLiveStream {
|
||||
hasLiveItems = true
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}, limit: 10000)
|
||||
|
||||
return (total, unseen, hasUnseenCloseFriends)
|
||||
return (total, unseen, hasUnseenCloseFriends, hasLiveItems)
|
||||
}
|
||||
|
||||
public func get(peerId: PeerId) -> [StoryItemsTableEntry] {
|
||||
|
|
@ -199,6 +211,7 @@ final class StoryItemsTable: Table {
|
|||
let entry: CodableEntry
|
||||
var expirationTimestamp: Int32?
|
||||
var isCloseFriends = false
|
||||
var isLiveStream = false
|
||||
|
||||
let readBuffer = ReadBuffer(data: value.makeData())
|
||||
var magic: UInt32 = 0
|
||||
|
|
@ -234,6 +247,7 @@ final class StoryItemsTable: Table {
|
|||
var flags: UInt8 = 0
|
||||
readBuffer.read(&flags, offset: 0, length: 1)
|
||||
isCloseFriends = (flags & (1 << 0)) != 0
|
||||
isLiveStream = (flags & (1 << 1)) != 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -245,7 +259,7 @@ final class StoryItemsTable: Table {
|
|||
entry = CodableEntry(data: value.makeData())
|
||||
}
|
||||
|
||||
result.append(StoryItemsTableEntry(value: entry, id: id, expirationTimestamp: expirationTimestamp, isCloseFriends: isCloseFriends))
|
||||
result.append(StoryItemsTableEntry(value: entry, id: id, expirationTimestamp: expirationTimestamp, isCloseFriends: isCloseFriends, isLiveStream: isLiveStream))
|
||||
|
||||
return true
|
||||
}, limit: 10000)
|
||||
|
|
@ -386,6 +400,9 @@ final class StoryItemsTable: Table {
|
|||
if entry.isCloseFriends {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
if entry.isLiveStream {
|
||||
flags |= (1 << 1)
|
||||
}
|
||||
buffer.write(&flags, length: 1)
|
||||
|
||||
self.valueBox.set(self.table, key: self.key(Key(peerId: peerId, id: entry.id)), value: buffer.readBufferNoCopy())
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ private final class AvatarComponent: Component {
|
|||
AvatarStoryIndicatorComponent(
|
||||
hasUnseen: true,
|
||||
hasUnseenCloseFriendsItems: false,
|
||||
hasLiveItems: false,
|
||||
colors: AvatarStoryIndicatorComponent.Colors(unseenColors: colors, unseenCloseFriendsColors: colors, seenColors: colors),
|
||||
activeLineWidth: 3.0,
|
||||
inactiveLineWidth: 3.0,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,14 @@ private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat, shado
|
|||
|
||||
|
||||
final class ReactionContextBackgroundNode: ASDisplayNode {
|
||||
struct GlassParams {
|
||||
var isTinted: Bool
|
||||
|
||||
init(isTinted: Bool) {
|
||||
self.isTinted = isTinted
|
||||
}
|
||||
}
|
||||
|
||||
private let largeCircleSize: CGFloat
|
||||
private let smallCircleSize: CGFloat
|
||||
|
||||
|
|
@ -59,14 +67,16 @@ final class ReactionContextBackgroundNode: ASDisplayNode {
|
|||
private let smallCircleLayer: SimpleLayer
|
||||
private let smallCircleShadowLayer: SimpleLayer
|
||||
|
||||
private let glass: GlassParams?
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
init(glass: Bool, largeCircleSize: CGFloat, smallCircleSize: CGFloat, maskNode: ASDisplayNode) {
|
||||
init(glass: GlassParams?, largeCircleSize: CGFloat, smallCircleSize: CGFloat, maskNode: ASDisplayNode) {
|
||||
self.glass = glass
|
||||
self.largeCircleSize = largeCircleSize
|
||||
self.smallCircleSize = smallCircleSize
|
||||
|
||||
self.backgroundView = BlurredBackgroundView(color: nil, enableBlur: true)
|
||||
if glass {
|
||||
if glass != nil {
|
||||
self.glassBackgroundView = GlassBackgroundView()
|
||||
} else {
|
||||
self.glassBackgroundView = nil
|
||||
|
|
@ -237,7 +247,13 @@ final class ReactionContextBackgroundNode: ASDisplayNode {
|
|||
var glassBackgroundFrame = contentBounds.insetBy(dx: 10.0, dy: 10.0)
|
||||
glassBackgroundFrame.size.height -= 8.0
|
||||
transition.updateFrame(view: glassBackgroundView, frame: glassBackgroundFrame, beginWithCurrentState: true)
|
||||
glassBackgroundView.update(size: glassBackgroundFrame.size, cornerRadius: 23.0, isDark: true, tintColor: .init(kind: .custom, color: UIColor(rgb: 0x25272e, alpha: 0.72)), transition: ComponentTransition(transition))
|
||||
let glassTintColor: GlassBackgroundView.TintColor
|
||||
if let glass = self.glass, glass.isTinted {
|
||||
glassTintColor = .init(kind: .custom, color: UIColor(rgb: 0x25272e, alpha: 0.72))
|
||||
} else {
|
||||
glassTintColor = .init(kind: .panel, color: defaultDarkPresentationTheme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
|
||||
}
|
||||
glassBackgroundView.update(size: glassBackgroundFrame.size, cornerRadius: 23.0, isDark: true, tintColor: glassTintColor, transition: ComponentTransition(transition))
|
||||
|
||||
transition.updateFrame(view: self.backgroundTintView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentBounds.width, height: contentBounds.height)).insetBy(dx: -10.0, dy: -10.0))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -489,7 +489,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
|||
|
||||
public enum Style {
|
||||
case legacy
|
||||
case glass
|
||||
case glass(isTinted: Bool)
|
||||
}
|
||||
|
||||
public init(context: AccountContext, animationCache: AnimationCache, presentationData: PresentationData, style: Style = .legacy, items: [ReactionContextItem], selectedItems: Set<AnyHashable>, title: String? = nil, reactionsLocked: Bool, alwaysAllowPremiumReactions: Bool, allPresetReactionsAreAvailable: Bool, getEmojiContent: ((AnimationCache, MultiAnimationRenderer) -> Signal<EmojiPagerContentComponent, NoError>)?, isExpandedUpdated: @escaping (ContainedViewLayoutTransition) -> Void, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, requestUpdateOverlayWantsToBeBelowKeyboard: @escaping (ContainedViewLayoutTransition) -> Void) {
|
||||
|
|
@ -508,7 +508,11 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
|||
(self.animationRenderer as? MultiAnimationRendererImpl)?.useYuvA = context.sharedContext.immediateExperimentalUISettings.compressedEmojiCache
|
||||
|
||||
self.backgroundMaskNode = ASDisplayNode()
|
||||
self.backgroundNode = ReactionContextBackgroundNode(glass: style == .glass, largeCircleSize: largeCircleSize, smallCircleSize: smallCircleSize, maskNode: self.backgroundMaskNode)
|
||||
var backgroundGlassParams: ReactionContextBackgroundNode.GlassParams?
|
||||
if case let .glass(isTinted) = style {
|
||||
backgroundGlassParams = ReactionContextBackgroundNode.GlassParams(isTinted: isTinted)
|
||||
}
|
||||
self.backgroundNode = ReactionContextBackgroundNode(glass: backgroundGlassParams, largeCircleSize: largeCircleSize, smallCircleSize: smallCircleSize, maskNode: self.backgroundMaskNode)
|
||||
self.leftBackgroundMaskNode = ASDisplayNode()
|
||||
self.leftBackgroundMaskNode.backgroundColor = .black
|
||||
self.rightBackgroundMaskNode = ASDisplayNode()
|
||||
|
|
|
|||
|
|
@ -661,6 +661,7 @@ final class StatsMessageItemNode: ListViewItemNode, ItemListItemNode {
|
|||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||
hasUnseen: true,
|
||||
hasUnseenCloseFriendsItems: false,
|
||||
hasLiveItems: false,
|
||||
colors: AvatarStoryIndicatorComponent.Colors(
|
||||
unseenColors: item.presentationData.theme.chatList.storyUnseenColors.array,
|
||||
unseenCloseFriendsColors: item.presentationData.theme.chatList.storyUnseenPrivateColors.array,
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ final class StoryIconNode: ASDisplayNode {
|
|||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||
hasUnseen: true,
|
||||
hasUnseenCloseFriendsItems: false,
|
||||
hasLiveItems: false,
|
||||
colors: AvatarStoryIndicatorComponent.Colors(
|
||||
unseenColors: theme.chatList.storyUnseenColors.array,
|
||||
unseenCloseFriendsColors: theme.chatList.storyUnseenPrivateColors.array,
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-29248689] = { return Api.GlobalPrivacySettings.parse_globalPrivacySettings($0) }
|
||||
dict[1429932961] = { return Api.GroupCall.parse_groupCall($0) }
|
||||
dict[2004925620] = { return Api.GroupCall.parse_groupCallDiscarded($0) }
|
||||
dict[-341428482] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
|
||||
dict[708691884] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
|
||||
dict[1735736008] = { return Api.GroupCallParticipantVideo.parse_groupCallParticipantVideo($0) }
|
||||
dict[-592373577] = { return Api.GroupCallParticipantVideoSourceGroup.parse_groupCallParticipantVideoSourceGroup($0) }
|
||||
dict[-2132064081] = { return Api.GroupCallStreamChannel.parse_groupCallStreamChannel($0) }
|
||||
|
|
@ -355,7 +355,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1968737087] = { return Api.InputClientProxy.parse_inputClientProxy($0) }
|
||||
dict[-1562241884] = { return Api.InputCollectible.parse_inputCollectiblePhone($0) }
|
||||
dict[-476815191] = { return Api.InputCollectible.parse_inputCollectibleUsername($0) }
|
||||
dict[-208488460] = { return Api.InputContact.parse_inputPhoneContact($0) }
|
||||
dict[1780335806] = { return Api.InputContact.parse_inputPhoneContact($0) }
|
||||
dict[-55902537] = { return Api.InputDialogPeer.parse_inputDialogPeer($0) }
|
||||
dict[1684014375] = { return Api.InputDialogPeer.parse_inputDialogPeerFolder($0) }
|
||||
dict[448771445] = { return Api.InputDocument.parse_inputDocument($0) }
|
||||
|
|
@ -562,7 +562,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) }
|
||||
dict[1235637404] = { return Api.MediaArea.parse_mediaAreaWeather($0) }
|
||||
dict[-808853502] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) }
|
||||
dict[-1743401272] = { return Api.Message.parse_message($0) }
|
||||
dict[-1188071729] = { return Api.Message.parse_message($0) }
|
||||
dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) }
|
||||
dict[2055212554] = { return Api.Message.parse_messageService($0) }
|
||||
dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($0) }
|
||||
|
|
@ -664,6 +664,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1974226924] = { return Api.MessageMedia.parse_messageMediaToDo($0) }
|
||||
dict[-1618676578] = { return Api.MessageMedia.parse_messageMediaUnsupported($0) }
|
||||
dict[784356159] = { return Api.MessageMedia.parse_messageMediaVenue($0) }
|
||||
dict[1059290001] = { return Api.MessageMedia.parse_messageMediaVideoStream($0) }
|
||||
dict[-571405253] = { return Api.MessageMedia.parse_messageMediaWebPage($0) }
|
||||
dict[-1938180548] = { return Api.MessagePeerReaction.parse_messagePeerReaction($0) }
|
||||
dict[-1228133028] = { return Api.MessagePeerVote.parse_messagePeerVote($0) }
|
||||
|
|
@ -1107,11 +1108,11 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-451831443] = { return Api.Update.parse_updateFavedStickers($0) }
|
||||
dict[422972864] = { return Api.Update.parse_updateFolderPeers($0) }
|
||||
dict[-2027964103] = { return Api.Update.parse_updateGeoLiveViewed($0) }
|
||||
dict[-1747565759] = { return Api.Update.parse_updateGroupCall($0) }
|
||||
dict[-1658710304] = { return Api.Update.parse_updateGroupCall($0) }
|
||||
dict[-1535694705] = { return Api.Update.parse_updateGroupCallChainBlocks($0) }
|
||||
dict[192428418] = { return Api.Update.parse_updateGroupCallConnection($0) }
|
||||
dict[-917002394] = { return Api.Update.parse_updateGroupCallEncryptedMessage($0) }
|
||||
dict[2026050784] = { return Api.Update.parse_updateGroupCallMessage($0) }
|
||||
dict[-964095818] = { return Api.Update.parse_updateGroupCallMessage($0) }
|
||||
dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) }
|
||||
dict[1763610706] = { return Api.Update.parse_updateInlineBotCallbackQuery($0) }
|
||||
dict[1442983757] = { return Api.Update.parse_updateLangPack($0) }
|
||||
|
|
|
|||
|
|
@ -60,15 +60,15 @@ public extension Api {
|
|||
}
|
||||
public extension Api {
|
||||
indirect enum Message: TypeConstructorDescription {
|
||||
case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?, effect: Int64?, factcheck: Api.FactCheck?, reportDeliveryUntilDate: Int32?, paidMessageStars: Int64?, suggestedPost: Api.SuggestedPost?)
|
||||
case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?, effect: Int64?, factcheck: Api.FactCheck?, reportDeliveryUntilDate: Int32?, paidMessageStars: Int64?, suggestedPost: Api.SuggestedPost?, scheduleRepeatPeriod: Int32?)
|
||||
case messageEmpty(flags: Int32, id: Int32, peerId: Api.Peer?)
|
||||
case messageService(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, savedPeerId: Api.Peer?, replyTo: Api.MessageReplyHeader?, date: Int32, action: Api.MessageAction, reactions: Api.MessageReactions?, ttlPeriod: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck, let reportDeliveryUntilDate, let paidMessageStars, let suggestedPost):
|
||||
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck, let reportDeliveryUntilDate, let paidMessageStars, let suggestedPost, let scheduleRepeatPeriod):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1743401272)
|
||||
buffer.appendInt32(-1188071729)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(flags2, buffer: buffer, boxed: false)
|
||||
|
|
@ -109,6 +109,7 @@ public extension Api {
|
|||
if Int(flags2) & Int(1 << 5) != 0 {serializeInt32(reportDeliveryUntilDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags2) & Int(1 << 6) != 0 {serializeInt64(paidMessageStars!, buffer: buffer, boxed: false)}
|
||||
if Int(flags2) & Int(1 << 7) != 0 {suggestedPost!.serialize(buffer, true)}
|
||||
if Int(flags2) & Int(1 << 10) != 0 {serializeInt32(scheduleRepeatPeriod!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .messageEmpty(let flags, let id, let peerId):
|
||||
if boxed {
|
||||
|
|
@ -138,8 +139,8 @@ public extension Api {
|
|||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck, let reportDeliveryUntilDate, let paidMessageStars, let suggestedPost):
|
||||
return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any), ("effect", effect as Any), ("factcheck", factcheck as Any), ("reportDeliveryUntilDate", reportDeliveryUntilDate as Any), ("paidMessageStars", paidMessageStars as Any), ("suggestedPost", suggestedPost as Any)])
|
||||
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck, let reportDeliveryUntilDate, let paidMessageStars, let suggestedPost, let scheduleRepeatPeriod):
|
||||
return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any), ("effect", effect as Any), ("factcheck", factcheck as Any), ("reportDeliveryUntilDate", reportDeliveryUntilDate as Any), ("paidMessageStars", paidMessageStars as Any), ("suggestedPost", suggestedPost as Any), ("scheduleRepeatPeriod", scheduleRepeatPeriod as Any)])
|
||||
case .messageEmpty(let flags, let id, let peerId):
|
||||
return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)])
|
||||
case .messageService(let flags, let id, let fromId, let peerId, let savedPeerId, let replyTo, let date, let action, let reactions, let ttlPeriod):
|
||||
|
|
@ -236,6 +237,8 @@ public extension Api {
|
|||
if Int(_2!) & Int(1 << 7) != 0 {if let signature = reader.readInt32() {
|
||||
_31 = Api.parse(reader, signature: signature) as? Api.SuggestedPost
|
||||
} }
|
||||
var _32: Int32?
|
||||
if Int(_2!) & Int(1 << 10) != 0 {_32 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
|
|
@ -267,8 +270,9 @@ public extension Api {
|
|||
let _c29 = (Int(_2!) & Int(1 << 5) == 0) || _29 != nil
|
||||
let _c30 = (Int(_2!) & Int(1 << 6) == 0) || _30 != nil
|
||||
let _c31 = (Int(_2!) & Int(1 << 7) == 0) || _31 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 {
|
||||
return Api.Message.message(flags: _1!, flags2: _2!, id: _3!, fromId: _4, fromBoostsApplied: _5, peerId: _6!, savedPeerId: _7, fwdFrom: _8, viaBotId: _9, viaBusinessBotId: _10, replyTo: _11, date: _12!, message: _13!, media: _14, replyMarkup: _15, entities: _16, views: _17, forwards: _18, replies: _19, editDate: _20, postAuthor: _21, groupedId: _22, reactions: _23, restrictionReason: _24, ttlPeriod: _25, quickReplyShortcutId: _26, effect: _27, factcheck: _28, reportDeliveryUntilDate: _29, paidMessageStars: _30, suggestedPost: _31)
|
||||
let _c32 = (Int(_2!) & Int(1 << 10) == 0) || _32 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 {
|
||||
return Api.Message.message(flags: _1!, flags2: _2!, id: _3!, fromId: _4, fromBoostsApplied: _5, peerId: _6!, savedPeerId: _7, fwdFrom: _8, viaBotId: _9, viaBusinessBotId: _10, replyTo: _11, date: _12!, message: _13!, media: _14, replyMarkup: _15, entities: _16, views: _17, forwards: _18, replies: _19, editDate: _20, postAuthor: _21, groupedId: _22, reactions: _23, restrictionReason: _24, ttlPeriod: _25, quickReplyShortcutId: _26, effect: _27, factcheck: _28, reportDeliveryUntilDate: _29, paidMessageStars: _30, suggestedPost: _31, scheduleRepeatPeriod: _32)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -725,6 +725,7 @@ public extension Api {
|
|||
case messageMediaToDo(flags: Int32, todo: Api.TodoList, completions: [Api.TodoCompletion]?)
|
||||
case messageMediaUnsupported
|
||||
case messageMediaVenue(geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String)
|
||||
case messageMediaVideoStream(call: Api.InputGroupCall)
|
||||
case messageMediaWebPage(flags: Int32, webpage: Api.WebPage)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
|
|
@ -908,6 +909,12 @@ public extension Api {
|
|||
serializeString(venueId, buffer: buffer, boxed: false)
|
||||
serializeString(venueType, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .messageMediaVideoStream(let call):
|
||||
if boxed {
|
||||
buffer.appendInt32(1059290001)
|
||||
}
|
||||
call.serialize(buffer, true)
|
||||
break
|
||||
case .messageMediaWebPage(let flags, let webpage):
|
||||
if boxed {
|
||||
buffer.appendInt32(-571405253)
|
||||
|
|
@ -954,6 +961,8 @@ public extension Api {
|
|||
return ("messageMediaUnsupported", [])
|
||||
case .messageMediaVenue(let geo, let title, let address, let provider, let venueId, let venueType):
|
||||
return ("messageMediaVenue", [("geo", geo as Any), ("title", title as Any), ("address", address as Any), ("provider", provider as Any), ("venueId", venueId as Any), ("venueType", venueType as Any)])
|
||||
case .messageMediaVideoStream(let call):
|
||||
return ("messageMediaVideoStream", [("call", call as Any)])
|
||||
case .messageMediaWebPage(let flags, let webpage):
|
||||
return ("messageMediaWebPage", [("flags", flags as Any), ("webpage", webpage as Any)])
|
||||
}
|
||||
|
|
@ -1329,6 +1338,19 @@ public extension Api {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageMediaVideoStream(_ reader: BufferReader) -> MessageMedia? {
|
||||
var _1: Api.InputGroupCall?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.MessageMedia.messageMediaVideoStream(call: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageMediaWebPage(_ reader: BufferReader) -> MessageMedia? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
|
|
|
|||
|
|
@ -604,11 +604,11 @@ public extension Api {
|
|||
case updateFavedStickers
|
||||
case updateFolderPeers(folderPeers: [Api.FolderPeer], pts: Int32, ptsCount: Int32)
|
||||
case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32)
|
||||
case updateGroupCall(flags: Int32, chatId: Int64?, call: Api.GroupCall)
|
||||
case updateGroupCall(flags: Int32, peer: Api.Peer?, call: Api.GroupCall)
|
||||
case updateGroupCallChainBlocks(call: Api.InputGroupCall, subChainId: Int32, blocks: [Buffer], nextOffset: Int32)
|
||||
case updateGroupCallConnection(flags: Int32, params: Api.DataJSON)
|
||||
case updateGroupCallEncryptedMessage(call: Api.InputGroupCall, fromId: Api.Peer, encryptedMessage: Buffer)
|
||||
case updateGroupCallMessage(call: Api.InputGroupCall, fromId: Api.Peer, randomId: Int64, message: Api.TextWithEntities)
|
||||
case updateGroupCallMessage(flags: Int32, call: Api.InputGroupCall, fromId: Api.Peer, randomId: Int64, message: Api.TextWithEntities, paidMessageStars: Int64?)
|
||||
case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32)
|
||||
case updateInlineBotCallbackQuery(flags: Int32, queryId: Int64, userId: Int64, msgId: Api.InputBotInlineMessageID, chatInstance: Int64, data: Buffer?, gameShortName: String?)
|
||||
case updateLangPack(difference: Api.LangPackDifference)
|
||||
|
|
@ -1271,12 +1271,12 @@ public extension Api {
|
|||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateGroupCall(let flags, let chatId, let call):
|
||||
case .updateGroupCall(let flags, let peer, let call):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1747565759)
|
||||
buffer.appendInt32(-1658710304)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(chatId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {peer!.serialize(buffer, true)}
|
||||
call.serialize(buffer, true)
|
||||
break
|
||||
case .updateGroupCallChainBlocks(let call, let subChainId, let blocks, let nextOffset):
|
||||
|
|
@ -1307,14 +1307,16 @@ public extension Api {
|
|||
fromId.serialize(buffer, true)
|
||||
serializeBytes(encryptedMessage, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateGroupCallMessage(let call, let fromId, let randomId, let message):
|
||||
case .updateGroupCallMessage(let flags, let call, let fromId, let randomId, let message, let paidMessageStars):
|
||||
if boxed {
|
||||
buffer.appendInt32(2026050784)
|
||||
buffer.appendInt32(-964095818)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
call.serialize(buffer, true)
|
||||
fromId.serialize(buffer, true)
|
||||
serializeInt64(randomId, buffer: buffer, boxed: false)
|
||||
message.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(paidMessageStars!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .updateGroupCallParticipants(let call, let participants, let version):
|
||||
if boxed {
|
||||
|
|
@ -2110,16 +2112,16 @@ public extension Api {
|
|||
return ("updateFolderPeers", [("folderPeers", folderPeers as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)])
|
||||
case .updateGeoLiveViewed(let peer, let msgId):
|
||||
return ("updateGeoLiveViewed", [("peer", peer as Any), ("msgId", msgId as Any)])
|
||||
case .updateGroupCall(let flags, let chatId, let call):
|
||||
return ("updateGroupCall", [("flags", flags as Any), ("chatId", chatId as Any), ("call", call as Any)])
|
||||
case .updateGroupCall(let flags, let peer, let call):
|
||||
return ("updateGroupCall", [("flags", flags as Any), ("peer", peer as Any), ("call", call as Any)])
|
||||
case .updateGroupCallChainBlocks(let call, let subChainId, let blocks, let nextOffset):
|
||||
return ("updateGroupCallChainBlocks", [("call", call as Any), ("subChainId", subChainId as Any), ("blocks", blocks as Any), ("nextOffset", nextOffset as Any)])
|
||||
case .updateGroupCallConnection(let flags, let params):
|
||||
return ("updateGroupCallConnection", [("flags", flags as Any), ("params", params as Any)])
|
||||
case .updateGroupCallEncryptedMessage(let call, let fromId, let encryptedMessage):
|
||||
return ("updateGroupCallEncryptedMessage", [("call", call as Any), ("fromId", fromId as Any), ("encryptedMessage", encryptedMessage as Any)])
|
||||
case .updateGroupCallMessage(let call, let fromId, let randomId, let message):
|
||||
return ("updateGroupCallMessage", [("call", call as Any), ("fromId", fromId as Any), ("randomId", randomId as Any), ("message", message as Any)])
|
||||
case .updateGroupCallMessage(let flags, let call, let fromId, let randomId, let message, let paidMessageStars):
|
||||
return ("updateGroupCallMessage", [("flags", flags as Any), ("call", call as Any), ("fromId", fromId as Any), ("randomId", randomId as Any), ("message", message as Any), ("paidMessageStars", paidMessageStars as Any)])
|
||||
case .updateGroupCallParticipants(let call, let participants, let version):
|
||||
return ("updateGroupCallParticipants", [("call", call as Any), ("participants", participants as Any), ("version", version as Any)])
|
||||
case .updateInlineBotCallbackQuery(let flags, let queryId, let userId, let msgId, let chatInstance, let data, let gameShortName):
|
||||
|
|
@ -3513,17 +3515,19 @@ public extension Api {
|
|||
public static func parse_updateGroupCall(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt64() }
|
||||
var _2: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _3: Api.GroupCall?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.GroupCall
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Update.updateGroupCall(flags: _1!, chatId: _2, call: _3!)
|
||||
return Api.Update.updateGroupCall(flags: _1!, peer: _2, call: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
@ -3591,26 +3595,32 @@ public extension Api {
|
|||
}
|
||||
}
|
||||
public static func parse_updateGroupCallMessage(_ reader: BufferReader) -> Update? {
|
||||
var _1: Api.InputGroupCall?
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.InputGroupCall?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
||||
}
|
||||
var _2: Api.Peer?
|
||||
var _3: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Api.TextWithEntities?
|
||||
var _4: Int64?
|
||||
_4 = reader.readInt64()
|
||||
var _5: Api.TextWithEntities?
|
||||
if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.TextWithEntities
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.TextWithEntities
|
||||
}
|
||||
var _6: Int64?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt64() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.Update.updateGroupCallMessage(call: _1!, fromId: _2!, randomId: _3!, message: _4!)
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.Update.updateGroupCallMessage(flags: _1!, call: _2!, fromId: _3!, randomId: _4!, message: _5!, paidMessageStars: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -5656,9 +5656,9 @@ public extension Api.functions.messages {
|
|||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, quickReplyShortcutId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, scheduleRepeatPeriod: Int32?, quickReplyShortcutId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-539934715)
|
||||
buffer.appendInt32(1374175969)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
|
|
@ -5671,8 +5671,9 @@ public extension Api.functions.messages {
|
|||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 15) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 18) != 0 {serializeInt32(scheduleRepeatPeriod!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {serializeInt32(quickReplyShortcutId!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "messages.editMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("message", String(describing: message)), ("media", String(describing: media)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("quickReplyShortcutId", String(describing: quickReplyShortcutId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
return (FunctionDescription(name: "messages.editMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("message", String(describing: message)), ("media", String(describing: media)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("scheduleRepeatPeriod", String(describing: scheduleRepeatPeriod)), ("quickReplyShortcutId", String(describing: quickReplyShortcutId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
@ -5735,9 +5736,9 @@ public extension Api.functions.messages {
|
|||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, topMsgId: Int32?, replyTo: Api.InputReplyTo?, scheduleDate: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?, videoTimestamp: Int32?, allowPaidStars: Int64?, suggestedPost: Api.SuggestedPost?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, topMsgId: Int32?, replyTo: Api.InputReplyTo?, scheduleDate: Int32?, scheduleRepeatPeriod: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?, videoTimestamp: Int32?, allowPaidStars: Int64?, suggestedPost: Api.SuggestedPost?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1752618806)
|
||||
buffer.appendInt32(1104419550)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
fromPeer.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
|
|
@ -5754,12 +5755,13 @@ public extension Api.functions.messages {
|
|||
if Int(flags) & Int(1 << 9) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 22) != 0 {replyTo!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 24) != 0 {serializeInt32(scheduleRepeatPeriod!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {quickReplyShortcut!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 20) != 0 {serializeInt32(videoTimestamp!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 21) != 0 {serializeInt64(allowPaidStars!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 23) != 0 {suggestedPost!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", String(describing: flags)), ("fromPeer", String(describing: fromPeer)), ("id", String(describing: id)), ("randomId", String(describing: randomId)), ("toPeer", String(describing: toPeer)), ("topMsgId", String(describing: topMsgId)), ("replyTo", String(describing: replyTo)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut)), ("videoTimestamp", String(describing: videoTimestamp)), ("allowPaidStars", String(describing: allowPaidStars)), ("suggestedPost", String(describing: suggestedPost))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", String(describing: flags)), ("fromPeer", String(describing: fromPeer)), ("id", String(describing: id)), ("randomId", String(describing: randomId)), ("toPeer", String(describing: toPeer)), ("topMsgId", String(describing: topMsgId)), ("replyTo", String(describing: replyTo)), ("scheduleDate", String(describing: scheduleDate)), ("scheduleRepeatPeriod", String(describing: scheduleRepeatPeriod)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut)), ("videoTimestamp", String(describing: videoTimestamp)), ("allowPaidStars", String(describing: allowPaidStars)), ("suggestedPost", String(describing: suggestedPost))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
@ -8355,9 +8357,9 @@ public extension Api.functions.messages {
|
|||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func sendMedia(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, media: Api.InputMedia, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?, effect: Int64?, allowPaidStars: Int64?, suggestedPost: Api.SuggestedPost?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func sendMedia(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, media: Api.InputMedia, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, scheduleRepeatPeriod: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?, effect: Int64?, allowPaidStars: Int64?, suggestedPost: Api.SuggestedPost?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1403659839)
|
||||
buffer.appendInt32(53536639)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)}
|
||||
|
|
@ -8371,12 +8373,13 @@ public extension Api.functions.messages {
|
|||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 24) != 0 {serializeInt32(scheduleRepeatPeriod!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {quickReplyShortcut!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 18) != 0 {serializeInt64(effect!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 21) != 0 {serializeInt64(allowPaidStars!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 22) != 0 {suggestedPost!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "messages.sendMedia", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("media", String(describing: media)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut)), ("effect", String(describing: effect)), ("allowPaidStars", String(describing: allowPaidStars)), ("suggestedPost", String(describing: suggestedPost))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
return (FunctionDescription(name: "messages.sendMedia", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("media", String(describing: media)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("scheduleRepeatPeriod", String(describing: scheduleRepeatPeriod)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut)), ("effect", String(describing: effect)), ("allowPaidStars", String(describing: allowPaidStars)), ("suggestedPost", String(describing: suggestedPost))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
@ -8387,9 +8390,9 @@ public extension Api.functions.messages {
|
|||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func sendMessage(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?, effect: Int64?, allowPaidStars: Int64?, suggestedPost: Api.SuggestedPost?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func sendMessage(flags: Int32, peer: Api.InputPeer, replyTo: Api.InputReplyTo?, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?, scheduleRepeatPeriod: Int32?, sendAs: Api.InputPeer?, quickReplyShortcut: Api.InputQuickReplyShortcut?, effect: Int64?, allowPaidStars: Int64?, suggestedPost: Api.SuggestedPost?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-33170278)
|
||||
buffer.appendInt32(1415369050)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {replyTo!.serialize(buffer, true)}
|
||||
|
|
@ -8402,12 +8405,13 @@ public extension Api.functions.messages {
|
|||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 24) != 0 {serializeInt32(scheduleRepeatPeriod!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {quickReplyShortcut!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 18) != 0 {serializeInt64(effect!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 21) != 0 {serializeInt64(allowPaidStars!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 22) != 0 {suggestedPost!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "messages.sendMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut)), ("effect", String(describing: effect)), ("allowPaidStars", String(describing: allowPaidStars)), ("suggestedPost", String(describing: suggestedPost))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
return (FunctionDescription(name: "messages.sendMessage", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("replyTo", String(describing: replyTo)), ("message", String(describing: message)), ("randomId", String(describing: randomId)), ("replyMarkup", String(describing: replyMarkup)), ("entities", String(describing: entities)), ("scheduleDate", String(describing: scheduleDate)), ("scheduleRepeatPeriod", String(describing: scheduleRepeatPeriod)), ("sendAs", String(describing: sendAs)), ("quickReplyShortcut", String(describing: quickReplyShortcut)), ("effect", String(describing: effect)), ("allowPaidStars", String(describing: allowPaidStars)), ("suggestedPost", String(describing: suggestedPost))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
@ -10567,12 +10571,13 @@ public extension Api.functions.phone {
|
|||
}
|
||||
}
|
||||
public extension Api.functions.phone {
|
||||
static func getGroupCallStreamRtmpUrl(peer: Api.InputPeer, revoke: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.GroupCallStreamRtmpUrl>) {
|
||||
static func getGroupCallStreamRtmpUrl(flags: Int32, peer: Api.InputPeer, revoke: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.GroupCallStreamRtmpUrl>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-558650433)
|
||||
buffer.appendInt32(1525991226)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
revoke.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "phone.getGroupCallStreamRtmpUrl", parameters: [("peer", String(describing: peer)), ("revoke", String(describing: revoke))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupCallStreamRtmpUrl? in
|
||||
return (FunctionDescription(name: "phone.getGroupCallStreamRtmpUrl", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("revoke", String(describing: revoke))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupCallStreamRtmpUrl? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.phone.GroupCallStreamRtmpUrl?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
@ -10829,13 +10834,15 @@ public extension Api.functions.phone {
|
|||
}
|
||||
}
|
||||
public extension Api.functions.phone {
|
||||
static func sendGroupCallMessage(call: Api.InputGroupCall, randomId: Int64, message: Api.TextWithEntities) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
static func sendGroupCallMessage(flags: Int32, call: Api.InputGroupCall, randomId: Int64, message: Api.TextWithEntities, allowPaidStars: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-2021052396)
|
||||
buffer.appendInt32(2124127245)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
call.serialize(buffer, true)
|
||||
serializeInt64(randomId, buffer: buffer, boxed: false)
|
||||
message.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "phone.sendGroupCallMessage", parameters: [("call", String(describing: call)), ("randomId", String(describing: randomId)), ("message", String(describing: message))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(allowPaidStars!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "phone.sendGroupCallMessage", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("randomId", String(describing: randomId)), ("message", String(describing: message)), ("allowPaidStars", String(describing: allowPaidStars))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
@ -10913,14 +10920,15 @@ public extension Api.functions.phone {
|
|||
}
|
||||
}
|
||||
public extension Api.functions.phone {
|
||||
static func toggleGroupCallSettings(flags: Int32, call: Api.InputGroupCall, joinMuted: Api.Bool?, messagesEnabled: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func toggleGroupCallSettings(flags: Int32, call: Api.InputGroupCall, joinMuted: Api.Bool?, messagesEnabled: Api.Bool?, sendPaidMessagesStars: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-378390524)
|
||||
buffer.appendInt32(-1757179150)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
call.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {joinMuted!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {messagesEnabled!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "phone.toggleGroupCallSettings", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("joinMuted", String(describing: joinMuted)), ("messagesEnabled", String(describing: messagesEnabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt64(sendPaidMessagesStars!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "phone.toggleGroupCallSettings", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("joinMuted", String(describing: joinMuted)), ("messagesEnabled", String(describing: messagesEnabled)), ("sendPaidMessagesStars", String(describing: sendPaidMessagesStars))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
@ -12060,6 +12068,34 @@ public extension Api.functions.stories {
|
|||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.stories {
|
||||
static func startLive(flags: Int32, peer: Api.InputPeer, caption: String?, entities: [Api.MessageEntity]?, privacyRules: [Api.InputPrivacyRule], randomId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1294237155)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(caption!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(entities!.count))
|
||||
for item in entities! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(privacyRules.count))
|
||||
for item in privacyRules {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
serializeInt64(randomId, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "stories.startLive", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("caption", String(describing: caption)), ("entities", String(describing: entities)), ("privacyRules", String(describing: privacyRules)), ("randomId", String(describing: randomId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.stories {
|
||||
static func toggleAllStoriesHidden(hidden: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
|
|
|
|||
|
|
@ -1330,13 +1330,13 @@ public extension Api {
|
|||
}
|
||||
public extension Api {
|
||||
enum GroupCallParticipant: TypeConstructorDescription {
|
||||
case groupCallParticipant(flags: Int32, peer: Api.Peer, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?, about: String?, raiseHandRating: Int64?, video: Api.GroupCallParticipantVideo?, presentation: Api.GroupCallParticipantVideo?)
|
||||
case groupCallParticipant(flags: Int32, peer: Api.Peer, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?, about: String?, raiseHandRating: Int64?, video: Api.GroupCallParticipantVideo?, presentation: Api.GroupCallParticipantVideo?, paidStarsTotal: Int64?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating, let video, let presentation):
|
||||
case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating, let video, let presentation, let paidStarsTotal):
|
||||
if boxed {
|
||||
buffer.appendInt32(-341428482)
|
||||
buffer.appendInt32(708691884)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
|
|
@ -1348,14 +1348,15 @@ public extension Api {
|
|||
if Int(flags) & Int(1 << 13) != 0 {serializeInt64(raiseHandRating!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 6) != 0 {video!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 14) != 0 {presentation!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 16) != 0 {serializeInt64(paidStarsTotal!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating, let video, let presentation):
|
||||
return ("groupCallParticipant", [("flags", flags as Any), ("peer", peer as Any), ("date", date as Any), ("activeDate", activeDate as Any), ("source", source as Any), ("volume", volume as Any), ("about", about as Any), ("raiseHandRating", raiseHandRating as Any), ("video", video as Any), ("presentation", presentation as Any)])
|
||||
case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating, let video, let presentation, let paidStarsTotal):
|
||||
return ("groupCallParticipant", [("flags", flags as Any), ("peer", peer as Any), ("date", date as Any), ("activeDate", activeDate as Any), ("source", source as Any), ("volume", volume as Any), ("about", about as Any), ("raiseHandRating", raiseHandRating as Any), ("video", video as Any), ("presentation", presentation as Any), ("paidStarsTotal", paidStarsTotal as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1386,6 +1387,8 @@ public extension Api {
|
|||
if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() {
|
||||
_10 = Api.parse(reader, signature: signature) as? Api.GroupCallParticipantVideo
|
||||
} }
|
||||
var _11: Int64?
|
||||
if Int(_1!) & Int(1 << 16) != 0 {_11 = reader.readInt64() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
|
|
@ -1396,8 +1399,9 @@ public extension Api {
|
|||
let _c8 = (Int(_1!) & Int(1 << 13) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
|
||||
return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7, raiseHandRating: _8, video: _9, presentation: _10)
|
||||
let _c11 = (Int(_1!) & Int(1 << 16) == 0) || _11 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
|
||||
return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7, raiseHandRating: _8, video: _9, presentation: _10, paidStarsTotal: _11)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -572,44 +572,54 @@ public extension Api {
|
|||
}
|
||||
public extension Api {
|
||||
enum InputContact: TypeConstructorDescription {
|
||||
case inputPhoneContact(clientId: Int64, phone: String, firstName: String, lastName: String)
|
||||
case inputPhoneContact(flags: Int32, clientId: Int64, phone: String, firstName: String, lastName: String, note: Api.TextWithEntities?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputPhoneContact(let clientId, let phone, let firstName, let lastName):
|
||||
case .inputPhoneContact(let flags, let clientId, let phone, let firstName, let lastName, let note):
|
||||
if boxed {
|
||||
buffer.appendInt32(-208488460)
|
||||
buffer.appendInt32(1780335806)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(clientId, buffer: buffer, boxed: false)
|
||||
serializeString(phone, buffer: buffer, boxed: false)
|
||||
serializeString(firstName, buffer: buffer, boxed: false)
|
||||
serializeString(lastName, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {note!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputPhoneContact(let clientId, let phone, let firstName, let lastName):
|
||||
return ("inputPhoneContact", [("clientId", clientId as Any), ("phone", phone as Any), ("firstName", firstName as Any), ("lastName", lastName as Any)])
|
||||
case .inputPhoneContact(let flags, let clientId, let phone, let firstName, let lastName, let note):
|
||||
return ("inputPhoneContact", [("flags", flags as Any), ("clientId", clientId as Any), ("phone", phone as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("note", note as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputPhoneContact(_ reader: BufferReader) -> InputContact? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
var _6: Api.TextWithEntities?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.TextWithEntities
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.InputContact.inputPhoneContact(clientId: _1!, phone: _2!, firstName: _3!, lastName: _4!)
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.InputContact.inputPhoneContact(flags: _1!, clientId: _2!, phone: _3!, firstName: _4!, lastName: _5!, note: _6)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -84,7 +84,6 @@ swift_library(
|
|||
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
|
||||
"//submodules/AlertUI:AlertUI",
|
||||
"//submodules/DirectionalPanGesture:DirectionalPanGesture",
|
||||
"//submodules/PeerInfoUI:PeerInfoUI",
|
||||
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
|
||||
"//submodules/DeviceProximity:DeviceProximity",
|
||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||
|
|
|
|||
|
|
@ -367,6 +367,7 @@ public final class MediaStreamComponent: CombinedComponent {
|
|||
isVisible: environment.isVisible && context.state.isVisibleInHierarchy,
|
||||
isAdmin: context.state.canManageCall,
|
||||
peerTitle: context.state.peerTitle,
|
||||
addInset: !isFullscreen,
|
||||
isFullscreen: isFullscreen,
|
||||
videoLoading: context.state.videoStalled,
|
||||
callPeer: context.state.chatPeer,
|
||||
|
|
@ -595,7 +596,7 @@ public final class MediaStreamComponent: CombinedComponent {
|
|||
}
|
||||
|
||||
let credentialsPromise = Promise<GroupCallStreamCredentials>()
|
||||
credentialsPromise.set(call.accountContext.engine.calls.getGroupCallStreamCredentials(peerId: peerId, revokePreviousCredentials: false) |> `catch` { _ -> Signal<GroupCallStreamCredentials, NoError> in return .never() })
|
||||
credentialsPromise.set(call.accountContext.engine.calls.getGroupCallStreamCredentials(peerId: peerId, isLiveStream: false, revokePreviousCredentials: false) |> `catch` { _ -> Signal<GroupCallStreamCredentials, NoError> in return .never() })
|
||||
|
||||
items.append(.action(ContextMenuActionItem(id: nil, text: presentationData.strings.LiveStream_ViewCredentials, textColor: .primary, textLayout: .singleLine, textFont: .regular, badge: nil, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor, backgroundColor: nil)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import Postbox
|
|||
import TelegramVoip
|
||||
import ComponentDisplayAdapters
|
||||
|
||||
final class MediaStreamVideoComponent: Component {
|
||||
public final class MediaStreamVideoComponent: Component {
|
||||
let call: PresentationGroupCallImpl
|
||||
let hasVideo: Bool
|
||||
let isVisible: Bool
|
||||
|
|
@ -23,18 +23,20 @@ final class MediaStreamVideoComponent: Component {
|
|||
let deactivatePictureInPicture: ActionSlot<Void>
|
||||
let bringBackControllerForPictureInPictureDeactivation: (@escaping () -> Void) -> Void
|
||||
let pictureInPictureClosed: () -> Void
|
||||
let addInset: Bool
|
||||
let isFullscreen: Bool
|
||||
let onVideoSizeRetrieved: (CGSize) -> Void
|
||||
let videoLoading: Bool
|
||||
let callPeer: Peer?
|
||||
let onVideoPlaybackLiveChange: (Bool) -> Void
|
||||
|
||||
init(
|
||||
public init(
|
||||
call: PresentationGroupCallImpl,
|
||||
hasVideo: Bool,
|
||||
isVisible: Bool,
|
||||
isAdmin: Bool,
|
||||
peerTitle: String,
|
||||
addInset: Bool,
|
||||
isFullscreen: Bool,
|
||||
videoLoading: Bool,
|
||||
callPeer: Peer?,
|
||||
|
|
@ -58,6 +60,7 @@ final class MediaStreamVideoComponent: Component {
|
|||
self.onVideoPlaybackLiveChange = onVideoPlaybackLiveChange
|
||||
|
||||
self.callPeer = callPeer
|
||||
self.addInset = addInset
|
||||
self.isFullscreen = isFullscreen
|
||||
self.onVideoSizeRetrieved = onVideoSizeRetrieved
|
||||
}
|
||||
|
|
@ -78,6 +81,9 @@ final class MediaStreamVideoComponent: Component {
|
|||
if lhs.peerTitle != rhs.peerTitle {
|
||||
return false
|
||||
}
|
||||
if lhs.addInset != rhs.addInset {
|
||||
return false
|
||||
}
|
||||
if lhs.isFullscreen != rhs.isFullscreen {
|
||||
return false
|
||||
}
|
||||
|
|
@ -334,7 +340,7 @@ final class MediaStreamVideoComponent: Component {
|
|||
}
|
||||
})
|
||||
stallTimer = _stallTimer
|
||||
self.clipsToBounds = component.isFullscreen // or just true
|
||||
self.clipsToBounds = true
|
||||
|
||||
if let videoView = self.videoRenderingContext.makeView(input: input, blur: false, forceSampleBufferDisplayLayer: true) {
|
||||
self.videoView = videoView
|
||||
|
|
@ -420,7 +426,7 @@ final class MediaStreamVideoComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
if let videoView = self.videoView, let videoBlurView = self.videoRenderingContext.makeBlurView(input: input, mainView: videoView) {
|
||||
if component.addInset, let videoView = self.videoView, let videoBlurView = self.videoRenderingContext.makeBlurView(input: input, mainView: videoView) {
|
||||
self.videoBlurView = videoBlurView
|
||||
self.insertSubview(videoBlurView, belowSubview: self.blurTintView)
|
||||
videoBlurView.alpha = 0
|
||||
|
|
@ -453,14 +459,14 @@ final class MediaStreamVideoComponent: Component {
|
|||
fullScreenBackgroundPlaceholder.frame = .init(origin: .zero, size: availableSize)
|
||||
|
||||
let videoInset: CGFloat
|
||||
if !component.isFullscreen {
|
||||
if component.addInset && !component.isFullscreen {
|
||||
videoInset = 16
|
||||
} else {
|
||||
videoInset = 0
|
||||
}
|
||||
|
||||
let videoSize: CGSize
|
||||
let videoCornerRadius: CGFloat = component.isFullscreen ? 0 : 10
|
||||
let videoCornerRadius: CGFloat = (component.isFullscreen || !component.addInset) ? 0 : 10
|
||||
|
||||
let videoFrameUpdateTransition: ComponentTransition
|
||||
if self.wasFullscreen != component.isFullscreen {
|
||||
|
|
@ -478,6 +484,7 @@ final class MediaStreamVideoComponent: Component {
|
|||
}
|
||||
|
||||
var aspect = videoView.getAspect()
|
||||
|
||||
if component.isFullscreen && self.hadVideo {
|
||||
if aspect <= 0.01 {
|
||||
aspect = 16.0 / 9
|
||||
|
|
@ -485,8 +492,12 @@ final class MediaStreamVideoComponent: Component {
|
|||
} else if !self.hadVideo {
|
||||
aspect = 16.0 / 9
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
aspect = 9.0 / 16.0
|
||||
#endif
|
||||
|
||||
if component.isFullscreen {
|
||||
if component.isFullscreen || !component.addInset {
|
||||
videoSize = CGSize(width: aspect * 100.0, height: 100.0).aspectFitted(.init(width: availableSize.width - videoInset * 2, height: availableSize.height))
|
||||
} else {
|
||||
// Limiting by smallest side -- redundant if passing precalculated availableSize
|
||||
|
|
@ -629,17 +640,15 @@ final class MediaStreamVideoComponent: Component {
|
|||
|
||||
if !self.hadVideo && !component.call.accountContext.sharedContext.immediateExperimentalUISettings.liveStreamV2 {
|
||||
if self.noSignalTimer == nil {
|
||||
if #available(iOS 10.0, *) {
|
||||
let noSignalTimer = Timer(timeInterval: 20.0, repeats: false, block: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.noSignalTimeout = true
|
||||
strongSelf.state?.updated(transition: .immediate)
|
||||
})
|
||||
self.noSignalTimer = noSignalTimer
|
||||
RunLoop.main.add(noSignalTimer, forMode: .common)
|
||||
}
|
||||
let noSignalTimer = Timer(timeInterval: 20.0, repeats: false, block: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.noSignalTimeout = true
|
||||
strongSelf.state?.updated(transition: .immediate)
|
||||
})
|
||||
self.noSignalTimer = noSignalTimer
|
||||
RunLoop.main.add(noSignalTimer, forMode: .common)
|
||||
}
|
||||
|
||||
if self.noSignalTimeout, !"".isEmpty {
|
||||
|
|
@ -696,7 +705,7 @@ final class MediaStreamVideoComponent: Component {
|
|||
return availableSize
|
||||
}
|
||||
|
||||
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||
public func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||
if let videoView = self.videoView, let presentation = videoView.snapshotView(afterScreenUpdates: false) {
|
||||
let presentationParent = self.window ?? self
|
||||
presentationParent.addSubview(presentation)
|
||||
|
|
@ -750,12 +759,12 @@ final class MediaStreamVideoComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||
public func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||
self.didRequestBringBack = false
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
|
||||
func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||
public func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||
if self.requestedExpansion {
|
||||
self.requestedExpansion = false
|
||||
} else if !didRequestBringBack {
|
||||
|
|
@ -772,7 +781,7 @@ final class MediaStreamVideoComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||
public func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||
self.videoView?.alpha = 1
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
|
|
@ -810,94 +819,3 @@ private final class CustomIntensityVisualEffectView: UIVisualEffectView {
|
|||
animator.stopAnimation(true)
|
||||
}
|
||||
}
|
||||
|
||||
private final class ProxyVideoView: UIView {
|
||||
private let call: PresentationGroupCallImpl
|
||||
private let id: Int64
|
||||
private let player: AVPlayer
|
||||
private let playerItem: AVPlayerItem
|
||||
let playerLayer: AVPlayerLayer
|
||||
|
||||
private var contextDisposable: Disposable?
|
||||
|
||||
private var failureObserverId: AnyObject?
|
||||
private var errorObserverId: AnyObject?
|
||||
private var rateObserver: NSKeyValueObservation?
|
||||
|
||||
private var isActiveDisposable: Disposable?
|
||||
|
||||
init(context: AccountContext, call: PresentationGroupCallImpl) {
|
||||
self.call = call
|
||||
|
||||
self.id = Int64.random(in: Int64.min ... Int64.max)
|
||||
|
||||
let assetUrl = "http://127.0.0.1:\(SharedHLSServer.shared.port)/\(call.internalId)/master.m3u8"
|
||||
Logger.shared.log("MediaStreamVideoComponent", "Initializing HLS asset at \(assetUrl)")
|
||||
#if DEBUG
|
||||
print("Initializing HLS asset at \(assetUrl)")
|
||||
#endif
|
||||
let asset = AVURLAsset(url: URL(string: assetUrl)!, options: [:])
|
||||
self.playerItem = AVPlayerItem(asset: asset)
|
||||
self.player = AVPlayer(playerItem: self.playerItem)
|
||||
self.player.allowsExternalPlayback = true
|
||||
self.playerLayer = AVPlayerLayer(player: self.player)
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.failureObserverId = NotificationCenter.default.addObserver(forName: AVPlayerItem.failedToPlayToEndTimeNotification, object: playerItem, queue: .main, using: { notification in
|
||||
print("Player Error: \(notification.description)")
|
||||
})
|
||||
self.errorObserverId = NotificationCenter.default.addObserver(forName: AVPlayerItem.newErrorLogEntryNotification, object: playerItem, queue: .main, using: { notification in
|
||||
print("Player Error: \(notification.description)")
|
||||
})
|
||||
self.rateObserver = self.player.observe(\.rate, changeHandler: { [weak self] _, change in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
print("Player rate: \(self.player.rate)")
|
||||
})
|
||||
|
||||
self.layer.addSublayer(self.playerLayer)
|
||||
|
||||
self.isActiveDisposable = (context.sharedContext.applicationBindings.applicationIsActive
|
||||
|> distinctUntilChanged
|
||||
|> deliverOnMainQueue).start(next: { [weak self] isActive in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if isActive {
|
||||
self.playerLayer.player = self.player
|
||||
if self.player.rate == 0.0 {
|
||||
self.player.play()
|
||||
}
|
||||
} else {
|
||||
self.playerLayer.player = nil
|
||||
}
|
||||
})
|
||||
|
||||
self.player.play()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.contextDisposable?.dispose()
|
||||
if let failureObserverId = self.failureObserverId {
|
||||
NotificationCenter.default.removeObserver(failureObserverId)
|
||||
}
|
||||
if let errorObserverId = self.errorObserverId {
|
||||
NotificationCenter.default.removeObserver(errorObserverId)
|
||||
}
|
||||
if let rateObserver = self.rateObserver {
|
||||
rateObserver.invalidate()
|
||||
}
|
||||
self.isActiveDisposable?.dispose()
|
||||
}
|
||||
|
||||
func update(size: CGSize) {
|
||||
self.playerLayer.frame = CGRect(origin: CGPoint(), size: size)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -838,7 +838,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
|
||||
private var lastErrorAlertTimestamp: Double = 0.0
|
||||
|
||||
init(
|
||||
public init(
|
||||
accountContext: AccountContext,
|
||||
audioSession: ManagedAudioSession,
|
||||
callKitIntegration: CallKitIntegration?,
|
||||
|
|
@ -1356,7 +1356,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
|
||||
volume: nil,
|
||||
about: about,
|
||||
joinedVideo: self.temporaryJoinedVideo
|
||||
joinedVideo: self.temporaryJoinedVideo,
|
||||
paidStarsTotal: nil
|
||||
))
|
||||
participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) })
|
||||
}
|
||||
|
|
@ -1437,7 +1438,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
|
||||
volume: nil,
|
||||
about: about,
|
||||
joinedVideo: self.temporaryJoinedVideo
|
||||
joinedVideo: self.temporaryJoinedVideo,
|
||||
paidStarsTotal: nil
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -1625,7 +1627,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: canManageCall || !state.defaultParticipantsAreMuted.isMuted, mutedByYou: false),
|
||||
volume: nil,
|
||||
about: about,
|
||||
joinedVideo: self.temporaryJoinedVideo
|
||||
joinedVideo: self.temporaryJoinedVideo,
|
||||
paidStarsTotal: nil
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -1743,7 +1746,30 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
shouldJoin = callInfo.scheduleTimestamp == nil
|
||||
activeCallInfo = callInfo
|
||||
} else {
|
||||
activeCallInfo = nil
|
||||
if case let .id(id, accessHash) = self.initialCall?.reference, case .requesting = internalState, self.isStream {
|
||||
if self.genericCallContext == nil {
|
||||
shouldJoin = true
|
||||
}
|
||||
activeCallInfo = GroupCallInfo(
|
||||
id: id,
|
||||
accessHash: accessHash,
|
||||
participantCount: 0,
|
||||
streamDcId: nil,
|
||||
title: nil,
|
||||
scheduleTimestamp: nil,
|
||||
subscribedToScheduled: false,
|
||||
recordingStartTimestamp: nil,
|
||||
sortAscending: false,
|
||||
defaultParticipantsAreMuted: nil,
|
||||
messagesAreEnabled: nil,
|
||||
isVideoEnabled: false,
|
||||
unmutedVideoLimit: 0,
|
||||
isStream: true,
|
||||
isCreator: false
|
||||
)
|
||||
} else {
|
||||
activeCallInfo = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.leaving {
|
||||
|
|
@ -2003,6 +2029,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
joinAs: self.joinAsPeerId,
|
||||
callId: callInfo.id,
|
||||
reference: reference,
|
||||
isStream: self.isStream,
|
||||
preferMuted: isEffectivelyMuted,
|
||||
joinPayload: joinPayload,
|
||||
peerAdminIds: peerAdminIds,
|
||||
|
|
@ -2460,7 +2487,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
|
||||
volume: nil,
|
||||
about: about,
|
||||
joinedVideo: self.temporaryJoinedVideo
|
||||
joinedVideo: self.temporaryJoinedVideo,
|
||||
paidStarsTotal: nil
|
||||
))
|
||||
participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) })
|
||||
}
|
||||
|
|
@ -3649,7 +3677,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
return
|
||||
}
|
||||
|
||||
if let value = value {
|
||||
if let value {
|
||||
var reference: InternalGroupCallReference = .id(id: value.id, accessHash: value.accessHash)
|
||||
if let current = self.initialCall {
|
||||
switch current.reference {
|
||||
|
|
|
|||
|
|
@ -1263,7 +1263,8 @@ final class VideoChatScreenComponent: Component {
|
|||
muteState: nil,
|
||||
volume: nil,
|
||||
about: nil,
|
||||
joinedVideo: false
|
||||
joinedVideo: false,
|
||||
paidStarsTotal: nil
|
||||
))
|
||||
}
|
||||
if let remotePeer {
|
||||
|
|
@ -1289,7 +1290,8 @@ final class VideoChatScreenComponent: Component {
|
|||
muteState: nil,
|
||||
volume: nil,
|
||||
about: nil,
|
||||
joinedVideo: false
|
||||
joinedVideo: false,
|
||||
paidStarsTotal: nil
|
||||
))
|
||||
}
|
||||
let members = PresentationGroupCallMembers(
|
||||
|
|
@ -3712,7 +3714,7 @@ final class VideoChatScreenComponent: Component {
|
|||
context: call.accountContext,
|
||||
animationCache: call.accountContext.animationCache,
|
||||
presentationData: call.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme),
|
||||
style: .glass,
|
||||
style: .glass(isTinted: true),
|
||||
items: reactionItems.map { ReactionContextItem.reaction(item: $0, icon: .none) },
|
||||
selectedItems: Set(),
|
||||
title: nil,
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ import UIKit
|
|||
import Display
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import PeerInfoUI
|
||||
import OverlayStatusController
|
||||
import PresentationDataUtils
|
||||
import InviteLinksUI
|
||||
import UndoUI
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
|
||||
extension VideoChatScreenComponent.View {
|
||||
func openInviteMembers() {
|
||||
|
|
@ -113,7 +113,7 @@ extension VideoChatScreenComponent.View {
|
|||
filters.append(.excludeBots)
|
||||
|
||||
var dismissController: (() -> Void)?
|
||||
let controller = ChannelMembersSearchController(context: groupCall.accountContext, peerId: groupPeer.id, forceTheme: environment.theme, mode: .inviteToCall, filters: filters, openPeer: { [weak self] peer, participant in
|
||||
let controller = groupCall.accountContext.sharedContext.makeChannelMembersSearchController(params: ChannelMembersSearchControllerParams(context: groupCall.accountContext, peerId: groupPeer.id, forceTheme: environment.theme, mode: .inviteToCall, filters: filters, openPeer: { [weak self] peer, participant in
|
||||
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
|
||||
dismissController?()
|
||||
return
|
||||
|
|
@ -327,7 +327,7 @@ extension VideoChatScreenComponent.View {
|
|||
})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
})
|
||||
}))
|
||||
controller.copyInviteLink = { [weak self] in
|
||||
dismissController?()
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import UndoUI
|
|||
import AlertUI
|
||||
import PresentationDataUtils
|
||||
import DirectionalPanGesture
|
||||
import PeerInfoUI
|
||||
import AvatarNode
|
||||
import TooltipUI
|
||||
import LegacyUI
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import TelegramUIPreferences
|
|||
import AccountContext
|
||||
import AlertUI
|
||||
import PresentationDataUtils
|
||||
import PeerInfoUI
|
||||
import ShareController
|
||||
import AvatarNode
|
||||
import UndoUI
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ private var declaredEncodables: Void = {
|
|||
declareEncodable(TelegramMediaTodo.Completion.self, f: { TelegramMediaTodo.Completion(decoder: $0) })
|
||||
declareEncodable(SuggestedPostMessageAttribute.self, f: { SuggestedPostMessageAttribute(decoder: $0) })
|
||||
declareEncodable(PublishedSuggestedPostMessageAttribute.self, f: { PublishedSuggestedPostMessageAttribute(decoder: $0) })
|
||||
declareEncodable(TelegramMediaLiveStream.self, f: { TelegramMediaLiveStream(decoder: $0) })
|
||||
return
|
||||
}()
|
||||
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
|||
|
||||
func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
|
||||
switch messsage {
|
||||
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
let chatPeerId = messagePeerId
|
||||
return chatPeerId.peerId
|
||||
case let .messageEmpty(_, _, peerId):
|
||||
|
|
@ -144,7 +144,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
|
|||
|
||||
func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
||||
switch message {
|
||||
case let .message(_, _, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, viaBusinessBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, viaBusinessBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
let peerId: PeerId = chatPeerId.peerId
|
||||
|
||||
var result = [peerId]
|
||||
|
|
@ -279,7 +279,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
|||
|
||||
func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: ReferencedReplyMessageIds, generalIds: [MessageId])? {
|
||||
switch message {
|
||||
case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
if let replyTo = replyTo {
|
||||
let peerId: PeerId = chatPeerId.peerId
|
||||
|
||||
|
|
@ -495,6 +495,10 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
|
|||
return (TelegramMediaGiveawayResults(flags: flags, launchMessageId: MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), namespace: Namespaces.Message.Cloud, id: launchMsgId), additionalChannelsCount: additionalPeersCount ?? 0, winnersPeerIds: winners.map { PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value($0)) }, winnersCount: winnersCount, unclaimedCount: unclaimedCount, prize: prize, untilDate: untilDate, prizeDescription: prizeDescription), nil, nil, nil, nil, nil)
|
||||
case let .messageMediaPaidMedia(starsAmount, apiExtendedMedia):
|
||||
return (TelegramMediaPaidContent(amount: starsAmount, extendedMedia: apiExtendedMedia.compactMap({ TelegramExtendedMedia(apiExtendedMedia: $0, peerId: peerId) })), nil, nil, nil, nil, nil)
|
||||
case let .messageMediaVideoStream(call):
|
||||
if let call = GroupCallReference(call) {
|
||||
return (TelegramMediaLiveStream(call: call), nil, nil, nil, nil, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -693,7 +697,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
|
|||
extension StoreMessage {
|
||||
convenience init?(apiMessage: Api.Message, accountPeerId: PeerId, peerIsForum: Bool, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
|
||||
switch apiMessage {
|
||||
case let .message(flags, flags2, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, viaBusinessBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId, messageEffectId, factCheck, reportDeliveryUntilDate, paidMessageStars, suggestedPost):
|
||||
case let .message(flags, flags2, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, viaBusinessBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId, messageEffectId, factCheck, reportDeliveryUntilDate, paidMessageStars, suggestedPost, _):
|
||||
var attributes: [MessageAttribute] = []
|
||||
|
||||
if (flags2 & (1 << 4)) != 0 {
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox,
|
|||
flags |= Int32(1 << 17)
|
||||
}
|
||||
|
||||
return network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: text, media: inputMedia, replyMarkup: nil, entities: apiEntities, scheduleDate: effectiveScheduleTime, quickReplyShortcutId: quickReplyShortcutId))
|
||||
return network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: text, media: inputMedia, replyMarkup: nil, entities: apiEntities, scheduleDate: effectiveScheduleTime, scheduleRepeatPeriod: nil, quickReplyShortcutId: quickReplyShortcutId))
|
||||
|> map { result -> Api.Updates? in
|
||||
return result
|
||||
}
|
||||
|
|
@ -340,7 +340,7 @@ func _internal_requestEditLiveLocation(postbox: Postbox, network: Network, state
|
|||
inputMedia = .inputMediaGeoLive(flags: 1 << 0, geoPoint: .inputGeoPoint(flags: 0, lat: media.latitude, long: media.longitude, accuracyRadius: nil), heading: nil, period: nil, proximityNotificationRadius: nil)
|
||||
}
|
||||
|
||||
return network.request(Api.functions.messages.editMessage(flags: 1 << 14, peer: inputPeer, id: messageId.id, message: nil, media: inputMedia, replyMarkup: nil, entities: nil, scheduleDate: nil, quickReplyShortcutId: nil))
|
||||
return network.request(Api.functions.messages.editMessage(flags: 1 << 14, peer: inputPeer, id: messageId.id, message: nil, media: inputMedia, replyMarkup: nil, entities: nil, scheduleDate: nil, scheduleRepeatPeriod: nil, quickReplyShortcutId: nil))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
return .single(nil)
|
||||
|
|
|
|||
|
|
@ -454,7 +454,7 @@ private func sendUploadedMessageContent(
|
|||
flags |= 1 << 22
|
||||
}
|
||||
|
||||
sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), info: .acknowledgement, tag: dependencyTag)
|
||||
sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, scheduleRepeatPeriod: nil, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), info: .acknowledgement, tag: dependencyTag)
|
||||
case let .media(inputMedia, text):
|
||||
if bubbleUpEmojiOrStickersets {
|
||||
flags |= Int32(1 << 15)
|
||||
|
|
@ -486,7 +486,7 @@ private func sendUploadedMessageContent(
|
|||
flags |= 1 << 22
|
||||
}
|
||||
|
||||
sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), tag: dependencyTag)
|
||||
sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, scheduleRepeatPeriod: nil, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), tag: dependencyTag)
|
||||
|> map(NetworkRequestResult.result)
|
||||
case let .forward(sourceInfo):
|
||||
if topMsgId != nil {
|
||||
|
|
@ -494,7 +494,7 @@ private func sendUploadedMessageContent(
|
|||
}
|
||||
|
||||
if let forwardSourceInfoAttribute = forwardSourceInfoAttribute, let sourcePeer = transaction.getPeer(forwardSourceInfoAttribute.messageId.peerId), let sourceInputPeer = apiInputPeer(sourcePeer) {
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer, topMsgId: topMsgId, replyTo: nil, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, videoTimestamp: videoTimestamp, allowPaidStars: allowPaidStars, suggestedPost: nil), tag: dependencyTag)
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer, topMsgId: topMsgId, replyTo: nil, scheduleDate: scheduleTime, scheduleRepeatPeriod: nil, sendAs: sendAsInputPeer, quickReplyShortcut: nil, videoTimestamp: videoTimestamp, allowPaidStars: allowPaidStars, suggestedPost: nil), tag: dependencyTag)
|
||||
|> map(NetworkRequestResult.result)
|
||||
} else {
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
||||
|
|
@ -701,7 +701,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|
|||
replyTo = .inputReplyToMessage(flags: flags, replyToMsgId: threadId, topMsgId: threadId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil, todoItemId: nil)
|
||||
}
|
||||
|
||||
sendMessageRequest = account.network.request(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars, suggestedPost: nil))
|
||||
sendMessageRequest = account.network.request(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, scheduleRepeatPeriod: nil, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars, suggestedPost: nil))
|
||||
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|
|
@ -726,7 +726,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|
|||
flags |= 1 << 22
|
||||
}
|
||||
|
||||
sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost))
|
||||
sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, scheduleRepeatPeriod: nil, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost))
|
||||
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1678,13 +1678,12 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
|||
break
|
||||
}
|
||||
case let .updateGroupCall(_, channelId, call):
|
||||
updatedState.updateGroupCall(peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value($0)) }, call: call)
|
||||
updatedState.updateGroupCall(peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value($0)) }, call: call)
|
||||
updatedState.updateGroupCall(peerId: channelId?.peerId, call: call)
|
||||
case let .updateGroupCallChainBlocks(call, subChainId, blocks, nextOffset):
|
||||
if case let .inputGroupCall(id, accessHash) = call {
|
||||
updatedState.updateGroupCallChainBlocks(id: id, accessHash: accessHash, subChainId: subChainId, blocks: blocks.map { $0.makeData() }, nextOffset: nextOffset)
|
||||
}
|
||||
case let .updateGroupCallMessage(call, fromId, randomId, message):
|
||||
case let .updateGroupCallMessage(_, call, fromId, randomId, message, _):
|
||||
if case let .inputGroupCall(id, _) = call {
|
||||
updatedState.updateGroupCallMessage(id: id, authorId: fromId.peerId, randomId: randomId, text: message)
|
||||
}
|
||||
|
|
@ -5156,12 +5155,12 @@ func replayFinalState(
|
|||
if let currentIndex = updatedPeerEntries.firstIndex(where: { $0.id == storedItem.id }) {
|
||||
if case .item = storedItem {
|
||||
if let codedEntry = CodableEntry(storedItem) {
|
||||
updatedPeerEntries[currentIndex] = StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends)
|
||||
updatedPeerEntries[currentIndex] = StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends, isLiveStream: storedItem.isLiveStream)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let codedEntry = CodableEntry(storedItem) {
|
||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends))
|
||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends, isLiveStream: storedItem.isLiveStream))
|
||||
}
|
||||
}
|
||||
if case .item = storedItem {
|
||||
|
|
@ -5246,7 +5245,7 @@ func replayFinalState(
|
|||
folderIds: item.folderIds
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
updatedPeerEntries[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: item.expirationTimestamp, isCloseFriends: item.isCloseFriends)
|
||||
updatedPeerEntries[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: item.expirationTimestamp, isCloseFriends: item.isCloseFriends, isLiveStream: item.isLiveStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
|
|||
var updatedTimestamp: Int32?
|
||||
if let apiMessage = apiMessage {
|
||||
switch apiMessage {
|
||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
updatedTimestamp = date
|
||||
case .messageEmpty:
|
||||
break
|
||||
|
|
@ -400,7 +400,7 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage
|
|||
} else if let message = messages.first, let apiMessage = result.messages.first {
|
||||
if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
|
||||
namespace = Namespaces.Message.ScheduledCloud
|
||||
} else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 {
|
||||
} else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 {
|
||||
namespace = Namespaces.Message.ScheduledCloud
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public struct CallId: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
public struct GroupCallReference: Equatable {
|
||||
public struct GroupCallReference: Codable, Equatable {
|
||||
public var id: Int64
|
||||
public var accessHash: Int64
|
||||
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ private func pushDeviceContactData(accountPeerId: PeerId, postbox: Postbox, netw
|
|||
batches = batches
|
||||
|> mapToSignal { intermediateResult -> Signal<PushDeviceContactsResult, NoError> in
|
||||
return network.request(Api.functions.contacts.importContacts(contacts: zip(0 ..< batch.count, batch).map { index, item -> Api.InputContact in
|
||||
return .inputPhoneContact(clientId: Int64(index), phone: item.0.rawValue, firstName: item.1.firstName, lastName: item.1.lastName)
|
||||
return .inputPhoneContact(flags: 0, clientId: Int64(index), phone: item.0.rawValue, firstName: item.1.firstName, lastName: item.1.lastName, note: nil)
|
||||
}))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.contacts.ImportedContacts?, NoError> in
|
||||
|
|
|
|||
|
|
@ -1122,7 +1122,7 @@ public final class PendingMessageManager {
|
|||
} else if let inputSourcePeerId = forwardPeerIds.first, let inputSourcePeer = transaction.getPeer(inputSourcePeerId).flatMap(apiInputPeer) {
|
||||
let dependencyTag = PendingMessageRequestDependencyTag(messageId: messages[0].0.id)
|
||||
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: inputSourcePeer, id: forwardIds.map { $0.0.id }, randomId: forwardIds.map { $0.1 }, toPeer: inputPeer, topMsgId: topMsgId, replyTo: replyTo, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, videoTimestamp: videoTimestamp, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), tag: dependencyTag)
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: inputSourcePeer, id: forwardIds.map { $0.0.id }, randomId: forwardIds.map { $0.1 }, toPeer: inputPeer, topMsgId: topMsgId, replyTo: replyTo, scheduleDate: scheduleTime, scheduleRepeatPeriod: nil, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, videoTimestamp: videoTimestamp, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), tag: dependencyTag)
|
||||
} else {
|
||||
assertionFailure()
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "Invalid forward source"))
|
||||
|
|
@ -1671,7 +1671,7 @@ public final class PendingMessageManager {
|
|||
flags |= 1 << 22
|
||||
}
|
||||
|
||||
sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: message.text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, effect: messageEffectId, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), info: .acknowledgement, tag: dependencyTag)
|
||||
sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: message.text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, scheduleRepeatPeriod: nil, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, effect: messageEffectId, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), info: .acknowledgement, tag: dependencyTag)
|
||||
case let .media(inputMedia, text):
|
||||
if bubbleUpEmojiOrStickersets {
|
||||
flags |= Int32(1 << 15)
|
||||
|
|
@ -1770,7 +1770,7 @@ public final class PendingMessageManager {
|
|||
flags |= 1 << 22
|
||||
}
|
||||
|
||||
sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, effect: messageEffectId, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), tag: dependencyTag)
|
||||
sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, scheduleRepeatPeriod: nil, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, effect: messageEffectId, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), tag: dependencyTag)
|
||||
|> map(NetworkRequestResult.result)
|
||||
case let .forward(sourceInfo):
|
||||
var topMsgId: Int32?
|
||||
|
|
@ -1815,7 +1815,7 @@ public final class PendingMessageManager {
|
|||
}
|
||||
|
||||
if let forwardSourceInfoAttribute = forwardSourceInfoAttribute, let sourcePeer = transaction.getPeer(forwardSourceInfoAttribute.messageId.peerId), let sourceInputPeer = apiInputPeer(sourcePeer) {
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer, topMsgId: topMsgId, replyTo: replyTo, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, videoTimestamp: videoTimestamp, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), tag: dependencyTag)
|
||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer, topMsgId: topMsgId, replyTo: replyTo, scheduleDate: scheduleTime, scheduleRepeatPeriod: nil, sendAs: sendAsInputPeer, quickReplyShortcut: quickReplyShortcut, videoTimestamp: videoTimestamp, allowPaidStars: allowPaidStars, suggestedPost: suggestedPost), tag: dependencyTag)
|
||||
|> map(NetworkRequestResult.result)
|
||||
} else {
|
||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
||||
|
|
@ -2060,7 +2060,7 @@ public final class PendingMessageManager {
|
|||
if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
|
||||
isScheduled = true
|
||||
}
|
||||
if case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage {
|
||||
if case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage {
|
||||
if (flags2 & (1 << 4)) != 0 {
|
||||
isScheduled = true
|
||||
}
|
||||
|
|
@ -2104,7 +2104,7 @@ public final class PendingMessageManager {
|
|||
namespace = Namespaces.Message.QuickReplyCloud
|
||||
} else if let apiMessage = result.messages.first, message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
|
||||
namespace = Namespaces.Message.ScheduledCloud
|
||||
} else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 {
|
||||
} else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 {
|
||||
namespace = Namespaces.Message.ScheduledCloud
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
|||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 216
|
||||
return 217
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class UpdateMessageService: NSObject, MTMessageService {
|
|||
self.putNext(groups)
|
||||
}
|
||||
case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyHeader, entities, ttlPeriod):
|
||||
let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: .peerUser(userId: fromId), fromBoostsApplied: nil, peerId: Api.Peer.peerChat(chatId: chatId), savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil, reportDeliveryUntilDate: nil, paidMessageStars: nil, suggestedPost: nil)
|
||||
let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: .peerUser(userId: fromId), fromBoostsApplied: nil, peerId: Api.Peer.peerChat(chatId: chatId), savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil, reportDeliveryUntilDate: nil, paidMessageStars: nil, suggestedPost: nil, scheduleRepeatPeriod: nil)
|
||||
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
|
||||
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
|
||||
if groups.count != 0 {
|
||||
|
|
@ -74,7 +74,7 @@ class UpdateMessageService: NSObject, MTMessageService {
|
|||
|
||||
let generatedPeerId = Api.Peer.peerUser(userId: userId)
|
||||
|
||||
let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: generatedFromId, fromBoostsApplied: nil, peerId: generatedPeerId, savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil, reportDeliveryUntilDate: nil, paidMessageStars: nil, suggestedPost: nil)
|
||||
let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: generatedFromId, fromBoostsApplied: nil, peerId: generatedPeerId, savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil, reportDeliveryUntilDate: nil, paidMessageStars: nil, suggestedPost: nil, scheduleRepeatPeriod: nil)
|
||||
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
|
||||
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
|
||||
if groups.count != 0 {
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ extension Api.MessageMedia {
|
|||
extension Api.Message {
|
||||
var rawId: Int32 {
|
||||
switch self {
|
||||
case let .message(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return id
|
||||
case let .messageEmpty(_, id, _):
|
||||
return id
|
||||
|
|
@ -115,7 +115,7 @@ extension Api.Message {
|
|||
|
||||
func id(namespace: MessageId.Namespace = Namespaces.Message.Cloud) -> MessageId? {
|
||||
switch self {
|
||||
case let .message(_, flags2, id, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, flags2, id, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
var namespace = namespace
|
||||
if (flags2 & (1 << 4)) != 0 {
|
||||
namespace = Namespaces.Message.ScheduledCloud
|
||||
|
|
@ -136,7 +136,7 @@ extension Api.Message {
|
|||
|
||||
var peerId: PeerId? {
|
||||
switch self {
|
||||
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
let peerId: PeerId = messagePeerId.peerId
|
||||
return peerId
|
||||
case let .messageEmpty(_, _, peerId):
|
||||
|
|
@ -149,7 +149,7 @@ extension Api.Message {
|
|||
|
||||
var timestamp: Int32? {
|
||||
switch self {
|
||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return date
|
||||
case let .messageService(_, _, _, _, _, _, date, _, _, _):
|
||||
return date
|
||||
|
|
@ -160,7 +160,7 @@ extension Api.Message {
|
|||
|
||||
var preCachedResources: [(MediaResource, Data)]? {
|
||||
switch self {
|
||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return media?.preCachedResources
|
||||
default:
|
||||
return nil
|
||||
|
|
@ -169,7 +169,7 @@ extension Api.Message {
|
|||
|
||||
var preCachedStories: [StoryId: Api.StoryItem]? {
|
||||
switch self {
|
||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return media?.preCachedStories
|
||||
default:
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import Foundation
|
||||
import Postbox
|
||||
|
||||
public final class TelegramMediaLiveStream: Media, Equatable {
|
||||
public let peerIds: [PeerId] = []
|
||||
|
||||
public var id: MediaId? {
|
||||
return nil
|
||||
}
|
||||
|
||||
public let call: GroupCallReference
|
||||
|
||||
public init(call: GroupCallReference) {
|
||||
self.call = call
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.call = decoder.decodeCodable(GroupCallReference.self, forKey: "call")!
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeCodable(self.call, forKey: "call")
|
||||
}
|
||||
|
||||
public static func ==(lhs: TelegramMediaLiveStream, rhs: TelegramMediaLiveStream) -> Bool {
|
||||
return lhs.isEqual(to: rhs)
|
||||
}
|
||||
|
||||
public func isEqual(to other: Media) -> Bool {
|
||||
guard let other = other as? TelegramMediaLiveStream else {
|
||||
return false
|
||||
}
|
||||
|
||||
if self.call != other.call {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
public func isSemanticallyEqual(to other: Media) -> Bool {
|
||||
return self.isEqual(to: other)
|
||||
}
|
||||
}
|
||||
|
|
@ -621,7 +621,7 @@ public class JoinGroupCallE2E {
|
|||
}
|
||||
}
|
||||
|
||||
func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, callId: Int64, reference: InternalGroupCallReference, 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, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil, generateE2E: ((Data?) -> JoinGroupCallE2E?)?) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
|
||||
enum InternalJoinError {
|
||||
case error(JoinGroupCallError)
|
||||
case restart
|
||||
|
|
@ -719,9 +719,31 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||
}
|
||||
}
|
||||
|
||||
let getParticipantsRequest = _internal_getGroupCallParticipants(account: account, reference: reference, offset: "", ssrcs: [], limit: 100, sortAscending: true)
|
||||
|> mapError { _ -> InternalJoinError in
|
||||
return .error(.generic)
|
||||
let getParticipantsRequest: Signal<GroupCallParticipantsContext.State, InternalJoinError>
|
||||
if isStream {
|
||||
getParticipantsRequest = .single(GroupCallParticipantsContext.State(
|
||||
participants: [],
|
||||
nextParticipantsFetchOffset: nil,
|
||||
adminIds: Set(),
|
||||
isCreator: false,
|
||||
defaultParticipantsAreMuted: .init(isMuted: true, canChange: false),
|
||||
messagesAreEnabled: .init(isEnabled: true, canChange: false),
|
||||
sortAscending: true,
|
||||
recordingStartTimestamp: nil,
|
||||
title: nil,
|
||||
scheduleTimestamp: nil,
|
||||
subscribedToScheduled: false,
|
||||
totalCount: 0,
|
||||
isVideoEnabled: false,
|
||||
unmutedVideoLimit: 0,
|
||||
isStream: true,
|
||||
version: 0
|
||||
))
|
||||
} else {
|
||||
getParticipantsRequest = _internal_getGroupCallParticipants(account: account, reference: reference, offset: "", ssrcs: [], limit: 100, sortAscending: true)
|
||||
|> mapError { _ -> InternalJoinError in
|
||||
return .error(.generic)
|
||||
}
|
||||
}
|
||||
|
||||
return combineLatest(
|
||||
|
|
@ -835,7 +857,7 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||
case let .updateGroupCallParticipants(_, participants, _):
|
||||
loop: for participant in participants {
|
||||
switch participant {
|
||||
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, video, presentation):
|
||||
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, video, presentation, paidStarsTotal):
|
||||
let peerId: PeerId = apiPeerId.peerId
|
||||
let ssrc = UInt32(bitPattern: source)
|
||||
guard let peer = transaction.getPeer(peerId) else {
|
||||
|
|
@ -872,7 +894,8 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||
muteState: muteState,
|
||||
volume: volume,
|
||||
about: about,
|
||||
joinedVideo: joinedVideo
|
||||
joinedVideo: joinedVideo,
|
||||
paidStarsTotal: paidStarsTotal
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -1233,6 +1256,7 @@ public final class GroupCallParticipantsContext {
|
|||
public var volume: Int32?
|
||||
public var about: String?
|
||||
public var joinedVideo: Bool
|
||||
public var paidStarsTotal: Int64?
|
||||
|
||||
public init(
|
||||
id: Id,
|
||||
|
|
@ -1248,7 +1272,8 @@ public final class GroupCallParticipantsContext {
|
|||
muteState: MuteState?,
|
||||
volume: Int32?,
|
||||
about: String?,
|
||||
joinedVideo: Bool
|
||||
joinedVideo: Bool,
|
||||
paidStarsTotal: Int64?
|
||||
) {
|
||||
self.id = id
|
||||
self.peer = peer
|
||||
|
|
@ -1264,6 +1289,7 @@ public final class GroupCallParticipantsContext {
|
|||
self.volume = volume
|
||||
self.about = about
|
||||
self.joinedVideo = joinedVideo
|
||||
self.paidStarsTotal = paidStarsTotal
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
|
|
@ -1320,6 +1346,9 @@ public final class GroupCallParticipantsContext {
|
|||
if lhs.raiseHandRating != rhs.raiseHandRating {
|
||||
return false
|
||||
}
|
||||
if lhs.paidStarsTotal != rhs.paidStarsTotal {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -1547,6 +1576,7 @@ public final class GroupCallParticipantsContext {
|
|||
public var volume: Int32?
|
||||
public var about: String?
|
||||
public var joinedVideo: Bool
|
||||
public var paidStarsTotal: Int64?
|
||||
public var isMin: Bool
|
||||
|
||||
init(
|
||||
|
|
@ -1562,6 +1592,7 @@ public final class GroupCallParticipantsContext {
|
|||
volume: Int32?,
|
||||
about: String?,
|
||||
joinedVideo: Bool,
|
||||
paidStarsTotal: Int64?,
|
||||
isMin: Bool
|
||||
) {
|
||||
self.peerId = peerId
|
||||
|
|
@ -1576,6 +1607,7 @@ public final class GroupCallParticipantsContext {
|
|||
self.volume = volume
|
||||
self.about = about
|
||||
self.joinedVideo = joinedVideo
|
||||
self.paidStarsTotal = paidStarsTotal
|
||||
self.isMin = isMin
|
||||
}
|
||||
}
|
||||
|
|
@ -1679,7 +1711,8 @@ public final class GroupCallParticipantsContext {
|
|||
muteState: nil,
|
||||
volume: nil,
|
||||
about: nil,
|
||||
joinedVideo: false
|
||||
joinedVideo: false,
|
||||
paidStarsTotal: nil
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -2266,7 +2299,8 @@ public final class GroupCallParticipantsContext {
|
|||
muteState: muteState,
|
||||
volume: volume,
|
||||
about: participantUpdate.about,
|
||||
joinedVideo: participantUpdate.joinedVideo
|
||||
joinedVideo: participantUpdate.joinedVideo,
|
||||
paidStarsTotal: participantUpdate.paidStarsTotal
|
||||
)
|
||||
updatedParticipants.append(participant)
|
||||
}
|
||||
|
|
@ -2579,7 +2613,7 @@ public final class GroupCallParticipantsContext {
|
|||
}
|
||||
self.stateValue.state.defaultParticipantsAreMuted.isMuted = isMuted
|
||||
|
||||
self.updateDefaultMuteDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 0, call: self.reference.apiInputGroupCall, joinMuted: isMuted ? .boolTrue : .boolFalse, messagesEnabled: nil))
|
||||
self.updateDefaultMuteDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 0, call: self.reference.apiInputGroupCall, joinMuted: isMuted ? .boolTrue : .boolFalse, messagesEnabled: nil, sendPaidMessagesStars: nil))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
|
@ -2594,7 +2628,7 @@ public final class GroupCallParticipantsContext {
|
|||
}
|
||||
self.stateValue.state.messagesAreEnabled.isEnabled = isEnabled
|
||||
|
||||
self.updateMessagesEnabledDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 2, call: self.reference.apiInputGroupCall, joinMuted: nil, messagesEnabled: isEnabled ? .boolTrue : .boolFalse))
|
||||
self.updateMessagesEnabledDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 2, call: self.reference.apiInputGroupCall, joinMuted: nil, messagesEnabled: isEnabled ? .boolTrue : .boolFalse, sendPaidMessagesStars: nil))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
|
@ -2604,7 +2638,7 @@ public final class GroupCallParticipantsContext {
|
|||
}
|
||||
|
||||
public func resetInviteLinks() {
|
||||
self.resetInviteLinksDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 1, call: self.reference.apiInputGroupCall, joinMuted: nil, messagesEnabled: nil))
|
||||
self.resetInviteLinksDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 1, call: self.reference.apiInputGroupCall, joinMuted: nil, messagesEnabled: nil, sendPaidMessagesStars: nil))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
|
@ -2662,7 +2696,7 @@ public final class GroupCallParticipantsContext {
|
|||
extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
|
||||
init(_ apiParticipant: Api.GroupCallParticipant) {
|
||||
switch apiParticipant {
|
||||
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, video, presentation):
|
||||
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, video, presentation, paidStarsTotal):
|
||||
let peerId: PeerId = apiPeerId.peerId
|
||||
let ssrc = UInt32(bitPattern: source)
|
||||
let muted = (flags & (1 << 0)) != 0
|
||||
|
|
@ -2707,6 +2741,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
|
|||
volume: volume,
|
||||
about: about,
|
||||
joinedVideo: joinedVideo,
|
||||
paidStarsTotal: paidStarsTotal,
|
||||
isMin: isMin
|
||||
)
|
||||
}
|
||||
|
|
@ -3135,7 +3170,7 @@ func _internal_getVideoBroadcastPart(dataSource: AudioBroadcastDataSource, callI
|
|||
extension GroupCallParticipantsContext.Participant {
|
||||
init?(_ apiParticipant: Api.GroupCallParticipant, transaction: Transaction) {
|
||||
switch apiParticipant {
|
||||
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, video, presentation):
|
||||
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, video, presentation, paidStarsTotal):
|
||||
let peerId: PeerId = apiPeerId.peerId
|
||||
let ssrc = UInt32(bitPattern: source)
|
||||
guard let peer = transaction.getPeer(peerId) else {
|
||||
|
|
@ -3173,7 +3208,8 @@ extension GroupCallParticipantsContext.Participant {
|
|||
muteState: muteState,
|
||||
volume: volume,
|
||||
about: about,
|
||||
joinedVideo: joinedVideo
|
||||
joinedVideo: joinedVideo,
|
||||
paidStarsTotal: paidStarsTotal
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3205,7 +3241,7 @@ public enum GetGroupCallStreamCredentialsError {
|
|||
case generic
|
||||
}
|
||||
|
||||
func _internal_getGroupCallStreamCredentials(account: Account, peerId: PeerId, revokePreviousCredentials: Bool) -> Signal<GroupCallStreamCredentials, GetGroupCallStreamCredentialsError> {
|
||||
func _internal_getGroupCallStreamCredentials(account: Account, peerId: PeerId, isLiveStream: Bool, revokePreviousCredentials: Bool) -> Signal<GroupCallStreamCredentials, GetGroupCallStreamCredentialsError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|
|
@ -3215,7 +3251,11 @@ func _internal_getGroupCallStreamCredentials(account: Account, peerId: PeerId, r
|
|||
return .fail(.generic)
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.phone.getGroupCallStreamRtmpUrl(peer: inputPeer, revoke: revokePreviousCredentials ? .boolTrue : .boolFalse))
|
||||
var flags: Int32 = 0
|
||||
if isLiveStream {
|
||||
flags |= 1 << 0
|
||||
}
|
||||
return account.network.request(Api.functions.phone.getGroupCallStreamRtmpUrl(flags: flags, peer: inputPeer, revoke: revokePreviousCredentials ? .boolTrue : .boolFalse))
|
||||
|> mapError { _ -> GetGroupCallStreamCredentialsError in
|
||||
return .generic
|
||||
}
|
||||
|
|
@ -3295,7 +3335,7 @@ public enum RevokeConferenceInviteLinkError {
|
|||
}
|
||||
|
||||
func _internal_revokeConferenceInviteLink(account: Account, reference: InternalGroupCallReference, link: String) -> Signal<GroupCallInviteLinks, RevokeConferenceInviteLinkError> {
|
||||
return account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 1, call: reference.apiInputGroupCall, joinMuted: .boolFalse, messagesEnabled: nil))
|
||||
return account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 1, call: reference.apiInputGroupCall, joinMuted: .boolFalse, messagesEnabled: nil, sendPaidMessagesStars: nil))
|
||||
|> mapError { _ -> RevokeConferenceInviteLinkError in
|
||||
return .generic
|
||||
}
|
||||
|
|
@ -3882,12 +3922,14 @@ public final class GroupCallMessagesContext {
|
|||
arc4random_buf(&randomId, 8)
|
||||
}
|
||||
self.sendMessageDisposables.add(self.account.network.request(Api.functions.phone.sendGroupCallMessage(
|
||||
flags: 0,
|
||||
call: self.reference.apiInputGroupCall,
|
||||
randomId: randomId,
|
||||
message: .textWithEntities(
|
||||
text: text,
|
||||
entities: apiEntitiesFromMessageTextEntities(entities, associatedPeers: SimpleDictionary())
|
||||
)
|
||||
),
|
||||
allowPaidStars: nil
|
||||
)).startStrict())
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ public extension TelegramEngine {
|
|||
return _internal_getGroupCallParticipants(account: self.account, reference: reference, offset: offset, ssrcs: ssrcs, limit: limit, sortAscending: sortAscending)
|
||||
}
|
||||
|
||||
public func joinGroupCall(peerId: PeerId?, joinAs: PeerId?, callId: Int64, reference: InternalGroupCallReference, 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, preferMuted: preferMuted, joinPayload: joinPayload, peerAdminIds: peerAdminIds, inviteHash: inviteHash, generateE2E: generateE2E)
|
||||
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 joinGroupCallAsScreencast(callId: Int64, accessHash: Int64, joinPayload: String) -> Signal<JoinGroupCallAsScreencastResult, JoinGroupCallError> {
|
||||
|
|
@ -176,8 +176,8 @@ public extension TelegramEngine {
|
|||
}
|
||||
}
|
||||
|
||||
public func getGroupCallStreamCredentials(peerId: EnginePeer.Id, revokePreviousCredentials: Bool) -> Signal<GroupCallStreamCredentials, GetGroupCallStreamCredentialsError> {
|
||||
return _internal_getGroupCallStreamCredentials(account: self.account, peerId: peerId, revokePreviousCredentials: revokePreviousCredentials)
|
||||
public func getGroupCallStreamCredentials(peerId: EnginePeer.Id, isLiveStream: Bool, revokePreviousCredentials: Bool) -> Signal<GroupCallStreamCredentials, GetGroupCallStreamCredentialsError> {
|
||||
return _internal_getGroupCallStreamCredentials(account: self.account, peerId: peerId, isLiveStream: isLiveStream, revokePreviousCredentials: revokePreviousCredentials)
|
||||
}
|
||||
|
||||
public func getGroupCallPersistentSettings(callId: Int64) -> Signal<CodableEntry?, NoError> {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import SwiftSignalKit
|
|||
func _internal_importContact(account: Account, firstName: String, lastName: String, phoneNumber: String) -> Signal<PeerId?, NoError> {
|
||||
let accountPeerId = account.peerId
|
||||
|
||||
let input = Api.InputContact.inputPhoneContact(clientId: 1, phone: phoneNumber, firstName: firstName, lastName: lastName)
|
||||
let input = Api.InputContact.inputPhoneContact(flags: 0, clientId: 1, phone: phoneNumber, firstName: firstName, lastName: lastName, note: nil)
|
||||
|
||||
return account.network.request(Api.functions.contacts.importContacts(contacts: [input]))
|
||||
|> map(Optional.init)
|
||||
|
|
|
|||
|
|
@ -645,7 +645,7 @@ public final class EngineStoryViewListContext {
|
|||
folderIds: item.folderIds
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends, isLiveStream: updatedItem.isLiveStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ func _internal_forwardGameWithScore(account: Account, messageId: MessageId, to p
|
|||
flags |= (1 << 13)
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: fromInputPeer, id: [messageId.id], randomId: [Int64.random(in: Int64.min ... Int64.max)], toPeer: toInputPeer, topMsgId: threadId.flatMap { Int32(clamping: $0) }, replyTo: nil, scheduleDate: nil, sendAs: sendAsInputPeer, quickReplyShortcut: nil, videoTimestamp: nil, allowPaidStars: nil, suggestedPost: nil))
|
||||
return account.network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: fromInputPeer, id: [messageId.id], randomId: [Int64.random(in: Int64.min ... Int64.max)], toPeer: toInputPeer, topMsgId: threadId.flatMap { Int32(clamping: $0) }, replyTo: nil, scheduleDate: nil, scheduleRepeatPeriod: nil, sendAs: sendAsInputPeer, quickReplyShortcut: nil, videoTimestamp: nil, allowPaidStars: nil, suggestedPost: nil))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
return .single(nil)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ public enum EngineMedia: Equatable {
|
|||
case giveawayResults(TelegramMediaGiveawayResults)
|
||||
case paidContent(TelegramMediaPaidContent)
|
||||
case todo(TelegramMediaTodo)
|
||||
case liveStream(TelegramMediaLiveStream)
|
||||
}
|
||||
|
||||
public extension EngineMedia {
|
||||
|
|
@ -62,6 +63,8 @@ public extension EngineMedia {
|
|||
return paidContent.id
|
||||
case .todo:
|
||||
return nil
|
||||
case .liveStream:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,6 +108,8 @@ public extension EngineMedia {
|
|||
self = .paidContent(paidContent)
|
||||
case let todo as TelegramMediaTodo:
|
||||
self = .todo(todo)
|
||||
case let liveStream as TelegramMediaLiveStream:
|
||||
self = .liveStream(liveStream)
|
||||
default:
|
||||
preconditionFailure()
|
||||
}
|
||||
|
|
@ -148,6 +153,8 @@ public extension EngineMedia {
|
|||
return paidContent
|
||||
case let .todo(todo):
|
||||
return todo
|
||||
case let .liveStream(liveStream):
|
||||
return liveStream
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ func _internal_requestClosePoll(postbox: Postbox, network: Network, stateManager
|
|||
pollMediaFlags |= 1 << 1
|
||||
}
|
||||
|
||||
return network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: nil, media: .inputMediaPoll(flags: pollMediaFlags, poll: .poll(id: poll.pollId.id, flags: pollFlags, question: .textWithEntities(text: poll.text, entities: apiEntitiesFromMessageTextEntities(poll.textEntities, associatedPeers: SimpleDictionary())), answers: poll.options.map({ $0.apiOption }), closePeriod: poll.deadlineTimeout, closeDate: nil), correctAnswers: correctAnswers, solution: mappedSolution, solutionEntities: mappedSolutionEntities), replyMarkup: nil, entities: nil, scheduleDate: nil, quickReplyShortcutId: nil))
|
||||
return network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: nil, media: .inputMediaPoll(flags: pollMediaFlags, poll: .poll(id: poll.pollId.id, flags: pollFlags, question: .textWithEntities(text: poll.text, entities: apiEntitiesFromMessageTextEntities(poll.textEntities, associatedPeers: SimpleDictionary())), answers: poll.options.map({ $0.apiOption }), closePeriod: poll.deadlineTimeout, closeDate: nil), correctAnswers: correctAnswers, solution: mappedSolution, solutionEntities: mappedSolutionEntities), replyMarkup: nil, entities: nil, scheduleDate: nil, scheduleRepeatPeriod: nil, quickReplyShortcutId: nil))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
return .single(nil)
|
||||
|
|
|
|||
|
|
@ -290,6 +290,10 @@ public enum Stories {
|
|||
public let authorId: PeerId?
|
||||
public let folderIds: [Int64]?
|
||||
|
||||
public var isLiveStream: Bool {
|
||||
return self.media is TelegramMediaLiveStream
|
||||
}
|
||||
|
||||
public init(
|
||||
id: Int32,
|
||||
timestamp: Int32,
|
||||
|
|
@ -620,6 +624,15 @@ public enum Stories {
|
|||
}
|
||||
}
|
||||
|
||||
public var isLiveStream: Bool {
|
||||
switch self {
|
||||
case let .item(item):
|
||||
return item.media is TelegramMediaLiveStream
|
||||
case .placeholder:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
|
|
@ -759,6 +772,7 @@ public final class EngineStorySubscriptions: Equatable {
|
|||
public let peer: EnginePeer
|
||||
public let hasUnseen: Bool
|
||||
public let hasUnseenCloseFriends: Bool
|
||||
public let hasLiveItems: Bool
|
||||
public let hasPending: Bool
|
||||
public let storyCount: Int
|
||||
public let unseenCount: Int
|
||||
|
|
@ -768,6 +782,7 @@ public final class EngineStorySubscriptions: Equatable {
|
|||
peer: EnginePeer,
|
||||
hasUnseen: Bool,
|
||||
hasUnseenCloseFriends: Bool,
|
||||
hasLiveItems: Bool,
|
||||
hasPending: Bool,
|
||||
storyCount: Int,
|
||||
unseenCount: Int,
|
||||
|
|
@ -776,6 +791,7 @@ public final class EngineStorySubscriptions: Equatable {
|
|||
self.peer = peer
|
||||
self.hasUnseen = hasUnseen
|
||||
self.hasUnseenCloseFriends = hasUnseenCloseFriends
|
||||
self.hasLiveItems = hasLiveItems
|
||||
self.hasPending = hasPending
|
||||
self.storyCount = storyCount
|
||||
self.unseenCount = unseenCount
|
||||
|
|
@ -795,6 +811,9 @@ public final class EngineStorySubscriptions: Equatable {
|
|||
if lhs.hasUnseenCloseFriends != rhs.hasUnseenCloseFriends {
|
||||
return false
|
||||
}
|
||||
if lhs.hasLiveItems != rhs.hasLiveItems {
|
||||
return false
|
||||
}
|
||||
if lhs.storyCount != rhs.storyCount {
|
||||
return false
|
||||
}
|
||||
|
|
@ -1093,6 +1112,22 @@ func _internal_cancelStoryUpload(account: Account, stableId: Int32) {
|
|||
}).start()
|
||||
}
|
||||
|
||||
func _internal_beginStoryLivestream(account: Account) -> Signal<Never, NoError> {
|
||||
var flags: Int32 = 0
|
||||
flags |= 1 << 5
|
||||
return account.network.request(Api.functions.stories.startLive(flags: flags, peer: .inputPeerSelf, caption: nil, entities: nil, privacyRules: [.inputPrivacyValueAllowAll], randomId: Int64.random(in: Int64.min ... Int64.max)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { updates -> Signal<Never, NoError> in
|
||||
if let updates {
|
||||
account.stateManager.addUpdates(updates)
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
private struct PendingStoryIdMappingKey: Hashable {
|
||||
var peerId: PeerId
|
||||
var stableId: Int32
|
||||
|
|
@ -1307,7 +1342,7 @@ func _internal_uploadStoryImpl(
|
|||
folderIds: item.folderIds
|
||||
)
|
||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||
items.append(StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends))
|
||||
items.append(StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends, isLiveStream: updatedItem.isLiveStream))
|
||||
}
|
||||
updatedItems.append(updatedItem)
|
||||
}
|
||||
|
|
@ -1749,7 +1784,7 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
|
|||
folderIds: item.folderIds
|
||||
)
|
||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends, isLiveStream: updatedItem.isLiveStream)
|
||||
}
|
||||
|
||||
updatedItems.append(updatedItem)
|
||||
|
|
@ -1946,7 +1981,7 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In
|
|||
folderIds: item.folderIds
|
||||
)
|
||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends, isLiveStream: updatedItem.isLiveStream)
|
||||
}
|
||||
|
||||
updatedItems.append(updatedItem)
|
||||
|
|
@ -2490,7 +2525,7 @@ func _internal_refreshStories(account: Account, peerId: PeerId, ids: [Int32]) ->
|
|||
if let updatedItem = result.first(where: { $0.id == currentItems[i].id }) {
|
||||
if case .item = updatedItem {
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends, isLiveStream: updatedItem.isLiveStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2772,7 +2807,7 @@ func _internal_setStoryReaction(account: Account, peerId: EnginePeer.Id, id: Int
|
|||
))
|
||||
updatedItemValue = updatedItem
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends, isLiveStream: updatedItem.isLiveStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ public final class StorySubscriptionsContext {
|
|||
updatedPeerEntries.append(previousEntry)
|
||||
} else {
|
||||
if let codedEntry = CodableEntry(storedItem) {
|
||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends))
|
||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends, isLiveStream: storedItem.isLiveStream))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2608,7 +2608,7 @@ public final class PeerExpiringStoryListContext {
|
|||
updatedPeerEntries.append(previousEntry)
|
||||
} else {
|
||||
if let codedEntry = CodableEntry(storedItem) {
|
||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends))
|
||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends, isLiveStream: storedItem.isLiveStream))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2699,6 +2699,20 @@ public final class PeerExpiringStoryListContext {
|
|||
return self.items.contains(where: { $0.id > self.maxReadId && $0.isCloseFriends })
|
||||
}
|
||||
|
||||
public var hasLiveItems: Bool {
|
||||
return self.items.contains(where: { item in
|
||||
switch item {
|
||||
case let .item(item):
|
||||
if case .liveStream = item.media {
|
||||
return true
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
public init(items: [Item], isCached: Bool, maxReadId: Int32, isLoading: Bool) {
|
||||
self.items = items
|
||||
self.isCached = isCached
|
||||
|
|
@ -2775,7 +2789,7 @@ public func _internal_pollPeerStories(postbox: Postbox, network: Network, accoun
|
|||
updatedPeerEntries.append(previousEntry)
|
||||
} else {
|
||||
if let codedEntry = CodableEntry(storedItem) {
|
||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends))
|
||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends, isLiveStream: storedItem.isLiveStream))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -999,6 +999,7 @@ public extension TelegramEngine {
|
|||
peer: EnginePeer(accountPeer),
|
||||
hasUnseen: false,
|
||||
hasUnseenCloseFriends: false,
|
||||
hasLiveItems: false,
|
||||
hasPending: accountPendingItemCount != 0,
|
||||
storyCount: accountPendingItemCount,
|
||||
unseenCount: 0,
|
||||
|
|
@ -1015,8 +1016,9 @@ public extension TelegramEngine {
|
|||
let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self)
|
||||
var hasUnseen = false
|
||||
var hasUnseenCloseFriends = false
|
||||
var hasLiveItems = false
|
||||
var unseenCount = 0
|
||||
if let peerState = peerState {
|
||||
if let peerState {
|
||||
hasUnseen = peerState.maxReadId < lastEntry.id
|
||||
|
||||
for item in itemsView.items {
|
||||
|
|
@ -1030,6 +1032,9 @@ public extension TelegramEngine {
|
|||
hasUnseenCloseFriends = true
|
||||
}
|
||||
}
|
||||
if item.media is TelegramMediaLiveStream {
|
||||
hasLiveItems = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1038,6 +1043,7 @@ public extension TelegramEngine {
|
|||
peer: EnginePeer(accountPeer),
|
||||
hasUnseen: hasUnseen,
|
||||
hasUnseenCloseFriends: hasUnseenCloseFriends,
|
||||
hasLiveItems: hasLiveItems,
|
||||
hasPending: accountPendingItemCount != 0,
|
||||
storyCount: itemsView.items.count + accountPendingItemCount,
|
||||
unseenCount: unseenCount,
|
||||
|
|
@ -1079,6 +1085,7 @@ public extension TelegramEngine {
|
|||
let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self)
|
||||
var hasUnseen = false
|
||||
var hasUnseenCloseFriends = false
|
||||
var hasLiveItems = false
|
||||
var unseenCount = 0
|
||||
if let peerState = peerState {
|
||||
hasUnseen = peerState.maxReadId < lastEntry.id
|
||||
|
|
@ -1091,6 +1098,9 @@ public extension TelegramEngine {
|
|||
if item.isCloseFriends {
|
||||
hasUnseenCloseFriends = true
|
||||
}
|
||||
if item.media is TelegramMediaLiveStream {
|
||||
hasLiveItems = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1116,6 +1126,7 @@ public extension TelegramEngine {
|
|||
peer: EnginePeer(peer),
|
||||
hasUnseen: hasUnseen,
|
||||
hasUnseenCloseFriends: hasUnseenCloseFriends,
|
||||
hasLiveItems: hasLiveItems,
|
||||
hasPending: maxPendingTimestamp != nil,
|
||||
storyCount: itemsView.items.count,
|
||||
unseenCount: unseenCount,
|
||||
|
|
@ -1153,6 +1164,7 @@ public extension TelegramEngine {
|
|||
peer: EnginePeer(peer),
|
||||
hasUnseen: false,
|
||||
hasUnseenCloseFriends: false,
|
||||
hasLiveItems: false,
|
||||
hasPending: true,
|
||||
storyCount: 0,
|
||||
unseenCount: 0,
|
||||
|
|
@ -1387,7 +1399,7 @@ public extension TelegramEngine {
|
|||
folderIds: item.folderIds
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends, isLiveStream: updatedItem.isLiveStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1402,6 +1414,10 @@ public extension TelegramEngine {
|
|||
return _internal_uploadStory(account: self.account, target: target, media: media, mediaAreas: mediaAreas, text: text, entities: entities, pin: pin, privacy: privacy, isForwardingDisabled: isForwardingDisabled, period: period, randomId: randomId, forwardInfo: forwardInfo, folders: folders, uploadInfo: uploadInfo)
|
||||
}
|
||||
|
||||
public func beginStoryLivestream() -> Signal<Never, NoError> {
|
||||
return _internal_beginStoryLivestream(account: self.account)
|
||||
}
|
||||
|
||||
public func allStoriesUploadEvents() -> Signal<(Int32, Int32), NoError> {
|
||||
guard let pendingStoryManager = self.account.pendingStoryManager else {
|
||||
return .complete()
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
|
|||
private var avatarVideoNode: AvatarVideoNode?
|
||||
|
||||
public private(set) var avatarStoryView: ComponentView<Empty>?
|
||||
public var storyData: (hasUnseen: Bool, hasUnseenCloseFriends: Bool)?
|
||||
public var storyData: (hasUnseen: Bool, hasUnseenCloseFriends: Bool, hasLiveItems: Bool)?
|
||||
|
||||
public var statusView: ComponentView<Empty>
|
||||
private var starView: StarView?
|
||||
|
|
@ -241,6 +241,7 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
|
|||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||
hasUnseen: storyData.hasUnseen,
|
||||
hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends,
|
||||
hasLiveItems: storyData.hasLiveItems,
|
||||
colors: AvatarStoryIndicatorComponent.Colors(theme: theme),
|
||||
activeLineWidth: 1.0,
|
||||
inactiveLineWidth: 1.0,
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ public struct ChatMessageItemLayoutConstants {
|
|||
let instantVideo = ChatMessageItemInstantVideoConstants(insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0), dimensions: CGSize(width: 212.0, height: 212.0))
|
||||
let wallpapers = ChatMessageItemWallpaperLayoutConstants(maxTextWidth: 180.0)
|
||||
|
||||
return ChatMessageItemLayoutConstants(avatarInset: 44.0, timestampHeaderHeight: 34.0, timestampDateAndTopicHeaderHeight: 7.0 * 2.0 + 20.0 * 2.0 + 7.0, bubble: bubble, image: image, video: video, text: text, file: file, instantVideo: instantVideo, wallpapers: wallpapers)
|
||||
return ChatMessageItemLayoutConstants(avatarInset: 34.0 + 4.0, timestampHeaderHeight: 34.0, timestampDateAndTopicHeaderHeight: 7.0 * 2.0 + 20.0 * 2.0 + 7.0, bubble: bubble, image: image, video: video, text: text, file: file, instantVideo: instantVideo, wallpapers: wallpapers)
|
||||
}
|
||||
|
||||
public static var regular: ChatMessageItemLayoutConstants {
|
||||
|
|
@ -156,7 +156,7 @@ public struct ChatMessageItemLayoutConstants {
|
|||
let instantVideo = ChatMessageItemInstantVideoConstants(insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0), dimensions: CGSize(width: 240.0, height: 240.0))
|
||||
let wallpapers = ChatMessageItemWallpaperLayoutConstants(maxTextWidth: 180.0)
|
||||
|
||||
return ChatMessageItemLayoutConstants(avatarInset: 44.0, timestampHeaderHeight: 34.0, timestampDateAndTopicHeaderHeight: 7.0 * 2.0 + 20.0 * 2.0 + 7.0, bubble: bubble, image: image, video: video, text: text, file: file, instantVideo: instantVideo, wallpapers: wallpapers)
|
||||
return ChatMessageItemLayoutConstants(avatarInset: 34.0 + 4.0, timestampHeaderHeight: 34.0, timestampDateAndTopicHeaderHeight: 7.0 * 2.0 + 20.0 * 2.0 + 7.0, bubble: bubble, image: image, video: video, text: text, file: file, instantVideo: instantVideo, wallpapers: wallpapers)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -875,6 +875,10 @@ public final class ChatMessageDateHeaderNodeImpl: ListViewItemHeaderNode, ChatMe
|
|||
}
|
||||
}
|
||||
|
||||
private func avatarHeaderSize() -> CGFloat {
|
||||
return 34.0
|
||||
}
|
||||
|
||||
public final class ChatMessageAvatarHeader: ListViewItemHeader {
|
||||
public struct Id: Hashable {
|
||||
public var peerId: PeerId
|
||||
|
|
@ -923,7 +927,7 @@ public final class ChatMessageAvatarHeader: ListViewItemHeader {
|
|||
public let stickDirection: ListViewItemHeaderStickDirection
|
||||
public let stickOverInsets: Bool = false
|
||||
|
||||
public let height: CGFloat = 40.0
|
||||
public let height: CGFloat = avatarHeaderSize()
|
||||
|
||||
public func combinesWith(other: ListViewItemHeader) -> Bool {
|
||||
if let other = other as? ChatMessageAvatarHeader, other.id == self.id {
|
||||
|
|
@ -1053,9 +1057,9 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat
|
|||
if let previousPeer = self.peer, previousPeer.nameColor != peer.nameColor {
|
||||
self.peer = peer
|
||||
if peer.smallProfileImage != nil {
|
||||
self.avatarNode.setPeerV2(context: self.context, theme: self.presentationData.theme.theme, peer: EnginePeer(peer), authorOfMessage: self.messageReference, overrideImage: nil, emptyColor: .black, synchronousLoad: false, displayDimensions: CGSize(width: 40.0, height: 40.0))
|
||||
self.avatarNode.setPeerV2(context: self.context, theme: self.presentationData.theme.theme, peer: EnginePeer(peer), authorOfMessage: self.messageReference, overrideImage: nil, emptyColor: .black, synchronousLoad: false, displayDimensions: CGSize(width: avatarHeaderSize(), height: avatarHeaderSize()))
|
||||
} else {
|
||||
self.avatarNode.setPeer(context: self.context, theme: self.presentationData.theme.theme, peer: EnginePeer(peer), authorOfMessage: self.messageReference, overrideImage: nil, emptyColor: .black, synchronousLoad: false, displayDimensions: CGSize(width: 40.0, height: 40.0))
|
||||
self.avatarNode.setPeer(context: self.context, theme: self.presentationData.theme.theme, peer: EnginePeer(peer), authorOfMessage: self.messageReference, overrideImage: nil, emptyColor: .black, synchronousLoad: false, displayDimensions: CGSize(width: avatarHeaderSize(), height: avatarHeaderSize()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1072,9 +1076,9 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat
|
|||
overrideImage = .deletedIcon
|
||||
}
|
||||
if peer.smallProfileImage != nil {
|
||||
self.avatarNode.setPeerV2(context: context, theme: theme, peer: EnginePeer(peer), authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: 40.0, height: 40.0))
|
||||
self.avatarNode.setPeerV2(context: context, theme: theme, peer: EnginePeer(peer), authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: avatarHeaderSize(), height: avatarHeaderSize()))
|
||||
} else {
|
||||
self.avatarNode.setPeer(context: context, theme: theme, peer: EnginePeer(peer), authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: 40.0, height: 40.0))
|
||||
self.avatarNode.setPeer(context: context, theme: theme, peer: EnginePeer(peer), authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: avatarHeaderSize(), height: avatarHeaderSize()))
|
||||
}
|
||||
|
||||
if peer.isPremium && context.sharedContext.energyUsageSettings.autoplayVideo {
|
||||
|
|
@ -1111,7 +1115,7 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat
|
|||
strongSelf.avatarNode.contentNode.addSubnode(videoNode)
|
||||
strongSelf.avatarVideoNode = videoNode
|
||||
}
|
||||
videoNode.update(peer: EnginePeer(peer), photo: photo, size: CGSize(width: 40.0, height: 40.0))
|
||||
videoNode.update(peer: EnginePeer(peer), photo: photo, size: CGSize(width: avatarHeaderSize(), height: avatarHeaderSize()))
|
||||
|
||||
if strongSelf.hierarchyTrackingLayer == nil {
|
||||
let hierarchyTrackingLayer = HierarchyTrackingLayer()
|
||||
|
|
@ -1220,8 +1224,8 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat
|
|||
}
|
||||
|
||||
override public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: leftInset + 8.0, y: 0.0), size: CGSize(width: 40.0, height: 40.0)))
|
||||
let avatarFrame = CGRect(origin: CGPoint(), size: CGSize(width: 40.0, height: 40.0))
|
||||
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: leftInset + 8.0, y: -3.0), size: CGSize(width: avatarHeaderSize(), height: avatarHeaderSize())))
|
||||
let avatarFrame = CGRect(origin: CGPoint(), size: CGSize(width: avatarHeaderSize(), height: avatarHeaderSize()))
|
||||
self.avatarNode.position = avatarFrame.center
|
||||
self.avatarNode.bounds = CGRect(origin: CGPoint(), size: avatarFrame.size)
|
||||
self.avatarNode.updateSize(size: avatarFrame.size)
|
||||
|
|
@ -1265,7 +1269,7 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat
|
|||
var avatarTransform: CATransform3D = CATransform3DIdentity
|
||||
if isHidden {
|
||||
let scale: CGFloat = isHidden ? 0.001 : 1.0
|
||||
avatarTransform = CATransform3DTranslate(avatarTransform, -40.0 * 0.5, 40.0 * 0.5, 0.0)
|
||||
avatarTransform = CATransform3DTranslate(avatarTransform, -avatarHeaderSize() * 0.5, avatarHeaderSize() * 0.5, 0.0)
|
||||
avatarTransform = CATransform3DScale(avatarTransform, scale, scale, 1.0)
|
||||
}
|
||||
transition.updateTransform(node: self.avatarNode, transform: avatarTransform)
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ private final class ChatMessageSelectionInputPanelNodeViewForOverlayContent: UIV
|
|||
}
|
||||
}
|
||||
|
||||
private final class GlassButtonView: HighlightTrackingButton {
|
||||
private final class GlassButtonView: UIView {
|
||||
private struct Params: Equatable {
|
||||
let theme: PresentationTheme
|
||||
let size: CGSize
|
||||
|
|
@ -86,6 +86,7 @@ private final class GlassButtonView: HighlightTrackingButton {
|
|||
}
|
||||
|
||||
private let backgroundView: GlassBackgroundView
|
||||
let button: HighlightTrackingButton
|
||||
private let iconView: GlassBackgroundView.ContentImageView
|
||||
|
||||
private var params: Params?
|
||||
|
|
@ -96,7 +97,7 @@ private final class GlassButtonView: HighlightTrackingButton {
|
|||
}
|
||||
}
|
||||
|
||||
override var isEnabled: Bool {
|
||||
var isEnabled: Bool = true {
|
||||
didSet {
|
||||
self.updateIsEnabled()
|
||||
}
|
||||
|
|
@ -125,19 +126,25 @@ private final class GlassButtonView: HighlightTrackingButton {
|
|||
self.iconView = GlassBackgroundView.ContentImageView()
|
||||
self.backgroundView.contentView.addSubview(self.iconView)
|
||||
|
||||
self.button = HighlightTrackingButton()
|
||||
self.backgroundView.contentView.addSubview(self.button)
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
|
||||
self.highligthedChanged = { [weak self] highlighted in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if highlighted && self.isEnabled && !self.isImplicitlyDisabled {
|
||||
self.backgroundView.contentView.alpha = 0.6
|
||||
} else {
|
||||
self.backgroundView.contentView.alpha = 1.0
|
||||
self.backgroundView.contentView.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2)
|
||||
if #available(iOS 26.0, *) {
|
||||
} else {
|
||||
self.button.highligthedChanged = { [weak self] highlighted in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if highlighted && self.isEnabled && !self.isImplicitlyDisabled {
|
||||
self.backgroundView.contentView.alpha = 0.6
|
||||
} else {
|
||||
self.backgroundView.contentView.alpha = 1.0
|
||||
self.backgroundView.contentView.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -156,17 +163,22 @@ private final class GlassButtonView: HighlightTrackingButton {
|
|||
}
|
||||
|
||||
private func updateImpl(params: Params, transition: ComponentTransition) {
|
||||
let isEnabled = self.isEnabled && !self.isImplicitlyDisabled
|
||||
|
||||
if let image = self.iconView.image {
|
||||
let iconFrame = image.size.centered(in: CGRect(origin: CGPoint(), size: params.size))
|
||||
transition.setFrame(view: self.iconView, frame: iconFrame)
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: params.size))
|
||||
self.backgroundView.update(size: params.size, cornerRadius: min(params.size.width, params.size.height) * 0.5, isDark: params.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: params.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: transition)
|
||||
transition.setFrame(view: self.button, frame: CGRect(origin: CGPoint(), size: params.size))
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: params.size))
|
||||
self.backgroundView.update(size: params.size, cornerRadius: min(params.size.width, params.size.height) * 0.5, isDark: params.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: params.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), isInteractive: isEnabled, transition: transition)
|
||||
|
||||
let isEnabled = self.isEnabled && !self.isImplicitlyDisabled
|
||||
self.iconView.alpha = isEnabled ? 1.0 : 0.5
|
||||
self.iconView.tintMask.alpha = self.iconView.alpha
|
||||
|
||||
self.button.isEnabled = isEnabled
|
||||
}
|
||||
|
||||
private func updateIsEnabled() {
|
||||
|
|
@ -255,12 +267,12 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
|||
self.forwardButton.isImplicitlyDisabled = true
|
||||
self.shareButton.isImplicitlyDisabled = true
|
||||
|
||||
self.deleteButton.addTarget(self, action: #selector(self.deleteButtonPressed), for: .touchUpInside)
|
||||
self.reportButton.addTarget(self, action: #selector(self.reportButtonPressed), for: .touchUpInside)
|
||||
self.forwardButton.addTarget(self, action: #selector(self.forwardButtonPressed), for: .touchUpInside)
|
||||
self.shareButton.addTarget(self, action: #selector(self.shareButtonPressed), for: .touchUpInside)
|
||||
self.tagButton.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside)
|
||||
self.tagEditButton.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside)
|
||||
self.deleteButton.button.addTarget(self, action: #selector(self.deleteButtonPressed), for: .touchUpInside)
|
||||
self.reportButton.button.addTarget(self, action: #selector(self.reportButtonPressed), for: .touchUpInside)
|
||||
self.forwardButton.button.addTarget(self, action: #selector(self.forwardButtonPressed), for: .touchUpInside)
|
||||
self.shareButton.button.addTarget(self, action: #selector(self.shareButtonPressed), for: .touchUpInside)
|
||||
self.tagButton.button.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside)
|
||||
self.tagEditButton.button.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
|
|
|||
|
|
@ -286,6 +286,7 @@ public class ChatMessageStoryMentionContentNode: ChatMessageBubbleContentNode {
|
|||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||
hasUnseen: hasUnseen,
|
||||
hasUnseenCloseFriendsItems: hasUnseen && (story?.isCloseFriends ?? false),
|
||||
hasLiveItems: false,
|
||||
colors: storyColors,
|
||||
activeLineWidth: 3.0,
|
||||
inactiveLineWidth: 1.0 + UIScreenPixel,
|
||||
|
|
|
|||
|
|
@ -273,7 +273,7 @@ public final class ChatFloatingTopicsPanel: Component {
|
|||
}
|
||||
|
||||
transition.setFrame(view: self.containerView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
self.containerView.update(size: availableSize, transition: transition)
|
||||
self.containerView.update(size: availableSize, isDark: component.theme.overallDarkAppearance, transition: transition)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag
|
|||
|
||||
self.micButton.layer.allowsGroupOpacity = true
|
||||
self.view.addSubview(self.micButtonBackgroundView)
|
||||
self.view.addSubview(self.micButton)
|
||||
self.micButtonBackgroundView.contentView.addSubview(self.micButton)
|
||||
|
||||
self.addSubnode(self.sendContainerNode)
|
||||
self.sendContainerNode.view.addSubview(self.sendButtonBackgroundView)
|
||||
|
|
|
|||
|
|
@ -2405,7 +2405,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
if useBounceAnimation, case let .animated(_, curve) = transition, case .spring = curve {
|
||||
textInputContainerBackgroundTransition = textInputContainerBackgroundTransition.withUserData(GlassBackgroundView.TransitionFlagBounce())
|
||||
}
|
||||
self.textInputContainerBackgroundView.update(size: textInputContainerBackgroundFrame.size, cornerRadius: floor(minimalInputHeight * 0.5), isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), isInteractive: false, transition: textInputContainerBackgroundTransition)
|
||||
self.textInputContainerBackgroundView.update(size: textInputContainerBackgroundFrame.size, cornerRadius: floor(minimalInputHeight * 0.5), isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), isInteractive: true, transition: textInputContainerBackgroundTransition)
|
||||
|
||||
transition.updateFrame(layer: self.textInputBackgroundNode.layer, frame: textInputContainerBackgroundFrame)
|
||||
transition.updateAlpha(node: self.textInputBackgroundNode, alpha: audioRecordingItemsAlpha)
|
||||
|
|
@ -2931,7 +2931,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
|
||||
let containerFrame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: contentHeight + 64.0))
|
||||
transition.updateFrame(view: self.glassBackgroundContainer, frame: containerFrame)
|
||||
self.glassBackgroundContainer.update(size: containerFrame.size, transition: ComponentTransition(transition))
|
||||
self.glassBackgroundContainer.update(size: containerFrame.size, isDark: interfaceState.theme.overallDarkAppearance, transition: ComponentTransition(transition))
|
||||
|
||||
return contentHeight
|
||||
}
|
||||
|
|
|
|||
|
|
@ -545,8 +545,10 @@ public final class GlassBackgroundContainerView: UIView {
|
|||
return result
|
||||
}
|
||||
|
||||
public func update(size: CGSize, transition: ComponentTransition) {
|
||||
public func update(size: CGSize, isDark: Bool, transition: ComponentTransition) {
|
||||
if let nativeView = self.nativeView {
|
||||
nativeView.overrideUserInterfaceStyle = isDark ? .dark : .light
|
||||
|
||||
transition.setFrame(view: nativeView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
} else if let legacyView = self.legacyView {
|
||||
transition.setFrame(view: legacyView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
|
|
|||
|
|
@ -160,9 +160,9 @@ public final class MessageInputActionButtonComponent: Component {
|
|||
case up
|
||||
}
|
||||
|
||||
public enum Style {
|
||||
public enum Style: Equatable {
|
||||
case legacy
|
||||
case glass
|
||||
case glass(isTinted: Bool)
|
||||
}
|
||||
|
||||
public let mode: Mode
|
||||
|
|
@ -519,7 +519,7 @@ public final class MessageInputActionButtonComponent: Component {
|
|||
microphoneAlpha = 0.4
|
||||
}
|
||||
|
||||
if component.style == .glass, [.send, .close].contains(component.mode) {
|
||||
if case let .glass(isTinted) = component.style {
|
||||
let backgroundView: GlassBackgroundView
|
||||
if let current = self.backgroundView {
|
||||
backgroundView = current
|
||||
|
|
@ -534,8 +534,16 @@ public final class MessageInputActionButtonComponent: Component {
|
|||
if case .send = component.mode {
|
||||
tintColor = UIColor(rgb: 0x029dff)
|
||||
}
|
||||
|
||||
let glassTint: GlassBackgroundView.TintColor
|
||||
if isTinted {
|
||||
glassTint = .init(kind: tintKind, color: tintColor)
|
||||
} else {
|
||||
glassTint = .init(kind: .panel, color: defaultDarkPresentationTheme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
|
||||
}
|
||||
|
||||
let buttonSize = CGSize(width: 40.0, height: 40.0)
|
||||
backgroundView.update(size: buttonSize, cornerRadius: buttonSize.height / 2.0, isDark: true, tintColor: .init(kind: tintKind, color: tintColor), transition: transition)
|
||||
backgroundView.update(size: buttonSize, cornerRadius: buttonSize.height / 2.0, isDark: true, tintColor: glassTint, transition: transition)
|
||||
backgroundView.frame = CGRect(origin: .zero, size: buttonSize)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -809,18 +809,16 @@ public final class MessageInputPanelComponent: Component {
|
|||
insets.left = 41.0
|
||||
}
|
||||
if let _ = component.setMediaRecordingActive {
|
||||
insets.right = 41.0
|
||||
insets.right = 40.0 + 8.0 * 2.0
|
||||
}
|
||||
|
||||
let textFieldSideInset: CGFloat
|
||||
switch component.style {
|
||||
case .media, .glass:
|
||||
textFieldSideInset = 8.0
|
||||
default:
|
||||
textFieldSideInset = 9.0
|
||||
var textFieldSideInset: CGFloat = 8.0
|
||||
if component.bottomInset <= 32.0 && !component.forceIsEditing && !component.hideKeyboard && !self.textFieldExternalState.isEditing {
|
||||
textFieldSideInset += 18.0
|
||||
insets.right += 18.0
|
||||
}
|
||||
|
||||
var mediaInsets = UIEdgeInsets(top: insets.top, left: textFieldSideInset, bottom: insets.bottom, right: 41.0)
|
||||
var mediaInsets = UIEdgeInsets(top: insets.top, left: textFieldSideInset, bottom: insets.bottom, right: 40.0 + 8.0)
|
||||
if case .glass = component.style {
|
||||
mediaInsets.right = 54.0
|
||||
}
|
||||
|
|
@ -1095,7 +1093,7 @@ public final class MessageInputPanelComponent: Component {
|
|||
//transition.setFrame(view: self.vibrancyEffectView, frame: CGRect(origin: CGPoint(), size: fieldBackgroundFrame.size))
|
||||
|
||||
switch component.style {
|
||||
case .glass:
|
||||
case .glass, .story:
|
||||
if self.fieldGlassBackgroundView == nil {
|
||||
let fieldGlassBackgroundView = GlassBackgroundView(frame: fieldBackgroundFrame)
|
||||
self.insertSubview(fieldGlassBackgroundView, aboveSubview: self.fieldBackgroundView)
|
||||
|
|
@ -1105,7 +1103,7 @@ public final class MessageInputPanelComponent: Component {
|
|||
self.fieldBackgroundTint.isHidden = true
|
||||
}
|
||||
if let fieldGlassBackgroundView = self.fieldGlassBackgroundView {
|
||||
fieldGlassBackgroundView.update(size: fieldBackgroundFrame.size, cornerRadius: baseFieldHeight * 0.5, isDark: true, tintColor: .init(kind: .custom, color: UIColor(rgb: 0x25272e, alpha: 0.72)), transition: transition)
|
||||
fieldGlassBackgroundView.update(size: fieldBackgroundFrame.size, cornerRadius: baseFieldHeight * 0.5, isDark: true, tintColor: component.style == .story ? .init(kind: .panel, color: defaultDarkPresentationTheme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)) : .init(kind: .custom, color: UIColor(rgb: 0x25272e, alpha: 0.72)), transition: transition)
|
||||
transition.setFrame(view: fieldGlassBackgroundView, frame: fieldBackgroundFrame)
|
||||
}
|
||||
default:
|
||||
|
|
@ -1551,6 +1549,9 @@ public final class MessageInputPanelComponent: Component {
|
|||
inputActionButtonMode = .close
|
||||
}
|
||||
} else {
|
||||
if case .story = component.style {
|
||||
inputActionButtonAvailableSize = CGSize(width: 40.0, height: 40.0)
|
||||
}
|
||||
if hasMediaEditing {
|
||||
inputActionButtonMode = .send
|
||||
} else {
|
||||
|
|
@ -1571,11 +1572,19 @@ public final class MessageInputPanelComponent: Component {
|
|||
}
|
||||
}
|
||||
}
|
||||
let inputActionButtonStyle: MessageInputActionButtonComponent.Style
|
||||
if component.style == .glass {
|
||||
inputActionButtonStyle = .glass(isTinted: true)
|
||||
} else if component.style == .story {
|
||||
inputActionButtonStyle = .glass(isTinted: false)
|
||||
} else {
|
||||
inputActionButtonStyle = .legacy
|
||||
}
|
||||
let inputActionButtonSize = self.inputActionButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(MessageInputActionButtonComponent(
|
||||
mode: inputActionButtonMode,
|
||||
style: component.style == .glass ? .glass : .legacy,
|
||||
style: inputActionButtonStyle,
|
||||
storyId: component.storyItem?.id,
|
||||
action: { [weak self] mode, action, sendAction in
|
||||
guard let self, let component = self.component else {
|
||||
|
|
@ -1690,26 +1699,22 @@ public final class MessageInputPanelComponent: Component {
|
|||
if rightButtonsOffsetX != 0.0 {
|
||||
inputActionButtonOriginX = availableSize.width - 3.0 + rightButtonsOffsetX
|
||||
if displayLikeAction {
|
||||
inputActionButtonOriginX -= 39.0
|
||||
inputActionButtonOriginX -= 40.0 + 8.0
|
||||
}
|
||||
if component.forwardAction != nil {
|
||||
inputActionButtonOriginX -= 46.0
|
||||
inputActionButtonOriginX -= 40.0 + 8.0
|
||||
}
|
||||
} else {
|
||||
if component.setMediaRecordingActive != nil || isEditing || component.style == .glass {
|
||||
switch component.style {
|
||||
case .glass:
|
||||
inputActionButtonOriginX = fieldBackgroundFrame.maxX + 6.0
|
||||
case .glass, .story:
|
||||
inputActionButtonOriginX = fieldBackgroundFrame.maxX + 8.0
|
||||
default:
|
||||
inputActionButtonOriginX = fieldBackgroundFrame.maxX + floorToScreenPixels((41.0 - inputActionButtonSize.width) * 0.5)
|
||||
}
|
||||
} else {
|
||||
inputActionButtonOriginX = size.width
|
||||
}
|
||||
|
||||
if hasLikeAction {
|
||||
inputActionButtonOriginX += 3.0
|
||||
}
|
||||
}
|
||||
|
||||
if let inputActionButtonView = self.inputActionButton.view {
|
||||
|
|
@ -1727,21 +1732,27 @@ public final class MessageInputPanelComponent: Component {
|
|||
transition.setBounds(view: inputActionButtonView, bounds: CGRect(origin: CGPoint(), size: inputActionButtonFrame.size))
|
||||
transition.setAlpha(view: inputActionButtonView, alpha: likeActionReplacesInputAction ? 0.0 : inputActionButtonAlpha)
|
||||
|
||||
if rightButtonsOffsetX != 0.0 {
|
||||
if hasLikeAction {
|
||||
inputActionButtonOriginX += 46.0
|
||||
}
|
||||
} else {
|
||||
if hasLikeAction {
|
||||
inputActionButtonOriginX += 41.0
|
||||
}
|
||||
if hasLikeAction {
|
||||
inputActionButtonOriginX += 40.0 + 8.0
|
||||
}
|
||||
}
|
||||
|
||||
let likeActionButtonStyle: MessageInputActionButtonComponent.Style
|
||||
var likeButtonContainerSize = CGSize(width: 33.0, height: 33.0)
|
||||
if component.style == .glass {
|
||||
likeActionButtonStyle = .glass(isTinted: true)
|
||||
likeButtonContainerSize = CGSize(width: 40.0, height: 40.0)
|
||||
} else if component.style == .story {
|
||||
likeActionButtonStyle = .glass(isTinted: false)
|
||||
likeButtonContainerSize = CGSize(width: 40.0, height: 40.0)
|
||||
} else {
|
||||
likeActionButtonStyle = .legacy
|
||||
}
|
||||
let likeButtonSize = self.likeButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(MessageInputActionButtonComponent(
|
||||
mode: .like(reaction: component.myReaction?.reaction, file: component.myReaction?.file, animationFileId: component.myReaction?.animationFileId),
|
||||
style: likeActionButtonStyle,
|
||||
storyId: component.storyItem?.id,
|
||||
action: { [weak self] _, action, _ in
|
||||
guard let self, let component = self.component else {
|
||||
|
|
@ -1770,7 +1781,7 @@ public final class MessageInputPanelComponent: Component {
|
|||
videoRecordingStatus: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 33.0, height: 33.0)
|
||||
containerSize: likeButtonContainerSize
|
||||
)
|
||||
if let likeButtonView = self.likeButton.view {
|
||||
if likeButtonView.superview == nil {
|
||||
|
|
@ -1783,7 +1794,7 @@ public final class MessageInputPanelComponent: Component {
|
|||
transition.setPosition(view: likeButtonView, position: likeButtonFrame.center)
|
||||
transition.setBounds(view: likeButtonView, bounds: CGRect(origin: CGPoint(), size: likeButtonFrame.size))
|
||||
transition.setAlpha(view: likeButtonView, alpha: displayLikeAction ? 1.0 : 0.0)
|
||||
inputActionButtonOriginX += 41.0
|
||||
inputActionButtonOriginX += 40.0 + 8.0
|
||||
}
|
||||
|
||||
var fieldIconNextX = fieldBackgroundFrame.maxX - 4.0
|
||||
|
|
@ -1855,7 +1866,7 @@ public final class MessageInputPanelComponent: Component {
|
|||
component: AnyComponent(Button(
|
||||
content: AnyComponent(LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: animationName),
|
||||
color: .white
|
||||
color: defaultDarkPresentationTheme.chat.inputPanel.inputControlColor
|
||||
)),
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
|
|
|
|||
|
|
@ -183,7 +183,8 @@ private enum PeerMembersListEntry: Comparable, Identifiable {
|
|||
return (
|
||||
total: storyStats.totalCount,
|
||||
unseen: storyStats.unseenCount,
|
||||
hasUnseenCloseFriends: storyStats.hasUnseenCloseFriends
|
||||
hasUnseenCloseFriends: storyStats.hasUnseenCloseFriends,
|
||||
hasLiveItems: storyStats.hasLiveItems
|
||||
)
|
||||
},
|
||||
openStories: { _, sourceNode in
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||
|
||||
private let playbackStartDisposable = MetaDisposable()
|
||||
|
||||
var storyData: (totalCount: Int, unseenCount: Int, hasUnseenCloseFriends: Bool)?
|
||||
var storyData: (totalCount: Int, unseenCount: Int, hasUnseenCloseFriends: Bool, hasLiveItems: Bool)?
|
||||
var storyProgress: Float?
|
||||
|
||||
init(context: AccountContext) {
|
||||
|
|
@ -146,6 +146,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||
totalCount: storyData.totalCount,
|
||||
unseenCount: storyData.unseenCount,
|
||||
hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends,
|
||||
hasLiveItems: storyData.hasLiveItems,
|
||||
progress: self.storyProgress
|
||||
)
|
||||
} else if let storyProgress = self.storyProgress {
|
||||
|
|
@ -153,6 +154,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||
totalCount: 1,
|
||||
unseenCount: 1,
|
||||
hasUnseenCloseFriendsItems: false,
|
||||
hasLiveItems: false,
|
||||
progress: storyProgress
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5352,7 +5352,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
}
|
||||
}
|
||||
|
||||
self.headerNode.avatarListNode.avatarContainerNode.storyData = (totalCount, unseenCount, state.hasUnseenCloseFriends && peer.id != self.context.account.peerId)
|
||||
self.headerNode.avatarListNode.avatarContainerNode.storyData = (totalCount, unseenCount, state.hasUnseenCloseFriends && peer.id != self.context.account.peerId, state.hasLiveItems)
|
||||
self.headerNode.avatarListNode.listContainerNode.storyParams = (peer, state.items.prefix(3).compactMap { item -> EngineStoryItem? in
|
||||
switch item {
|
||||
case let .item(item):
|
||||
|
|
@ -8997,7 +8997,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
if channel.hasPermission(.manageCalls) {
|
||||
canCreateStream = true
|
||||
credentialsPromise = Promise()
|
||||
credentialsPromise?.set(context.engine.calls.getGroupCallStreamCredentials(peerId: peerId, revokePreviousCredentials: false) |> `catch` { _ -> Signal<GroupCallStreamCredentials, NoError> in return .never() })
|
||||
credentialsPromise?.set(context.engine.calls.getGroupCallStreamCredentials(peerId: peerId, isLiveStream: false, revokePreviousCredentials: false) |> `catch` { _ -> Signal<GroupCallStreamCredentials, NoError> in return .never() })
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
|
|
|||
|
|
@ -420,6 +420,7 @@ public final class AvatarStoryIndicatorComponent: Component {
|
|||
|
||||
public let hasUnseen: Bool
|
||||
public let hasUnseenCloseFriendsItems: Bool
|
||||
public let hasLiveItems: Bool
|
||||
public let colors: Colors
|
||||
public let activeLineWidth: CGFloat
|
||||
public let inactiveLineWidth: CGFloat
|
||||
|
|
@ -430,6 +431,7 @@ public final class AvatarStoryIndicatorComponent: Component {
|
|||
public init(
|
||||
hasUnseen: Bool,
|
||||
hasUnseenCloseFriendsItems: Bool,
|
||||
hasLiveItems: Bool,
|
||||
colors: Colors,
|
||||
activeLineWidth: CGFloat,
|
||||
inactiveLineWidth: CGFloat,
|
||||
|
|
@ -439,6 +441,7 @@ public final class AvatarStoryIndicatorComponent: Component {
|
|||
) {
|
||||
self.hasUnseen = hasUnseen
|
||||
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
|
||||
self.hasLiveItems = hasLiveItems
|
||||
self.colors = colors
|
||||
self.activeLineWidth = activeLineWidth
|
||||
self.inactiveLineWidth = inactiveLineWidth
|
||||
|
|
@ -454,6 +457,9 @@ public final class AvatarStoryIndicatorComponent: Component {
|
|||
if lhs.hasUnseenCloseFriendsItems != rhs.hasUnseenCloseFriendsItems {
|
||||
return false
|
||||
}
|
||||
if lhs.hasLiveItems != rhs.hasLiveItems {
|
||||
return false
|
||||
}
|
||||
if lhs.colors != rhs.colors {
|
||||
return false
|
||||
}
|
||||
|
|
@ -659,7 +665,10 @@ public final class AvatarStoryIndicatorComponent: Component {
|
|||
let activeColors: [CGColor]
|
||||
let inactiveColors: [CGColor]
|
||||
|
||||
if component.hasUnseenCloseFriendsItems {
|
||||
if component.hasLiveItems {
|
||||
//TODO:localize
|
||||
activeColors = [UIColor(rgb: 0xFF3777).cgColor, UIColor(rgb: 0xFF2D55).cgColor]
|
||||
} else if component.hasUnseenCloseFriendsItems {
|
||||
activeColors = component.colors.unseenCloseFriendsColors.map(\.cgColor)
|
||||
} else {
|
||||
activeColors = component.colors.unseenColors.map(\.cgColor)
|
||||
|
|
@ -681,7 +690,7 @@ public final class AvatarStoryIndicatorComponent: Component {
|
|||
|
||||
var locations: [CGFloat] = [0.0, 1.0]
|
||||
|
||||
if let counters = component.counters, counters.totalCount > 1 {
|
||||
if let counters = component.counters, !component.hasLiveItems, counters.totalCount > 1 {
|
||||
if component.isRoundedRect {
|
||||
let lineWidth: CGFloat = component.hasUnseen ? component.activeLineWidth : component.inactiveLineWidth
|
||||
context.setLineWidth(lineWidth)
|
||||
|
|
@ -839,7 +848,9 @@ public final class AvatarStoryIndicatorComponent: Component {
|
|||
context.clip()
|
||||
|
||||
let colors: [CGColor]
|
||||
if component.hasUnseen {
|
||||
if component.hasLiveItems {
|
||||
colors = activeColors
|
||||
} else if component.hasUnseen {
|
||||
colors = activeColors
|
||||
} else {
|
||||
colors = inactiveColors
|
||||
|
|
|
|||
|
|
@ -940,7 +940,8 @@ public final class PeerListItemComponent: Component {
|
|||
return AvatarNode.StoryStats(
|
||||
totalCount: storyStats.totalCount == 0 ? 0 : 1,
|
||||
unseenCount: storyStats.unseenCount == 0 ? 0 : 1,
|
||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends,
|
||||
hasLiveItems: storyStats.hasLiveItems
|
||||
)
|
||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||
colors: AvatarNode.Colors(theme: component.theme),
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController",
|
||||
"//submodules/DirectMediaImageCache",
|
||||
"//submodules/PromptUI",
|
||||
"//submodules/TelegramCallsUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@ final class StoryAuthorInfoComponent: Component {
|
|||
let timestamp: Int32
|
||||
let counters: Counters?
|
||||
let isEdited: Bool
|
||||
let isLiveStream: Bool
|
||||
|
||||
init(context: AccountContext, strings: PresentationStrings, peer: EnginePeer?, forwardInfo: EngineStoryItem.ForwardInfo?, author: EnginePeer?, timestamp: Int32, counters: Counters?, isEdited: Bool) {
|
||||
init(context: AccountContext, strings: PresentationStrings, peer: EnginePeer?, forwardInfo: EngineStoryItem.ForwardInfo?, author: EnginePeer?, timestamp: Int32, counters: Counters?, isEdited: Bool, isLiveStream: Bool) {
|
||||
self.context = context
|
||||
self.strings = strings
|
||||
self.peer = peer
|
||||
|
|
@ -33,6 +34,7 @@ final class StoryAuthorInfoComponent: Component {
|
|||
self.timestamp = timestamp
|
||||
self.counters = counters
|
||||
self.isEdited = isEdited
|
||||
self.isLiveStream = isLiveStream
|
||||
}
|
||||
|
||||
static func ==(lhs: StoryAuthorInfoComponent, rhs: StoryAuthorInfoComponent) -> Bool {
|
||||
|
|
@ -59,6 +61,9 @@ final class StoryAuthorInfoComponent: Component {
|
|||
}
|
||||
if lhs.isEdited != rhs.isEdited {
|
||||
return false
|
||||
}
|
||||
if lhs.isLiveStream != rhs.isLiveStream {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
@ -68,6 +73,7 @@ final class StoryAuthorInfoComponent: Component {
|
|||
private var repostIconView: UIImageView?
|
||||
private var avatarNode: AvatarNode?
|
||||
private let subtitle = ComponentView<Empty>()
|
||||
private var liveBadgeView: UIImageView?
|
||||
private var counterLabel: ComponentView<Empty>?
|
||||
|
||||
private var component: StoryAuthorInfoComponent?
|
||||
|
|
@ -238,7 +244,43 @@ final class StoryAuthorInfoComponent: Component {
|
|||
avatarNode.view.removeFromSuperview()
|
||||
}
|
||||
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: leftInset + subtitleOffset, y: titleFrame.maxY + spacing + UIScreenPixel), size: subtitleSize)
|
||||
var subtitleFrame = CGRect(origin: CGPoint(x: leftInset + subtitleOffset, y: titleFrame.maxY + spacing + UIScreenPixel), size: subtitleSize)
|
||||
|
||||
if component.isLiveStream {
|
||||
let liveBadgeView: UIImageView
|
||||
if let current = self.liveBadgeView {
|
||||
liveBadgeView = current
|
||||
} else {
|
||||
liveBadgeView = UIImageView()
|
||||
self.liveBadgeView = liveBadgeView
|
||||
self.addSubview(liveBadgeView)
|
||||
//TODO:localize
|
||||
let liveString = NSAttributedString(string: "LIVE", font: Font.semibold(10.0), textColor: .white)
|
||||
let liveStringBounds = liveString.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
|
||||
let liveBadgeSize = CGSize(width: ceil(liveStringBounds.width) + 3.0 * 2.0, height: ceil(liveStringBounds.height) + 1.0 * 2.0)
|
||||
liveBadgeView.image = generateImage(liveBadgeSize, rotatedContext: { size, context in
|
||||
UIGraphicsPushContext(context)
|
||||
defer {
|
||||
UIGraphicsPopContext()
|
||||
}
|
||||
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(UIColor(rgb: 0xFF2D55).cgColor)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: size.height * 0.5).cgPath)
|
||||
context.fillPath()
|
||||
|
||||
liveString.draw(at: CGPoint(x: floorToScreenPixels((size.width - liveStringBounds.width) * 0.5), y: floorToScreenPixels((size.height - liveStringBounds.height) * 0.5)))
|
||||
})
|
||||
}
|
||||
if let image = liveBadgeView.image {
|
||||
let liveBadgeFrame = CGRect(origin: CGPoint(x: subtitleFrame.minX, y: subtitleFrame.minY), size: image.size)
|
||||
liveBadgeView.frame = liveBadgeFrame
|
||||
subtitleFrame.origin.x += image.size.width + 3.0
|
||||
}
|
||||
} else if let liveBadgeView = self.liveBadgeView {
|
||||
self.liveBadgeView = nil
|
||||
liveBadgeView.removeFromSuperview()
|
||||
}
|
||||
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
|
|
|
|||
|
|
@ -10,10 +10,12 @@ import AvatarNode
|
|||
final class StoryAvatarInfoComponent: Component {
|
||||
let context: AccountContext
|
||||
let peer: EnginePeer
|
||||
let isLiveStream: Bool
|
||||
|
||||
init(context: AccountContext, peer: EnginePeer) {
|
||||
init(context: AccountContext, peer: EnginePeer, isLiveStream: Bool) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.isLiveStream = isLiveStream
|
||||
}
|
||||
|
||||
static func ==(lhs: StoryAvatarInfoComponent, rhs: StoryAvatarInfoComponent) -> Bool {
|
||||
|
|
@ -23,6 +25,9 @@ final class StoryAvatarInfoComponent: Component {
|
|||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
if lhs.isLiveStream != rhs.isLiveStream {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -57,6 +62,24 @@ final class StoryAvatarInfoComponent: Component {
|
|||
peer: component.peer,
|
||||
synchronousLoad: true
|
||||
)
|
||||
self.avatarNode.setStoryStats(
|
||||
storyStats: component.isLiveStream ? AvatarNode.StoryStats(
|
||||
totalCount: 1,
|
||||
unseenCount: 1,
|
||||
hasUnseenCloseFriendsItems: false,
|
||||
hasLiveItems: true
|
||||
) : nil,
|
||||
presentationParams: AvatarNode.StoryPresentationParams(
|
||||
colors: AvatarNode.Colors(
|
||||
unseenColors: [.white],
|
||||
unseenCloseFriendsColors: [.white],
|
||||
seenColors: [.white]
|
||||
),
|
||||
lineWidth: 1.33,
|
||||
inactiveLineWidth: 1.33
|
||||
),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
return size
|
||||
}
|
||||
|
|
|
|||
|
|
@ -649,6 +649,7 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||
peer: peer,
|
||||
hasUnseen: state.hasUnseen,
|
||||
hasUnseenCloseFriends: state.hasUnseenCloseFriends,
|
||||
hasLiveItems: false,
|
||||
hasPending: false,
|
||||
storyCount: state.items.count,
|
||||
unseenCount: 0,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import HierarchyTrackingLayer
|
|||
import ButtonComponent
|
||||
import MultilineTextComponent
|
||||
import TelegramPresentationData
|
||||
import TelegramCallsUI
|
||||
|
||||
final class StoryItemContentComponent: Component {
|
||||
typealias EnvironmentType = StoryContentItem.Environment
|
||||
|
|
@ -92,6 +93,8 @@ final class StoryItemContentComponent: Component {
|
|||
private let imageView: StoryItemImageView
|
||||
private let overlaysView: StoryItemOverlaysView
|
||||
private var videoNode: UniversalVideoNode?
|
||||
private var mediaStreamCall: PresentationGroupCallImpl?
|
||||
private var mediaStream: ComponentView<Empty>?
|
||||
private var loadingEffectView: StoryItemLoadingEffectView?
|
||||
private var loadingEffectAppearanceTimer: SwiftSignalKit.Timer?
|
||||
|
||||
|
|
@ -385,6 +388,10 @@ final class StoryItemContentComponent: Component {
|
|||
if !self.isSeeking {
|
||||
self.updateVideoPlaybackProgress()
|
||||
}
|
||||
} else if case .liveStream = self.currentMessageMedia {
|
||||
if !self.isSeeking {
|
||||
self.updateVideoPlaybackProgress()
|
||||
}
|
||||
} else {
|
||||
if !self.markedAsSeen {
|
||||
self.markedAsSeen = true
|
||||
|
|
@ -599,7 +606,10 @@ final class StoryItemContentComponent: Component {
|
|||
|
||||
let selectedMedia: EngineMedia
|
||||
var messageMedia: EngineMedia?
|
||||
if !component.preferHighQuality, !component.item.isMy, let alternativeMediaValue = component.item.alternativeMediaList.first {
|
||||
if case .liveStream = component.item.media {
|
||||
selectedMedia = component.item.media
|
||||
messageMedia = selectedMedia
|
||||
} else if !component.preferHighQuality, !component.item.isMy, let alternativeMediaValue = component.item.alternativeMediaList.first {
|
||||
selectedMedia = alternativeMediaValue
|
||||
|
||||
switch alternativeMediaValue {
|
||||
|
|
@ -628,7 +638,7 @@ final class StoryItemContentComponent: Component {
|
|||
}
|
||||
|
||||
var reloadMedia = false
|
||||
if self.currentMessageMedia?.id != messageMedia?.id {
|
||||
if self.currentMessageMedia?.id != messageMedia?.id || (self.currentMessageMedia == nil) != (messageMedia == nil) {
|
||||
self.currentMessageMedia = messageMedia
|
||||
reloadMedia = true
|
||||
|
||||
|
|
@ -707,47 +717,154 @@ final class StoryItemContentComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
if let messageMedia {
|
||||
var applyState = false
|
||||
self.imageView.didLoadContents = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.contentLoaded = true
|
||||
if applyState {
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
if case let .liveStream(liveStream) = messageMedia {
|
||||
var mediaStreamTransition = transition
|
||||
let mediaStream: ComponentView<Empty>
|
||||
if let current = self.mediaStream {
|
||||
mediaStream = current
|
||||
} else {
|
||||
mediaStreamTransition = mediaStreamTransition.withAnimation(.none)
|
||||
mediaStream = ComponentView()
|
||||
self.mediaStream = mediaStream
|
||||
}
|
||||
self.imageView.update(
|
||||
context: component.context,
|
||||
strings: component.strings,
|
||||
peer: component.peer,
|
||||
storyId: component.item.id,
|
||||
media: messageMedia,
|
||||
size: availableSize,
|
||||
isCaptureProtected: component.item.isForwardingDisabled,
|
||||
attemptSynchronous: synchronousLoad,
|
||||
transition: transition
|
||||
)
|
||||
self.updateOverlays(component: component, size: availableSize, synchronousLoad: synchronousLoad, transition: transition)
|
||||
applyState = true
|
||||
if self.imageView.isContentLoaded {
|
||||
self.contentLoaded = true
|
||||
}
|
||||
transition.setFrame(view: self.imageView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
transition.setFrame(view: self.overlaysView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
|
||||
var dimensions: CGSize?
|
||||
switch messageMedia {
|
||||
case let .image(image):
|
||||
dimensions = image.representations.last?.dimensions.cgSize
|
||||
case let .file(file):
|
||||
dimensions = file.dimensions?.cgSize
|
||||
default:
|
||||
break
|
||||
let mediaStreamCall: PresentationGroupCallImpl
|
||||
if let current = self.mediaStreamCall {
|
||||
mediaStreamCall = current
|
||||
} else {
|
||||
let initialCall = EngineGroupCallDescription(
|
||||
id: liveStream.call.id,
|
||||
accessHash: liveStream.call.accessHash,
|
||||
title: nil,
|
||||
scheduleTimestamp: nil,
|
||||
subscribedToScheduled: false,
|
||||
isStream: true
|
||||
)
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
|
||||
mediaStreamCall = PresentationGroupCallImpl(
|
||||
accountContext: component.context,
|
||||
audioSession: component.context.sharedContext.mediaManager.audioSession,
|
||||
callKitIntegration: nil,
|
||||
getDeviceAccessData: {
|
||||
(
|
||||
presentationData: presentationData,
|
||||
present: { c, a in
|
||||
|
||||
},
|
||||
openSettings: {
|
||||
|
||||
}
|
||||
)
|
||||
},
|
||||
initialCall: (initialCall, .id(id: liveStream.call.id, accessHash: liveStream.call.accessHash)),
|
||||
internalId: CallSessionInternalId(),
|
||||
peerId: nil,
|
||||
isChannel: false,
|
||||
invite: nil,
|
||||
joinAsPeerId: nil,
|
||||
isStream: true,
|
||||
keyPair: nil,
|
||||
conferenceSourceId: nil,
|
||||
isConference: false,
|
||||
beginWithVideo: false,
|
||||
sharedAudioContext: nil,
|
||||
unmuteByDefault: false
|
||||
)
|
||||
self.mediaStreamCall = mediaStreamCall
|
||||
|
||||
let _ = mediaStreamCall.accountContext.engine.calls.getGroupCallStreamCredentials(peerId: mediaStreamCall.accountContext.account.peerId, isLiveStream: true, revokePreviousCredentials: false).startStandalone(next: { params in
|
||||
print("url: \(params.url), streamKey: \(params.streamKey)")
|
||||
})
|
||||
}
|
||||
if dimensions == nil {
|
||||
switch component.item.media {
|
||||
|
||||
let _ = mediaStream.update(
|
||||
transition: mediaStreamTransition,
|
||||
component: AnyComponent(MediaStreamVideoComponent(
|
||||
call: mediaStreamCall,
|
||||
hasVideo: true,
|
||||
isVisible: true,
|
||||
isAdmin: false,
|
||||
peerTitle: "",
|
||||
addInset: false,
|
||||
isFullscreen: false,
|
||||
videoLoading: false,
|
||||
callPeer: nil,
|
||||
activatePictureInPicture: ActionSlot(),
|
||||
deactivatePictureInPicture: ActionSlot(),
|
||||
bringBackControllerForPictureInPictureDeactivation: { f in
|
||||
f()
|
||||
},
|
||||
pictureInPictureClosed: {
|
||||
},
|
||||
onVideoSizeRetrieved: { _ in
|
||||
},
|
||||
onVideoPlaybackLiveChange: { [weak self] isLive in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.videoPlaybackStatus = MediaPlayerStatus(
|
||||
generationTimestamp: CACurrentMediaTime(),
|
||||
duration: .infinity,
|
||||
dimensions: CGSize(),
|
||||
timestamp: 0.0,
|
||||
baseRate: 1.0,
|
||||
seekId: 0,
|
||||
status: isLive ? .playing : .buffering(initial: false, whilePlaying: true, progress: 0.0, display: true),
|
||||
soundEnabled: true
|
||||
)
|
||||
if !self.isSeeking {
|
||||
self.updateVideoPlaybackProgress()
|
||||
}
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let mediaStreamFrame = CGRect(origin: CGPoint(), size: availableSize)
|
||||
if let mediaStreamView = mediaStream.view {
|
||||
if mediaStreamView.superview == nil {
|
||||
self.insertSubview(mediaStreamView, aboveSubview: self.imageView)
|
||||
}
|
||||
mediaStreamTransition.setFrame(view: mediaStreamView, frame: mediaStreamFrame)
|
||||
}
|
||||
} else {
|
||||
if let mediaStream = self.mediaStream {
|
||||
self.mediaStream = nil
|
||||
mediaStream.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
if let messageMedia {
|
||||
var applyState = false
|
||||
self.imageView.didLoadContents = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.contentLoaded = true
|
||||
if applyState {
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
}
|
||||
self.imageView.update(
|
||||
context: component.context,
|
||||
strings: component.strings,
|
||||
peer: component.peer,
|
||||
storyId: component.item.id,
|
||||
media: messageMedia,
|
||||
size: availableSize,
|
||||
isCaptureProtected: component.item.isForwardingDisabled,
|
||||
attemptSynchronous: synchronousLoad,
|
||||
transition: transition
|
||||
)
|
||||
self.updateOverlays(component: component, size: availableSize, synchronousLoad: synchronousLoad, transition: transition)
|
||||
applyState = true
|
||||
if self.imageView.isContentLoaded {
|
||||
self.contentLoaded = true
|
||||
}
|
||||
transition.setFrame(view: self.imageView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
transition.setFrame(view: self.overlaysView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
|
||||
var dimensions: CGSize?
|
||||
switch messageMedia {
|
||||
case let .image(image):
|
||||
dimensions = image.representations.last?.dimensions.cgSize
|
||||
case let .file(file):
|
||||
|
|
@ -755,28 +872,38 @@ final class StoryItemContentComponent: Component {
|
|||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let dimensions {
|
||||
var imageSize = dimensions.aspectFilled(availableSize)
|
||||
if imageSize.width < availableSize.width && imageSize.width >= availableSize.width - 5.0 {
|
||||
imageSize.width = availableSize.width
|
||||
if dimensions == nil {
|
||||
switch component.item.media {
|
||||
case let .image(image):
|
||||
dimensions = image.representations.last?.dimensions.cgSize
|
||||
case let .file(file):
|
||||
dimensions = file.dimensions?.cgSize
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
if imageSize.height < availableSize.height && imageSize.height >= availableSize.height - 5.0 {
|
||||
imageSize.height = availableSize.height
|
||||
}
|
||||
let _ = imageSize
|
||||
|
||||
if let videoNode = self.videoNode {
|
||||
let videoSize = dimensions.aspectFilled(availableSize)
|
||||
videoNode.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - videoSize.width) * 0.5), y: floor((availableSize.height - videoSize.height) * 0.5)), size: videoSize)
|
||||
videoNode.updateLayout(size: videoSize, transition: .immediate)
|
||||
if let dimensions {
|
||||
var imageSize = dimensions.aspectFilled(availableSize)
|
||||
if imageSize.width < availableSize.width && imageSize.width >= availableSize.width - 5.0 {
|
||||
imageSize.width = availableSize.width
|
||||
}
|
||||
if imageSize.height < availableSize.height && imageSize.height >= availableSize.height - 5.0 {
|
||||
imageSize.height = availableSize.height
|
||||
}
|
||||
let _ = imageSize
|
||||
|
||||
if let videoNode = self.videoNode {
|
||||
let videoSize = dimensions.aspectFilled(availableSize)
|
||||
videoNode.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - videoSize.width) * 0.5), y: floor((availableSize.height - videoSize.height) * 0.5)), size: videoSize)
|
||||
videoNode.updateLayout(size: videoSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch selectedMedia {
|
||||
case .image, .file:
|
||||
case .image, .file, .liveStream:
|
||||
if let unsupportedText = self.unsupportedText {
|
||||
self.unsupportedText = nil
|
||||
unsupportedText.view?.removeFromSuperview()
|
||||
|
|
|
|||
|
|
@ -1688,7 +1688,11 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
}
|
||||
}
|
||||
} else if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||
displayFooter = true
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
displayFooter = false
|
||||
} else {
|
||||
displayFooter = true
|
||||
}
|
||||
} else if component.slice.item.storyItem.isPending {
|
||||
displayFooter = true
|
||||
} else if case let .user(user) = component.slice.peer, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) {
|
||||
|
|
@ -1926,7 +1930,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
}
|
||||
|
||||
var displayViewLists = false
|
||||
if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
displayViewLists = false
|
||||
} else if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||
displayViewLists = true
|
||||
} else if case let .channel(channel) = component.slice.effectivePeer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats {
|
||||
displayViewLists = true
|
||||
|
|
@ -1941,7 +1947,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
return true
|
||||
} else {
|
||||
var canReply = false
|
||||
if case .user = component.slice.effectivePeer {
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
canReply = true
|
||||
} else if case .user = component.slice.effectivePeer {
|
||||
canReply = true
|
||||
|
||||
if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||
|
|
@ -1969,7 +1977,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
}
|
||||
|
||||
var canReply = false
|
||||
if case .user = component.slice.effectivePeer {
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
canReply = true
|
||||
} else if case .user = component.slice.effectivePeer {
|
||||
canReply = true
|
||||
|
||||
if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||
|
|
@ -2760,7 +2770,11 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
switch channel.info {
|
||||
case .broadcast:
|
||||
isChannel = true
|
||||
showMessageInputPanel = false
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
showMessageInputPanel = true
|
||||
} else {
|
||||
showMessageInputPanel = false
|
||||
}
|
||||
case .group:
|
||||
if let bannedSendText = channel.hasBannedPermission(.banSendText, ignoreDefault: canBypassRestrictions) {
|
||||
if bannedSendText.1 || component.slice.additionalPeerData.boostsToUnrestrict == nil {
|
||||
|
|
@ -2772,7 +2786,11 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
isGroup = true
|
||||
}
|
||||
} else {
|
||||
showMessageInputPanel = component.slice.effectivePeer.id != component.context.account.peerId
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
showMessageInputPanel = true
|
||||
} else {
|
||||
showMessageInputPanel = component.slice.effectivePeer.id != component.context.account.peerId
|
||||
}
|
||||
}
|
||||
if case let .user(user) = component.slice.peer, let _ = user.botInfo {
|
||||
showMessageInputPanel = false
|
||||
|
|
@ -2834,6 +2852,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
if let sendPaidMessageStars = component.slice.additionalPeerData.sendPaidMessageStars {
|
||||
let dateTimeFormat = component.context.sharedContext.currentPresentationData.with { $0 }.dateTimeFormat
|
||||
inputPlaceholder = .plain(component.strings.Chat_InputTextPaidMessagePlaceholder(" # \(presentationStringsFormattedNumber(Int32(sendPaidMessageStars.value), dateTimeFormat.groupingSeparator))").string)
|
||||
} else if case .liveStream = component.slice.item.storyItem.media {
|
||||
//TODO:localize
|
||||
inputPlaceholder = .plain("Comment")
|
||||
} else {
|
||||
inputPlaceholder = .plain(isGroup ? component.strings.Story_InputPlaceholderReplyInGroup : component.strings.Story_InputPlaceholderReplyPrivately)
|
||||
}
|
||||
|
|
@ -2859,6 +2880,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
self.inputPanel.parentState = state
|
||||
var inputPanelSize: CGSize?
|
||||
|
||||
let _ = inputNodeVisible
|
||||
|
||||
let startTime23 = CFAbsoluteTimeGetCurrent()
|
||||
|
||||
if showMessageInputPanel {
|
||||
|
|
@ -2871,6 +2894,14 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
var displayAttachmentAction = true
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
displayAttachmentAction = false
|
||||
}
|
||||
if component.slice.effectivePeer.isService {
|
||||
displayAttachmentAction = false
|
||||
}
|
||||
|
||||
inputPanelSize = self.inputPanel.update(
|
||||
transition: inputPanelTransition,
|
||||
component: AnyComponent(MessageInputPanelComponent(
|
||||
|
|
@ -2949,13 +2980,13 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
self.sendMessageContext.videoRecorderValue?.dismissVideo()
|
||||
self.sendMessageContext.discardMediaRecordingPreview(view: self)
|
||||
},
|
||||
attachmentAction: component.slice.effectivePeer.isService ? nil : { [weak self] in
|
||||
attachmentAction: !displayAttachmentAction ? nil : { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.sendMessageContext.presentAttachmentMenu(view: self, subject: .default)
|
||||
},
|
||||
attachmentButtonMode: component.slice.effectivePeer.isService ? nil : .attach,
|
||||
attachmentButtonMode: !displayAttachmentAction ? nil : .attach,
|
||||
myReaction: component.slice.item.storyItem.myReaction.flatMap { value -> MessageInputPanelComponent.MyReaction? in
|
||||
var centerAnimation: TelegramMediaFile?
|
||||
var animationFileId: Int64?
|
||||
|
|
@ -3082,7 +3113,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
timeoutValue: nil,
|
||||
timeoutSelected: false,
|
||||
displayGradient: false,
|
||||
bottomInset: component.inputHeight != 0.0 || inputNodeVisible ? 0.0 : bottomContentInset,
|
||||
bottomInset: max(bottomContentInset, component.inputHeight),
|
||||
isFormattingLocked: false,
|
||||
hideKeyboard: self.sendMessageContext.currentInputMode == .media,
|
||||
customInputView: nil,
|
||||
|
|
@ -3176,7 +3207,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
inputPanelIsOverlay = false
|
||||
} else {
|
||||
bottomContentInset += 44.0
|
||||
inputPanelBottomInset = inputHeight - inputPanelInset
|
||||
inputPanelBottomInset = inputHeight - inputPanelInset + 3.0
|
||||
inputPanelIsOverlay = true
|
||||
}
|
||||
|
||||
|
|
@ -3191,7 +3222,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
var validViewListIds: [StoryId] = []
|
||||
|
||||
var displayViewLists = false
|
||||
if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
displayViewLists = false
|
||||
} else if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||
displayViewLists = true
|
||||
} else if case let .channel(channel) = component.slice.effectivePeer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats {
|
||||
displayViewLists = true
|
||||
|
|
@ -4028,9 +4061,14 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
|
||||
let focusedItem: StoryContentItem? = component.slice.item
|
||||
|
||||
var isLiveStream = false
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
isLiveStream = true
|
||||
}
|
||||
|
||||
var currentLeftInfoItem: InfoItem?
|
||||
if focusedItem != nil {
|
||||
let leftInfoComponent = AnyComponent(StoryAvatarInfoComponent(context: component.context, peer: component.slice.effectivePeer))
|
||||
let leftInfoComponent = AnyComponent(StoryAvatarInfoComponent(context: component.context, peer: component.slice.effectivePeer, isLiveStream: isLiveStream))
|
||||
if let leftInfoItem = self.leftInfoItem, leftInfoItem.component == leftInfoComponent {
|
||||
currentLeftInfoItem = leftInfoItem
|
||||
} else {
|
||||
|
|
@ -4066,7 +4104,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
author: component.slice.item.storyItem.author,
|
||||
timestamp: component.slice.item.storyItem.timestamp,
|
||||
counters: counters,
|
||||
isEdited: component.slice.item.storyItem.isEdited
|
||||
isEdited: component.slice.item.storyItem.isEdited,
|
||||
isLiveStream: isLiveStream
|
||||
))
|
||||
if let centerInfoItem = self.centerInfoItem, centerInfoItem.component == centerInfoComponent {
|
||||
currentCenterInfoItem = centerInfoItem
|
||||
|
|
@ -4176,7 +4215,13 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
if let inputPanelSize {
|
||||
let inputPanelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - inputPanelSize.width) / 2.0), y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize)
|
||||
inputPanelFrameValue = inputPanelFrame
|
||||
var inputPanelAlpha: CGFloat = (component.slice.effectivePeer.id == component.context.account.peerId || component.hideUI || self.isEditingStory || component.slice.item.storyItem.isPending) ? 0.0 : 1.0
|
||||
|
||||
var inputPanelAlpha: CGFloat = (component.hideUI || self.isEditingStory || component.slice.item.storyItem.isPending) ? 0.0 : 1.0
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
} else if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||
inputPanelAlpha = 0.0
|
||||
}
|
||||
|
||||
if case .regular = component.metrics.widthClass {
|
||||
inputPanelAlpha *= component.visibilityFraction
|
||||
}
|
||||
|
|
@ -4465,14 +4510,21 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
if let current = self.reactionContextNode {
|
||||
reactionContextNode = current
|
||||
} else {
|
||||
var reactionAdditionalTitle: String?
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
} else {
|
||||
reactionAdditionalTitle = self.displayLikeReactions ? nil : (isGroup ? component.strings.Story_SendReactionAsGroupMessage : component.strings.Story_SendReactionAsMessage)
|
||||
}
|
||||
|
||||
reactionContextNodeTransition = .immediate
|
||||
reactionContextNode = ReactionContextNode(
|
||||
context: component.context,
|
||||
animationCache: component.context.animationCache,
|
||||
presentationData: component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme),
|
||||
style: .glass(isTinted: false),
|
||||
items: reactionItems.map { ReactionContextItem.reaction(item: $0, icon: .none) },
|
||||
selectedItems: component.slice.item.storyItem.myReaction.flatMap { Set([$0]) } ?? Set(),
|
||||
title: self.displayLikeReactions ? nil : (isGroup ? component.strings.Story_SendReactionAsGroupMessage : component.strings.Story_SendReactionAsMessage),
|
||||
title: reactionAdditionalTitle,
|
||||
reactionsLocked: false,
|
||||
alwaysAllowPremiumReactions: false,
|
||||
allPresetReactionsAreAvailable: false,
|
||||
|
|
@ -4982,12 +5034,21 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
}
|
||||
|
||||
component.externalState.derivedMediaSize = contentFrame.size
|
||||
if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||
component.externalState.derivedBottomInset = availableSize.height - itemsContainerFrame.maxY
|
||||
} else if let inputPanelFrameValue {
|
||||
component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrameValue.minY, contentFrame.maxY)
|
||||
|
||||
if case .liveStream = component.slice.item.storyItem.media {
|
||||
if let inputPanelFrameValue {
|
||||
component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrameValue.minY, contentFrame.maxY)
|
||||
} else {
|
||||
component.externalState.derivedBottomInset = availableSize.height - itemsContainerFrame.maxY
|
||||
}
|
||||
} else {
|
||||
component.externalState.derivedBottomInset = availableSize.height - itemsContainerFrame.maxY
|
||||
if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||
component.externalState.derivedBottomInset = availableSize.height - itemsContainerFrame.maxY
|
||||
} else if let inputPanelFrameValue {
|
||||
component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrameValue.minY, contentFrame.maxY)
|
||||
} else {
|
||||
component.externalState.derivedBottomInset = availableSize.height - itemsContainerFrame.maxY
|
||||
}
|
||||
}
|
||||
|
||||
if !"".isEmpty {
|
||||
|
|
|
|||
|
|
@ -1054,6 +1054,7 @@ public final class StoryPeerListComponent: Component {
|
|||
}
|
||||
|
||||
var hasUnseenCloseFriendsItems = itemSet.hasUnseenCloseFriends
|
||||
let hasLiveItems = itemSet.hasLiveItems
|
||||
|
||||
var hasItems = true
|
||||
var itemRingAnimation: StoryPeerListItemComponent.RingAnimation?
|
||||
|
|
@ -1139,6 +1140,7 @@ public final class StoryPeerListComponent: Component {
|
|||
totalCount: totalCount,
|
||||
unseenCount: unseenCount,
|
||||
hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems,
|
||||
hasLiveItems: hasLiveItems,
|
||||
hasItems: hasItems,
|
||||
ringAnimation: itemRingAnimation,
|
||||
scale: itemScale,
|
||||
|
|
@ -1278,6 +1280,7 @@ public final class StoryPeerListComponent: Component {
|
|||
totalCount: 1,
|
||||
unseenCount: itemSet.unseenCount != 0 ? 1 : 0,
|
||||
hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems,
|
||||
hasLiveItems: itemSet.hasLiveItems,
|
||||
hasItems: hasItems,
|
||||
ringAnimation: itemRingAnimation,
|
||||
scale: itemScale,
|
||||
|
|
|
|||
|
|
@ -373,6 +373,7 @@ public final class StoryPeerListItemComponent: Component {
|
|||
public let totalCount: Int
|
||||
public let unseenCount: Int
|
||||
public let hasUnseenCloseFriendsItems: Bool
|
||||
public let hasLiveItems: Bool
|
||||
public let hasItems: Bool
|
||||
public let ringAnimation: RingAnimation?
|
||||
public let scale: CGFloat
|
||||
|
|
@ -393,6 +394,7 @@ public final class StoryPeerListItemComponent: Component {
|
|||
totalCount: Int,
|
||||
unseenCount: Int,
|
||||
hasUnseenCloseFriendsItems: Bool,
|
||||
hasLiveItems: Bool,
|
||||
hasItems: Bool,
|
||||
ringAnimation: RingAnimation?,
|
||||
scale: CGFloat,
|
||||
|
|
@ -412,6 +414,7 @@ public final class StoryPeerListItemComponent: Component {
|
|||
self.totalCount = totalCount
|
||||
self.unseenCount = unseenCount
|
||||
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
|
||||
self.hasLiveItems = hasLiveItems
|
||||
self.hasItems = hasItems
|
||||
self.ringAnimation = ringAnimation
|
||||
self.scale = scale
|
||||
|
|
@ -447,6 +450,9 @@ public final class StoryPeerListItemComponent: Component {
|
|||
if lhs.hasUnseenCloseFriendsItems != rhs.hasUnseenCloseFriendsItems {
|
||||
return false
|
||||
}
|
||||
if lhs.hasLiveItems != rhs.hasLiveItems {
|
||||
return false
|
||||
}
|
||||
if lhs.hasItems != rhs.hasItems {
|
||||
return false
|
||||
}
|
||||
|
|
@ -494,6 +500,9 @@ public final class StoryPeerListItemComponent: Component {
|
|||
private let avatarBackgroundView: UIImageView
|
||||
private var avatarNode: AvatarNode?
|
||||
private var avatarAddBadgeView: UIImageView?
|
||||
private var avatarLiveBadgeView: UIImageView?
|
||||
private var avatarLiveBadgeMaskSeenLayer: SimpleLayer?
|
||||
private var avatarLiveBadgeMaskUnseenLayer: SimpleLayer?
|
||||
private let avatarShapeLayer: SimpleShapeLayer
|
||||
private let indicatorMaskSeenLayer: SimpleLayer
|
||||
private let indicatorMaskUnseenLayer: SimpleLayer
|
||||
|
|
@ -551,6 +560,8 @@ public final class StoryPeerListItemComponent: Component {
|
|||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.layer.allowsGroupOpacity = true
|
||||
|
||||
self.extractedContainerNode.contentNode.view.addSubview(self.extractedBackgroundView)
|
||||
|
||||
self.containerNode.addSubnode(self.extractedContainerNode)
|
||||
|
|
@ -568,6 +579,15 @@ public final class StoryPeerListItemComponent: Component {
|
|||
|
||||
self.avatarContent.layer.addSublayer(self.indicatorColorSeenLayer)
|
||||
self.avatarContent.layer.addSublayer(self.indicatorColorUnseenLayer)
|
||||
|
||||
if let filter = CALayer.luminanceToAlpha() {
|
||||
self.indicatorMaskSeenLayer.filters = [filter]
|
||||
self.indicatorMaskUnseenLayer.filters = [filter]
|
||||
}
|
||||
|
||||
self.indicatorMaskSeenLayer.backgroundColor = UIColor.black.cgColor
|
||||
self.indicatorMaskUnseenLayer.backgroundColor = UIColor.black.cgColor
|
||||
|
||||
self.indicatorMaskSeenLayer.addSublayer(self.indicatorShapeSeenLayer)
|
||||
self.indicatorMaskUnseenLayer.addSublayer(self.indicatorShapeUnseenLayer)
|
||||
self.indicatorColorSeenLayer.mask = self.indicatorMaskSeenLayer
|
||||
|
|
@ -772,6 +792,79 @@ public final class StoryPeerListItemComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
if component.hasLiveItems {
|
||||
let avatarLiveBadgeView: UIImageView
|
||||
var avatarLiveBadgeTransition = transition
|
||||
if let current = self.avatarLiveBadgeView {
|
||||
avatarLiveBadgeView = current
|
||||
} else {
|
||||
avatarLiveBadgeTransition = avatarLiveBadgeTransition.withAnimation(.none)
|
||||
avatarLiveBadgeView = UIImageView()
|
||||
self.avatarLiveBadgeView = avatarLiveBadgeView
|
||||
self.avatarContainer.addSubview(avatarLiveBadgeView)
|
||||
}
|
||||
let avatarLiveBadgeMaskSeenLayer: SimpleLayer
|
||||
if let current = self.avatarLiveBadgeMaskSeenLayer {
|
||||
avatarLiveBadgeMaskSeenLayer = current
|
||||
} else {
|
||||
avatarLiveBadgeMaskSeenLayer = SimpleLayer()
|
||||
avatarLiveBadgeMaskSeenLayer.backgroundColor = UIColor.black.cgColor
|
||||
self.avatarLiveBadgeMaskSeenLayer = avatarLiveBadgeMaskSeenLayer
|
||||
self.indicatorMaskSeenLayer.addSublayer(avatarLiveBadgeMaskSeenLayer)
|
||||
}
|
||||
let avatarLiveBadgeMaskUnseenLayer: SimpleLayer
|
||||
if let current = self.avatarLiveBadgeMaskUnseenLayer {
|
||||
avatarLiveBadgeMaskUnseenLayer = current
|
||||
} else {
|
||||
avatarLiveBadgeMaskUnseenLayer = SimpleLayer()
|
||||
avatarLiveBadgeMaskUnseenLayer.backgroundColor = UIColor.black.cgColor
|
||||
self.avatarLiveBadgeMaskUnseenLayer = avatarLiveBadgeMaskUnseenLayer
|
||||
self.indicatorMaskUnseenLayer.addSublayer(avatarLiveBadgeMaskUnseenLayer)
|
||||
}
|
||||
if avatarLiveBadgeView.image == nil || themeUpdated {
|
||||
//TODO:localize
|
||||
let liveString = NSAttributedString(string: "LIVE", font: Font.semibold(10.0), textColor: .white)
|
||||
let liveStringBounds = liveString.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
|
||||
let liveBadgeSize = CGSize(width: ceil(liveStringBounds.width) + 4.0 * 2.0, height: ceil(liveStringBounds.height) + 2.0 * 2.0)
|
||||
avatarLiveBadgeView.image = generateImage(liveBadgeSize, rotatedContext: { size, context in
|
||||
UIGraphicsPushContext(context)
|
||||
defer {
|
||||
UIGraphicsPopContext()
|
||||
}
|
||||
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(UIColor(rgb: 0xFF2D55).cgColor)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: size.height * 0.5).cgPath)
|
||||
context.fillPath()
|
||||
|
||||
liveString.draw(at: CGPoint(x: floorToScreenPixels((size.width - liveStringBounds.width) * 0.5), y: floorToScreenPixels((size.height - liveStringBounds.height) * 0.5)))
|
||||
})
|
||||
}
|
||||
if let image = avatarLiveBadgeView.image {
|
||||
let badgeSize = image.size
|
||||
let badgeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((avatarFrame.width - badgeSize.width) * 0.5), y: avatarFrame.height + 5.0 - badgeSize.height), size: badgeSize)
|
||||
avatarLiveBadgeTransition.setFrame(view: avatarLiveBadgeView, frame: badgeFrame)
|
||||
|
||||
avatarLiveBadgeMaskSeenLayer.frame = badgeFrame.offsetBy(dx: 8.0, dy: 8.0).insetBy(dx: -2.0, dy: -2.0)
|
||||
avatarLiveBadgeMaskSeenLayer.cornerRadius = avatarLiveBadgeMaskSeenLayer.bounds.height * 0.5
|
||||
avatarLiveBadgeMaskUnseenLayer.frame = badgeFrame.offsetBy(dx: 8.0, dy: 8.0).insetBy(dx: -2.0, dy: -2.0)
|
||||
avatarLiveBadgeMaskUnseenLayer.cornerRadius = avatarLiveBadgeMaskUnseenLayer.bounds.height * 0.5
|
||||
}
|
||||
} else {
|
||||
if let avatarLiveBadgeView = self.avatarLiveBadgeView {
|
||||
self.avatarLiveBadgeView = nil
|
||||
avatarLiveBadgeView.removeFromSuperview()
|
||||
}
|
||||
if let avatarLiveBadgeMaskSeenLayer = self.avatarLiveBadgeMaskSeenLayer {
|
||||
self.avatarLiveBadgeMaskSeenLayer = nil
|
||||
avatarLiveBadgeMaskSeenLayer.removeFromSuperlayer()
|
||||
}
|
||||
if let avatarLiveBadgeMaskUnseenLayer = self.avatarLiveBadgeMaskUnseenLayer {
|
||||
self.avatarLiveBadgeMaskUnseenLayer = nil
|
||||
avatarLiveBadgeMaskUnseenLayer.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
|
||||
self.avatarBackgroundView.isHidden = component.ringAnimation != nil || self.indicatorColorSeenLayer.isHidden
|
||||
|
||||
let baseRadius: CGFloat = 30.66
|
||||
|
|
@ -788,7 +881,10 @@ public final class StoryPeerListItemComponent: Component {
|
|||
let seenColors: [CGColor]
|
||||
let unseenColors: [CGColor]
|
||||
|
||||
if component.hasUnseenCloseFriendsItems {
|
||||
if component.hasLiveItems {
|
||||
//TODO:localize
|
||||
unseenColors = [UIColor(rgb: 0xFF3777).cgColor, UIColor(rgb: 0xFF2D55).cgColor]
|
||||
} else if component.hasUnseenCloseFriendsItems {
|
||||
unseenColors = [component.theme.chatList.storyUnseenPrivateColors.topColor.cgColor, component.theme.chatList.storyUnseenPrivateColors.bottomColor.cgColor]
|
||||
} else {
|
||||
unseenColors = [component.theme.chatList.storyUnseenColors.topColor.cgColor, component.theme.chatList.storyUnseenColors.bottomColor.cgColor]
|
||||
|
|
@ -831,7 +927,9 @@ public final class StoryPeerListItemComponent: Component {
|
|||
|
||||
let avatarPath = CGMutablePath()
|
||||
avatarPath.addEllipse(in: CGRect(origin: CGPoint(), size: avatarSize).insetBy(dx: -1.0, dy: -1.0))
|
||||
if component.peer.id == component.context.account.peerId && !component.hasItems && component.ringAnimation == nil {
|
||||
if let avatarLiveBadgeView = self.avatarLiveBadgeView {
|
||||
avatarPath.addPath(UIBezierPath(roundedRect: avatarLiveBadgeView.frame.insetBy(dx: -2.0, dy: -2.0), cornerRadius: avatarLiveBadgeView.bounds.height * 0.5).cgPath)
|
||||
} else if component.peer.id == component.context.account.peerId && !component.hasItems && component.ringAnimation == nil {
|
||||
let cutoutSize: CGFloat = 18.0 + UIScreenPixel * 2.0
|
||||
avatarPath.addEllipse(in: CGRect(origin: CGPoint(x: avatarSize.width - cutoutSize + UIScreenPixel, y: avatarSize.height - 1.0 - cutoutSize + UIScreenPixel), size: CGSize(width: cutoutSize, height: cutoutSize)))
|
||||
} else if let mappedLeftCenter {
|
||||
|
|
@ -839,8 +937,8 @@ public final class StoryPeerListItemComponent: Component {
|
|||
}
|
||||
ComponentTransition.immediate.setShapeLayerPath(layer: self.avatarShapeLayer, path: avatarPath)
|
||||
|
||||
ComponentTransition.immediate.setShapeLayerPath(layer: self.indicatorShapeSeenLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineUnseenWidth * 0.5, totalCount: component.totalCount, unseenCount: component.unseenCount, isSeen: true, segmentFraction: component.expandedAlphaFraction, rotationFraction: component.expandEffectFraction))
|
||||
ComponentTransition.immediate.setShapeLayerPath(layer: self.indicatorShapeUnseenLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineUnseenWidth * 0.5, totalCount: component.totalCount, unseenCount: component.unseenCount, isSeen: false, segmentFraction: component.expandedAlphaFraction, rotationFraction: component.expandEffectFraction))
|
||||
ComponentTransition.immediate.setShapeLayerPath(layer: self.indicatorShapeSeenLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineUnseenWidth * 0.5, totalCount: component.hasLiveItems ? 1 : component.totalCount, unseenCount: component.hasLiveItems ? 1 : component.unseenCount, isSeen: true, segmentFraction: component.expandedAlphaFraction, rotationFraction: component.expandEffectFraction))
|
||||
ComponentTransition.immediate.setShapeLayerPath(layer: self.indicatorShapeUnseenLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineUnseenWidth * 0.5, totalCount: component.hasLiveItems ? 1 : component.totalCount, unseenCount: component.hasLiveItems ? 1 : component.unseenCount, isSeen: false, segmentFraction: component.expandedAlphaFraction, rotationFraction: component.expandEffectFraction))
|
||||
|
||||
let titleString: String
|
||||
if component.peer.id == component.context.account.peerId {
|
||||
|
|
@ -899,7 +997,7 @@ public final class StoryPeerListItemComponent: Component {
|
|||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: titleString, font: Font.regular(11.0), textColor: (component.unseenCount != 0 || component.peer.id == component.context.account.peerId) ? component.theme.list.itemPrimaryTextColor : component.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.5))),
|
||||
text: .plain(NSAttributedString(string: titleString, font: Font.regular(11.0), textColor: (component.unseenCount != 0 || component.hasLiveItems || component.peer.id == component.context.account.peerId) ? component.theme.list.itemPrimaryTextColor : component.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.5))),
|
||||
maximumNumberOfLines: 1
|
||||
)),
|
||||
environment: {},
|
||||
|
|
|
|||
|
|
@ -264,7 +264,8 @@ extension ChatControllerImpl {
|
|||
return AvatarNode.StoryStats(
|
||||
totalCount: storyStats.totalCount,
|
||||
unseenCount: storyStats.unseenCount,
|
||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends,
|
||||
hasLiveItems: storyStats.hasLiveItems
|
||||
)
|
||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||
colors: AvatarNode.Colors(theme: self.presentationData.theme),
|
||||
|
|
|
|||
|
|
@ -2446,6 +2446,9 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||
|
||||
let navigateButtonsSize = self.navigateButtons.updateLayout(transition: transition)
|
||||
var navigateButtonsFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.right - navigateButtonsSize.width - 8.0, y: layout.size.height - containerInsets.bottom - inputPanelsHeight - navigateButtonsSize.height - 20.0), size: navigateButtonsSize)
|
||||
if containerInsets.bottom <= 32.0 {
|
||||
navigateButtonsFrame.origin.x -= 18.0
|
||||
}
|
||||
if case .overlay = self.chatPresentationInterfaceState.mode {
|
||||
navigateButtonsFrame = navigateButtonsFrame.offsetBy(dx: -8.0, dy: -8.0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3967,6 +3967,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
completion: completion
|
||||
)
|
||||
}
|
||||
|
||||
public func makeChannelMembersSearchController(params: ChannelMembersSearchControllerParams) -> ChannelMembersSearchController {
|
||||
return ChannelMembersSearchControllerImpl(params: params)
|
||||
}
|
||||
}
|
||||
|
||||
private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? {
|
||||
|
|
|
|||
|
|
@ -726,6 +726,12 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||
}
|
||||
|
||||
if let media {
|
||||
#if DEBUG
|
||||
if "".isEmpty {
|
||||
let _ = context.engine.messages.beginStoryLivestream().startStandalone()
|
||||
}
|
||||
#endif
|
||||
|
||||
let _ = (context.engine.messages.uploadStory(
|
||||
target: target,
|
||||
media: media,
|
||||
|
|
|
|||
|
|
@ -930,6 +930,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||
hasUnseen: true,
|
||||
hasUnseenCloseFriendsItems: false,
|
||||
hasLiveItems: false,
|
||||
colors: AvatarStoryIndicatorComponent.Colors(theme: defaultDarkPresentationTheme),
|
||||
activeLineWidth: 1.0 + UIScreenPixel,
|
||||
inactiveLineWidth: 1.0 + UIScreenPixel,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue