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)
|
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 {
|
public protocol SharedAccountContext: AnyObject {
|
||||||
var sharedContainerPath: String { get }
|
var sharedContainerPath: String { get }
|
||||||
var basePath: 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 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 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 makeDebugSettingsController(context: AccountContext?) -> ViewController?
|
||||||
|
|
||||||
func openCreateGroupCallUI(context: AccountContext, peerIds: [EnginePeer.Id], parentController: ViewController)
|
func openCreateGroupCallUI(context: AccountContext, peerIds: [EnginePeer.Id], parentController: ViewController)
|
||||||
|
|
|
||||||
|
|
@ -1156,6 +1156,11 @@ public final class AvatarNode: ASDisplayNode {
|
||||||
|
|
||||||
public let contentNode: ContentNode
|
public let contentNode: ContentNode
|
||||||
private var storyIndicator: ComponentView<Empty>?
|
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?
|
public private(set) var storyPresentationParams: StoryPresentationParams?
|
||||||
|
|
||||||
private var loadingStatuses = Bag<Disposable>()
|
private var loadingStatuses = Bag<Disposable>()
|
||||||
|
|
@ -1164,22 +1169,26 @@ public final class AvatarNode: ASDisplayNode {
|
||||||
public var totalCount: Int
|
public var totalCount: Int
|
||||||
public var unseenCount: Int
|
public var unseenCount: Int
|
||||||
public var hasUnseenCloseFriendsItems: Bool
|
public var hasUnseenCloseFriendsItems: Bool
|
||||||
|
public var hasLiveItems: Bool
|
||||||
public var progress: Float?
|
public var progress: Float?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
totalCount: Int,
|
totalCount: Int,
|
||||||
unseenCount: Int,
|
unseenCount: Int,
|
||||||
hasUnseenCloseFriendsItems: Bool,
|
hasUnseenCloseFriendsItems: Bool,
|
||||||
|
hasLiveItems: Bool,
|
||||||
progress: Float? = nil
|
progress: Float? = nil
|
||||||
) {
|
) {
|
||||||
self.totalCount = totalCount
|
self.totalCount = totalCount
|
||||||
self.unseenCount = unseenCount
|
self.unseenCount = unseenCount
|
||||||
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
|
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
|
||||||
|
self.hasLiveItems = hasLiveItems
|
||||||
self.progress = progress
|
self.progress = progress
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public private(set) var storyStats: StoryStats?
|
public private(set) var storyStats: StoryStats?
|
||||||
|
public var displayLiveBadge: Bool = false
|
||||||
|
|
||||||
public var font: UIFont {
|
public var font: UIFont {
|
||||||
get {
|
get {
|
||||||
|
|
@ -1459,6 +1468,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||||
hasUnseen: storyStats.unseenCount != 0,
|
hasUnseen: storyStats.unseenCount != 0,
|
||||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriendsItems,
|
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriendsItems,
|
||||||
|
hasLiveItems: storyStats.hasLiveItems,
|
||||||
colors: AvatarStoryIndicatorComponent.Colors(
|
colors: AvatarStoryIndicatorComponent.Colors(
|
||||||
unseenColors: storyPresentationParams.colors.unseenColors,
|
unseenColors: storyPresentationParams.colors.unseenColors,
|
||||||
unseenCloseFriendsColors: storyPresentationParams.colors.unseenCloseFriendsColors,
|
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() {
|
public func cancelLoading() {
|
||||||
|
|
|
||||||
|
|
@ -2251,7 +2251,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||||
stats: EngineChatList.StoryStats(
|
stats: EngineChatList.StoryStats(
|
||||||
totalCount: rawStoryArchiveSubscriptions.items.count,
|
totalCount: rawStoryArchiveSubscriptions.items.count,
|
||||||
unseenCount: unseenCount,
|
unseenCount: unseenCount,
|
||||||
hasUnseenCloseFriends: hasUnseenCloseFriends
|
hasUnseenCloseFriends: hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: false
|
||||||
),
|
),
|
||||||
hasUnseenCloseFriends: hasUnseenCloseFriends
|
hasUnseenCloseFriends: hasUnseenCloseFriends
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -367,7 +367,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||||
animationCache: animationCache,
|
animationCache: animationCache,
|
||||||
animationRenderer: animationRenderer,
|
animationRenderer: animationRenderer,
|
||||||
storyStats: storyStats.flatMap { stats in
|
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
|
openStories: { itemPeer, sourceNode in
|
||||||
guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else {
|
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
|
}, 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
|
}, openStories: { itemPeer, sourceNode in
|
||||||
guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else {
|
guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else {
|
||||||
return
|
return
|
||||||
|
|
@ -1002,7 +1002,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, arrowAction: nil, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer, storyStats: storyStats.flatMap { stats in
|
}, 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
|
}, openStories: { itemPeer, sourceNode in
|
||||||
guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else {
|
guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else {
|
||||||
return
|
return
|
||||||
|
|
@ -1080,7 +1080,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||||
peerContextAction(EnginePeer(peer.peer), .search(nil), node, gesture, location)
|
peerContextAction(EnginePeer(peer.peer), .search(nil), node, gesture, location)
|
||||||
}
|
}
|
||||||
}, animationCache: interaction.animationCache, animationRenderer: interaction.animationRenderer, storyStats: storyStats.flatMap { stats in
|
}, 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
|
}, openStories: { itemPeer, sourceNode in
|
||||||
guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else {
|
guard case let .peer(_, chatPeer) = itemPeer, let peer = chatPeer else {
|
||||||
return
|
return
|
||||||
|
|
@ -1205,7 +1205,8 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||||
stats: EngineChatList.StoryStats(
|
stats: EngineChatList.StoryStats(
|
||||||
totalCount: stats.totalCount,
|
totalCount: stats.totalCount,
|
||||||
unseenCount: stats.unseenCount,
|
unseenCount: stats.unseenCount,
|
||||||
hasUnseenCloseFriends: stats.hasUnseenCloseFriends
|
hasUnseenCloseFriends: stats.hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: stats.hasLiveItems
|
||||||
),
|
),
|
||||||
hasUnseenCloseFriends: stats.hasUnseenCloseFriends
|
hasUnseenCloseFriends: stats.hasUnseenCloseFriends
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1601,6 +1601,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||||
|
|
||||||
self.avatarContainerNode = ASDisplayNode()
|
self.avatarContainerNode = ASDisplayNode()
|
||||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||||
|
self.avatarNode.displayLiveBadge = true
|
||||||
|
|
||||||
self.highlightedBackgroundNode = ASDisplayNode()
|
self.highlightedBackgroundNode = ASDisplayNode()
|
||||||
self.highlightedBackgroundNode.isLayerBacked = true
|
self.highlightedBackgroundNode.isLayerBacked = true
|
||||||
|
|
@ -1791,7 +1792,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||||
return AvatarNode.StoryStats(
|
return AvatarNode.StoryStats(
|
||||||
totalCount: storyState.stats.totalCount,
|
totalCount: storyState.stats.totalCount,
|
||||||
unseenCount: storyState.stats.unseenCount,
|
unseenCount: storyState.stats.unseenCount,
|
||||||
hasUnseenCloseFriendsItems: storyState.hasUnseenCloseFriends
|
hasUnseenCloseFriendsItems: storyState.hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: storyState.stats.hasLiveItems
|
||||||
)
|
)
|
||||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||||
colors: AvatarNode.Colors(theme: item.presentationData.theme),
|
colors: AvatarNode.Colors(theme: item.presentationData.theme),
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
|
||||||
let arrowAction: (() -> Void)?
|
let arrowAction: (() -> Void)?
|
||||||
let animationCache: AnimationCache?
|
let animationCache: AnimationCache?
|
||||||
let animationRenderer: MultiAnimationRenderer?
|
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 openStories: ((ContactsPeerItemPeer, ASDisplayNode) -> Void)?
|
||||||
let adButtonAction: ((ASDisplayNode) -> Void)?
|
let adButtonAction: ((ASDisplayNode) -> Void)?
|
||||||
let visibilityUpdated: ((Bool) -> Void)?
|
let visibilityUpdated: ((Bool) -> Void)?
|
||||||
|
|
@ -253,7 +253,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
|
||||||
contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil, arrowAction: (() -> Void)? = nil,
|
contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil, arrowAction: (() -> Void)? = nil,
|
||||||
animationCache: AnimationCache? = nil,
|
animationCache: AnimationCache? = nil,
|
||||||
animationRenderer: MultiAnimationRenderer? = 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,
|
openStories: ((ContactsPeerItemPeer, ASDisplayNode) -> Void)? = nil,
|
||||||
adButtonAction: ((ASDisplayNode) -> Void)? = nil,
|
adButtonAction: ((ASDisplayNode) -> Void)? = nil,
|
||||||
visibilityUpdated: ((Bool) -> Void)? = nil
|
visibilityUpdated: ((Bool) -> Void)? = nil
|
||||||
|
|
@ -1286,7 +1286,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||||
return AvatarNode.StoryStats(
|
return AvatarNode.StoryStats(
|
||||||
totalCount: stats.total,
|
totalCount: stats.total,
|
||||||
unseenCount: stats.unseen,
|
unseenCount: stats.unseen,
|
||||||
hasUnseenCloseFriendsItems: stats.hasUnseenCloseFriends
|
hasUnseenCloseFriendsItems: stats.hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: stats.hasLiveItems
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
presentationParams: AvatarNode.StoryPresentationParams(
|
presentationParams: AvatarNode.StoryPresentationParams(
|
||||||
|
|
|
||||||
|
|
@ -1233,6 +1233,9 @@ public class Window1 {
|
||||||
let updatedInputOffset = inputHeightOffsetForLayout(self.windowLayout)
|
let updatedInputOffset = inputHeightOffsetForLayout(self.windowLayout)
|
||||||
if !previousInputOffset.isEqual(to: updatedInputOffset) {
|
if !previousInputOffset.isEqual(to: updatedInputOffset) {
|
||||||
let hide = updatingLayout.transition.isAnimated && updatingLayout.layout.upperKeyboardInputPositionBound == updatingLayout.layout.size.height
|
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
|
self.keyboardManager?.updateInteractiveInputOffset(updatedInputOffset, transition: updatingLayout.transition, completion: { [weak self] in
|
||||||
if let strongSelf = self, hide {
|
if let strongSelf = self, hide {
|
||||||
strongSelf.updateLayout {
|
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 {
|
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 {
|
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 {
|
} else {
|
||||||
self.updateLayout {
|
self.updateLayout {
|
||||||
|
|
|
||||||
|
|
@ -1749,7 +1749,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||||
return AvatarNode.StoryStats(
|
return AvatarNode.StoryStats(
|
||||||
totalCount: storyStats.totalCount,
|
totalCount: storyStats.totalCount,
|
||||||
unseenCount: storyStats.unseenCount,
|
unseenCount: storyStats.unseenCount,
|
||||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: storyStats.hasLiveItems
|
||||||
)
|
)
|
||||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||||
colors: AvatarNode.Colors(theme: item.presentationData.theme),
|
colors: AvatarNode.Colors(theme: item.presentationData.theme),
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ private final class CreateExternalMediaStreamScreenComponent: CombinedComponent
|
||||||
if let credentialsPromise = credentialsPromise {
|
if let credentialsPromise = credentialsPromise {
|
||||||
credentialsSignal = credentialsPromise.get()
|
credentialsSignal = credentialsPromise.get()
|
||||||
} else {
|
} 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
|
|> `catch` { _ -> Signal<GroupCallStreamCredentials, NoError> in
|
||||||
return .never()
|
return .never()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -754,7 +754,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
||||||
|> deliverOnMainQueue).start(next: { peerView in
|
|> deliverOnMainQueue).start(next: { peerView in
|
||||||
updateState { current in
|
updateState { current in
|
||||||
var dismissController: (() -> Void)?
|
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?()
|
dismissController?()
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
if peer.id == context.account.peerId {
|
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
|
pushControllerImpl?(channelAdminController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, adminId: peer.id, initialParticipant: participant?.participant, updated: { _ in
|
||||||
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership))
|
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership))
|
||||||
})
|
}))
|
||||||
dismissController = { [weak controller] in
|
dismissController = { [weak controller] in
|
||||||
controller?.dismiss()
|
controller?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,7 @@ public func channelBlacklistController(context: AccountContext, updatedPresentat
|
||||||
}
|
}
|
||||||
}, addPeer: {
|
}, addPeer: {
|
||||||
var dismissController: (() -> Void)?
|
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 {
|
if let participant = participant {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
switch participant.participant {
|
switch participant.participant {
|
||||||
|
|
@ -329,7 +329,7 @@ public func channelBlacklistController(context: AccountContext, updatedPresentat
|
||||||
dismissController?()
|
dismissController?()
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
})
|
}))
|
||||||
dismissController = { [weak controller] in
|
dismissController = { [weak controller] in
|
||||||
controller?.dismiss()
|
controller?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,7 @@ import TelegramPresentationData
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import SearchUI
|
import SearchUI
|
||||||
|
|
||||||
public enum ChannelMembersSearchControllerMode {
|
public final class ChannelMembersSearchControllerImpl: ViewController, ChannelMembersSearchController {
|
||||||
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 {
|
|
||||||
private let queue = Queue()
|
private let queue = Queue()
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
|
|
@ -43,15 +30,15 @@ public final class ChannelMembersSearchController: ViewController {
|
||||||
|
|
||||||
private var searchContentNode: NavigationBarSearchContentNode?
|
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) {
|
public init(params: ChannelMembersSearchControllerParams) {
|
||||||
self.context = context
|
self.context = params.context
|
||||||
self.peerId = peerId
|
self.peerId = params.peerId
|
||||||
self.mode = mode
|
self.mode = params.mode
|
||||||
self.openPeer = openPeer
|
self.openPeer = params.openPeer
|
||||||
self.filters = filters
|
self.filters = params.filters
|
||||||
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = params.updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||||
self.forceTheme = forceTheme
|
self.forceTheme = params.forceTheme
|
||||||
if let forceTheme = forceTheme {
|
if let forceTheme = params.forceTheme {
|
||||||
self.presentationData = self.presentationData.withUpdated(theme: 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.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
|
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
|
@ -88,7 +75,7 @@ public final class ChannelMembersSearchController: ViewController {
|
||||||
strongSelf.controllerNode.updatePresentationData(presentationData)
|
strongSelf.controllerNode.updatePresentationData(presentationData)
|
||||||
})
|
})
|
||||||
|
|
||||||
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|
let _ = (params.context.account.postbox.loadedPeerWithId(peerId)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
|
|
||||||
|
|
@ -1027,7 +1027,7 @@ public func channelPermissionsController(context: AccountContext, updatedPresent
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { peerId, _ in
|
|> deliverOnMainQueue).start(next: { peerId, _ in
|
||||||
var dismissController: (() -> Void)?
|
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 {
|
if let participant = participant {
|
||||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||||
switch participant.participant {
|
switch participant.participant {
|
||||||
|
|
@ -1048,7 +1048,7 @@ public func channelPermissionsController(context: AccountContext, updatedPresent
|
||||||
upgradedToSupergroupImpl?(upgradedPeerId, f)
|
upgradedToSupergroupImpl?(upgradedPeerId, f)
|
||||||
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
})
|
})
|
||||||
})
|
}))
|
||||||
dismissController = { [weak controller] in
|
dismissController = { [weak controller] in
|
||||||
controller?.dismiss()
|
controller?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -282,11 +282,13 @@ public struct PeerStoryStats: Equatable {
|
||||||
public var totalCount: Int
|
public var totalCount: Int
|
||||||
public var unseenCount: Int
|
public var unseenCount: Int
|
||||||
public var hasUnseenCloseFriends: Bool
|
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.totalCount = totalCount
|
||||||
self.unseenCount = unseenCount
|
self.unseenCount = unseenCount
|
||||||
self.hasUnseenCloseFriends = hasUnseenCloseFriends
|
self.hasUnseenCloseFriends = hasUnseenCloseFriends
|
||||||
|
self.hasLiveItems = hasLiveItems
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -305,9 +307,9 @@ func fetchPeerStoryStats(postbox: PostboxImpl, peerId: PeerId) -> PeerStoryStats
|
||||||
|
|
||||||
if topItems.isExact {
|
if topItems.isExact {
|
||||||
let stats = postbox.storyItemsTable.getStats(peerId: peerId, maxSeenId: maxSeenId)
|
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 {
|
} 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 id: Int32
|
||||||
public let expirationTimestamp: Int32?
|
public let expirationTimestamp: Int32?
|
||||||
public let isCloseFriends: Bool
|
public let isCloseFriends: Bool
|
||||||
|
public let isLiveStream: Bool
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
value: CodableEntry,
|
value: CodableEntry,
|
||||||
id: Int32,
|
id: Int32,
|
||||||
expirationTimestamp: Int32?,
|
expirationTimestamp: Int32?,
|
||||||
isCloseFriends: Bool
|
isCloseFriends: Bool,
|
||||||
|
isLiveStream: Bool
|
||||||
) {
|
) {
|
||||||
self.value = value
|
self.value = value
|
||||||
self.id = id
|
self.id = id
|
||||||
self.expirationTimestamp = expirationTimestamp
|
self.expirationTimestamp = expirationTimestamp
|
||||||
self.isCloseFriends = isCloseFriends
|
self.isCloseFriends = isCloseFriends
|
||||||
|
self.isLiveStream = isLiveStream
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: StoryItemsTableEntry, rhs: StoryItemsTableEntry) -> Bool {
|
public static func ==(lhs: StoryItemsTableEntry, rhs: StoryItemsTableEntry) -> Bool {
|
||||||
|
|
@ -34,6 +37,9 @@ public final class StoryItemsTableEntry: Equatable {
|
||||||
if lhs.isCloseFriends != rhs.isCloseFriends {
|
if lhs.isCloseFriends != rhs.isCloseFriends {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.isLiveStream != rhs.isLiveStream {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -139,10 +145,11 @@ final class StoryItemsTable: Table {
|
||||||
return key.successor
|
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 total = 0
|
||||||
var unseen = 0
|
var unseen = 0
|
||||||
var hasUnseenCloseFriends = false
|
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
|
self.valueBox.range(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId), values: { key, value in
|
||||||
let id = key.getInt32(8)
|
let id = key.getInt32(8)
|
||||||
|
|
@ -152,6 +159,7 @@ final class StoryItemsTable: Table {
|
||||||
unseen += 1
|
unseen += 1
|
||||||
|
|
||||||
var isCloseFriends = false
|
var isCloseFriends = false
|
||||||
|
var isLiveStream = false
|
||||||
let readBuffer = ReadBuffer(data: value.makeData())
|
let readBuffer = ReadBuffer(data: value.makeData())
|
||||||
var magic: UInt32 = 0
|
var magic: UInt32 = 0
|
||||||
readBuffer.read(&magic, offset: 0, length: 4)
|
readBuffer.read(&magic, offset: 0, length: 4)
|
||||||
|
|
@ -168,6 +176,7 @@ final class StoryItemsTable: Table {
|
||||||
var flags: UInt8 = 0
|
var flags: UInt8 = 0
|
||||||
readBuffer.read(&flags, offset: 0, length: 1)
|
readBuffer.read(&flags, offset: 0, length: 1)
|
||||||
isCloseFriends = (flags & (1 << 0)) != 0
|
isCloseFriends = (flags & (1 << 0)) != 0
|
||||||
|
isLiveStream = (flags & (1 << 1)) != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -180,12 +189,15 @@ final class StoryItemsTable: Table {
|
||||||
if isCloseFriends {
|
if isCloseFriends {
|
||||||
hasUnseenCloseFriends = true
|
hasUnseenCloseFriends = true
|
||||||
}
|
}
|
||||||
|
if isLiveStream {
|
||||||
|
hasLiveItems = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}, limit: 10000)
|
}, limit: 10000)
|
||||||
|
|
||||||
return (total, unseen, hasUnseenCloseFriends)
|
return (total, unseen, hasUnseenCloseFriends, hasLiveItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func get(peerId: PeerId) -> [StoryItemsTableEntry] {
|
public func get(peerId: PeerId) -> [StoryItemsTableEntry] {
|
||||||
|
|
@ -199,6 +211,7 @@ final class StoryItemsTable: Table {
|
||||||
let entry: CodableEntry
|
let entry: CodableEntry
|
||||||
var expirationTimestamp: Int32?
|
var expirationTimestamp: Int32?
|
||||||
var isCloseFriends = false
|
var isCloseFriends = false
|
||||||
|
var isLiveStream = false
|
||||||
|
|
||||||
let readBuffer = ReadBuffer(data: value.makeData())
|
let readBuffer = ReadBuffer(data: value.makeData())
|
||||||
var magic: UInt32 = 0
|
var magic: UInt32 = 0
|
||||||
|
|
@ -234,6 +247,7 @@ final class StoryItemsTable: Table {
|
||||||
var flags: UInt8 = 0
|
var flags: UInt8 = 0
|
||||||
readBuffer.read(&flags, offset: 0, length: 1)
|
readBuffer.read(&flags, offset: 0, length: 1)
|
||||||
isCloseFriends = (flags & (1 << 0)) != 0
|
isCloseFriends = (flags & (1 << 0)) != 0
|
||||||
|
isLiveStream = (flags & (1 << 1)) != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -245,7 +259,7 @@ final class StoryItemsTable: Table {
|
||||||
entry = CodableEntry(data: value.makeData())
|
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
|
return true
|
||||||
}, limit: 10000)
|
}, limit: 10000)
|
||||||
|
|
@ -386,6 +400,9 @@ final class StoryItemsTable: Table {
|
||||||
if entry.isCloseFriends {
|
if entry.isCloseFriends {
|
||||||
flags |= (1 << 0)
|
flags |= (1 << 0)
|
||||||
}
|
}
|
||||||
|
if entry.isLiveStream {
|
||||||
|
flags |= (1 << 1)
|
||||||
|
}
|
||||||
buffer.write(&flags, length: 1)
|
buffer.write(&flags, length: 1)
|
||||||
|
|
||||||
self.valueBox.set(self.table, key: self.key(Key(peerId: peerId, id: entry.id)), value: buffer.readBufferNoCopy())
|
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(
|
AvatarStoryIndicatorComponent(
|
||||||
hasUnseen: true,
|
hasUnseen: true,
|
||||||
hasUnseenCloseFriendsItems: false,
|
hasUnseenCloseFriendsItems: false,
|
||||||
|
hasLiveItems: false,
|
||||||
colors: AvatarStoryIndicatorComponent.Colors(unseenColors: colors, unseenCloseFriendsColors: colors, seenColors: colors),
|
colors: AvatarStoryIndicatorComponent.Colors(unseenColors: colors, unseenCloseFriendsColors: colors, seenColors: colors),
|
||||||
activeLineWidth: 3.0,
|
activeLineWidth: 3.0,
|
||||||
inactiveLineWidth: 3.0,
|
inactiveLineWidth: 3.0,
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,14 @@ private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat, shado
|
||||||
|
|
||||||
|
|
||||||
final class ReactionContextBackgroundNode: ASDisplayNode {
|
final class ReactionContextBackgroundNode: ASDisplayNode {
|
||||||
|
struct GlassParams {
|
||||||
|
var isTinted: Bool
|
||||||
|
|
||||||
|
init(isTinted: Bool) {
|
||||||
|
self.isTinted = isTinted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private let largeCircleSize: CGFloat
|
private let largeCircleSize: CGFloat
|
||||||
private let smallCircleSize: CGFloat
|
private let smallCircleSize: CGFloat
|
||||||
|
|
||||||
|
|
@ -59,14 +67,16 @@ final class ReactionContextBackgroundNode: ASDisplayNode {
|
||||||
private let smallCircleLayer: SimpleLayer
|
private let smallCircleLayer: SimpleLayer
|
||||||
private let smallCircleShadowLayer: SimpleLayer
|
private let smallCircleShadowLayer: SimpleLayer
|
||||||
|
|
||||||
|
private let glass: GlassParams?
|
||||||
private var theme: PresentationTheme?
|
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.largeCircleSize = largeCircleSize
|
||||||
self.smallCircleSize = smallCircleSize
|
self.smallCircleSize = smallCircleSize
|
||||||
|
|
||||||
self.backgroundView = BlurredBackgroundView(color: nil, enableBlur: true)
|
self.backgroundView = BlurredBackgroundView(color: nil, enableBlur: true)
|
||||||
if glass {
|
if glass != nil {
|
||||||
self.glassBackgroundView = GlassBackgroundView()
|
self.glassBackgroundView = GlassBackgroundView()
|
||||||
} else {
|
} else {
|
||||||
self.glassBackgroundView = nil
|
self.glassBackgroundView = nil
|
||||||
|
|
@ -237,7 +247,13 @@ final class ReactionContextBackgroundNode: ASDisplayNode {
|
||||||
var glassBackgroundFrame = contentBounds.insetBy(dx: 10.0, dy: 10.0)
|
var glassBackgroundFrame = contentBounds.insetBy(dx: 10.0, dy: 10.0)
|
||||||
glassBackgroundFrame.size.height -= 8.0
|
glassBackgroundFrame.size.height -= 8.0
|
||||||
transition.updateFrame(view: glassBackgroundView, frame: glassBackgroundFrame, beginWithCurrentState: true)
|
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))
|
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 {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -489,7 +489,7 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
|
||||||
|
|
||||||
public enum Style {
|
public enum Style {
|
||||||
case legacy
|
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) {
|
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.animationRenderer as? MultiAnimationRendererImpl)?.useYuvA = context.sharedContext.immediateExperimentalUISettings.compressedEmojiCache
|
||||||
|
|
||||||
self.backgroundMaskNode = ASDisplayNode()
|
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 = ASDisplayNode()
|
||||||
self.leftBackgroundMaskNode.backgroundColor = .black
|
self.leftBackgroundMaskNode.backgroundColor = .black
|
||||||
self.rightBackgroundMaskNode = ASDisplayNode()
|
self.rightBackgroundMaskNode = ASDisplayNode()
|
||||||
|
|
|
||||||
|
|
@ -661,6 +661,7 @@ final class StatsMessageItemNode: ListViewItemNode, ItemListItemNode {
|
||||||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||||
hasUnseen: true,
|
hasUnseen: true,
|
||||||
hasUnseenCloseFriendsItems: false,
|
hasUnseenCloseFriendsItems: false,
|
||||||
|
hasLiveItems: false,
|
||||||
colors: AvatarStoryIndicatorComponent.Colors(
|
colors: AvatarStoryIndicatorComponent.Colors(
|
||||||
unseenColors: item.presentationData.theme.chatList.storyUnseenColors.array,
|
unseenColors: item.presentationData.theme.chatList.storyUnseenColors.array,
|
||||||
unseenCloseFriendsColors: item.presentationData.theme.chatList.storyUnseenPrivateColors.array,
|
unseenCloseFriendsColors: item.presentationData.theme.chatList.storyUnseenPrivateColors.array,
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ final class StoryIconNode: ASDisplayNode {
|
||||||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||||
hasUnseen: true,
|
hasUnseen: true,
|
||||||
hasUnseenCloseFriendsItems: false,
|
hasUnseenCloseFriendsItems: false,
|
||||||
|
hasLiveItems: false,
|
||||||
colors: AvatarStoryIndicatorComponent.Colors(
|
colors: AvatarStoryIndicatorComponent.Colors(
|
||||||
unseenColors: theme.chatList.storyUnseenColors.array,
|
unseenColors: theme.chatList.storyUnseenColors.array,
|
||||||
unseenCloseFriendsColors: theme.chatList.storyUnseenPrivateColors.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[-29248689] = { return Api.GlobalPrivacySettings.parse_globalPrivacySettings($0) }
|
||||||
dict[1429932961] = { return Api.GroupCall.parse_groupCall($0) }
|
dict[1429932961] = { return Api.GroupCall.parse_groupCall($0) }
|
||||||
dict[2004925620] = { return Api.GroupCall.parse_groupCallDiscarded($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[1735736008] = { return Api.GroupCallParticipantVideo.parse_groupCallParticipantVideo($0) }
|
||||||
dict[-592373577] = { return Api.GroupCallParticipantVideoSourceGroup.parse_groupCallParticipantVideoSourceGroup($0) }
|
dict[-592373577] = { return Api.GroupCallParticipantVideoSourceGroup.parse_groupCallParticipantVideoSourceGroup($0) }
|
||||||
dict[-2132064081] = { return Api.GroupCallStreamChannel.parse_groupCallStreamChannel($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[1968737087] = { return Api.InputClientProxy.parse_inputClientProxy($0) }
|
||||||
dict[-1562241884] = { return Api.InputCollectible.parse_inputCollectiblePhone($0) }
|
dict[-1562241884] = { return Api.InputCollectible.parse_inputCollectiblePhone($0) }
|
||||||
dict[-476815191] = { return Api.InputCollectible.parse_inputCollectibleUsername($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[-55902537] = { return Api.InputDialogPeer.parse_inputDialogPeer($0) }
|
||||||
dict[1684014375] = { return Api.InputDialogPeer.parse_inputDialogPeerFolder($0) }
|
dict[1684014375] = { return Api.InputDialogPeer.parse_inputDialogPeerFolder($0) }
|
||||||
dict[448771445] = { return Api.InputDocument.parse_inputDocument($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[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) }
|
||||||
dict[1235637404] = { return Api.MediaArea.parse_mediaAreaWeather($0) }
|
dict[1235637404] = { return Api.MediaArea.parse_mediaAreaWeather($0) }
|
||||||
dict[-808853502] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($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[-1868117372] = { return Api.Message.parse_messageEmpty($0) }
|
||||||
dict[2055212554] = { return Api.Message.parse_messageService($0) }
|
dict[2055212554] = { return Api.Message.parse_messageService($0) }
|
||||||
dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($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[-1974226924] = { return Api.MessageMedia.parse_messageMediaToDo($0) }
|
||||||
dict[-1618676578] = { return Api.MessageMedia.parse_messageMediaUnsupported($0) }
|
dict[-1618676578] = { return Api.MessageMedia.parse_messageMediaUnsupported($0) }
|
||||||
dict[784356159] = { return Api.MessageMedia.parse_messageMediaVenue($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[-571405253] = { return Api.MessageMedia.parse_messageMediaWebPage($0) }
|
||||||
dict[-1938180548] = { return Api.MessagePeerReaction.parse_messagePeerReaction($0) }
|
dict[-1938180548] = { return Api.MessagePeerReaction.parse_messagePeerReaction($0) }
|
||||||
dict[-1228133028] = { return Api.MessagePeerVote.parse_messagePeerVote($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[-451831443] = { return Api.Update.parse_updateFavedStickers($0) }
|
||||||
dict[422972864] = { return Api.Update.parse_updateFolderPeers($0) }
|
dict[422972864] = { return Api.Update.parse_updateFolderPeers($0) }
|
||||||
dict[-2027964103] = { return Api.Update.parse_updateGeoLiveViewed($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[-1535694705] = { return Api.Update.parse_updateGroupCallChainBlocks($0) }
|
||||||
dict[192428418] = { return Api.Update.parse_updateGroupCallConnection($0) }
|
dict[192428418] = { return Api.Update.parse_updateGroupCallConnection($0) }
|
||||||
dict[-917002394] = { return Api.Update.parse_updateGroupCallEncryptedMessage($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[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) }
|
||||||
dict[1763610706] = { return Api.Update.parse_updateInlineBotCallbackQuery($0) }
|
dict[1763610706] = { return Api.Update.parse_updateInlineBotCallbackQuery($0) }
|
||||||
dict[1442983757] = { return Api.Update.parse_updateLangPack($0) }
|
dict[1442983757] = { return Api.Update.parse_updateLangPack($0) }
|
||||||
|
|
|
||||||
|
|
@ -60,15 +60,15 @@ public extension Api {
|
||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
indirect enum Message: TypeConstructorDescription {
|
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 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?)
|
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) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
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 {
|
if boxed {
|
||||||
buffer.appendInt32(-1743401272)
|
buffer.appendInt32(-1188071729)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt32(flags2, 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 << 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 << 6) != 0 {serializeInt64(paidMessageStars!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags2) & Int(1 << 7) != 0 {suggestedPost!.serialize(buffer, true)}
|
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
|
break
|
||||||
case .messageEmpty(let flags, let id, let peerId):
|
case .messageEmpty(let flags, let id, let peerId):
|
||||||
if boxed {
|
if boxed {
|
||||||
|
|
@ -138,8 +139,8 @@ public extension Api {
|
||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
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):
|
||||||
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)])
|
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):
|
case .messageEmpty(let flags, let id, let peerId):
|
||||||
return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)])
|
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):
|
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() {
|
if Int(_2!) & Int(1 << 7) != 0 {if let signature = reader.readInt32() {
|
||||||
_31 = Api.parse(reader, signature: signature) as? Api.SuggestedPost
|
_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 _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
|
|
@ -267,8 +270,9 @@ public extension Api {
|
||||||
let _c29 = (Int(_2!) & Int(1 << 5) == 0) || _29 != nil
|
let _c29 = (Int(_2!) & Int(1 << 5) == 0) || _29 != nil
|
||||||
let _c30 = (Int(_2!) & Int(1 << 6) == 0) || _30 != nil
|
let _c30 = (Int(_2!) & Int(1 << 6) == 0) || _30 != nil
|
||||||
let _c31 = (Int(_2!) & Int(1 << 7) == 0) || _31 != 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 {
|
let _c32 = (Int(_2!) & Int(1 << 10) == 0) || _32 != nil
|
||||||
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)
|
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 {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -725,6 +725,7 @@ public extension Api {
|
||||||
case messageMediaToDo(flags: Int32, todo: Api.TodoList, completions: [Api.TodoCompletion]?)
|
case messageMediaToDo(flags: Int32, todo: Api.TodoList, completions: [Api.TodoCompletion]?)
|
||||||
case messageMediaUnsupported
|
case messageMediaUnsupported
|
||||||
case messageMediaVenue(geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String)
|
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)
|
case messageMediaWebPage(flags: Int32, webpage: Api.WebPage)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
|
|
@ -908,6 +909,12 @@ public extension Api {
|
||||||
serializeString(venueId, buffer: buffer, boxed: false)
|
serializeString(venueId, buffer: buffer, boxed: false)
|
||||||
serializeString(venueType, buffer: buffer, boxed: false)
|
serializeString(venueType, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
|
case .messageMediaVideoStream(let call):
|
||||||
|
if boxed {
|
||||||
|
buffer.appendInt32(1059290001)
|
||||||
|
}
|
||||||
|
call.serialize(buffer, true)
|
||||||
|
break
|
||||||
case .messageMediaWebPage(let flags, let webpage):
|
case .messageMediaWebPage(let flags, let webpage):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-571405253)
|
buffer.appendInt32(-571405253)
|
||||||
|
|
@ -954,6 +961,8 @@ public extension Api {
|
||||||
return ("messageMediaUnsupported", [])
|
return ("messageMediaUnsupported", [])
|
||||||
case .messageMediaVenue(let geo, let title, let address, let provider, let venueId, let venueType):
|
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)])
|
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):
|
case .messageMediaWebPage(let flags, let webpage):
|
||||||
return ("messageMediaWebPage", [("flags", flags as Any), ("webpage", webpage as Any)])
|
return ("messageMediaWebPage", [("flags", flags as Any), ("webpage", webpage as Any)])
|
||||||
}
|
}
|
||||||
|
|
@ -1329,6 +1338,19 @@ public extension Api {
|
||||||
return nil
|
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? {
|
public static func parse_messageMediaWebPage(_ reader: BufferReader) -> MessageMedia? {
|
||||||
var _1: Int32?
|
var _1: Int32?
|
||||||
_1 = reader.readInt32()
|
_1 = reader.readInt32()
|
||||||
|
|
|
||||||
|
|
@ -604,11 +604,11 @@ public extension Api {
|
||||||
case updateFavedStickers
|
case updateFavedStickers
|
||||||
case updateFolderPeers(folderPeers: [Api.FolderPeer], pts: Int32, ptsCount: Int32)
|
case updateFolderPeers(folderPeers: [Api.FolderPeer], pts: Int32, ptsCount: Int32)
|
||||||
case updateGeoLiveViewed(peer: Api.Peer, msgId: 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 updateGroupCallChainBlocks(call: Api.InputGroupCall, subChainId: Int32, blocks: [Buffer], nextOffset: Int32)
|
||||||
case updateGroupCallConnection(flags: Int32, params: Api.DataJSON)
|
case updateGroupCallConnection(flags: Int32, params: Api.DataJSON)
|
||||||
case updateGroupCallEncryptedMessage(call: Api.InputGroupCall, fromId: Api.Peer, encryptedMessage: Buffer)
|
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 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 updateInlineBotCallbackQuery(flags: Int32, queryId: Int64, userId: Int64, msgId: Api.InputBotInlineMessageID, chatInstance: Int64, data: Buffer?, gameShortName: String?)
|
||||||
case updateLangPack(difference: Api.LangPackDifference)
|
case updateLangPack(difference: Api.LangPackDifference)
|
||||||
|
|
@ -1271,12 +1271,12 @@ public extension Api {
|
||||||
peer.serialize(buffer, true)
|
peer.serialize(buffer, true)
|
||||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
case .updateGroupCall(let flags, let chatId, let call):
|
case .updateGroupCall(let flags, let peer, let call):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-1747565759)
|
buffer.appendInt32(-1658710304)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
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)
|
call.serialize(buffer, true)
|
||||||
break
|
break
|
||||||
case .updateGroupCallChainBlocks(let call, let subChainId, let blocks, let nextOffset):
|
case .updateGroupCallChainBlocks(let call, let subChainId, let blocks, let nextOffset):
|
||||||
|
|
@ -1307,14 +1307,16 @@ public extension Api {
|
||||||
fromId.serialize(buffer, true)
|
fromId.serialize(buffer, true)
|
||||||
serializeBytes(encryptedMessage, buffer: buffer, boxed: false)
|
serializeBytes(encryptedMessage, buffer: buffer, boxed: false)
|
||||||
break
|
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 {
|
if boxed {
|
||||||
buffer.appendInt32(2026050784)
|
buffer.appendInt32(-964095818)
|
||||||
}
|
}
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
call.serialize(buffer, true)
|
call.serialize(buffer, true)
|
||||||
fromId.serialize(buffer, true)
|
fromId.serialize(buffer, true)
|
||||||
serializeInt64(randomId, buffer: buffer, boxed: false)
|
serializeInt64(randomId, buffer: buffer, boxed: false)
|
||||||
message.serialize(buffer, true)
|
message.serialize(buffer, true)
|
||||||
|
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(paidMessageStars!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .updateGroupCallParticipants(let call, let participants, let version):
|
case .updateGroupCallParticipants(let call, let participants, let version):
|
||||||
if boxed {
|
if boxed {
|
||||||
|
|
@ -2110,16 +2112,16 @@ public extension Api {
|
||||||
return ("updateFolderPeers", [("folderPeers", folderPeers as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)])
|
return ("updateFolderPeers", [("folderPeers", folderPeers as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)])
|
||||||
case .updateGeoLiveViewed(let peer, let msgId):
|
case .updateGeoLiveViewed(let peer, let msgId):
|
||||||
return ("updateGeoLiveViewed", [("peer", peer as Any), ("msgId", msgId as Any)])
|
return ("updateGeoLiveViewed", [("peer", peer as Any), ("msgId", msgId as Any)])
|
||||||
case .updateGroupCall(let flags, let chatId, let call):
|
case .updateGroupCall(let flags, let peer, let call):
|
||||||
return ("updateGroupCall", [("flags", flags as Any), ("chatId", chatId as Any), ("call", call as Any)])
|
return ("updateGroupCall", [("flags", flags as Any), ("peer", peer as Any), ("call", call as Any)])
|
||||||
case .updateGroupCallChainBlocks(let call, let subChainId, let blocks, let nextOffset):
|
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)])
|
return ("updateGroupCallChainBlocks", [("call", call as Any), ("subChainId", subChainId as Any), ("blocks", blocks as Any), ("nextOffset", nextOffset as Any)])
|
||||||
case .updateGroupCallConnection(let flags, let params):
|
case .updateGroupCallConnection(let flags, let params):
|
||||||
return ("updateGroupCallConnection", [("flags", flags as Any), ("params", params as Any)])
|
return ("updateGroupCallConnection", [("flags", flags as Any), ("params", params as Any)])
|
||||||
case .updateGroupCallEncryptedMessage(let call, let fromId, let encryptedMessage):
|
case .updateGroupCallEncryptedMessage(let call, let fromId, let encryptedMessage):
|
||||||
return ("updateGroupCallEncryptedMessage", [("call", call as Any), ("fromId", fromId as Any), ("encryptedMessage", encryptedMessage as Any)])
|
return ("updateGroupCallEncryptedMessage", [("call", call as Any), ("fromId", fromId as Any), ("encryptedMessage", encryptedMessage as Any)])
|
||||||
case .updateGroupCallMessage(let call, let fromId, let randomId, let message):
|
case .updateGroupCallMessage(let flags, let call, let fromId, let randomId, let message, let paidMessageStars):
|
||||||
return ("updateGroupCallMessage", [("call", call as Any), ("fromId", fromId as Any), ("randomId", randomId as Any), ("message", message as Any)])
|
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):
|
case .updateGroupCallParticipants(let call, let participants, let version):
|
||||||
return ("updateGroupCallParticipants", [("call", call as Any), ("participants", participants as Any), ("version", version as Any)])
|
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):
|
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? {
|
public static func parse_updateGroupCall(_ reader: BufferReader) -> Update? {
|
||||||
var _1: Int32?
|
var _1: Int32?
|
||||||
_1 = reader.readInt32()
|
_1 = reader.readInt32()
|
||||||
var _2: Int64?
|
var _2: Api.Peer?
|
||||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt64() }
|
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?
|
var _3: Api.GroupCall?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
_3 = Api.parse(reader, signature: signature) as? Api.GroupCall
|
_3 = Api.parse(reader, signature: signature) as? Api.GroupCall
|
||||||
}
|
}
|
||||||
let _c1 = _1 != nil
|
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
|
let _c3 = _3 != nil
|
||||||
if _c1 && _c2 && _c3 {
|
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 {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -3591,26 +3595,32 @@ public extension Api {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static func parse_updateGroupCallMessage(_ reader: BufferReader) -> Update? {
|
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() {
|
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() {
|
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?
|
var _4: Int64?
|
||||||
_3 = reader.readInt64()
|
_4 = reader.readInt64()
|
||||||
var _4: Api.TextWithEntities?
|
var _5: Api.TextWithEntities?
|
||||||
if let signature = reader.readInt32() {
|
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 _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
let _c4 = _4 != nil
|
let _c4 = _4 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 {
|
let _c5 = _5 != nil
|
||||||
return Api.Update.updateGroupCallMessage(call: _1!, fromId: _2!, randomId: _3!, message: _4!)
|
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 {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -5656,9 +5656,9 @@ public extension Api.functions.messages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(-539934715)
|
buffer.appendInt32(1374175969)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
peer.serialize(buffer, true)
|
peer.serialize(buffer, true)
|
||||||
serializeInt32(id, buffer: buffer, boxed: false)
|
serializeInt32(id, buffer: buffer, boxed: false)
|
||||||
|
|
@ -5671,8 +5671,9 @@ public extension Api.functions.messages {
|
||||||
item.serialize(buffer, true)
|
item.serialize(buffer, true)
|
||||||
}}
|
}}
|
||||||
if Int(flags) & Int(1 << 15) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
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)}
|
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)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Updates?
|
var result: Api.Updates?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
|
|
@ -5735,9 +5736,9 @@ public extension Api.functions.messages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(-1752618806)
|
buffer.appendInt32(1104419550)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
fromPeer.serialize(buffer, true)
|
fromPeer.serialize(buffer, true)
|
||||||
buffer.appendInt32(481674261)
|
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 << 9) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 22) != 0 {replyTo!.serialize(buffer, true)}
|
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 << 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 << 13) != 0 {sendAs!.serialize(buffer, true)}
|
||||||
if Int(flags) & Int(1 << 17) != 0 {quickReplyShortcut!.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 << 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 << 21) != 0 {serializeInt64(allowPaidStars!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 23) != 0 {suggestedPost!.serialize(buffer, true)}
|
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)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Updates?
|
var result: Api.Updates?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
|
|
@ -8355,9 +8357,9 @@ public extension Api.functions.messages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(-1403659839)
|
buffer.appendInt32(53536639)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
peer.serialize(buffer, true)
|
peer.serialize(buffer, true)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {replyTo!.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)
|
item.serialize(buffer, true)
|
||||||
}}
|
}}
|
||||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
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 << 13) != 0 {sendAs!.serialize(buffer, true)}
|
||||||
if Int(flags) & Int(1 << 17) != 0 {quickReplyShortcut!.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 << 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 << 21) != 0 {serializeInt64(allowPaidStars!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 22) != 0 {suggestedPost!.serialize(buffer, true)}
|
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)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Updates?
|
var result: Api.Updates?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
|
|
@ -8387,9 +8390,9 @@ public extension Api.functions.messages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(-33170278)
|
buffer.appendInt32(1415369050)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
peer.serialize(buffer, true)
|
peer.serialize(buffer, true)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {replyTo!.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)
|
item.serialize(buffer, true)
|
||||||
}}
|
}}
|
||||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
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 << 13) != 0 {sendAs!.serialize(buffer, true)}
|
||||||
if Int(flags) & Int(1 << 17) != 0 {quickReplyShortcut!.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 << 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 << 21) != 0 {serializeInt64(allowPaidStars!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 22) != 0 {suggestedPost!.serialize(buffer, true)}
|
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)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Updates?
|
var result: Api.Updates?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
|
|
@ -10567,12 +10571,13 @@ public extension Api.functions.phone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(-558650433)
|
buffer.appendInt32(1525991226)
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
peer.serialize(buffer, true)
|
peer.serialize(buffer, true)
|
||||||
revoke.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)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.phone.GroupCallStreamRtmpUrl?
|
var result: Api.phone.GroupCallStreamRtmpUrl?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
|
|
@ -10829,13 +10834,15 @@ public extension Api.functions.phone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(-2021052396)
|
buffer.appendInt32(2124127245)
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
call.serialize(buffer, true)
|
call.serialize(buffer, true)
|
||||||
serializeInt64(randomId, buffer: buffer, boxed: false)
|
serializeInt64(randomId, buffer: buffer, boxed: false)
|
||||||
message.serialize(buffer, true)
|
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)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Bool?
|
var result: Api.Bool?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
|
|
@ -10913,14 +10920,15 @@ public extension Api.functions.phone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(-378390524)
|
buffer.appendInt32(-1757179150)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
call.serialize(buffer, true)
|
call.serialize(buffer, true)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {joinMuted!.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)}
|
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)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Updates?
|
var result: Api.Updates?
|
||||||
if let signature = reader.readInt32() {
|
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 {
|
public extension Api.functions.stories {
|
||||||
static func toggleAllStoriesHidden(hidden: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
static func toggleAllStoriesHidden(hidden: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
|
|
|
||||||
|
|
@ -1330,13 +1330,13 @@ public extension Api {
|
||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
enum GroupCallParticipant: TypeConstructorDescription {
|
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) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
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 {
|
if boxed {
|
||||||
buffer.appendInt32(-341428482)
|
buffer.appendInt32(708691884)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
peer.serialize(buffer, true)
|
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 << 13) != 0 {serializeInt64(raiseHandRating!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 6) != 0 {video!.serialize(buffer, true)}
|
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 << 14) != 0 {presentation!.serialize(buffer, true)}
|
||||||
|
if Int(flags) & Int(1 << 16) != 0 {serializeInt64(paidStarsTotal!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
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):
|
||||||
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)])
|
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() {
|
if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() {
|
||||||
_10 = Api.parse(reader, signature: signature) as? Api.GroupCallParticipantVideo
|
_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 _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
|
|
@ -1396,8 +1399,9 @@ public extension Api {
|
||||||
let _c8 = (Int(_1!) & Int(1 << 13) == 0) || _8 != nil
|
let _c8 = (Int(_1!) & Int(1 << 13) == 0) || _8 != nil
|
||||||
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
|
let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil
|
||||||
let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil
|
let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
|
let _c11 = (Int(_1!) & Int(1 << 16) == 0) || _11 != nil
|
||||||
return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7, raiseHandRating: _8, video: _9, presentation: _10)
|
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 {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -572,44 +572,54 @@ public extension Api {
|
||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
enum InputContact: TypeConstructorDescription {
|
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) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
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 {
|
if boxed {
|
||||||
buffer.appendInt32(-208488460)
|
buffer.appendInt32(1780335806)
|
||||||
}
|
}
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt64(clientId, buffer: buffer, boxed: false)
|
serializeInt64(clientId, buffer: buffer, boxed: false)
|
||||||
serializeString(phone, buffer: buffer, boxed: false)
|
serializeString(phone, buffer: buffer, boxed: false)
|
||||||
serializeString(firstName, buffer: buffer, boxed: false)
|
serializeString(firstName, buffer: buffer, boxed: false)
|
||||||
serializeString(lastName, buffer: buffer, boxed: false)
|
serializeString(lastName, buffer: buffer, boxed: false)
|
||||||
|
if Int(flags) & Int(1 << 0) != 0 {note!.serialize(buffer, true)}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
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):
|
||||||
return ("inputPhoneContact", [("clientId", clientId as Any), ("phone", phone as Any), ("firstName", firstName as Any), ("lastName", lastName as Any)])
|
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? {
|
public static func parse_inputPhoneContact(_ reader: BufferReader) -> InputContact? {
|
||||||
var _1: Int64?
|
var _1: Int32?
|
||||||
_1 = reader.readInt64()
|
_1 = reader.readInt32()
|
||||||
var _2: String?
|
var _2: Int64?
|
||||||
_2 = parseString(reader)
|
_2 = reader.readInt64()
|
||||||
var _3: String?
|
var _3: String?
|
||||||
_3 = parseString(reader)
|
_3 = parseString(reader)
|
||||||
var _4: String?
|
var _4: String?
|
||||||
_4 = parseString(reader)
|
_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 _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
let _c4 = _4 != nil
|
let _c4 = _4 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 {
|
let _c5 = _5 != nil
|
||||||
return Api.InputContact.inputPhoneContact(clientId: _1!, phone: _2!, firstName: _3!, lastName: _4!)
|
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 {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,6 @@ swift_library(
|
||||||
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
|
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
|
||||||
"//submodules/AlertUI:AlertUI",
|
"//submodules/AlertUI:AlertUI",
|
||||||
"//submodules/DirectionalPanGesture:DirectionalPanGesture",
|
"//submodules/DirectionalPanGesture:DirectionalPanGesture",
|
||||||
"//submodules/PeerInfoUI:PeerInfoUI",
|
|
||||||
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
|
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
|
||||||
"//submodules/DeviceProximity:DeviceProximity",
|
"//submodules/DeviceProximity:DeviceProximity",
|
||||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||||
|
|
|
||||||
|
|
@ -367,6 +367,7 @@ public final class MediaStreamComponent: CombinedComponent {
|
||||||
isVisible: environment.isVisible && context.state.isVisibleInHierarchy,
|
isVisible: environment.isVisible && context.state.isVisibleInHierarchy,
|
||||||
isAdmin: context.state.canManageCall,
|
isAdmin: context.state.canManageCall,
|
||||||
peerTitle: context.state.peerTitle,
|
peerTitle: context.state.peerTitle,
|
||||||
|
addInset: !isFullscreen,
|
||||||
isFullscreen: isFullscreen,
|
isFullscreen: isFullscreen,
|
||||||
videoLoading: context.state.videoStalled,
|
videoLoading: context.state.videoStalled,
|
||||||
callPeer: context.state.chatPeer,
|
callPeer: context.state.chatPeer,
|
||||||
|
|
@ -595,7 +596,7 @@ public final class MediaStreamComponent: CombinedComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
let credentialsPromise = Promise<GroupCallStreamCredentials>()
|
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
|
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)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor, backgroundColor: nil)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import Postbox
|
||||||
import TelegramVoip
|
import TelegramVoip
|
||||||
import ComponentDisplayAdapters
|
import ComponentDisplayAdapters
|
||||||
|
|
||||||
final class MediaStreamVideoComponent: Component {
|
public final class MediaStreamVideoComponent: Component {
|
||||||
let call: PresentationGroupCallImpl
|
let call: PresentationGroupCallImpl
|
||||||
let hasVideo: Bool
|
let hasVideo: Bool
|
||||||
let isVisible: Bool
|
let isVisible: Bool
|
||||||
|
|
@ -23,18 +23,20 @@ final class MediaStreamVideoComponent: Component {
|
||||||
let deactivatePictureInPicture: ActionSlot<Void>
|
let deactivatePictureInPicture: ActionSlot<Void>
|
||||||
let bringBackControllerForPictureInPictureDeactivation: (@escaping () -> Void) -> Void
|
let bringBackControllerForPictureInPictureDeactivation: (@escaping () -> Void) -> Void
|
||||||
let pictureInPictureClosed: () -> Void
|
let pictureInPictureClosed: () -> Void
|
||||||
|
let addInset: Bool
|
||||||
let isFullscreen: Bool
|
let isFullscreen: Bool
|
||||||
let onVideoSizeRetrieved: (CGSize) -> Void
|
let onVideoSizeRetrieved: (CGSize) -> Void
|
||||||
let videoLoading: Bool
|
let videoLoading: Bool
|
||||||
let callPeer: Peer?
|
let callPeer: Peer?
|
||||||
let onVideoPlaybackLiveChange: (Bool) -> Void
|
let onVideoPlaybackLiveChange: (Bool) -> Void
|
||||||
|
|
||||||
init(
|
public init(
|
||||||
call: PresentationGroupCallImpl,
|
call: PresentationGroupCallImpl,
|
||||||
hasVideo: Bool,
|
hasVideo: Bool,
|
||||||
isVisible: Bool,
|
isVisible: Bool,
|
||||||
isAdmin: Bool,
|
isAdmin: Bool,
|
||||||
peerTitle: String,
|
peerTitle: String,
|
||||||
|
addInset: Bool,
|
||||||
isFullscreen: Bool,
|
isFullscreen: Bool,
|
||||||
videoLoading: Bool,
|
videoLoading: Bool,
|
||||||
callPeer: Peer?,
|
callPeer: Peer?,
|
||||||
|
|
@ -58,6 +60,7 @@ final class MediaStreamVideoComponent: Component {
|
||||||
self.onVideoPlaybackLiveChange = onVideoPlaybackLiveChange
|
self.onVideoPlaybackLiveChange = onVideoPlaybackLiveChange
|
||||||
|
|
||||||
self.callPeer = callPeer
|
self.callPeer = callPeer
|
||||||
|
self.addInset = addInset
|
||||||
self.isFullscreen = isFullscreen
|
self.isFullscreen = isFullscreen
|
||||||
self.onVideoSizeRetrieved = onVideoSizeRetrieved
|
self.onVideoSizeRetrieved = onVideoSizeRetrieved
|
||||||
}
|
}
|
||||||
|
|
@ -78,6 +81,9 @@ final class MediaStreamVideoComponent: Component {
|
||||||
if lhs.peerTitle != rhs.peerTitle {
|
if lhs.peerTitle != rhs.peerTitle {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.addInset != rhs.addInset {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.isFullscreen != rhs.isFullscreen {
|
if lhs.isFullscreen != rhs.isFullscreen {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -334,7 +340,7 @@ final class MediaStreamVideoComponent: Component {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
stallTimer = _stallTimer
|
stallTimer = _stallTimer
|
||||||
self.clipsToBounds = component.isFullscreen // or just true
|
self.clipsToBounds = true
|
||||||
|
|
||||||
if let videoView = self.videoRenderingContext.makeView(input: input, blur: false, forceSampleBufferDisplayLayer: true) {
|
if let videoView = self.videoRenderingContext.makeView(input: input, blur: false, forceSampleBufferDisplayLayer: true) {
|
||||||
self.videoView = videoView
|
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.videoBlurView = videoBlurView
|
||||||
self.insertSubview(videoBlurView, belowSubview: self.blurTintView)
|
self.insertSubview(videoBlurView, belowSubview: self.blurTintView)
|
||||||
videoBlurView.alpha = 0
|
videoBlurView.alpha = 0
|
||||||
|
|
@ -453,14 +459,14 @@ final class MediaStreamVideoComponent: Component {
|
||||||
fullScreenBackgroundPlaceholder.frame = .init(origin: .zero, size: availableSize)
|
fullScreenBackgroundPlaceholder.frame = .init(origin: .zero, size: availableSize)
|
||||||
|
|
||||||
let videoInset: CGFloat
|
let videoInset: CGFloat
|
||||||
if !component.isFullscreen {
|
if component.addInset && !component.isFullscreen {
|
||||||
videoInset = 16
|
videoInset = 16
|
||||||
} else {
|
} else {
|
||||||
videoInset = 0
|
videoInset = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
let videoSize: CGSize
|
let videoSize: CGSize
|
||||||
let videoCornerRadius: CGFloat = component.isFullscreen ? 0 : 10
|
let videoCornerRadius: CGFloat = (component.isFullscreen || !component.addInset) ? 0 : 10
|
||||||
|
|
||||||
let videoFrameUpdateTransition: ComponentTransition
|
let videoFrameUpdateTransition: ComponentTransition
|
||||||
if self.wasFullscreen != component.isFullscreen {
|
if self.wasFullscreen != component.isFullscreen {
|
||||||
|
|
@ -478,6 +484,7 @@ final class MediaStreamVideoComponent: Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
var aspect = videoView.getAspect()
|
var aspect = videoView.getAspect()
|
||||||
|
|
||||||
if component.isFullscreen && self.hadVideo {
|
if component.isFullscreen && self.hadVideo {
|
||||||
if aspect <= 0.01 {
|
if aspect <= 0.01 {
|
||||||
aspect = 16.0 / 9
|
aspect = 16.0 / 9
|
||||||
|
|
@ -485,8 +492,12 @@ final class MediaStreamVideoComponent: Component {
|
||||||
} else if !self.hadVideo {
|
} else if !self.hadVideo {
|
||||||
aspect = 16.0 / 9
|
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))
|
videoSize = CGSize(width: aspect * 100.0, height: 100.0).aspectFitted(.init(width: availableSize.width - videoInset * 2, height: availableSize.height))
|
||||||
} else {
|
} else {
|
||||||
// Limiting by smallest side -- redundant if passing precalculated availableSize
|
// 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.hadVideo && !component.call.accountContext.sharedContext.immediateExperimentalUISettings.liveStreamV2 {
|
||||||
if self.noSignalTimer == nil {
|
if self.noSignalTimer == nil {
|
||||||
if #available(iOS 10.0, *) {
|
let noSignalTimer = Timer(timeInterval: 20.0, repeats: false, block: { [weak self] _ in
|
||||||
let noSignalTimer = Timer(timeInterval: 20.0, repeats: false, block: { [weak self] _ in
|
guard let strongSelf = self else {
|
||||||
guard let strongSelf = self else {
|
return
|
||||||
return
|
}
|
||||||
}
|
strongSelf.noSignalTimeout = true
|
||||||
strongSelf.noSignalTimeout = true
|
strongSelf.state?.updated(transition: .immediate)
|
||||||
strongSelf.state?.updated(transition: .immediate)
|
})
|
||||||
})
|
self.noSignalTimer = noSignalTimer
|
||||||
self.noSignalTimer = noSignalTimer
|
RunLoop.main.add(noSignalTimer, forMode: .common)
|
||||||
RunLoop.main.add(noSignalTimer, forMode: .common)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.noSignalTimeout, !"".isEmpty {
|
if self.noSignalTimeout, !"".isEmpty {
|
||||||
|
|
@ -696,7 +705,7 @@ final class MediaStreamVideoComponent: Component {
|
||||||
return availableSize
|
return availableSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
public func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||||
if let videoView = self.videoView, let presentation = videoView.snapshotView(afterScreenUpdates: false) {
|
if let videoView = self.videoView, let presentation = videoView.snapshotView(afterScreenUpdates: false) {
|
||||||
let presentationParent = self.window ?? self
|
let presentationParent = self.window ?? self
|
||||||
presentationParent.addSubview(presentation)
|
presentationParent.addSubview(presentation)
|
||||||
|
|
@ -750,12 +759,12 @@ final class MediaStreamVideoComponent: Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
public func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||||
self.didRequestBringBack = false
|
self.didRequestBringBack = false
|
||||||
self.state?.updated(transition: .immediate)
|
self.state?.updated(transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
public func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||||||
if self.requestedExpansion {
|
if self.requestedExpansion {
|
||||||
self.requestedExpansion = false
|
self.requestedExpansion = false
|
||||||
} else if !didRequestBringBack {
|
} 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.videoView?.alpha = 1
|
||||||
self.state?.updated(transition: .immediate)
|
self.state?.updated(transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
@ -810,94 +819,3 @@ private final class CustomIntensityVisualEffectView: UIVisualEffectView {
|
||||||
animator.stopAnimation(true)
|
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
|
private var lastErrorAlertTimestamp: Double = 0.0
|
||||||
|
|
||||||
init(
|
public init(
|
||||||
accountContext: AccountContext,
|
accountContext: AccountContext,
|
||||||
audioSession: ManagedAudioSession,
|
audioSession: ManagedAudioSession,
|
||||||
callKitIntegration: CallKitIntegration?,
|
callKitIntegration: CallKitIntegration?,
|
||||||
|
|
@ -1356,7 +1356,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||||
muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
|
muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
|
||||||
volume: nil,
|
volume: nil,
|
||||||
about: about,
|
about: about,
|
||||||
joinedVideo: self.temporaryJoinedVideo
|
joinedVideo: self.temporaryJoinedVideo,
|
||||||
|
paidStarsTotal: nil
|
||||||
))
|
))
|
||||||
participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) })
|
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),
|
muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
|
||||||
volume: nil,
|
volume: nil,
|
||||||
about: about,
|
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),
|
muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: canManageCall || !state.defaultParticipantsAreMuted.isMuted, mutedByYou: false),
|
||||||
volume: nil,
|
volume: nil,
|
||||||
about: about,
|
about: about,
|
||||||
joinedVideo: self.temporaryJoinedVideo
|
joinedVideo: self.temporaryJoinedVideo,
|
||||||
|
paidStarsTotal: nil
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1743,7 +1746,30 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||||
shouldJoin = callInfo.scheduleTimestamp == nil
|
shouldJoin = callInfo.scheduleTimestamp == nil
|
||||||
activeCallInfo = callInfo
|
activeCallInfo = callInfo
|
||||||
} else {
|
} 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 {
|
if self.leaving {
|
||||||
|
|
@ -2003,6 +2029,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||||
joinAs: self.joinAsPeerId,
|
joinAs: self.joinAsPeerId,
|
||||||
callId: callInfo.id,
|
callId: callInfo.id,
|
||||||
reference: reference,
|
reference: reference,
|
||||||
|
isStream: self.isStream,
|
||||||
preferMuted: isEffectivelyMuted,
|
preferMuted: isEffectivelyMuted,
|
||||||
joinPayload: joinPayload,
|
joinPayload: joinPayload,
|
||||||
peerAdminIds: peerAdminIds,
|
peerAdminIds: peerAdminIds,
|
||||||
|
|
@ -2460,7 +2487,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||||
muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
|
muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
|
||||||
volume: nil,
|
volume: nil,
|
||||||
about: about,
|
about: about,
|
||||||
joinedVideo: self.temporaryJoinedVideo
|
joinedVideo: self.temporaryJoinedVideo,
|
||||||
|
paidStarsTotal: nil
|
||||||
))
|
))
|
||||||
participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) })
|
participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) })
|
||||||
}
|
}
|
||||||
|
|
@ -3649,7 +3677,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let value = value {
|
if let value {
|
||||||
var reference: InternalGroupCallReference = .id(id: value.id, accessHash: value.accessHash)
|
var reference: InternalGroupCallReference = .id(id: value.id, accessHash: value.accessHash)
|
||||||
if let current = self.initialCall {
|
if let current = self.initialCall {
|
||||||
switch current.reference {
|
switch current.reference {
|
||||||
|
|
|
||||||
|
|
@ -1263,7 +1263,8 @@ final class VideoChatScreenComponent: Component {
|
||||||
muteState: nil,
|
muteState: nil,
|
||||||
volume: nil,
|
volume: nil,
|
||||||
about: nil,
|
about: nil,
|
||||||
joinedVideo: false
|
joinedVideo: false,
|
||||||
|
paidStarsTotal: nil
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
if let remotePeer {
|
if let remotePeer {
|
||||||
|
|
@ -1289,7 +1290,8 @@ final class VideoChatScreenComponent: Component {
|
||||||
muteState: nil,
|
muteState: nil,
|
||||||
volume: nil,
|
volume: nil,
|
||||||
about: nil,
|
about: nil,
|
||||||
joinedVideo: false
|
joinedVideo: false,
|
||||||
|
paidStarsTotal: nil
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
let members = PresentationGroupCallMembers(
|
let members = PresentationGroupCallMembers(
|
||||||
|
|
@ -3712,7 +3714,7 @@ final class VideoChatScreenComponent: Component {
|
||||||
context: call.accountContext,
|
context: call.accountContext,
|
||||||
animationCache: call.accountContext.animationCache,
|
animationCache: call.accountContext.animationCache,
|
||||||
presentationData: call.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkPresentationTheme),
|
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) },
|
items: reactionItems.map { ReactionContextItem.reaction(item: $0, icon: .none) },
|
||||||
selectedItems: Set(),
|
selectedItems: Set(),
|
||||||
title: nil,
|
title: nil,
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ import UIKit
|
||||||
import Display
|
import Display
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import PeerInfoUI
|
|
||||||
import OverlayStatusController
|
import OverlayStatusController
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import InviteLinksUI
|
import InviteLinksUI
|
||||||
import UndoUI
|
import UndoUI
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import AccountContext
|
||||||
|
|
||||||
extension VideoChatScreenComponent.View {
|
extension VideoChatScreenComponent.View {
|
||||||
func openInviteMembers() {
|
func openInviteMembers() {
|
||||||
|
|
@ -113,7 +113,7 @@ extension VideoChatScreenComponent.View {
|
||||||
filters.append(.excludeBots)
|
filters.append(.excludeBots)
|
||||||
|
|
||||||
var dismissController: (() -> Void)?
|
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 {
|
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
|
||||||
dismissController?()
|
dismissController?()
|
||||||
return
|
return
|
||||||
|
|
@ -327,7 +327,7 @@ extension VideoChatScreenComponent.View {
|
||||||
})]), in: .window(.root))
|
})]), in: .window(.root))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
controller.copyInviteLink = { [weak self] in
|
controller.copyInviteLink = { [weak self] in
|
||||||
dismissController?()
|
dismissController?()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import UndoUI
|
||||||
import AlertUI
|
import AlertUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import DirectionalPanGesture
|
import DirectionalPanGesture
|
||||||
import PeerInfoUI
|
|
||||||
import AvatarNode
|
import AvatarNode
|
||||||
import TooltipUI
|
import TooltipUI
|
||||||
import LegacyUI
|
import LegacyUI
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import TelegramUIPreferences
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import AlertUI
|
import AlertUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import PeerInfoUI
|
|
||||||
import ShareController
|
import ShareController
|
||||||
import AvatarNode
|
import AvatarNode
|
||||||
import UndoUI
|
import UndoUI
|
||||||
|
|
|
||||||
|
|
@ -238,6 +238,7 @@ private var declaredEncodables: Void = {
|
||||||
declareEncodable(TelegramMediaTodo.Completion.self, f: { TelegramMediaTodo.Completion(decoder: $0) })
|
declareEncodable(TelegramMediaTodo.Completion.self, f: { TelegramMediaTodo.Completion(decoder: $0) })
|
||||||
declareEncodable(SuggestedPostMessageAttribute.self, f: { SuggestedPostMessageAttribute(decoder: $0) })
|
declareEncodable(SuggestedPostMessageAttribute.self, f: { SuggestedPostMessageAttribute(decoder: $0) })
|
||||||
declareEncodable(PublishedSuggestedPostMessageAttribute.self, f: { PublishedSuggestedPostMessageAttribute(decoder: $0) })
|
declareEncodable(PublishedSuggestedPostMessageAttribute.self, f: { PublishedSuggestedPostMessageAttribute(decoder: $0) })
|
||||||
|
declareEncodable(TelegramMediaLiveStream.self, f: { TelegramMediaLiveStream(decoder: $0) })
|
||||||
return
|
return
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
||||||
|
|
||||||
func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
|
func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
|
||||||
switch messsage {
|
switch messsage {
|
||||||
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
let chatPeerId = messagePeerId
|
let chatPeerId = messagePeerId
|
||||||
return chatPeerId.peerId
|
return chatPeerId.peerId
|
||||||
case let .messageEmpty(_, _, peerId):
|
case let .messageEmpty(_, _, peerId):
|
||||||
|
|
@ -144,7 +144,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
|
||||||
|
|
||||||
func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
||||||
switch message {
|
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
|
let peerId: PeerId = chatPeerId.peerId
|
||||||
|
|
||||||
var result = [peerId]
|
var result = [peerId]
|
||||||
|
|
@ -279,7 +279,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
||||||
|
|
||||||
func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: ReferencedReplyMessageIds, generalIds: [MessageId])? {
|
func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: ReferencedReplyMessageIds, generalIds: [MessageId])? {
|
||||||
switch message {
|
switch message {
|
||||||
case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
if let replyTo = replyTo {
|
if let replyTo = replyTo {
|
||||||
let peerId: PeerId = chatPeerId.peerId
|
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)
|
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):
|
case let .messageMediaPaidMedia(starsAmount, apiExtendedMedia):
|
||||||
return (TelegramMediaPaidContent(amount: starsAmount, extendedMedia: apiExtendedMedia.compactMap({ TelegramExtendedMedia(apiExtendedMedia: $0, peerId: peerId) })), nil, nil, nil, nil, nil)
|
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 {
|
extension StoreMessage {
|
||||||
convenience init?(apiMessage: Api.Message, accountPeerId: PeerId, peerIsForum: Bool, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
|
convenience init?(apiMessage: Api.Message, accountPeerId: PeerId, peerIsForum: Bool, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
|
||||||
switch apiMessage {
|
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] = []
|
var attributes: [MessageAttribute] = []
|
||||||
|
|
||||||
if (flags2 & (1 << 4)) != 0 {
|
if (flags2 & (1 << 4)) != 0 {
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,7 @@ private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox,
|
||||||
flags |= Int32(1 << 17)
|
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
|
|> map { result -> Api.Updates? in
|
||||||
return result
|
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)
|
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)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
|
|
|
||||||
|
|
@ -454,7 +454,7 @@ private func sendUploadedMessageContent(
|
||||||
flags |= 1 << 22
|
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):
|
case let .media(inputMedia, text):
|
||||||
if bubbleUpEmojiOrStickersets {
|
if bubbleUpEmojiOrStickersets {
|
||||||
flags |= Int32(1 << 15)
|
flags |= Int32(1 << 15)
|
||||||
|
|
@ -486,7 +486,7 @@ private func sendUploadedMessageContent(
|
||||||
flags |= 1 << 22
|
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)
|
|> map(NetworkRequestResult.result)
|
||||||
case let .forward(sourceInfo):
|
case let .forward(sourceInfo):
|
||||||
if topMsgId != nil {
|
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) {
|
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)
|
|> map(NetworkRequestResult.result)
|
||||||
} else {
|
} else {
|
||||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
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)
|
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
|
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
|
|
@ -726,7 +726,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|
||||||
flags |= 1 << 22
|
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
|
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1678,13 +1678,12 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case let .updateGroupCall(_, channelId, call):
|
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?.peerId, call: call)
|
||||||
updatedState.updateGroupCall(peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value($0)) }, call: call)
|
|
||||||
case let .updateGroupCallChainBlocks(call, subChainId, blocks, nextOffset):
|
case let .updateGroupCallChainBlocks(call, subChainId, blocks, nextOffset):
|
||||||
if case let .inputGroupCall(id, accessHash) = call {
|
if case let .inputGroupCall(id, accessHash) = call {
|
||||||
updatedState.updateGroupCallChainBlocks(id: id, accessHash: accessHash, subChainId: subChainId, blocks: blocks.map { $0.makeData() }, nextOffset: nextOffset)
|
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 {
|
if case let .inputGroupCall(id, _) = call {
|
||||||
updatedState.updateGroupCallMessage(id: id, authorId: fromId.peerId, randomId: randomId, text: message)
|
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 let currentIndex = updatedPeerEntries.firstIndex(where: { $0.id == storedItem.id }) {
|
||||||
if case .item = storedItem {
|
if case .item = storedItem {
|
||||||
if let codedEntry = CodableEntry(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 {
|
} else {
|
||||||
if let codedEntry = CodableEntry(storedItem) {
|
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 {
|
if case .item = storedItem {
|
||||||
|
|
@ -5246,7 +5245,7 @@ func replayFinalState(
|
||||||
folderIds: item.folderIds
|
folderIds: item.folderIds
|
||||||
))
|
))
|
||||||
if let entry = CodableEntry(updatedItem) {
|
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?
|
var updatedTimestamp: Int32?
|
||||||
if let apiMessage = apiMessage {
|
if let apiMessage = apiMessage {
|
||||||
switch apiMessage {
|
switch apiMessage {
|
||||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
updatedTimestamp = date
|
updatedTimestamp = date
|
||||||
case .messageEmpty:
|
case .messageEmpty:
|
||||||
break
|
break
|
||||||
|
|
@ -400,7 +400,7 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage
|
||||||
} else if let message = messages.first, let apiMessage = result.messages.first {
|
} else if let message = messages.first, let apiMessage = result.messages.first {
|
||||||
if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
|
if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
|
||||||
namespace = Namespaces.Message.ScheduledCloud
|
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
|
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 id: Int64
|
||||||
public var accessHash: Int64
|
public var accessHash: Int64
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -326,7 +326,7 @@ private func pushDeviceContactData(accountPeerId: PeerId, postbox: Postbox, netw
|
||||||
batches = batches
|
batches = batches
|
||||||
|> mapToSignal { intermediateResult -> Signal<PushDeviceContactsResult, NoError> in
|
|> 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 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)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.contacts.ImportedContacts?, NoError> in
|
|> `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) {
|
} else if let inputSourcePeerId = forwardPeerIds.first, let inputSourcePeer = transaction.getPeer(inputSourcePeerId).flatMap(apiInputPeer) {
|
||||||
let dependencyTag = PendingMessageRequestDependencyTag(messageId: messages[0].0.id)
|
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 {
|
} else {
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "Invalid forward source"))
|
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "Invalid forward source"))
|
||||||
|
|
@ -1671,7 +1671,7 @@ public final class PendingMessageManager {
|
||||||
flags |= 1 << 22
|
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):
|
case let .media(inputMedia, text):
|
||||||
if bubbleUpEmojiOrStickersets {
|
if bubbleUpEmojiOrStickersets {
|
||||||
flags |= Int32(1 << 15)
|
flags |= Int32(1 << 15)
|
||||||
|
|
@ -1770,7 +1770,7 @@ public final class PendingMessageManager {
|
||||||
flags |= 1 << 22
|
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)
|
|> map(NetworkRequestResult.result)
|
||||||
case let .forward(sourceInfo):
|
case let .forward(sourceInfo):
|
||||||
var topMsgId: Int32?
|
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) {
|
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)
|
|> map(NetworkRequestResult.result)
|
||||||
} else {
|
} else {
|
||||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
||||||
|
|
@ -2060,7 +2060,7 @@ public final class PendingMessageManager {
|
||||||
if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
|
if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
|
||||||
isScheduled = true
|
isScheduled = true
|
||||||
}
|
}
|
||||||
if case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage {
|
if case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage {
|
||||||
if (flags2 & (1 << 4)) != 0 {
|
if (flags2 & (1 << 4)) != 0 {
|
||||||
isScheduled = true
|
isScheduled = true
|
||||||
}
|
}
|
||||||
|
|
@ -2104,7 +2104,7 @@ public final class PendingMessageManager {
|
||||||
namespace = Namespaces.Message.QuickReplyCloud
|
namespace = Namespaces.Message.QuickReplyCloud
|
||||||
} else if let apiMessage = result.messages.first, message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
|
} else if let apiMessage = result.messages.first, message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp {
|
||||||
namespace = Namespaces.Message.ScheduledCloud
|
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
|
namespace = Namespaces.Message.ScheduledCloud
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
||||||
|
|
||||||
public class Serialization: NSObject, MTSerialization {
|
public class Serialization: NSObject, MTSerialization {
|
||||||
public func currentLayer() -> UInt {
|
public func currentLayer() -> UInt {
|
||||||
return 216
|
return 217
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseMessage(_ data: Data!) -> Any! {
|
public func parseMessage(_ data: Data!) -> Any! {
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ class UpdateMessageService: NSObject, MTMessageService {
|
||||||
self.putNext(groups)
|
self.putNext(groups)
|
||||||
}
|
}
|
||||||
case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyHeader, entities, ttlPeriod):
|
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 update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
|
||||||
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
|
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
|
||||||
if groups.count != 0 {
|
if groups.count != 0 {
|
||||||
|
|
@ -74,7 +74,7 @@ class UpdateMessageService: NSObject, MTMessageService {
|
||||||
|
|
||||||
let generatedPeerId = Api.Peer.peerUser(userId: userId)
|
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 update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
|
||||||
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
|
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
|
||||||
if groups.count != 0 {
|
if groups.count != 0 {
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ extension Api.MessageMedia {
|
||||||
extension Api.Message {
|
extension Api.Message {
|
||||||
var rawId: Int32 {
|
var rawId: Int32 {
|
||||||
switch self {
|
switch self {
|
||||||
case let .message(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
return id
|
return id
|
||||||
case let .messageEmpty(_, id, _):
|
case let .messageEmpty(_, id, _):
|
||||||
return id
|
return id
|
||||||
|
|
@ -115,7 +115,7 @@ extension Api.Message {
|
||||||
|
|
||||||
func id(namespace: MessageId.Namespace = Namespaces.Message.Cloud) -> MessageId? {
|
func id(namespace: MessageId.Namespace = Namespaces.Message.Cloud) -> MessageId? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .message(_, flags2, id, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, flags2, id, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
var namespace = namespace
|
var namespace = namespace
|
||||||
if (flags2 & (1 << 4)) != 0 {
|
if (flags2 & (1 << 4)) != 0 {
|
||||||
namespace = Namespaces.Message.ScheduledCloud
|
namespace = Namespaces.Message.ScheduledCloud
|
||||||
|
|
@ -136,7 +136,7 @@ extension Api.Message {
|
||||||
|
|
||||||
var peerId: PeerId? {
|
var peerId: PeerId? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
let peerId: PeerId = messagePeerId.peerId
|
let peerId: PeerId = messagePeerId.peerId
|
||||||
return peerId
|
return peerId
|
||||||
case let .messageEmpty(_, _, peerId):
|
case let .messageEmpty(_, _, peerId):
|
||||||
|
|
@ -149,7 +149,7 @@ extension Api.Message {
|
||||||
|
|
||||||
var timestamp: Int32? {
|
var timestamp: Int32? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
return date
|
return date
|
||||||
case let .messageService(_, _, _, _, _, _, date, _, _, _):
|
case let .messageService(_, _, _, _, _, _, date, _, _, _):
|
||||||
return date
|
return date
|
||||||
|
|
@ -160,7 +160,7 @@ extension Api.Message {
|
||||||
|
|
||||||
var preCachedResources: [(MediaResource, Data)]? {
|
var preCachedResources: [(MediaResource, Data)]? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
return media?.preCachedResources
|
return media?.preCachedResources
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -169,7 +169,7 @@ extension Api.Message {
|
||||||
|
|
||||||
var preCachedStories: [StoryId: Api.StoryItem]? {
|
var preCachedStories: [StoryId: Api.StoryItem]? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
return media?.preCachedStories
|
return media?.preCachedStories
|
||||||
default:
|
default:
|
||||||
return nil
|
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 {
|
enum InternalJoinError {
|
||||||
case error(JoinGroupCallError)
|
case error(JoinGroupCallError)
|
||||||
case restart
|
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)
|
let getParticipantsRequest: Signal<GroupCallParticipantsContext.State, InternalJoinError>
|
||||||
|> mapError { _ -> InternalJoinError in
|
if isStream {
|
||||||
return .error(.generic)
|
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(
|
return combineLatest(
|
||||||
|
|
@ -835,7 +857,7 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
||||||
case let .updateGroupCallParticipants(_, participants, _):
|
case let .updateGroupCallParticipants(_, participants, _):
|
||||||
loop: for participant in participants {
|
loop: for participant in participants {
|
||||||
switch participant {
|
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 peerId: PeerId = apiPeerId.peerId
|
||||||
let ssrc = UInt32(bitPattern: source)
|
let ssrc = UInt32(bitPattern: source)
|
||||||
guard let peer = transaction.getPeer(peerId) else {
|
guard let peer = transaction.getPeer(peerId) else {
|
||||||
|
|
@ -872,7 +894,8 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
||||||
muteState: muteState,
|
muteState: muteState,
|
||||||
volume: volume,
|
volume: volume,
|
||||||
about: about,
|
about: about,
|
||||||
joinedVideo: joinedVideo
|
joinedVideo: joinedVideo,
|
||||||
|
paidStarsTotal: paidStarsTotal
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1233,6 +1256,7 @@ public final class GroupCallParticipantsContext {
|
||||||
public var volume: Int32?
|
public var volume: Int32?
|
||||||
public var about: String?
|
public var about: String?
|
||||||
public var joinedVideo: Bool
|
public var joinedVideo: Bool
|
||||||
|
public var paidStarsTotal: Int64?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
id: Id,
|
id: Id,
|
||||||
|
|
@ -1248,7 +1272,8 @@ public final class GroupCallParticipantsContext {
|
||||||
muteState: MuteState?,
|
muteState: MuteState?,
|
||||||
volume: Int32?,
|
volume: Int32?,
|
||||||
about: String?,
|
about: String?,
|
||||||
joinedVideo: Bool
|
joinedVideo: Bool,
|
||||||
|
paidStarsTotal: Int64?
|
||||||
) {
|
) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
|
|
@ -1264,6 +1289,7 @@ public final class GroupCallParticipantsContext {
|
||||||
self.volume = volume
|
self.volume = volume
|
||||||
self.about = about
|
self.about = about
|
||||||
self.joinedVideo = joinedVideo
|
self.joinedVideo = joinedVideo
|
||||||
|
self.paidStarsTotal = paidStarsTotal
|
||||||
}
|
}
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
|
|
@ -1320,6 +1346,9 @@ public final class GroupCallParticipantsContext {
|
||||||
if lhs.raiseHandRating != rhs.raiseHandRating {
|
if lhs.raiseHandRating != rhs.raiseHandRating {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.paidStarsTotal != rhs.paidStarsTotal {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1547,6 +1576,7 @@ public final class GroupCallParticipantsContext {
|
||||||
public var volume: Int32?
|
public var volume: Int32?
|
||||||
public var about: String?
|
public var about: String?
|
||||||
public var joinedVideo: Bool
|
public var joinedVideo: Bool
|
||||||
|
public var paidStarsTotal: Int64?
|
||||||
public var isMin: Bool
|
public var isMin: Bool
|
||||||
|
|
||||||
init(
|
init(
|
||||||
|
|
@ -1562,6 +1592,7 @@ public final class GroupCallParticipantsContext {
|
||||||
volume: Int32?,
|
volume: Int32?,
|
||||||
about: String?,
|
about: String?,
|
||||||
joinedVideo: Bool,
|
joinedVideo: Bool,
|
||||||
|
paidStarsTotal: Int64?,
|
||||||
isMin: Bool
|
isMin: Bool
|
||||||
) {
|
) {
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
|
|
@ -1576,6 +1607,7 @@ public final class GroupCallParticipantsContext {
|
||||||
self.volume = volume
|
self.volume = volume
|
||||||
self.about = about
|
self.about = about
|
||||||
self.joinedVideo = joinedVideo
|
self.joinedVideo = joinedVideo
|
||||||
|
self.paidStarsTotal = paidStarsTotal
|
||||||
self.isMin = isMin
|
self.isMin = isMin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1679,7 +1711,8 @@ public final class GroupCallParticipantsContext {
|
||||||
muteState: nil,
|
muteState: nil,
|
||||||
volume: nil,
|
volume: nil,
|
||||||
about: nil,
|
about: nil,
|
||||||
joinedVideo: false
|
joinedVideo: false,
|
||||||
|
paidStarsTotal: nil
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2266,7 +2299,8 @@ public final class GroupCallParticipantsContext {
|
||||||
muteState: muteState,
|
muteState: muteState,
|
||||||
volume: volume,
|
volume: volume,
|
||||||
about: participantUpdate.about,
|
about: participantUpdate.about,
|
||||||
joinedVideo: participantUpdate.joinedVideo
|
joinedVideo: participantUpdate.joinedVideo,
|
||||||
|
paidStarsTotal: participantUpdate.paidStarsTotal
|
||||||
)
|
)
|
||||||
updatedParticipants.append(participant)
|
updatedParticipants.append(participant)
|
||||||
}
|
}
|
||||||
|
|
@ -2579,7 +2613,7 @@ public final class GroupCallParticipantsContext {
|
||||||
}
|
}
|
||||||
self.stateValue.state.defaultParticipantsAreMuted.isMuted = isMuted
|
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
|
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
|
@ -2594,7 +2628,7 @@ public final class GroupCallParticipantsContext {
|
||||||
}
|
}
|
||||||
self.stateValue.state.messagesAreEnabled.isEnabled = isEnabled
|
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
|
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
|
@ -2604,7 +2638,7 @@ public final class GroupCallParticipantsContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func resetInviteLinks() {
|
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
|
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
|
@ -2662,7 +2696,7 @@ public final class GroupCallParticipantsContext {
|
||||||
extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
|
extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
|
||||||
init(_ apiParticipant: Api.GroupCallParticipant) {
|
init(_ apiParticipant: Api.GroupCallParticipant) {
|
||||||
switch apiParticipant {
|
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 peerId: PeerId = apiPeerId.peerId
|
||||||
let ssrc = UInt32(bitPattern: source)
|
let ssrc = UInt32(bitPattern: source)
|
||||||
let muted = (flags & (1 << 0)) != 0
|
let muted = (flags & (1 << 0)) != 0
|
||||||
|
|
@ -2707,6 +2741,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
|
||||||
volume: volume,
|
volume: volume,
|
||||||
about: about,
|
about: about,
|
||||||
joinedVideo: joinedVideo,
|
joinedVideo: joinedVideo,
|
||||||
|
paidStarsTotal: paidStarsTotal,
|
||||||
isMin: isMin
|
isMin: isMin
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -3135,7 +3170,7 @@ func _internal_getVideoBroadcastPart(dataSource: AudioBroadcastDataSource, callI
|
||||||
extension GroupCallParticipantsContext.Participant {
|
extension GroupCallParticipantsContext.Participant {
|
||||||
init?(_ apiParticipant: Api.GroupCallParticipant, transaction: Transaction) {
|
init?(_ apiParticipant: Api.GroupCallParticipant, transaction: Transaction) {
|
||||||
switch apiParticipant {
|
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 peerId: PeerId = apiPeerId.peerId
|
||||||
let ssrc = UInt32(bitPattern: source)
|
let ssrc = UInt32(bitPattern: source)
|
||||||
guard let peer = transaction.getPeer(peerId) else {
|
guard let peer = transaction.getPeer(peerId) else {
|
||||||
|
|
@ -3173,7 +3208,8 @@ extension GroupCallParticipantsContext.Participant {
|
||||||
muteState: muteState,
|
muteState: muteState,
|
||||||
volume: volume,
|
volume: volume,
|
||||||
about: about,
|
about: about,
|
||||||
joinedVideo: joinedVideo
|
joinedVideo: joinedVideo,
|
||||||
|
paidStarsTotal: paidStarsTotal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3205,7 +3241,7 @@ public enum GetGroupCallStreamCredentialsError {
|
||||||
case generic
|
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 account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||||
}
|
}
|
||||||
|
|
@ -3215,7 +3251,11 @@ func _internal_getGroupCallStreamCredentials(account: Account, peerId: PeerId, r
|
||||||
return .fail(.generic)
|
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
|
|> mapError { _ -> GetGroupCallStreamCredentialsError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
|
|
@ -3295,7 +3335,7 @@ public enum RevokeConferenceInviteLinkError {
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_revokeConferenceInviteLink(account: Account, reference: InternalGroupCallReference, link: String) -> Signal<GroupCallInviteLinks, 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
|
|> mapError { _ -> RevokeConferenceInviteLinkError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
|
|
@ -3882,12 +3922,14 @@ public final class GroupCallMessagesContext {
|
||||||
arc4random_buf(&randomId, 8)
|
arc4random_buf(&randomId, 8)
|
||||||
}
|
}
|
||||||
self.sendMessageDisposables.add(self.account.network.request(Api.functions.phone.sendGroupCallMessage(
|
self.sendMessageDisposables.add(self.account.network.request(Api.functions.phone.sendGroupCallMessage(
|
||||||
|
flags: 0,
|
||||||
call: self.reference.apiInputGroupCall,
|
call: self.reference.apiInputGroupCall,
|
||||||
randomId: randomId,
|
randomId: randomId,
|
||||||
message: .textWithEntities(
|
message: .textWithEntities(
|
||||||
text: text,
|
text: text,
|
||||||
entities: apiEntitiesFromMessageTextEntities(entities, associatedPeers: SimpleDictionary())
|
entities: apiEntitiesFromMessageTextEntities(entities, associatedPeers: SimpleDictionary())
|
||||||
)
|
),
|
||||||
|
allowPaidStars: nil
|
||||||
)).startStrict())
|
)).startStrict())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,8 @@ public extension TelegramEngine {
|
||||||
return _internal_getGroupCallParticipants(account: self.account, reference: reference, offset: offset, ssrcs: ssrcs, limit: limit, sortAscending: sortAscending)
|
return _internal_getGroupCallParticipants(account: self.account, reference: reference, offset: offset, ssrcs: ssrcs, limit: limit, sortAscending: sortAscending)
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
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, preferMuted: preferMuted, joinPayload: joinPayload, peerAdminIds: peerAdminIds, inviteHash: inviteHash, generateE2E: generateE2E)
|
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> {
|
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> {
|
public func getGroupCallStreamCredentials(peerId: EnginePeer.Id, isLiveStream: Bool, revokePreviousCredentials: Bool) -> Signal<GroupCallStreamCredentials, GetGroupCallStreamCredentialsError> {
|
||||||
return _internal_getGroupCallStreamCredentials(account: self.account, peerId: peerId, revokePreviousCredentials: revokePreviousCredentials)
|
return _internal_getGroupCallStreamCredentials(account: self.account, peerId: peerId, isLiveStream: isLiveStream, revokePreviousCredentials: revokePreviousCredentials)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getGroupCallPersistentSettings(callId: Int64) -> Signal<CodableEntry?, NoError> {
|
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> {
|
func _internal_importContact(account: Account, firstName: String, lastName: String, phoneNumber: String) -> Signal<PeerId?, NoError> {
|
||||||
let accountPeerId = account.peerId
|
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]))
|
return account.network.request(Api.functions.contacts.importContacts(contacts: [input]))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|
|
|
||||||
|
|
@ -645,7 +645,7 @@ public final class EngineStoryViewListContext {
|
||||||
folderIds: item.folderIds
|
folderIds: item.folderIds
|
||||||
))
|
))
|
||||||
if let entry = CodableEntry(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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ func _internal_forwardGameWithScore(account: Account, messageId: MessageId, to p
|
||||||
flags |= (1 << 13)
|
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)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ public enum EngineMedia: Equatable {
|
||||||
case giveawayResults(TelegramMediaGiveawayResults)
|
case giveawayResults(TelegramMediaGiveawayResults)
|
||||||
case paidContent(TelegramMediaPaidContent)
|
case paidContent(TelegramMediaPaidContent)
|
||||||
case todo(TelegramMediaTodo)
|
case todo(TelegramMediaTodo)
|
||||||
|
case liveStream(TelegramMediaLiveStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension EngineMedia {
|
public extension EngineMedia {
|
||||||
|
|
@ -62,6 +63,8 @@ public extension EngineMedia {
|
||||||
return paidContent.id
|
return paidContent.id
|
||||||
case .todo:
|
case .todo:
|
||||||
return nil
|
return nil
|
||||||
|
case .liveStream:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -105,6 +108,8 @@ public extension EngineMedia {
|
||||||
self = .paidContent(paidContent)
|
self = .paidContent(paidContent)
|
||||||
case let todo as TelegramMediaTodo:
|
case let todo as TelegramMediaTodo:
|
||||||
self = .todo(todo)
|
self = .todo(todo)
|
||||||
|
case let liveStream as TelegramMediaLiveStream:
|
||||||
|
self = .liveStream(liveStream)
|
||||||
default:
|
default:
|
||||||
preconditionFailure()
|
preconditionFailure()
|
||||||
}
|
}
|
||||||
|
|
@ -148,6 +153,8 @@ public extension EngineMedia {
|
||||||
return paidContent
|
return paidContent
|
||||||
case let .todo(todo):
|
case let .todo(todo):
|
||||||
return todo
|
return todo
|
||||||
|
case let .liveStream(liveStream):
|
||||||
|
return liveStream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ func _internal_requestClosePoll(postbox: Postbox, network: Network, stateManager
|
||||||
pollMediaFlags |= 1 << 1
|
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)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,10 @@ public enum Stories {
|
||||||
public let authorId: PeerId?
|
public let authorId: PeerId?
|
||||||
public let folderIds: [Int64]?
|
public let folderIds: [Int64]?
|
||||||
|
|
||||||
|
public var isLiveStream: Bool {
|
||||||
|
return self.media is TelegramMediaLiveStream
|
||||||
|
}
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
id: Int32,
|
id: Int32,
|
||||||
timestamp: 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 {
|
public init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
|
@ -759,6 +772,7 @@ public final class EngineStorySubscriptions: Equatable {
|
||||||
public let peer: EnginePeer
|
public let peer: EnginePeer
|
||||||
public let hasUnseen: Bool
|
public let hasUnseen: Bool
|
||||||
public let hasUnseenCloseFriends: Bool
|
public let hasUnseenCloseFriends: Bool
|
||||||
|
public let hasLiveItems: Bool
|
||||||
public let hasPending: Bool
|
public let hasPending: Bool
|
||||||
public let storyCount: Int
|
public let storyCount: Int
|
||||||
public let unseenCount: Int
|
public let unseenCount: Int
|
||||||
|
|
@ -768,6 +782,7 @@ public final class EngineStorySubscriptions: Equatable {
|
||||||
peer: EnginePeer,
|
peer: EnginePeer,
|
||||||
hasUnseen: Bool,
|
hasUnseen: Bool,
|
||||||
hasUnseenCloseFriends: Bool,
|
hasUnseenCloseFriends: Bool,
|
||||||
|
hasLiveItems: Bool,
|
||||||
hasPending: Bool,
|
hasPending: Bool,
|
||||||
storyCount: Int,
|
storyCount: Int,
|
||||||
unseenCount: Int,
|
unseenCount: Int,
|
||||||
|
|
@ -776,6 +791,7 @@ public final class EngineStorySubscriptions: Equatable {
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.hasUnseen = hasUnseen
|
self.hasUnseen = hasUnseen
|
||||||
self.hasUnseenCloseFriends = hasUnseenCloseFriends
|
self.hasUnseenCloseFriends = hasUnseenCloseFriends
|
||||||
|
self.hasLiveItems = hasLiveItems
|
||||||
self.hasPending = hasPending
|
self.hasPending = hasPending
|
||||||
self.storyCount = storyCount
|
self.storyCount = storyCount
|
||||||
self.unseenCount = unseenCount
|
self.unseenCount = unseenCount
|
||||||
|
|
@ -795,6 +811,9 @@ public final class EngineStorySubscriptions: Equatable {
|
||||||
if lhs.hasUnseenCloseFriends != rhs.hasUnseenCloseFriends {
|
if lhs.hasUnseenCloseFriends != rhs.hasUnseenCloseFriends {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.hasLiveItems != rhs.hasLiveItems {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.storyCount != rhs.storyCount {
|
if lhs.storyCount != rhs.storyCount {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -1093,6 +1112,22 @@ func _internal_cancelStoryUpload(account: Account, stableId: Int32) {
|
||||||
}).start()
|
}).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 {
|
private struct PendingStoryIdMappingKey: Hashable {
|
||||||
var peerId: PeerId
|
var peerId: PeerId
|
||||||
var stableId: Int32
|
var stableId: Int32
|
||||||
|
|
@ -1307,7 +1342,7 @@ func _internal_uploadStoryImpl(
|
||||||
folderIds: item.folderIds
|
folderIds: item.folderIds
|
||||||
)
|
)
|
||||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
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)
|
updatedItems.append(updatedItem)
|
||||||
}
|
}
|
||||||
|
|
@ -1749,7 +1784,7 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
|
||||||
folderIds: item.folderIds
|
folderIds: item.folderIds
|
||||||
)
|
)
|
||||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
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)
|
updatedItems.append(updatedItem)
|
||||||
|
|
@ -1946,7 +1981,7 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In
|
||||||
folderIds: item.folderIds
|
folderIds: item.folderIds
|
||||||
)
|
)
|
||||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
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)
|
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 let updatedItem = result.first(where: { $0.id == currentItems[i].id }) {
|
||||||
if case .item = updatedItem {
|
if case .item = updatedItem {
|
||||||
if let entry = CodableEntry(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
|
updatedItemValue = updatedItem
|
||||||
if let entry = CodableEntry(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)
|
updatedPeerEntries.append(previousEntry)
|
||||||
} else {
|
} else {
|
||||||
if let codedEntry = CodableEntry(storedItem) {
|
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)
|
updatedPeerEntries.append(previousEntry)
|
||||||
} else {
|
} else {
|
||||||
if let codedEntry = CodableEntry(storedItem) {
|
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 })
|
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) {
|
public init(items: [Item], isCached: Bool, maxReadId: Int32, isLoading: Bool) {
|
||||||
self.items = items
|
self.items = items
|
||||||
self.isCached = isCached
|
self.isCached = isCached
|
||||||
|
|
@ -2775,7 +2789,7 @@ public func _internal_pollPeerStories(postbox: Postbox, network: Network, accoun
|
||||||
updatedPeerEntries.append(previousEntry)
|
updatedPeerEntries.append(previousEntry)
|
||||||
} else {
|
} else {
|
||||||
if let codedEntry = CodableEntry(storedItem) {
|
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),
|
peer: EnginePeer(accountPeer),
|
||||||
hasUnseen: false,
|
hasUnseen: false,
|
||||||
hasUnseenCloseFriends: false,
|
hasUnseenCloseFriends: false,
|
||||||
|
hasLiveItems: false,
|
||||||
hasPending: accountPendingItemCount != 0,
|
hasPending: accountPendingItemCount != 0,
|
||||||
storyCount: accountPendingItemCount,
|
storyCount: accountPendingItemCount,
|
||||||
unseenCount: 0,
|
unseenCount: 0,
|
||||||
|
|
@ -1015,8 +1016,9 @@ public extension TelegramEngine {
|
||||||
let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self)
|
let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self)
|
||||||
var hasUnseen = false
|
var hasUnseen = false
|
||||||
var hasUnseenCloseFriends = false
|
var hasUnseenCloseFriends = false
|
||||||
|
var hasLiveItems = false
|
||||||
var unseenCount = 0
|
var unseenCount = 0
|
||||||
if let peerState = peerState {
|
if let peerState {
|
||||||
hasUnseen = peerState.maxReadId < lastEntry.id
|
hasUnseen = peerState.maxReadId < lastEntry.id
|
||||||
|
|
||||||
for item in itemsView.items {
|
for item in itemsView.items {
|
||||||
|
|
@ -1030,6 +1032,9 @@ public extension TelegramEngine {
|
||||||
hasUnseenCloseFriends = true
|
hasUnseenCloseFriends = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if item.media is TelegramMediaLiveStream {
|
||||||
|
hasLiveItems = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1038,6 +1043,7 @@ public extension TelegramEngine {
|
||||||
peer: EnginePeer(accountPeer),
|
peer: EnginePeer(accountPeer),
|
||||||
hasUnseen: hasUnseen,
|
hasUnseen: hasUnseen,
|
||||||
hasUnseenCloseFriends: hasUnseenCloseFriends,
|
hasUnseenCloseFriends: hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: hasLiveItems,
|
||||||
hasPending: accountPendingItemCount != 0,
|
hasPending: accountPendingItemCount != 0,
|
||||||
storyCount: itemsView.items.count + accountPendingItemCount,
|
storyCount: itemsView.items.count + accountPendingItemCount,
|
||||||
unseenCount: unseenCount,
|
unseenCount: unseenCount,
|
||||||
|
|
@ -1079,6 +1085,7 @@ public extension TelegramEngine {
|
||||||
let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self)
|
let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self)
|
||||||
var hasUnseen = false
|
var hasUnseen = false
|
||||||
var hasUnseenCloseFriends = false
|
var hasUnseenCloseFriends = false
|
||||||
|
var hasLiveItems = false
|
||||||
var unseenCount = 0
|
var unseenCount = 0
|
||||||
if let peerState = peerState {
|
if let peerState = peerState {
|
||||||
hasUnseen = peerState.maxReadId < lastEntry.id
|
hasUnseen = peerState.maxReadId < lastEntry.id
|
||||||
|
|
@ -1091,6 +1098,9 @@ public extension TelegramEngine {
|
||||||
if item.isCloseFriends {
|
if item.isCloseFriends {
|
||||||
hasUnseenCloseFriends = true
|
hasUnseenCloseFriends = true
|
||||||
}
|
}
|
||||||
|
if item.media is TelegramMediaLiveStream {
|
||||||
|
hasLiveItems = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1116,6 +1126,7 @@ public extension TelegramEngine {
|
||||||
peer: EnginePeer(peer),
|
peer: EnginePeer(peer),
|
||||||
hasUnseen: hasUnseen,
|
hasUnseen: hasUnseen,
|
||||||
hasUnseenCloseFriends: hasUnseenCloseFriends,
|
hasUnseenCloseFriends: hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: hasLiveItems,
|
||||||
hasPending: maxPendingTimestamp != nil,
|
hasPending: maxPendingTimestamp != nil,
|
||||||
storyCount: itemsView.items.count,
|
storyCount: itemsView.items.count,
|
||||||
unseenCount: unseenCount,
|
unseenCount: unseenCount,
|
||||||
|
|
@ -1153,6 +1164,7 @@ public extension TelegramEngine {
|
||||||
peer: EnginePeer(peer),
|
peer: EnginePeer(peer),
|
||||||
hasUnseen: false,
|
hasUnseen: false,
|
||||||
hasUnseenCloseFriends: false,
|
hasUnseenCloseFriends: false,
|
||||||
|
hasLiveItems: false,
|
||||||
hasPending: true,
|
hasPending: true,
|
||||||
storyCount: 0,
|
storyCount: 0,
|
||||||
unseenCount: 0,
|
unseenCount: 0,
|
||||||
|
|
@ -1387,7 +1399,7 @@ public extension TelegramEngine {
|
||||||
folderIds: item.folderIds
|
folderIds: item.folderIds
|
||||||
))
|
))
|
||||||
if let entry = CodableEntry(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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
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> {
|
public func allStoriesUploadEvents() -> Signal<(Int32, Int32), NoError> {
|
||||||
guard let pendingStoryManager = self.account.pendingStoryManager else {
|
guard let pendingStoryManager = self.account.pendingStoryManager else {
|
||||||
return .complete()
|
return .complete()
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
|
||||||
private var avatarVideoNode: AvatarVideoNode?
|
private var avatarVideoNode: AvatarVideoNode?
|
||||||
|
|
||||||
public private(set) var avatarStoryView: ComponentView<Empty>?
|
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>
|
public var statusView: ComponentView<Empty>
|
||||||
private var starView: StarView?
|
private var starView: StarView?
|
||||||
|
|
@ -241,6 +241,7 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
|
||||||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||||
hasUnseen: storyData.hasUnseen,
|
hasUnseen: storyData.hasUnseen,
|
||||||
hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends,
|
hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: storyData.hasLiveItems,
|
||||||
colors: AvatarStoryIndicatorComponent.Colors(theme: theme),
|
colors: AvatarStoryIndicatorComponent.Colors(theme: theme),
|
||||||
activeLineWidth: 1.0,
|
activeLineWidth: 1.0,
|
||||||
inactiveLineWidth: 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 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)
|
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 {
|
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 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)
|
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 final class ChatMessageAvatarHeader: ListViewItemHeader {
|
||||||
public struct Id: Hashable {
|
public struct Id: Hashable {
|
||||||
public var peerId: PeerId
|
public var peerId: PeerId
|
||||||
|
|
@ -923,7 +927,7 @@ public final class ChatMessageAvatarHeader: ListViewItemHeader {
|
||||||
public let stickDirection: ListViewItemHeaderStickDirection
|
public let stickDirection: ListViewItemHeaderStickDirection
|
||||||
public let stickOverInsets: Bool = false
|
public let stickOverInsets: Bool = false
|
||||||
|
|
||||||
public let height: CGFloat = 40.0
|
public let height: CGFloat = avatarHeaderSize()
|
||||||
|
|
||||||
public func combinesWith(other: ListViewItemHeader) -> Bool {
|
public func combinesWith(other: ListViewItemHeader) -> Bool {
|
||||||
if let other = other as? ChatMessageAvatarHeader, other.id == self.id {
|
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 {
|
if let previousPeer = self.peer, previousPeer.nameColor != peer.nameColor {
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
if peer.smallProfileImage != nil {
|
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 {
|
} 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
|
overrideImage = .deletedIcon
|
||||||
}
|
}
|
||||||
if peer.smallProfileImage != nil {
|
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 {
|
} 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 {
|
if peer.isPremium && context.sharedContext.energyUsageSettings.autoplayVideo {
|
||||||
|
|
@ -1111,7 +1115,7 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat
|
||||||
strongSelf.avatarNode.contentNode.addSubnode(videoNode)
|
strongSelf.avatarNode.contentNode.addSubnode(videoNode)
|
||||||
strongSelf.avatarVideoNode = 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 {
|
if strongSelf.hierarchyTrackingLayer == nil {
|
||||||
let hierarchyTrackingLayer = HierarchyTrackingLayer()
|
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) {
|
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)))
|
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: 40.0, height: 40.0))
|
let avatarFrame = CGRect(origin: CGPoint(), size: CGSize(width: avatarHeaderSize(), height: avatarHeaderSize()))
|
||||||
self.avatarNode.position = avatarFrame.center
|
self.avatarNode.position = avatarFrame.center
|
||||||
self.avatarNode.bounds = CGRect(origin: CGPoint(), size: avatarFrame.size)
|
self.avatarNode.bounds = CGRect(origin: CGPoint(), size: avatarFrame.size)
|
||||||
self.avatarNode.updateSize(size: avatarFrame.size)
|
self.avatarNode.updateSize(size: avatarFrame.size)
|
||||||
|
|
@ -1265,7 +1269,7 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat
|
||||||
var avatarTransform: CATransform3D = CATransform3DIdentity
|
var avatarTransform: CATransform3D = CATransform3DIdentity
|
||||||
if isHidden {
|
if isHidden {
|
||||||
let scale: CGFloat = isHidden ? 0.001 : 1.0
|
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)
|
avatarTransform = CATransform3DScale(avatarTransform, scale, scale, 1.0)
|
||||||
}
|
}
|
||||||
transition.updateTransform(node: self.avatarNode, transform: avatarTransform)
|
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 {
|
private struct Params: Equatable {
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let size: CGSize
|
let size: CGSize
|
||||||
|
|
@ -86,6 +86,7 @@ private final class GlassButtonView: HighlightTrackingButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
private let backgroundView: GlassBackgroundView
|
private let backgroundView: GlassBackgroundView
|
||||||
|
let button: HighlightTrackingButton
|
||||||
private let iconView: GlassBackgroundView.ContentImageView
|
private let iconView: GlassBackgroundView.ContentImageView
|
||||||
|
|
||||||
private var params: Params?
|
private var params: Params?
|
||||||
|
|
@ -96,7 +97,7 @@ private final class GlassButtonView: HighlightTrackingButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override var isEnabled: Bool {
|
var isEnabled: Bool = true {
|
||||||
didSet {
|
didSet {
|
||||||
self.updateIsEnabled()
|
self.updateIsEnabled()
|
||||||
}
|
}
|
||||||
|
|
@ -125,19 +126,25 @@ private final class GlassButtonView: HighlightTrackingButton {
|
||||||
self.iconView = GlassBackgroundView.ContentImageView()
|
self.iconView = GlassBackgroundView.ContentImageView()
|
||||||
self.backgroundView.contentView.addSubview(self.iconView)
|
self.backgroundView.contentView.addSubview(self.iconView)
|
||||||
|
|
||||||
|
self.button = HighlightTrackingButton()
|
||||||
|
self.backgroundView.contentView.addSubview(self.button)
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
self.addSubview(self.backgroundView)
|
self.addSubview(self.backgroundView)
|
||||||
|
|
||||||
self.highligthedChanged = { [weak self] highlighted in
|
if #available(iOS 26.0, *) {
|
||||||
guard let self else {
|
} else {
|
||||||
return
|
self.button.highligthedChanged = { [weak self] highlighted in
|
||||||
}
|
guard let self else {
|
||||||
if highlighted && self.isEnabled && !self.isImplicitlyDisabled {
|
return
|
||||||
self.backgroundView.contentView.alpha = 0.6
|
}
|
||||||
} else {
|
if highlighted && self.isEnabled && !self.isImplicitlyDisabled {
|
||||||
self.backgroundView.contentView.alpha = 1.0
|
self.backgroundView.contentView.alpha = 0.6
|
||||||
self.backgroundView.contentView.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2)
|
} 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) {
|
private func updateImpl(params: Params, transition: ComponentTransition) {
|
||||||
|
let isEnabled = self.isEnabled && !self.isImplicitlyDisabled
|
||||||
|
|
||||||
if let image = self.iconView.image {
|
if let image = self.iconView.image {
|
||||||
let iconFrame = image.size.centered(in: CGRect(origin: CGPoint(), size: params.size))
|
let iconFrame = image.size.centered(in: CGRect(origin: CGPoint(), size: params.size))
|
||||||
transition.setFrame(view: self.iconView, frame: iconFrame)
|
transition.setFrame(view: self.iconView, frame: iconFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: params.size))
|
transition.setFrame(view: self.button, 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.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.alpha = isEnabled ? 1.0 : 0.5
|
||||||
self.iconView.tintMask.alpha = self.iconView.alpha
|
self.iconView.tintMask.alpha = self.iconView.alpha
|
||||||
|
|
||||||
|
self.button.isEnabled = isEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateIsEnabled() {
|
private func updateIsEnabled() {
|
||||||
|
|
@ -255,12 +267,12 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
||||||
self.forwardButton.isImplicitlyDisabled = true
|
self.forwardButton.isImplicitlyDisabled = true
|
||||||
self.shareButton.isImplicitlyDisabled = true
|
self.shareButton.isImplicitlyDisabled = true
|
||||||
|
|
||||||
self.deleteButton.addTarget(self, action: #selector(self.deleteButtonPressed), for: .touchUpInside)
|
self.deleteButton.button.addTarget(self, action: #selector(self.deleteButtonPressed), for: .touchUpInside)
|
||||||
self.reportButton.addTarget(self, action: #selector(self.reportButtonPressed), for: .touchUpInside)
|
self.reportButton.button.addTarget(self, action: #selector(self.reportButtonPressed), for: .touchUpInside)
|
||||||
self.forwardButton.addTarget(self, action: #selector(self.forwardButtonPressed), for: .touchUpInside)
|
self.forwardButton.button.addTarget(self, action: #selector(self.forwardButtonPressed), for: .touchUpInside)
|
||||||
self.shareButton.addTarget(self, action: #selector(self.shareButtonPressed), for: .touchUpInside)
|
self.shareButton.button.addTarget(self, action: #selector(self.shareButtonPressed), for: .touchUpInside)
|
||||||
self.tagButton.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside)
|
self.tagButton.button.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside)
|
||||||
self.tagEditButton.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside)
|
self.tagEditButton.button.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
|
|
||||||
|
|
@ -286,6 +286,7 @@ public class ChatMessageStoryMentionContentNode: ChatMessageBubbleContentNode {
|
||||||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||||
hasUnseen: hasUnseen,
|
hasUnseen: hasUnseen,
|
||||||
hasUnseenCloseFriendsItems: hasUnseen && (story?.isCloseFriends ?? false),
|
hasUnseenCloseFriendsItems: hasUnseen && (story?.isCloseFriends ?? false),
|
||||||
|
hasLiveItems: false,
|
||||||
colors: storyColors,
|
colors: storyColors,
|
||||||
activeLineWidth: 3.0,
|
activeLineWidth: 3.0,
|
||||||
inactiveLineWidth: 1.0 + UIScreenPixel,
|
inactiveLineWidth: 1.0 + UIScreenPixel,
|
||||||
|
|
|
||||||
|
|
@ -273,7 +273,7 @@ public final class ChatFloatingTopicsPanel: Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.setFrame(view: self.containerView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
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
|
return availableSize
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,7 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag
|
||||||
|
|
||||||
self.micButton.layer.allowsGroupOpacity = true
|
self.micButton.layer.allowsGroupOpacity = true
|
||||||
self.view.addSubview(self.micButtonBackgroundView)
|
self.view.addSubview(self.micButtonBackgroundView)
|
||||||
self.view.addSubview(self.micButton)
|
self.micButtonBackgroundView.contentView.addSubview(self.micButton)
|
||||||
|
|
||||||
self.addSubnode(self.sendContainerNode)
|
self.addSubnode(self.sendContainerNode)
|
||||||
self.sendContainerNode.view.addSubview(self.sendButtonBackgroundView)
|
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 {
|
if useBounceAnimation, case let .animated(_, curve) = transition, case .spring = curve {
|
||||||
textInputContainerBackgroundTransition = textInputContainerBackgroundTransition.withUserData(GlassBackgroundView.TransitionFlagBounce())
|
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.updateFrame(layer: self.textInputBackgroundNode.layer, frame: textInputContainerBackgroundFrame)
|
||||||
transition.updateAlpha(node: self.textInputBackgroundNode, alpha: audioRecordingItemsAlpha)
|
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))
|
let containerFrame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: contentHeight + 64.0))
|
||||||
transition.updateFrame(view: self.glassBackgroundContainer, frame: containerFrame)
|
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
|
return contentHeight
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -545,8 +545,10 @@ public final class GlassBackgroundContainerView: UIView {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
public func update(size: CGSize, transition: ComponentTransition) {
|
public func update(size: CGSize, isDark: Bool, transition: ComponentTransition) {
|
||||||
if let nativeView = self.nativeView {
|
if let nativeView = self.nativeView {
|
||||||
|
nativeView.overrideUserInterfaceStyle = isDark ? .dark : .light
|
||||||
|
|
||||||
transition.setFrame(view: nativeView, frame: CGRect(origin: CGPoint(), size: size))
|
transition.setFrame(view: nativeView, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
} else if let legacyView = self.legacyView {
|
} else if let legacyView = self.legacyView {
|
||||||
transition.setFrame(view: legacyView, frame: CGRect(origin: CGPoint(), size: size))
|
transition.setFrame(view: legacyView, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
|
||||||
|
|
@ -160,9 +160,9 @@ public final class MessageInputActionButtonComponent: Component {
|
||||||
case up
|
case up
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Style {
|
public enum Style: Equatable {
|
||||||
case legacy
|
case legacy
|
||||||
case glass
|
case glass(isTinted: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
public let mode: Mode
|
public let mode: Mode
|
||||||
|
|
@ -519,7 +519,7 @@ public final class MessageInputActionButtonComponent: Component {
|
||||||
microphoneAlpha = 0.4
|
microphoneAlpha = 0.4
|
||||||
}
|
}
|
||||||
|
|
||||||
if component.style == .glass, [.send, .close].contains(component.mode) {
|
if case let .glass(isTinted) = component.style {
|
||||||
let backgroundView: GlassBackgroundView
|
let backgroundView: GlassBackgroundView
|
||||||
if let current = self.backgroundView {
|
if let current = self.backgroundView {
|
||||||
backgroundView = current
|
backgroundView = current
|
||||||
|
|
@ -534,8 +534,16 @@ public final class MessageInputActionButtonComponent: Component {
|
||||||
if case .send = component.mode {
|
if case .send = component.mode {
|
||||||
tintColor = UIColor(rgb: 0x029dff)
|
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)
|
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)
|
backgroundView.frame = CGRect(origin: .zero, size: buttonSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -809,18 +809,16 @@ public final class MessageInputPanelComponent: Component {
|
||||||
insets.left = 41.0
|
insets.left = 41.0
|
||||||
}
|
}
|
||||||
if let _ = component.setMediaRecordingActive {
|
if let _ = component.setMediaRecordingActive {
|
||||||
insets.right = 41.0
|
insets.right = 40.0 + 8.0 * 2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let textFieldSideInset: CGFloat
|
var textFieldSideInset: CGFloat = 8.0
|
||||||
switch component.style {
|
if component.bottomInset <= 32.0 && !component.forceIsEditing && !component.hideKeyboard && !self.textFieldExternalState.isEditing {
|
||||||
case .media, .glass:
|
textFieldSideInset += 18.0
|
||||||
textFieldSideInset = 8.0
|
insets.right += 18.0
|
||||||
default:
|
|
||||||
textFieldSideInset = 9.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 {
|
if case .glass = component.style {
|
||||||
mediaInsets.right = 54.0
|
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))
|
//transition.setFrame(view: self.vibrancyEffectView, frame: CGRect(origin: CGPoint(), size: fieldBackgroundFrame.size))
|
||||||
|
|
||||||
switch component.style {
|
switch component.style {
|
||||||
case .glass:
|
case .glass, .story:
|
||||||
if self.fieldGlassBackgroundView == nil {
|
if self.fieldGlassBackgroundView == nil {
|
||||||
let fieldGlassBackgroundView = GlassBackgroundView(frame: fieldBackgroundFrame)
|
let fieldGlassBackgroundView = GlassBackgroundView(frame: fieldBackgroundFrame)
|
||||||
self.insertSubview(fieldGlassBackgroundView, aboveSubview: self.fieldBackgroundView)
|
self.insertSubview(fieldGlassBackgroundView, aboveSubview: self.fieldBackgroundView)
|
||||||
|
|
@ -1105,7 +1103,7 @@ public final class MessageInputPanelComponent: Component {
|
||||||
self.fieldBackgroundTint.isHidden = true
|
self.fieldBackgroundTint.isHidden = true
|
||||||
}
|
}
|
||||||
if let fieldGlassBackgroundView = self.fieldGlassBackgroundView {
|
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)
|
transition.setFrame(view: fieldGlassBackgroundView, frame: fieldBackgroundFrame)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
@ -1551,6 +1549,9 @@ public final class MessageInputPanelComponent: Component {
|
||||||
inputActionButtonMode = .close
|
inputActionButtonMode = .close
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if case .story = component.style {
|
||||||
|
inputActionButtonAvailableSize = CGSize(width: 40.0, height: 40.0)
|
||||||
|
}
|
||||||
if hasMediaEditing {
|
if hasMediaEditing {
|
||||||
inputActionButtonMode = .send
|
inputActionButtonMode = .send
|
||||||
} else {
|
} 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(
|
let inputActionButtonSize = self.inputActionButton.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(MessageInputActionButtonComponent(
|
component: AnyComponent(MessageInputActionButtonComponent(
|
||||||
mode: inputActionButtonMode,
|
mode: inputActionButtonMode,
|
||||||
style: component.style == .glass ? .glass : .legacy,
|
style: inputActionButtonStyle,
|
||||||
storyId: component.storyItem?.id,
|
storyId: component.storyItem?.id,
|
||||||
action: { [weak self] mode, action, sendAction in
|
action: { [weak self] mode, action, sendAction in
|
||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
|
|
@ -1690,26 +1699,22 @@ public final class MessageInputPanelComponent: Component {
|
||||||
if rightButtonsOffsetX != 0.0 {
|
if rightButtonsOffsetX != 0.0 {
|
||||||
inputActionButtonOriginX = availableSize.width - 3.0 + rightButtonsOffsetX
|
inputActionButtonOriginX = availableSize.width - 3.0 + rightButtonsOffsetX
|
||||||
if displayLikeAction {
|
if displayLikeAction {
|
||||||
inputActionButtonOriginX -= 39.0
|
inputActionButtonOriginX -= 40.0 + 8.0
|
||||||
}
|
}
|
||||||
if component.forwardAction != nil {
|
if component.forwardAction != nil {
|
||||||
inputActionButtonOriginX -= 46.0
|
inputActionButtonOriginX -= 40.0 + 8.0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if component.setMediaRecordingActive != nil || isEditing || component.style == .glass {
|
if component.setMediaRecordingActive != nil || isEditing || component.style == .glass {
|
||||||
switch component.style {
|
switch component.style {
|
||||||
case .glass:
|
case .glass, .story:
|
||||||
inputActionButtonOriginX = fieldBackgroundFrame.maxX + 6.0
|
inputActionButtonOriginX = fieldBackgroundFrame.maxX + 8.0
|
||||||
default:
|
default:
|
||||||
inputActionButtonOriginX = fieldBackgroundFrame.maxX + floorToScreenPixels((41.0 - inputActionButtonSize.width) * 0.5)
|
inputActionButtonOriginX = fieldBackgroundFrame.maxX + floorToScreenPixels((41.0 - inputActionButtonSize.width) * 0.5)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
inputActionButtonOriginX = size.width
|
inputActionButtonOriginX = size.width
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasLikeAction {
|
|
||||||
inputActionButtonOriginX += 3.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let inputActionButtonView = self.inputActionButton.view {
|
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.setBounds(view: inputActionButtonView, bounds: CGRect(origin: CGPoint(), size: inputActionButtonFrame.size))
|
||||||
transition.setAlpha(view: inputActionButtonView, alpha: likeActionReplacesInputAction ? 0.0 : inputActionButtonAlpha)
|
transition.setAlpha(view: inputActionButtonView, alpha: likeActionReplacesInputAction ? 0.0 : inputActionButtonAlpha)
|
||||||
|
|
||||||
if rightButtonsOffsetX != 0.0 {
|
if hasLikeAction {
|
||||||
if hasLikeAction {
|
inputActionButtonOriginX += 40.0 + 8.0
|
||||||
inputActionButtonOriginX += 46.0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if hasLikeAction {
|
|
||||||
inputActionButtonOriginX += 41.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(
|
let likeButtonSize = self.likeButton.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(MessageInputActionButtonComponent(
|
component: AnyComponent(MessageInputActionButtonComponent(
|
||||||
mode: .like(reaction: component.myReaction?.reaction, file: component.myReaction?.file, animationFileId: component.myReaction?.animationFileId),
|
mode: .like(reaction: component.myReaction?.reaction, file: component.myReaction?.file, animationFileId: component.myReaction?.animationFileId),
|
||||||
|
style: likeActionButtonStyle,
|
||||||
storyId: component.storyItem?.id,
|
storyId: component.storyItem?.id,
|
||||||
action: { [weak self] _, action, _ in
|
action: { [weak self] _, action, _ in
|
||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
|
|
@ -1770,7 +1781,7 @@ public final class MessageInputPanelComponent: Component {
|
||||||
videoRecordingStatus: nil
|
videoRecordingStatus: nil
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: 33.0, height: 33.0)
|
containerSize: likeButtonContainerSize
|
||||||
)
|
)
|
||||||
if let likeButtonView = self.likeButton.view {
|
if let likeButtonView = self.likeButton.view {
|
||||||
if likeButtonView.superview == nil {
|
if likeButtonView.superview == nil {
|
||||||
|
|
@ -1783,7 +1794,7 @@ public final class MessageInputPanelComponent: Component {
|
||||||
transition.setPosition(view: likeButtonView, position: likeButtonFrame.center)
|
transition.setPosition(view: likeButtonView, position: likeButtonFrame.center)
|
||||||
transition.setBounds(view: likeButtonView, bounds: CGRect(origin: CGPoint(), size: likeButtonFrame.size))
|
transition.setBounds(view: likeButtonView, bounds: CGRect(origin: CGPoint(), size: likeButtonFrame.size))
|
||||||
transition.setAlpha(view: likeButtonView, alpha: displayLikeAction ? 1.0 : 0.0)
|
transition.setAlpha(view: likeButtonView, alpha: displayLikeAction ? 1.0 : 0.0)
|
||||||
inputActionButtonOriginX += 41.0
|
inputActionButtonOriginX += 40.0 + 8.0
|
||||||
}
|
}
|
||||||
|
|
||||||
var fieldIconNextX = fieldBackgroundFrame.maxX - 4.0
|
var fieldIconNextX = fieldBackgroundFrame.maxX - 4.0
|
||||||
|
|
@ -1855,7 +1866,7 @@ public final class MessageInputPanelComponent: Component {
|
||||||
component: AnyComponent(Button(
|
component: AnyComponent(Button(
|
||||||
content: AnyComponent(LottieComponent(
|
content: AnyComponent(LottieComponent(
|
||||||
content: LottieComponent.AppBundleContent(name: animationName),
|
content: LottieComponent.AppBundleContent(name: animationName),
|
||||||
color: .white
|
color: defaultDarkPresentationTheme.chat.inputPanel.inputControlColor
|
||||||
)),
|
)),
|
||||||
action: { [weak self] in
|
action: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,8 @@ private enum PeerMembersListEntry: Comparable, Identifiable {
|
||||||
return (
|
return (
|
||||||
total: storyStats.totalCount,
|
total: storyStats.totalCount,
|
||||||
unseen: storyStats.unseenCount,
|
unseen: storyStats.unseenCount,
|
||||||
hasUnseenCloseFriends: storyStats.hasUnseenCloseFriends
|
hasUnseenCloseFriends: storyStats.hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: storyStats.hasLiveItems
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
openStories: { _, sourceNode in
|
openStories: { _, sourceNode in
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||||
|
|
||||||
private let playbackStartDisposable = MetaDisposable()
|
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?
|
var storyProgress: Float?
|
||||||
|
|
||||||
init(context: AccountContext) {
|
init(context: AccountContext) {
|
||||||
|
|
@ -146,6 +146,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||||
totalCount: storyData.totalCount,
|
totalCount: storyData.totalCount,
|
||||||
unseenCount: storyData.unseenCount,
|
unseenCount: storyData.unseenCount,
|
||||||
hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends,
|
hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: storyData.hasLiveItems,
|
||||||
progress: self.storyProgress
|
progress: self.storyProgress
|
||||||
)
|
)
|
||||||
} else if let storyProgress = self.storyProgress {
|
} else if let storyProgress = self.storyProgress {
|
||||||
|
|
@ -153,6 +154,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||||
totalCount: 1,
|
totalCount: 1,
|
||||||
unseenCount: 1,
|
unseenCount: 1,
|
||||||
hasUnseenCloseFriendsItems: false,
|
hasUnseenCloseFriendsItems: false,
|
||||||
|
hasLiveItems: false,
|
||||||
progress: storyProgress
|
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
|
self.headerNode.avatarListNode.listContainerNode.storyParams = (peer, state.items.prefix(3).compactMap { item -> EngineStoryItem? in
|
||||||
switch item {
|
switch item {
|
||||||
case let .item(item):
|
case let .item(item):
|
||||||
|
|
@ -8997,7 +8997,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||||
if channel.hasPermission(.manageCalls) {
|
if channel.hasPermission(.manageCalls) {
|
||||||
canCreateStream = true
|
canCreateStream = true
|
||||||
credentialsPromise = Promise()
|
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:
|
default:
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -420,6 +420,7 @@ public final class AvatarStoryIndicatorComponent: Component {
|
||||||
|
|
||||||
public let hasUnseen: Bool
|
public let hasUnseen: Bool
|
||||||
public let hasUnseenCloseFriendsItems: Bool
|
public let hasUnseenCloseFriendsItems: Bool
|
||||||
|
public let hasLiveItems: Bool
|
||||||
public let colors: Colors
|
public let colors: Colors
|
||||||
public let activeLineWidth: CGFloat
|
public let activeLineWidth: CGFloat
|
||||||
public let inactiveLineWidth: CGFloat
|
public let inactiveLineWidth: CGFloat
|
||||||
|
|
@ -430,6 +431,7 @@ public final class AvatarStoryIndicatorComponent: Component {
|
||||||
public init(
|
public init(
|
||||||
hasUnseen: Bool,
|
hasUnseen: Bool,
|
||||||
hasUnseenCloseFriendsItems: Bool,
|
hasUnseenCloseFriendsItems: Bool,
|
||||||
|
hasLiveItems: Bool,
|
||||||
colors: Colors,
|
colors: Colors,
|
||||||
activeLineWidth: CGFloat,
|
activeLineWidth: CGFloat,
|
||||||
inactiveLineWidth: CGFloat,
|
inactiveLineWidth: CGFloat,
|
||||||
|
|
@ -439,6 +441,7 @@ public final class AvatarStoryIndicatorComponent: Component {
|
||||||
) {
|
) {
|
||||||
self.hasUnseen = hasUnseen
|
self.hasUnseen = hasUnseen
|
||||||
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
|
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
|
||||||
|
self.hasLiveItems = hasLiveItems
|
||||||
self.colors = colors
|
self.colors = colors
|
||||||
self.activeLineWidth = activeLineWidth
|
self.activeLineWidth = activeLineWidth
|
||||||
self.inactiveLineWidth = inactiveLineWidth
|
self.inactiveLineWidth = inactiveLineWidth
|
||||||
|
|
@ -454,6 +457,9 @@ public final class AvatarStoryIndicatorComponent: Component {
|
||||||
if lhs.hasUnseenCloseFriendsItems != rhs.hasUnseenCloseFriendsItems {
|
if lhs.hasUnseenCloseFriendsItems != rhs.hasUnseenCloseFriendsItems {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.hasLiveItems != rhs.hasLiveItems {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.colors != rhs.colors {
|
if lhs.colors != rhs.colors {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -659,7 +665,10 @@ public final class AvatarStoryIndicatorComponent: Component {
|
||||||
let activeColors: [CGColor]
|
let activeColors: [CGColor]
|
||||||
let inactiveColors: [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)
|
activeColors = component.colors.unseenCloseFriendsColors.map(\.cgColor)
|
||||||
} else {
|
} else {
|
||||||
activeColors = component.colors.unseenColors.map(\.cgColor)
|
activeColors = component.colors.unseenColors.map(\.cgColor)
|
||||||
|
|
@ -681,7 +690,7 @@ public final class AvatarStoryIndicatorComponent: Component {
|
||||||
|
|
||||||
var locations: [CGFloat] = [0.0, 1.0]
|
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 {
|
if component.isRoundedRect {
|
||||||
let lineWidth: CGFloat = component.hasUnseen ? component.activeLineWidth : component.inactiveLineWidth
|
let lineWidth: CGFloat = component.hasUnseen ? component.activeLineWidth : component.inactiveLineWidth
|
||||||
context.setLineWidth(lineWidth)
|
context.setLineWidth(lineWidth)
|
||||||
|
|
@ -839,7 +848,9 @@ public final class AvatarStoryIndicatorComponent: Component {
|
||||||
context.clip()
|
context.clip()
|
||||||
|
|
||||||
let colors: [CGColor]
|
let colors: [CGColor]
|
||||||
if component.hasUnseen {
|
if component.hasLiveItems {
|
||||||
|
colors = activeColors
|
||||||
|
} else if component.hasUnseen {
|
||||||
colors = activeColors
|
colors = activeColors
|
||||||
} else {
|
} else {
|
||||||
colors = inactiveColors
|
colors = inactiveColors
|
||||||
|
|
|
||||||
|
|
@ -940,7 +940,8 @@ public final class PeerListItemComponent: Component {
|
||||||
return AvatarNode.StoryStats(
|
return AvatarNode.StoryStats(
|
||||||
totalCount: storyStats.totalCount == 0 ? 0 : 1,
|
totalCount: storyStats.totalCount == 0 ? 0 : 1,
|
||||||
unseenCount: storyStats.unseenCount == 0 ? 0 : 1,
|
unseenCount: storyStats.unseenCount == 0 ? 0 : 1,
|
||||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: storyStats.hasLiveItems
|
||||||
)
|
)
|
||||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||||
colors: AvatarNode.Colors(theme: component.theme),
|
colors: AvatarNode.Colors(theme: component.theme),
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ swift_library(
|
||||||
"//submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController",
|
"//submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController",
|
||||||
"//submodules/DirectMediaImageCache",
|
"//submodules/DirectMediaImageCache",
|
||||||
"//submodules/PromptUI",
|
"//submodules/PromptUI",
|
||||||
|
"//submodules/TelegramCallsUI",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,9 @@ final class StoryAuthorInfoComponent: Component {
|
||||||
let timestamp: Int32
|
let timestamp: Int32
|
||||||
let counters: Counters?
|
let counters: Counters?
|
||||||
let isEdited: Bool
|
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.context = context
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
|
|
@ -33,6 +34,7 @@ final class StoryAuthorInfoComponent: Component {
|
||||||
self.timestamp = timestamp
|
self.timestamp = timestamp
|
||||||
self.counters = counters
|
self.counters = counters
|
||||||
self.isEdited = isEdited
|
self.isEdited = isEdited
|
||||||
|
self.isLiveStream = isLiveStream
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: StoryAuthorInfoComponent, rhs: StoryAuthorInfoComponent) -> Bool {
|
static func ==(lhs: StoryAuthorInfoComponent, rhs: StoryAuthorInfoComponent) -> Bool {
|
||||||
|
|
@ -59,6 +61,9 @@ final class StoryAuthorInfoComponent: Component {
|
||||||
}
|
}
|
||||||
if lhs.isEdited != rhs.isEdited {
|
if lhs.isEdited != rhs.isEdited {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
if lhs.isLiveStream != rhs.isLiveStream {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -68,6 +73,7 @@ final class StoryAuthorInfoComponent: Component {
|
||||||
private var repostIconView: UIImageView?
|
private var repostIconView: UIImageView?
|
||||||
private var avatarNode: AvatarNode?
|
private var avatarNode: AvatarNode?
|
||||||
private let subtitle = ComponentView<Empty>()
|
private let subtitle = ComponentView<Empty>()
|
||||||
|
private var liveBadgeView: UIImageView?
|
||||||
private var counterLabel: ComponentView<Empty>?
|
private var counterLabel: ComponentView<Empty>?
|
||||||
|
|
||||||
private var component: StoryAuthorInfoComponent?
|
private var component: StoryAuthorInfoComponent?
|
||||||
|
|
@ -238,7 +244,43 @@ final class StoryAuthorInfoComponent: Component {
|
||||||
avatarNode.view.removeFromSuperview()
|
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 let titleView = self.title.view {
|
||||||
if titleView.superview == nil {
|
if titleView.superview == nil {
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,12 @@ import AvatarNode
|
||||||
final class StoryAvatarInfoComponent: Component {
|
final class StoryAvatarInfoComponent: Component {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let peer: EnginePeer
|
let peer: EnginePeer
|
||||||
|
let isLiveStream: Bool
|
||||||
|
|
||||||
init(context: AccountContext, peer: EnginePeer) {
|
init(context: AccountContext, peer: EnginePeer, isLiveStream: Bool) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
|
self.isLiveStream = isLiveStream
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: StoryAvatarInfoComponent, rhs: StoryAvatarInfoComponent) -> Bool {
|
static func ==(lhs: StoryAvatarInfoComponent, rhs: StoryAvatarInfoComponent) -> Bool {
|
||||||
|
|
@ -23,6 +25,9 @@ final class StoryAvatarInfoComponent: Component {
|
||||||
if lhs.peer != rhs.peer {
|
if lhs.peer != rhs.peer {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.isLiveStream != rhs.isLiveStream {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,6 +62,24 @@ final class StoryAvatarInfoComponent: Component {
|
||||||
peer: component.peer,
|
peer: component.peer,
|
||||||
synchronousLoad: true
|
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
|
return size
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -649,6 +649,7 @@ public final class StoryContentContextImpl: StoryContentContext {
|
||||||
peer: peer,
|
peer: peer,
|
||||||
hasUnseen: state.hasUnseen,
|
hasUnseen: state.hasUnseen,
|
||||||
hasUnseenCloseFriends: state.hasUnseenCloseFriends,
|
hasUnseenCloseFriends: state.hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: false,
|
||||||
hasPending: false,
|
hasPending: false,
|
||||||
storyCount: state.items.count,
|
storyCount: state.items.count,
|
||||||
unseenCount: 0,
|
unseenCount: 0,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import HierarchyTrackingLayer
|
||||||
import ButtonComponent
|
import ButtonComponent
|
||||||
import MultilineTextComponent
|
import MultilineTextComponent
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import TelegramCallsUI
|
||||||
|
|
||||||
final class StoryItemContentComponent: Component {
|
final class StoryItemContentComponent: Component {
|
||||||
typealias EnvironmentType = StoryContentItem.Environment
|
typealias EnvironmentType = StoryContentItem.Environment
|
||||||
|
|
@ -92,6 +93,8 @@ final class StoryItemContentComponent: Component {
|
||||||
private let imageView: StoryItemImageView
|
private let imageView: StoryItemImageView
|
||||||
private let overlaysView: StoryItemOverlaysView
|
private let overlaysView: StoryItemOverlaysView
|
||||||
private var videoNode: UniversalVideoNode?
|
private var videoNode: UniversalVideoNode?
|
||||||
|
private var mediaStreamCall: PresentationGroupCallImpl?
|
||||||
|
private var mediaStream: ComponentView<Empty>?
|
||||||
private var loadingEffectView: StoryItemLoadingEffectView?
|
private var loadingEffectView: StoryItemLoadingEffectView?
|
||||||
private var loadingEffectAppearanceTimer: SwiftSignalKit.Timer?
|
private var loadingEffectAppearanceTimer: SwiftSignalKit.Timer?
|
||||||
|
|
||||||
|
|
@ -385,6 +388,10 @@ final class StoryItemContentComponent: Component {
|
||||||
if !self.isSeeking {
|
if !self.isSeeking {
|
||||||
self.updateVideoPlaybackProgress()
|
self.updateVideoPlaybackProgress()
|
||||||
}
|
}
|
||||||
|
} else if case .liveStream = self.currentMessageMedia {
|
||||||
|
if !self.isSeeking {
|
||||||
|
self.updateVideoPlaybackProgress()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if !self.markedAsSeen {
|
if !self.markedAsSeen {
|
||||||
self.markedAsSeen = true
|
self.markedAsSeen = true
|
||||||
|
|
@ -599,7 +606,10 @@ final class StoryItemContentComponent: Component {
|
||||||
|
|
||||||
let selectedMedia: EngineMedia
|
let selectedMedia: EngineMedia
|
||||||
var messageMedia: 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
|
selectedMedia = alternativeMediaValue
|
||||||
|
|
||||||
switch alternativeMediaValue {
|
switch alternativeMediaValue {
|
||||||
|
|
@ -628,7 +638,7 @@ final class StoryItemContentComponent: Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
var reloadMedia = false
|
var reloadMedia = false
|
||||||
if self.currentMessageMedia?.id != messageMedia?.id {
|
if self.currentMessageMedia?.id != messageMedia?.id || (self.currentMessageMedia == nil) != (messageMedia == nil) {
|
||||||
self.currentMessageMedia = messageMedia
|
self.currentMessageMedia = messageMedia
|
||||||
reloadMedia = true
|
reloadMedia = true
|
||||||
|
|
||||||
|
|
@ -707,47 +717,154 @@ final class StoryItemContentComponent: Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let messageMedia {
|
if case let .liveStream(liveStream) = messageMedia {
|
||||||
var applyState = false
|
var mediaStreamTransition = transition
|
||||||
self.imageView.didLoadContents = { [weak self] in
|
let mediaStream: ComponentView<Empty>
|
||||||
guard let self else {
|
if let current = self.mediaStream {
|
||||||
return
|
mediaStream = current
|
||||||
}
|
} else {
|
||||||
self.contentLoaded = true
|
mediaStreamTransition = mediaStreamTransition.withAnimation(.none)
|
||||||
if applyState {
|
mediaStream = ComponentView()
|
||||||
self.state?.updated(transition: .immediate)
|
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?
|
let mediaStreamCall: PresentationGroupCallImpl
|
||||||
switch messageMedia {
|
if let current = self.mediaStreamCall {
|
||||||
case let .image(image):
|
mediaStreamCall = current
|
||||||
dimensions = image.representations.last?.dimensions.cgSize
|
} else {
|
||||||
case let .file(file):
|
let initialCall = EngineGroupCallDescription(
|
||||||
dimensions = file.dimensions?.cgSize
|
id: liveStream.call.id,
|
||||||
default:
|
accessHash: liveStream.call.accessHash,
|
||||||
break
|
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):
|
case let .image(image):
|
||||||
dimensions = image.representations.last?.dimensions.cgSize
|
dimensions = image.representations.last?.dimensions.cgSize
|
||||||
case let .file(file):
|
case let .file(file):
|
||||||
|
|
@ -755,28 +872,38 @@ final class StoryItemContentComponent: Component {
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
if dimensions == nil {
|
||||||
|
switch component.item.media {
|
||||||
if let dimensions {
|
case let .image(image):
|
||||||
var imageSize = dimensions.aspectFilled(availableSize)
|
dimensions = image.representations.last?.dimensions.cgSize
|
||||||
if imageSize.width < availableSize.width && imageSize.width >= availableSize.width - 5.0 {
|
case let .file(file):
|
||||||
imageSize.width = availableSize.width
|
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 {
|
if let dimensions {
|
||||||
let videoSize = dimensions.aspectFilled(availableSize)
|
var imageSize = 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)
|
if imageSize.width < availableSize.width && imageSize.width >= availableSize.width - 5.0 {
|
||||||
videoNode.updateLayout(size: videoSize, transition: .immediate)
|
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 {
|
switch selectedMedia {
|
||||||
case .image, .file:
|
case .image, .file, .liveStream:
|
||||||
if let unsupportedText = self.unsupportedText {
|
if let unsupportedText = self.unsupportedText {
|
||||||
self.unsupportedText = nil
|
self.unsupportedText = nil
|
||||||
unsupportedText.view?.removeFromSuperview()
|
unsupportedText.view?.removeFromSuperview()
|
||||||
|
|
|
||||||
|
|
@ -1688,7 +1688,11 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if component.slice.effectivePeer.id == component.context.account.peerId {
|
} 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 {
|
} else if component.slice.item.storyItem.isPending {
|
||||||
displayFooter = true
|
displayFooter = true
|
||||||
} else if case let .user(user) = component.slice.peer, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) {
|
} 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
|
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
|
displayViewLists = true
|
||||||
} else if case let .channel(channel) = component.slice.effectivePeer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats {
|
} else if case let .channel(channel) = component.slice.effectivePeer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats {
|
||||||
displayViewLists = true
|
displayViewLists = true
|
||||||
|
|
@ -1941,7 +1947,9 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
var canReply = false
|
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
|
canReply = true
|
||||||
|
|
||||||
if component.slice.effectivePeer.id == component.context.account.peerId {
|
if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||||
|
|
@ -1969,7 +1977,9 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
var canReply = false
|
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
|
canReply = true
|
||||||
|
|
||||||
if component.slice.effectivePeer.id == component.context.account.peerId {
|
if component.slice.effectivePeer.id == component.context.account.peerId {
|
||||||
|
|
@ -2760,7 +2770,11 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
switch channel.info {
|
switch channel.info {
|
||||||
case .broadcast:
|
case .broadcast:
|
||||||
isChannel = true
|
isChannel = true
|
||||||
showMessageInputPanel = false
|
if case .liveStream = component.slice.item.storyItem.media {
|
||||||
|
showMessageInputPanel = true
|
||||||
|
} else {
|
||||||
|
showMessageInputPanel = false
|
||||||
|
}
|
||||||
case .group:
|
case .group:
|
||||||
if let bannedSendText = channel.hasBannedPermission(.banSendText, ignoreDefault: canBypassRestrictions) {
|
if let bannedSendText = channel.hasBannedPermission(.banSendText, ignoreDefault: canBypassRestrictions) {
|
||||||
if bannedSendText.1 || component.slice.additionalPeerData.boostsToUnrestrict == nil {
|
if bannedSendText.1 || component.slice.additionalPeerData.boostsToUnrestrict == nil {
|
||||||
|
|
@ -2772,7 +2786,11 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
isGroup = true
|
isGroup = true
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
if case let .user(user) = component.slice.peer, let _ = user.botInfo {
|
||||||
showMessageInputPanel = false
|
showMessageInputPanel = false
|
||||||
|
|
@ -2834,6 +2852,9 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
if let sendPaidMessageStars = component.slice.additionalPeerData.sendPaidMessageStars {
|
if let sendPaidMessageStars = component.slice.additionalPeerData.sendPaidMessageStars {
|
||||||
let dateTimeFormat = component.context.sharedContext.currentPresentationData.with { $0 }.dateTimeFormat
|
let dateTimeFormat = component.context.sharedContext.currentPresentationData.with { $0 }.dateTimeFormat
|
||||||
inputPlaceholder = .plain(component.strings.Chat_InputTextPaidMessagePlaceholder(" # \(presentationStringsFormattedNumber(Int32(sendPaidMessageStars.value), dateTimeFormat.groupingSeparator))").string)
|
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 {
|
} else {
|
||||||
inputPlaceholder = .plain(isGroup ? component.strings.Story_InputPlaceholderReplyInGroup : component.strings.Story_InputPlaceholderReplyPrivately)
|
inputPlaceholder = .plain(isGroup ? component.strings.Story_InputPlaceholderReplyInGroup : component.strings.Story_InputPlaceholderReplyPrivately)
|
||||||
}
|
}
|
||||||
|
|
@ -2859,6 +2880,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
self.inputPanel.parentState = state
|
self.inputPanel.parentState = state
|
||||||
var inputPanelSize: CGSize?
|
var inputPanelSize: CGSize?
|
||||||
|
|
||||||
|
let _ = inputNodeVisible
|
||||||
|
|
||||||
let startTime23 = CFAbsoluteTimeGetCurrent()
|
let startTime23 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
if showMessageInputPanel {
|
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(
|
inputPanelSize = self.inputPanel.update(
|
||||||
transition: inputPanelTransition,
|
transition: inputPanelTransition,
|
||||||
component: AnyComponent(MessageInputPanelComponent(
|
component: AnyComponent(MessageInputPanelComponent(
|
||||||
|
|
@ -2949,13 +2980,13 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
self.sendMessageContext.videoRecorderValue?.dismissVideo()
|
self.sendMessageContext.videoRecorderValue?.dismissVideo()
|
||||||
self.sendMessageContext.discardMediaRecordingPreview(view: self)
|
self.sendMessageContext.discardMediaRecordingPreview(view: self)
|
||||||
},
|
},
|
||||||
attachmentAction: component.slice.effectivePeer.isService ? nil : { [weak self] in
|
attachmentAction: !displayAttachmentAction ? nil : { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.sendMessageContext.presentAttachmentMenu(view: self, subject: .default)
|
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
|
myReaction: component.slice.item.storyItem.myReaction.flatMap { value -> MessageInputPanelComponent.MyReaction? in
|
||||||
var centerAnimation: TelegramMediaFile?
|
var centerAnimation: TelegramMediaFile?
|
||||||
var animationFileId: Int64?
|
var animationFileId: Int64?
|
||||||
|
|
@ -3082,7 +3113,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
timeoutValue: nil,
|
timeoutValue: nil,
|
||||||
timeoutSelected: false,
|
timeoutSelected: false,
|
||||||
displayGradient: false,
|
displayGradient: false,
|
||||||
bottomInset: component.inputHeight != 0.0 || inputNodeVisible ? 0.0 : bottomContentInset,
|
bottomInset: max(bottomContentInset, component.inputHeight),
|
||||||
isFormattingLocked: false,
|
isFormattingLocked: false,
|
||||||
hideKeyboard: self.sendMessageContext.currentInputMode == .media,
|
hideKeyboard: self.sendMessageContext.currentInputMode == .media,
|
||||||
customInputView: nil,
|
customInputView: nil,
|
||||||
|
|
@ -3176,7 +3207,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
inputPanelIsOverlay = false
|
inputPanelIsOverlay = false
|
||||||
} else {
|
} else {
|
||||||
bottomContentInset += 44.0
|
bottomContentInset += 44.0
|
||||||
inputPanelBottomInset = inputHeight - inputPanelInset
|
inputPanelBottomInset = inputHeight - inputPanelInset + 3.0
|
||||||
inputPanelIsOverlay = true
|
inputPanelIsOverlay = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3191,7 +3222,9 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
var validViewListIds: [StoryId] = []
|
var validViewListIds: [StoryId] = []
|
||||||
|
|
||||||
var displayViewLists = false
|
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
|
displayViewLists = true
|
||||||
} else if case let .channel(channel) = component.slice.effectivePeer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats {
|
} else if case let .channel(channel) = component.slice.effectivePeer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats {
|
||||||
displayViewLists = true
|
displayViewLists = true
|
||||||
|
|
@ -4028,9 +4061,14 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
|
|
||||||
let focusedItem: StoryContentItem? = component.slice.item
|
let focusedItem: StoryContentItem? = component.slice.item
|
||||||
|
|
||||||
|
var isLiveStream = false
|
||||||
|
if case .liveStream = component.slice.item.storyItem.media {
|
||||||
|
isLiveStream = true
|
||||||
|
}
|
||||||
|
|
||||||
var currentLeftInfoItem: InfoItem?
|
var currentLeftInfoItem: InfoItem?
|
||||||
if focusedItem != nil {
|
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 {
|
if let leftInfoItem = self.leftInfoItem, leftInfoItem.component == leftInfoComponent {
|
||||||
currentLeftInfoItem = leftInfoItem
|
currentLeftInfoItem = leftInfoItem
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -4066,7 +4104,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
author: component.slice.item.storyItem.author,
|
author: component.slice.item.storyItem.author,
|
||||||
timestamp: component.slice.item.storyItem.timestamp,
|
timestamp: component.slice.item.storyItem.timestamp,
|
||||||
counters: counters,
|
counters: counters,
|
||||||
isEdited: component.slice.item.storyItem.isEdited
|
isEdited: component.slice.item.storyItem.isEdited,
|
||||||
|
isLiveStream: isLiveStream
|
||||||
))
|
))
|
||||||
if let centerInfoItem = self.centerInfoItem, centerInfoItem.component == centerInfoComponent {
|
if let centerInfoItem = self.centerInfoItem, centerInfoItem.component == centerInfoComponent {
|
||||||
currentCenterInfoItem = centerInfoItem
|
currentCenterInfoItem = centerInfoItem
|
||||||
|
|
@ -4176,7 +4215,13 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
if let inputPanelSize {
|
if let inputPanelSize {
|
||||||
let inputPanelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - inputPanelSize.width) / 2.0), y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize)
|
let inputPanelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - inputPanelSize.width) / 2.0), y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize)
|
||||||
inputPanelFrameValue = inputPanelFrame
|
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 {
|
if case .regular = component.metrics.widthClass {
|
||||||
inputPanelAlpha *= component.visibilityFraction
|
inputPanelAlpha *= component.visibilityFraction
|
||||||
}
|
}
|
||||||
|
|
@ -4465,14 +4510,21 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
if let current = self.reactionContextNode {
|
if let current = self.reactionContextNode {
|
||||||
reactionContextNode = current
|
reactionContextNode = current
|
||||||
} else {
|
} 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
|
reactionContextNodeTransition = .immediate
|
||||||
reactionContextNode = ReactionContextNode(
|
reactionContextNode = ReactionContextNode(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
animationCache: component.context.animationCache,
|
animationCache: component.context.animationCache,
|
||||||
presentationData: component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme),
|
presentationData: component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme),
|
||||||
|
style: .glass(isTinted: false),
|
||||||
items: reactionItems.map { ReactionContextItem.reaction(item: $0, icon: .none) },
|
items: reactionItems.map { ReactionContextItem.reaction(item: $0, icon: .none) },
|
||||||
selectedItems: component.slice.item.storyItem.myReaction.flatMap { Set([$0]) } ?? Set(),
|
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,
|
reactionsLocked: false,
|
||||||
alwaysAllowPremiumReactions: false,
|
alwaysAllowPremiumReactions: false,
|
||||||
allPresetReactionsAreAvailable: false,
|
allPresetReactionsAreAvailable: false,
|
||||||
|
|
@ -4982,12 +5034,21 @@ public final class StoryItemSetContainerComponent: Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
component.externalState.derivedMediaSize = contentFrame.size
|
component.externalState.derivedMediaSize = contentFrame.size
|
||||||
if component.slice.effectivePeer.id == component.context.account.peerId {
|
|
||||||
component.externalState.derivedBottomInset = availableSize.height - itemsContainerFrame.maxY
|
if case .liveStream = component.slice.item.storyItem.media {
|
||||||
} else if let inputPanelFrameValue {
|
if let inputPanelFrameValue {
|
||||||
component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrameValue.minY, contentFrame.maxY)
|
component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrameValue.minY, contentFrame.maxY)
|
||||||
|
} else {
|
||||||
|
component.externalState.derivedBottomInset = availableSize.height - itemsContainerFrame.maxY
|
||||||
|
}
|
||||||
} else {
|
} 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 {
|
if !"".isEmpty {
|
||||||
|
|
|
||||||
|
|
@ -1054,6 +1054,7 @@ public final class StoryPeerListComponent: Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasUnseenCloseFriendsItems = itemSet.hasUnseenCloseFriends
|
var hasUnseenCloseFriendsItems = itemSet.hasUnseenCloseFriends
|
||||||
|
let hasLiveItems = itemSet.hasLiveItems
|
||||||
|
|
||||||
var hasItems = true
|
var hasItems = true
|
||||||
var itemRingAnimation: StoryPeerListItemComponent.RingAnimation?
|
var itemRingAnimation: StoryPeerListItemComponent.RingAnimation?
|
||||||
|
|
@ -1139,6 +1140,7 @@ public final class StoryPeerListComponent: Component {
|
||||||
totalCount: totalCount,
|
totalCount: totalCount,
|
||||||
unseenCount: unseenCount,
|
unseenCount: unseenCount,
|
||||||
hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems,
|
hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems,
|
||||||
|
hasLiveItems: hasLiveItems,
|
||||||
hasItems: hasItems,
|
hasItems: hasItems,
|
||||||
ringAnimation: itemRingAnimation,
|
ringAnimation: itemRingAnimation,
|
||||||
scale: itemScale,
|
scale: itemScale,
|
||||||
|
|
@ -1278,6 +1280,7 @@ public final class StoryPeerListComponent: Component {
|
||||||
totalCount: 1,
|
totalCount: 1,
|
||||||
unseenCount: itemSet.unseenCount != 0 ? 1 : 0,
|
unseenCount: itemSet.unseenCount != 0 ? 1 : 0,
|
||||||
hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems,
|
hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems,
|
||||||
|
hasLiveItems: itemSet.hasLiveItems,
|
||||||
hasItems: hasItems,
|
hasItems: hasItems,
|
||||||
ringAnimation: itemRingAnimation,
|
ringAnimation: itemRingAnimation,
|
||||||
scale: itemScale,
|
scale: itemScale,
|
||||||
|
|
|
||||||
|
|
@ -373,6 +373,7 @@ public final class StoryPeerListItemComponent: Component {
|
||||||
public let totalCount: Int
|
public let totalCount: Int
|
||||||
public let unseenCount: Int
|
public let unseenCount: Int
|
||||||
public let hasUnseenCloseFriendsItems: Bool
|
public let hasUnseenCloseFriendsItems: Bool
|
||||||
|
public let hasLiveItems: Bool
|
||||||
public let hasItems: Bool
|
public let hasItems: Bool
|
||||||
public let ringAnimation: RingAnimation?
|
public let ringAnimation: RingAnimation?
|
||||||
public let scale: CGFloat
|
public let scale: CGFloat
|
||||||
|
|
@ -393,6 +394,7 @@ public final class StoryPeerListItemComponent: Component {
|
||||||
totalCount: Int,
|
totalCount: Int,
|
||||||
unseenCount: Int,
|
unseenCount: Int,
|
||||||
hasUnseenCloseFriendsItems: Bool,
|
hasUnseenCloseFriendsItems: Bool,
|
||||||
|
hasLiveItems: Bool,
|
||||||
hasItems: Bool,
|
hasItems: Bool,
|
||||||
ringAnimation: RingAnimation?,
|
ringAnimation: RingAnimation?,
|
||||||
scale: CGFloat,
|
scale: CGFloat,
|
||||||
|
|
@ -412,6 +414,7 @@ public final class StoryPeerListItemComponent: Component {
|
||||||
self.totalCount = totalCount
|
self.totalCount = totalCount
|
||||||
self.unseenCount = unseenCount
|
self.unseenCount = unseenCount
|
||||||
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
|
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
|
||||||
|
self.hasLiveItems = hasLiveItems
|
||||||
self.hasItems = hasItems
|
self.hasItems = hasItems
|
||||||
self.ringAnimation = ringAnimation
|
self.ringAnimation = ringAnimation
|
||||||
self.scale = scale
|
self.scale = scale
|
||||||
|
|
@ -447,6 +450,9 @@ public final class StoryPeerListItemComponent: Component {
|
||||||
if lhs.hasUnseenCloseFriendsItems != rhs.hasUnseenCloseFriendsItems {
|
if lhs.hasUnseenCloseFriendsItems != rhs.hasUnseenCloseFriendsItems {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.hasLiveItems != rhs.hasLiveItems {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.hasItems != rhs.hasItems {
|
if lhs.hasItems != rhs.hasItems {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -494,6 +500,9 @@ public final class StoryPeerListItemComponent: Component {
|
||||||
private let avatarBackgroundView: UIImageView
|
private let avatarBackgroundView: UIImageView
|
||||||
private var avatarNode: AvatarNode?
|
private var avatarNode: AvatarNode?
|
||||||
private var avatarAddBadgeView: UIImageView?
|
private var avatarAddBadgeView: UIImageView?
|
||||||
|
private var avatarLiveBadgeView: UIImageView?
|
||||||
|
private var avatarLiveBadgeMaskSeenLayer: SimpleLayer?
|
||||||
|
private var avatarLiveBadgeMaskUnseenLayer: SimpleLayer?
|
||||||
private let avatarShapeLayer: SimpleShapeLayer
|
private let avatarShapeLayer: SimpleShapeLayer
|
||||||
private let indicatorMaskSeenLayer: SimpleLayer
|
private let indicatorMaskSeenLayer: SimpleLayer
|
||||||
private let indicatorMaskUnseenLayer: SimpleLayer
|
private let indicatorMaskUnseenLayer: SimpleLayer
|
||||||
|
|
@ -551,6 +560,8 @@ public final class StoryPeerListItemComponent: Component {
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.layer.allowsGroupOpacity = true
|
||||||
|
|
||||||
self.extractedContainerNode.contentNode.view.addSubview(self.extractedBackgroundView)
|
self.extractedContainerNode.contentNode.view.addSubview(self.extractedBackgroundView)
|
||||||
|
|
||||||
self.containerNode.addSubnode(self.extractedContainerNode)
|
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.indicatorColorSeenLayer)
|
||||||
self.avatarContent.layer.addSublayer(self.indicatorColorUnseenLayer)
|
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.indicatorMaskSeenLayer.addSublayer(self.indicatorShapeSeenLayer)
|
||||||
self.indicatorMaskUnseenLayer.addSublayer(self.indicatorShapeUnseenLayer)
|
self.indicatorMaskUnseenLayer.addSublayer(self.indicatorShapeUnseenLayer)
|
||||||
self.indicatorColorSeenLayer.mask = self.indicatorMaskSeenLayer
|
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
|
self.avatarBackgroundView.isHidden = component.ringAnimation != nil || self.indicatorColorSeenLayer.isHidden
|
||||||
|
|
||||||
let baseRadius: CGFloat = 30.66
|
let baseRadius: CGFloat = 30.66
|
||||||
|
|
@ -788,7 +881,10 @@ public final class StoryPeerListItemComponent: Component {
|
||||||
let seenColors: [CGColor]
|
let seenColors: [CGColor]
|
||||||
let unseenColors: [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]
|
unseenColors = [component.theme.chatList.storyUnseenPrivateColors.topColor.cgColor, component.theme.chatList.storyUnseenPrivateColors.bottomColor.cgColor]
|
||||||
} else {
|
} else {
|
||||||
unseenColors = [component.theme.chatList.storyUnseenColors.topColor.cgColor, component.theme.chatList.storyUnseenColors.bottomColor.cgColor]
|
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()
|
let avatarPath = CGMutablePath()
|
||||||
avatarPath.addEllipse(in: CGRect(origin: CGPoint(), size: avatarSize).insetBy(dx: -1.0, dy: -1.0))
|
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
|
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)))
|
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 {
|
} 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.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.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.totalCount, unseenCount: component.unseenCount, isSeen: false, 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
|
let titleString: String
|
||||||
if component.peer.id == component.context.account.peerId {
|
if component.peer.id == component.context.account.peerId {
|
||||||
|
|
@ -899,7 +997,7 @@ public final class StoryPeerListItemComponent: Component {
|
||||||
let titleSize = self.title.update(
|
let titleSize = self.title.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(MultilineTextComponent(
|
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
|
maximumNumberOfLines: 1
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,8 @@ extension ChatControllerImpl {
|
||||||
return AvatarNode.StoryStats(
|
return AvatarNode.StoryStats(
|
||||||
totalCount: storyStats.totalCount,
|
totalCount: storyStats.totalCount,
|
||||||
unseenCount: storyStats.unseenCount,
|
unseenCount: storyStats.unseenCount,
|
||||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends,
|
||||||
|
hasLiveItems: storyStats.hasLiveItems
|
||||||
)
|
)
|
||||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||||
colors: AvatarNode.Colors(theme: self.presentationData.theme),
|
colors: AvatarNode.Colors(theme: self.presentationData.theme),
|
||||||
|
|
|
||||||
|
|
@ -2446,6 +2446,9 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||||
|
|
||||||
let navigateButtonsSize = self.navigateButtons.updateLayout(transition: transition)
|
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)
|
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 {
|
if case .overlay = self.chatPresentationInterfaceState.mode {
|
||||||
navigateButtonsFrame = navigateButtonsFrame.offsetBy(dx: -8.0, dy: -8.0)
|
navigateButtonsFrame = navigateButtonsFrame.offsetBy(dx: -8.0, dy: -8.0)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3967,6 +3967,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||||
completion: completion
|
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? {
|
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 let media {
|
||||||
|
#if DEBUG
|
||||||
|
if "".isEmpty {
|
||||||
|
let _ = context.engine.messages.beginStoryLivestream().startStandalone()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
let _ = (context.engine.messages.uploadStory(
|
let _ = (context.engine.messages.uploadStory(
|
||||||
target: target,
|
target: target,
|
||||||
media: media,
|
media: media,
|
||||||
|
|
|
||||||
|
|
@ -930,6 +930,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||||
component: AnyComponent(AvatarStoryIndicatorComponent(
|
component: AnyComponent(AvatarStoryIndicatorComponent(
|
||||||
hasUnseen: true,
|
hasUnseen: true,
|
||||||
hasUnseenCloseFriendsItems: false,
|
hasUnseenCloseFriendsItems: false,
|
||||||
|
hasLiveItems: false,
|
||||||
colors: AvatarStoryIndicatorComponent.Colors(theme: defaultDarkPresentationTheme),
|
colors: AvatarStoryIndicatorComponent.Colors(theme: defaultDarkPresentationTheme),
|
||||||
activeLineWidth: 1.0 + UIScreenPixel,
|
activeLineWidth: 1.0 + UIScreenPixel,
|
||||||
inactiveLineWidth: 1.0 + UIScreenPixel,
|
inactiveLineWidth: 1.0 + UIScreenPixel,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue