mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
03ed993f80
commit
9a038722d3
@ -1182,6 +1182,27 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
self.openStarsTopup(amount: amount)
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.mainContainerNode.openWebApp = { [weak self] user in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
//TODO:localize
|
||||
self.context.sharedContext.openWebApp(
|
||||
context: self.context,
|
||||
parentController: self,
|
||||
updatedPresentationData: self.updatedPresentationData,
|
||||
botPeer: .user(user),
|
||||
chatPeer: nil,
|
||||
threadId: nil,
|
||||
buttonText: "",
|
||||
url: "",
|
||||
simple: true,
|
||||
source: .generic,
|
||||
skipTermsOfService: true,
|
||||
payload: nil
|
||||
)
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.mainContainerNode.openPremiumManagement = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
|
@ -351,6 +351,9 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
|
||||
itemNode.listNode.openStarsTopup = { [weak self] amount in
|
||||
self?.openStarsTopup?(amount)
|
||||
}
|
||||
itemNode.listNode.openWebApp = { [weak self] amount in
|
||||
self?.openWebApp?(amount)
|
||||
}
|
||||
|
||||
self.currentItemStateValue.set(itemNode.listNode.state |> map { state in
|
||||
let filterId: Int32?
|
||||
@ -417,6 +420,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
|
||||
var openPremiumManagement: (() -> Void)?
|
||||
var openStories: ((ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void)?
|
||||
var openStarsTopup: ((Int64?) -> Void)?
|
||||
var openWebApp: ((TelegramUser) -> Void)?
|
||||
var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
|
||||
var didBeginSelectingChats: (() -> Void)?
|
||||
var canExpandHiddenItems: (() -> Bool)?
|
||||
|
@ -3013,6 +3013,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { _ in
|
||||
})
|
||||
chatListInteraction.isSearchMode = true
|
||||
|
||||
@ -4905,6 +4906,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { _ in
|
||||
})
|
||||
var isInlineMode = false
|
||||
if case .topics = key {
|
||||
|
@ -159,6 +159,7 @@ public final class ChatListShimmerNode: ASDisplayNode {
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _, _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { _ in
|
||||
})
|
||||
interaction.isInlineMode = isInlineMode
|
||||
|
||||
|
@ -1234,6 +1234,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
var credibilityIconComponent: EmojiStatusComponent?
|
||||
let mutedIconNode: ASImageNode
|
||||
var itemTagList: ComponentView<Empty>?
|
||||
var actionButtonTitleNode: TextNode?
|
||||
var actionButtonBackgroundView: UIImageView?
|
||||
var actionButtonNode: HighlightableButtonNode?
|
||||
|
||||
private var hierarchyTrackingLayer: HierarchyTrackingLayer?
|
||||
private var cachedDataDisposable = MetaDisposable()
|
||||
@ -1890,6 +1893,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let onlineLayout = self.onlineNode.asyncLayout()
|
||||
let selectableControlLayout = ItemListSelectableControlNode.asyncLayout(self.selectableControlNode)
|
||||
let reorderControlLayout = ItemListEditableReorderControlNode.asyncLayout(self.reorderControlNode)
|
||||
let makeActionButtonTitleNodeLayout = TextNode.asyncLayout(self.actionButtonTitleNode)
|
||||
|
||||
let currentItem = self.layoutParams?.0
|
||||
let currentChatListText = self.cachedChatListText
|
||||
@ -3061,6 +3065,12 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let (mentionBadgeLayout, mentionBadgeApply) = mentionBadgeLayout(CGSize(width: rawContentWidth, height: CGFloat.greatestFiniteMagnitude), badgeDiameter, badgeFont, currentMentionBadgeImage, mentionBadgeContent)
|
||||
|
||||
var actionButtonTitleNodeLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
||||
if case .none = badgeContent, case .none = mentionBadgeContent, case let .chat(itemPeer) = contentPeer, case let .user(user) = itemPeer.chatMainPeer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp) {
|
||||
//TODO:localize
|
||||
actionButtonTitleNodeLayoutAndApply = makeActionButtonTitleNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "OPEN", font: Font.semibold(15.0), textColor: theme.unreadBadgeActiveTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: rawContentWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
}
|
||||
|
||||
var badgeSize: CGFloat = 0.0
|
||||
if !badgeLayout.width.isZero {
|
||||
badgeSize += badgeLayout.width + 5.0
|
||||
@ -3081,6 +3091,14 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
badgeSize += currentPinnedIconImage.size.width
|
||||
}
|
||||
if let (actionButtonTitleNodeLayout, _) = actionButtonTitleNodeLayoutAndApply {
|
||||
if !badgeSize.isZero {
|
||||
badgeSize += 4.0
|
||||
} else {
|
||||
badgeSize += 5.0
|
||||
}
|
||||
badgeSize += actionButtonTitleNodeLayout.size.width + 12.0 * 2.0
|
||||
}
|
||||
badgeSize = max(badgeSize, reorderInset)
|
||||
|
||||
if !itemTags.isEmpty {
|
||||
@ -3816,23 +3834,19 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.statusNode.fontSize = item.presentationData.fontSize.itemListBaseFontSize
|
||||
let _ = strongSelf.statusNode.transitionToState(statusState, animated: animateContent)
|
||||
|
||||
var nextBadgeX: CGFloat = contentRect.maxX
|
||||
if let _ = currentBadgeBackgroundImage {
|
||||
let badgeFrame = CGRect(x: contentRect.maxX - badgeLayout.width, y: contentRect.maxY - badgeLayout.height - 2.0, width: badgeLayout.width, height: badgeLayout.height)
|
||||
let badgeFrame = CGRect(x: nextBadgeX - badgeLayout.width, y: contentRect.maxY - badgeLayout.height - 2.0, width: badgeLayout.width, height: badgeLayout.height)
|
||||
|
||||
transition.updateFrame(node: strongSelf.badgeNode, frame: badgeFrame)
|
||||
nextBadgeX -= badgeLayout.width + 6.0
|
||||
}
|
||||
|
||||
if currentMentionBadgeImage != nil || currentBadgeBackgroundImage != nil {
|
||||
let mentionBadgeOffset: CGFloat
|
||||
if badgeLayout.width.isZero {
|
||||
mentionBadgeOffset = contentRect.maxX - mentionBadgeLayout.width
|
||||
} else {
|
||||
mentionBadgeOffset = contentRect.maxX - badgeLayout.width - 6.0 - mentionBadgeLayout.width
|
||||
}
|
||||
|
||||
let badgeFrame = CGRect(x: mentionBadgeOffset, y: contentRect.maxY - mentionBadgeLayout.height - 2.0, width: mentionBadgeLayout.width, height: mentionBadgeLayout.height)
|
||||
let badgeFrame = CGRect(x: nextBadgeX - mentionBadgeLayout.width, y: contentRect.maxY - mentionBadgeLayout.height - 2.0, width: mentionBadgeLayout.width, height: mentionBadgeLayout.height)
|
||||
|
||||
transition.updateFrame(node: strongSelf.mentionBadgeNode, frame: badgeFrame)
|
||||
nextBadgeX -= mentionBadgeLayout.width + 6.0
|
||||
}
|
||||
|
||||
if let currentPinnedIconImage = currentPinnedIconImage {
|
||||
@ -3840,14 +3854,71 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.pinnedIconNode.isHidden = false
|
||||
|
||||
let pinnedIconSize = currentPinnedIconImage.size
|
||||
let pinnedIconFrame = CGRect(x: contentRect.maxX - pinnedIconSize.width, y: contentRect.maxY - pinnedIconSize.height - 2.0, width: pinnedIconSize.width, height: pinnedIconSize.height)
|
||||
let pinnedIconFrame = CGRect(x: nextBadgeX - pinnedIconSize.width, y: contentRect.maxY - pinnedIconSize.height - 2.0, width: pinnedIconSize.width, height: pinnedIconSize.height)
|
||||
|
||||
strongSelf.pinnedIconNode.frame = pinnedIconFrame
|
||||
nextBadgeX -= pinnedIconSize.width + 6.0
|
||||
} else {
|
||||
strongSelf.pinnedIconNode.image = nil
|
||||
strongSelf.pinnedIconNode.isHidden = true
|
||||
}
|
||||
|
||||
if let (actionButtonTitleNodeLayout, apply) = actionButtonTitleNodeLayoutAndApply {
|
||||
let actionButtonSize = CGSize(width: actionButtonTitleNodeLayout.size.width + 12.0 * 2.0, height: actionButtonTitleNodeLayout.size.height + 5.0 + 4.0)
|
||||
let actionButtonFrame = CGRect(x: nextBadgeX - actionButtonSize.width, y: contentRect.maxY - actionButtonSize.height, width: actionButtonSize.width, height: actionButtonSize.height)
|
||||
|
||||
let actionButtonNode: HighlightableButtonNode
|
||||
if let current = strongSelf.actionButtonNode {
|
||||
actionButtonNode = current
|
||||
} else {
|
||||
actionButtonNode = HighlightableButtonNode()
|
||||
strongSelf.actionButtonNode = actionButtonNode
|
||||
strongSelf.mainContentContainerNode.addSubnode(actionButtonNode)
|
||||
actionButtonNode.addTarget(strongSelf, action: #selector(strongSelf.actionButtonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
let actionButtonBackgroundView: UIImageView
|
||||
if let current = strongSelf.actionButtonBackgroundView {
|
||||
actionButtonBackgroundView = current
|
||||
} else {
|
||||
actionButtonBackgroundView = UIImageView()
|
||||
strongSelf.actionButtonBackgroundView = actionButtonBackgroundView
|
||||
actionButtonNode.view.addSubview(actionButtonBackgroundView)
|
||||
|
||||
if actionButtonBackgroundView.image?.size.height != actionButtonSize.height {
|
||||
actionButtonBackgroundView.image = generateStretchableFilledCircleImage(diameter: actionButtonSize.height, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
}
|
||||
|
||||
actionButtonBackgroundView.tintColor = theme.unreadBadgeActiveBackgroundColor
|
||||
|
||||
let actionButtonTitleNode = apply()
|
||||
if strongSelf.actionButtonTitleNode !== actionButtonTitleNode {
|
||||
strongSelf.actionButtonTitleNode?.removeFromSupernode()
|
||||
strongSelf.actionButtonTitleNode = actionButtonTitleNode
|
||||
actionButtonNode.addSubnode(actionButtonTitleNode)
|
||||
}
|
||||
|
||||
actionButtonNode.frame = actionButtonFrame
|
||||
actionButtonBackgroundView.frame = CGRect(origin: CGPoint(), size: actionButtonFrame.size)
|
||||
actionButtonTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((actionButtonFrame.width - actionButtonTitleNodeLayout.size.width) * 0.5), y: 5.0), size: actionButtonTitleNodeLayout.size)
|
||||
|
||||
nextBadgeX -= actionButtonSize.width + 6.0
|
||||
} else {
|
||||
if let actionButtonTitleNode = strongSelf.actionButtonTitleNode {
|
||||
actionButtonTitleNode.removeFromSupernode()
|
||||
strongSelf.actionButtonTitleNode = nil
|
||||
}
|
||||
if let actionButtonBackgroundView = strongSelf.actionButtonBackgroundView {
|
||||
actionButtonBackgroundView.removeFromSuperview()
|
||||
strongSelf.actionButtonBackgroundView = nil
|
||||
}
|
||||
if let actionButtonNode = strongSelf.actionButtonNode {
|
||||
actionButtonNode.removeFromSupernode()
|
||||
strongSelf.actionButtonNode = nil
|
||||
}
|
||||
}
|
||||
|
||||
var titleOffset: CGFloat = 0.0
|
||||
if let currentSecretIconImage = currentSecretIconImage {
|
||||
let iconNode: ASImageNode
|
||||
@ -4495,6 +4566,18 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
item.interaction.openForumThread(index.messageIndex.id.peerId, topicItem.id)
|
||||
}
|
||||
|
||||
@objc private func actionButtonPressed() {
|
||||
guard let item else {
|
||||
return
|
||||
}
|
||||
guard case let .peer(peerData) = item.content else {
|
||||
return
|
||||
}
|
||||
if case let .user(user) = peerData.peer.peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp) {
|
||||
item.interaction.openWebApp(user)
|
||||
}
|
||||
}
|
||||
|
||||
override public func animateInsertion(_ currentTimestamp: Double, duration: Double, options: ListViewItemAnimationOptions) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
}
|
||||
|
@ -112,6 +112,7 @@ public final class ChatListNodeInteraction {
|
||||
let openStarsTopup: (Int64?) -> Void
|
||||
let dismissNotice: (ChatListNotice) -> Void
|
||||
let editPeer: (ChatListItem) -> Void
|
||||
let openWebApp: (TelegramUser) -> Void
|
||||
|
||||
public var searchTextHighightState: String?
|
||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||
@ -167,7 +168,8 @@ public final class ChatListNodeInteraction {
|
||||
openStories: @escaping (ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void,
|
||||
openStarsTopup: @escaping (Int64?) -> Void,
|
||||
dismissNotice: @escaping (ChatListNotice) -> Void,
|
||||
editPeer: @escaping (ChatListItem) -> Void
|
||||
editPeer: @escaping (ChatListItem) -> Void,
|
||||
openWebApp: @escaping (TelegramUser) -> Void
|
||||
) {
|
||||
self.activateSearch = activateSearch
|
||||
self.peerSelected = peerSelected
|
||||
@ -211,6 +213,7 @@ public final class ChatListNodeInteraction {
|
||||
self.openStarsTopup = openStarsTopup
|
||||
self.dismissNotice = dismissNotice
|
||||
self.editPeer = editPeer
|
||||
self.openWebApp = openWebApp
|
||||
}
|
||||
}
|
||||
|
||||
@ -1220,6 +1223,7 @@ public final class ChatListNode: ListView {
|
||||
public var openBirthdaySetup: (() -> Void)?
|
||||
public var openPremiumManagement: (() -> Void)?
|
||||
public var openStarsTopup: ((Int64?) -> Void)?
|
||||
public var openWebApp: ((TelegramUser) -> Void)?
|
||||
|
||||
private var theme: PresentationTheme
|
||||
|
||||
@ -1867,6 +1871,11 @@ public final class ChatListNode: ListView {
|
||||
break
|
||||
}
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { [weak self] user in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openWebApp?(user)
|
||||
})
|
||||
nodeInteraction.isInlineMode = isInlineMode
|
||||
|
||||
|
@ -21,7 +21,7 @@ public enum ChatMessageGalleryControllerData {
|
||||
case pass(TelegramMediaFile)
|
||||
case instantPage(InstantPageGalleryController, Int, Media)
|
||||
case map(TelegramMediaMap)
|
||||
case stickerPack(StickerPackReference)
|
||||
case stickerPack(StickerPackReference, TelegramMediaFile?)
|
||||
case audio(TelegramMediaFile)
|
||||
case document(TelegramMediaFile, Bool)
|
||||
case gallery(Signal<GalleryController, NoError>)
|
||||
@ -104,7 +104,7 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati
|
||||
for attribute in file.attributes {
|
||||
if case let .CustomEmoji(_, _, _, reference) = attribute {
|
||||
if let reference = reference {
|
||||
return .stickerPack(reference)
|
||||
return .stickerPack(reference, file)
|
||||
}
|
||||
break
|
||||
}
|
||||
@ -214,7 +214,7 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati
|
||||
for attribute in file.attributes {
|
||||
if case let .Sticker(_, reference, _) = attribute {
|
||||
if let reference = reference {
|
||||
return .stickerPack(reference)
|
||||
return .stickerPack(reference, file)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -230,6 +230,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, ASScrollView
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { _ in
|
||||
})
|
||||
|
||||
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
@ -379,6 +379,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { _ in
|
||||
})
|
||||
|
||||
func makeChatListItem(
|
||||
|
@ -42,6 +42,7 @@ swift_library(
|
||||
"//submodules/Pasteboard:Pasteboard",
|
||||
"//submodules/TelegramUI/Components/Stickers/StickerPackEditTitleController",
|
||||
"//submodules/TelegramUI/Components/CameraScreen",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -25,6 +25,8 @@ import Pasteboard
|
||||
import StickerPackEditTitleController
|
||||
import EntityKeyboard
|
||||
import CameraScreen
|
||||
import ComponentFlow
|
||||
import EmojiStatusComponent
|
||||
|
||||
private let maxStickersCount = 120
|
||||
|
||||
@ -134,6 +136,8 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
private let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
||||
private let sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?
|
||||
private let backgroundNode: ASImageNode
|
||||
private let previewIconFile: TelegramMediaFile?
|
||||
private var mainPreviewIcon: ComponentView<Empty>?
|
||||
private let gridNode: GridNode
|
||||
private let actionAreaBackgroundNode: NavigationBackgroundNode
|
||||
private let actionAreaSeparatorNode: ASDisplayNode
|
||||
@ -190,6 +194,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
presentationData: PresentationData,
|
||||
stickerPacks: [StickerPackReference],
|
||||
loadedStickerPacks: [LoadedStickerPack],
|
||||
previewIconFile: TelegramMediaFile?,
|
||||
decideNextAction: @escaping (StickerPackContainer, StickerPackAction) -> StickerPackNextAction,
|
||||
requestDismiss: @escaping () -> Void,
|
||||
expandProgressUpdated: @escaping (StickerPackContainer, ContainedViewLayoutTransition, ContainedViewLayoutTransition) -> Void,
|
||||
@ -218,6 +223,11 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
self.backgroundNode.displayWithoutProcessing = true
|
||||
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 20.0, color: self.presentationData.theme.actionSheet.opaqueItemBackgroundColor)
|
||||
|
||||
self.previewIconFile = previewIconFile
|
||||
if self.previewIconFile != nil {
|
||||
self.mainPreviewIcon = ComponentView()
|
||||
}
|
||||
|
||||
self.gridNode = GridNode()
|
||||
self.gridNode.scrollView.alwaysBounceVertical = true
|
||||
self.gridNode.scrollView.showsVerticalScrollIndicator = false
|
||||
@ -292,6 +302,24 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
self?.gridPresentationLayoutUpdated(presentationLayout, transition: transition)
|
||||
}
|
||||
|
||||
self.gridNode.scrollingInitiated = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let mainPreviewIconView = self.mainPreviewIcon?.view {
|
||||
mainPreviewIconView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let mainPreviewIconView = self.mainPreviewIcon?.view {
|
||||
self.mainPreviewIcon = nil
|
||||
mainPreviewIconView.removeFromSuperview()
|
||||
}
|
||||
})
|
||||
mainPreviewIconView.layer.animateScale(from: 1.0, to: 0.5, duration: 0.2, removeOnCompletion: false)
|
||||
}
|
||||
}
|
||||
|
||||
self.gridNode.interactiveScrollingEnded = { [weak self] in
|
||||
guard let strongSelf = self, !strongSelf.isDismissed else {
|
||||
return
|
||||
@ -1273,7 +1301,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
commit()
|
||||
|
||||
let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil)
|
||||
let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], previewIconFile: nil, expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil)
|
||||
(navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root))
|
||||
|
||||
Queue.mainQueue().after(0.1) {
|
||||
@ -1329,7 +1357,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash)
|
||||
let _ = (context.engine.stickers.addStickerToStickerSet(packReference: packReference, sticker: sticker)
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil)
|
||||
let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], previewIconFile: nil, expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil)
|
||||
(navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root))
|
||||
|
||||
Queue.mainQueue().after(0.1) {
|
||||
@ -1378,7 +1406,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
commit()
|
||||
|
||||
let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil)
|
||||
let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], previewIconFile: nil, expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil)
|
||||
(navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root))
|
||||
|
||||
Queue.mainQueue().after(0.1) {
|
||||
@ -1956,6 +1984,20 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
self.modalProgress = modalProgress
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
if let mainPreviewIconView = self.mainPreviewIcon?.view {
|
||||
mainPreviewIconView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||
mainPreviewIconView.layer.animateScale(from: 0.5, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
}
|
||||
|
||||
func animateOut() {
|
||||
if let mainPreviewIconView = self.mainPreviewIcon?.view {
|
||||
mainPreviewIconView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
mainPreviewIconView.layer.animateScale(from: 1.0, to: 0.5, duration: 0.2, removeOnCompletion: false)
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
var insets = layout.insets(options: [.statusBar])
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
@ -2140,6 +2182,38 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
titleContainerFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + floor((backgroundFrame.width) / 2.0), y: backgroundFrame.minY + floor((56.0) / 2.0)), size: CGSize())
|
||||
}
|
||||
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
|
||||
|
||||
if let previewIconFile = self.previewIconFile, let mainPreviewIcon = self.mainPreviewIcon {
|
||||
let iconFitSize = CGSize(width: 90.0, height: 90.0)
|
||||
let iconSize = mainPreviewIcon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: self.context,
|
||||
animationCache: self.context.animationCache,
|
||||
animationRenderer: self.context.animationRenderer,
|
||||
content: .animation(
|
||||
content: .file(file: previewIconFile),
|
||||
size: iconFitSize,
|
||||
placeholderColor: .clear,
|
||||
themeColor: self.presentationData.theme.list.itemPrimaryTextColor,
|
||||
loopMode: .forever
|
||||
),
|
||||
isVisibleForAnimations: true,
|
||||
action: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: iconFitSize
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + floor((backgroundFrame.width - iconSize.width) * 0.5), y: backgroundFrame.minY - 50.0 - iconSize.height), size: iconSize)
|
||||
if let iconView = mainPreviewIcon.view {
|
||||
if iconView.superview == nil {
|
||||
self.backgroundNode.view.superview?.addSubview(iconView)
|
||||
}
|
||||
transition.updatePosition(layer: iconView.layer, position: iconFrame.center)
|
||||
iconView.bounds = CGRect(origin: CGPoint(), size: iconFrame.size)
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.titleContainer, frame: titleContainerFrame)
|
||||
transition.updateFrame(node: self.titleSeparatorNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY + 56.0 - UIScreenPixel), size: CGSize(width: backgroundFrame.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.titleBackgroundnode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.width, height: 56.0)))
|
||||
@ -2218,6 +2292,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
||||
private weak var controller: StickerPackScreenImpl?
|
||||
private var presentationData: PresentationData
|
||||
private let stickerPacks: [StickerPackReference]
|
||||
private let previewIconFile: TelegramMediaFile?
|
||||
private let modalProgressUpdated: (CGFloat, ContainedViewLayoutTransition) -> Void
|
||||
private let dismissed: () -> Void
|
||||
private let presentInGlobalOverlay: (ViewController, Any?) -> Void
|
||||
@ -2251,6 +2326,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
||||
context: AccountContext,
|
||||
controller: StickerPackScreenImpl,
|
||||
stickerPacks: [StickerPackReference],
|
||||
previewIconFile: TelegramMediaFile?,
|
||||
initialSelectedStickerPackIndex: Int,
|
||||
modalProgressUpdated: @escaping (CGFloat, ContainedViewLayoutTransition) -> Void,
|
||||
dismissed: @escaping () -> Void,
|
||||
@ -2264,6 +2340,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
||||
self.controller = controller
|
||||
self.presentationData = controller.presentationData
|
||||
self.stickerPacks = stickerPacks
|
||||
self.previewIconFile = previewIconFile
|
||||
self.selectedStickerPackIndex = initialSelectedStickerPackIndex
|
||||
self.modalProgressUpdated = modalProgressUpdated
|
||||
self.dismissed = dismissed
|
||||
@ -2398,7 +2475,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
||||
wasAdded = true
|
||||
containerTransition = .immediate
|
||||
let index = i
|
||||
container = StickerPackContainer(index: index, context: self.context, presentationData: self.presentationData, stickerPacks: self.stickerPacks, loadedStickerPacks: self.controller?.loadedStickerPacks ?? [], decideNextAction: { [weak self] container, action in
|
||||
container = StickerPackContainer(index: index, context: self.context, presentationData: self.presentationData, stickerPacks: self.stickerPacks, loadedStickerPacks: self.controller?.loadedStickerPacks ?? [], previewIconFile: self.previewIconFile, decideNextAction: { [weak self] container, action in
|
||||
guard let strongSelf = self, let layout = strongSelf.validLayout else {
|
||||
return .dismiss
|
||||
}
|
||||
@ -2565,6 +2642,10 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
||||
let minInset: CGFloat = (self.containers.map { (_, container) -> CGFloat in container.topContentInset }).max() ?? 0.0
|
||||
self.containerContainingNode.layer.animatePosition(from: CGPoint(x: 0.0, y: self.containerContainingNode.bounds.height - minInset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
}
|
||||
|
||||
for (_, container) in self.containers {
|
||||
container.animateIn()
|
||||
}
|
||||
}
|
||||
|
||||
func animateOut(completion: @escaping () -> Void) {
|
||||
@ -2587,6 +2668,10 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
||||
|
||||
self.modalProgressUpdated(0.0, .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
|
||||
for (_, container) in self.containers {
|
||||
container.animateOut()
|
||||
}
|
||||
}
|
||||
|
||||
func dismiss() {
|
||||
@ -2659,6 +2744,7 @@ public final class StickerPackScreenImpl: ViewController, StickerPackScreen {
|
||||
|
||||
private let stickerPacks: [StickerPackReference]
|
||||
fileprivate let loadedStickerPacks: [LoadedStickerPack]
|
||||
let previewIconFile: TelegramMediaFile?
|
||||
|
||||
private let initialSelectedStickerPackIndex: Int
|
||||
fileprivate weak var parentNavigationController: NavigationController?
|
||||
@ -2698,6 +2784,7 @@ public final class StickerPackScreenImpl: ViewController, StickerPackScreen {
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||
stickerPacks: [StickerPackReference],
|
||||
loadedStickerPacks: [LoadedStickerPack],
|
||||
previewIconFile: TelegramMediaFile?,
|
||||
selectedStickerPackIndex: Int = 0,
|
||||
mainActionTitle: String? = nil,
|
||||
actionTitle: String? = nil,
|
||||
@ -2714,6 +2801,7 @@ public final class StickerPackScreenImpl: ViewController, StickerPackScreen {
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.stickerPacks = stickerPacks
|
||||
self.loadedStickerPacks = loadedStickerPacks
|
||||
self.previewIconFile = previewIconFile
|
||||
self.initialSelectedStickerPackIndex = selectedStickerPackIndex
|
||||
self.mainActionTitle = mainActionTitle
|
||||
self.actionTitle = actionTitle
|
||||
@ -2751,7 +2839,7 @@ public final class StickerPackScreenImpl: ViewController, StickerPackScreen {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = StickerPackScreenNode(context: self.context, controller: self, stickerPacks: self.stickerPacks, initialSelectedStickerPackIndex: self.initialSelectedStickerPackIndex, modalProgressUpdated: { [weak self] value, transition in
|
||||
self.displayNode = StickerPackScreenNode(context: self.context, controller: self, stickerPacks: self.stickerPacks, previewIconFile: self.previewIconFile, initialSelectedStickerPackIndex: self.initialSelectedStickerPackIndex, modalProgressUpdated: { [weak self] value, transition in
|
||||
DispatchQueue.main.async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -2950,6 +3038,7 @@ public func StickerPackScreen(
|
||||
mainStickerPack: StickerPackReference,
|
||||
stickerPacks: [StickerPackReference],
|
||||
loadedStickerPacks: [LoadedStickerPack] = [],
|
||||
previewIconFile: TelegramMediaFile? = nil,
|
||||
mainActionTitle: String? = nil,
|
||||
actionTitle: String? = nil,
|
||||
isEditing: Bool = false,
|
||||
@ -2967,6 +3056,7 @@ public func StickerPackScreen(
|
||||
updatedPresentationData: updatedPresentationData,
|
||||
stickerPacks: stickerPacks,
|
||||
loadedStickerPacks: loadedStickerPacks,
|
||||
previewIconFile: previewIconFile,
|
||||
selectedStickerPackIndex: stickerPacks.firstIndex(of: mainStickerPack) ?? 0,
|
||||
mainActionTitle: mainActionTitle,
|
||||
actionTitle: actionTitle,
|
||||
|
@ -20,6 +20,7 @@ final class VideoChatParticipantThumbnailComponent: Component {
|
||||
let isPresentation: Bool
|
||||
let isSelected: Bool
|
||||
let isSpeaking: Bool
|
||||
let displayVideo: Bool
|
||||
let interfaceOrientation: UIInterfaceOrientation
|
||||
let action: (() -> Void)?
|
||||
let contextAction: ((EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void)?
|
||||
@ -31,6 +32,7 @@ final class VideoChatParticipantThumbnailComponent: Component {
|
||||
isPresentation: Bool,
|
||||
isSelected: Bool,
|
||||
isSpeaking: Bool,
|
||||
displayVideo: Bool,
|
||||
interfaceOrientation: UIInterfaceOrientation,
|
||||
action: (() -> Void)?,
|
||||
contextAction: ((EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void)?
|
||||
@ -41,6 +43,7 @@ final class VideoChatParticipantThumbnailComponent: Component {
|
||||
self.isPresentation = isPresentation
|
||||
self.isSelected = isSelected
|
||||
self.isSpeaking = isSpeaking
|
||||
self.displayVideo = displayVideo
|
||||
self.interfaceOrientation = interfaceOrientation
|
||||
self.action = action
|
||||
self.contextAction = contextAction
|
||||
@ -65,6 +68,9 @@ final class VideoChatParticipantThumbnailComponent: Component {
|
||||
if lhs.isSpeaking != rhs.isSpeaking {
|
||||
return false
|
||||
}
|
||||
if lhs.displayVideo != rhs.displayVideo {
|
||||
return false
|
||||
}
|
||||
if lhs.interfaceOrientation != rhs.interfaceOrientation {
|
||||
return false
|
||||
}
|
||||
@ -251,7 +257,7 @@ final class VideoChatParticipantThumbnailComponent: Component {
|
||||
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
|
||||
}
|
||||
|
||||
if let videoDescription = component.isPresentation ? component.participant.presentationDescription : component.participant.videoDescription {
|
||||
if component.displayVideo, let videoDescription = component.isPresentation ? component.participant.presentationDescription : component.participant.videoDescription {
|
||||
let videoBackgroundLayer: SimpleLayer
|
||||
if let current = self.videoBackgroundLayer {
|
||||
videoBackgroundLayer = current
|
||||
@ -470,6 +476,7 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
|
||||
|
||||
let call: PresentationGroupCall
|
||||
let theme: PresentationTheme
|
||||
let displayVideo: Bool
|
||||
let participants: [Participant]
|
||||
let selectedParticipant: Participant.Key?
|
||||
let speakingParticipants: Set<EnginePeer.Id>
|
||||
@ -480,6 +487,7 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
|
||||
init(
|
||||
call: PresentationGroupCall,
|
||||
theme: PresentationTheme,
|
||||
displayVideo: Bool,
|
||||
participants: [Participant],
|
||||
selectedParticipant: Participant.Key?,
|
||||
speakingParticipants: Set<EnginePeer.Id>,
|
||||
@ -489,6 +497,7 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
|
||||
) {
|
||||
self.call = call
|
||||
self.theme = theme
|
||||
self.displayVideo = displayVideo
|
||||
self.participants = participants
|
||||
self.selectedParticipant = selectedParticipant
|
||||
self.speakingParticipants = speakingParticipants
|
||||
@ -504,6 +513,9 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.displayVideo != rhs.displayVideo {
|
||||
return false
|
||||
}
|
||||
if lhs.participants != rhs.participants {
|
||||
return false
|
||||
}
|
||||
@ -654,6 +666,7 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
|
||||
isPresentation: participant.isPresentation,
|
||||
isSelected: component.selectedParticipant == participant.key,
|
||||
isSpeaking: component.speakingParticipants.contains(participant.participant.peer.id),
|
||||
displayVideo: component.displayVideo,
|
||||
interfaceOrientation: component.interfaceOrientation,
|
||||
action: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
|
@ -45,6 +45,7 @@ final class VideoChatParticipantVideoComponent: Component {
|
||||
let isMyPeer: Bool
|
||||
let isPresentation: Bool
|
||||
let isSpeaking: Bool
|
||||
let maxVideoQuality: Int
|
||||
let isExpanded: Bool
|
||||
let isUIHidden: Bool
|
||||
let contentInsets: UIEdgeInsets
|
||||
@ -63,6 +64,7 @@ final class VideoChatParticipantVideoComponent: Component {
|
||||
isMyPeer: Bool,
|
||||
isPresentation: Bool,
|
||||
isSpeaking: Bool,
|
||||
maxVideoQuality: Int,
|
||||
isExpanded: Bool,
|
||||
isUIHidden: Bool,
|
||||
contentInsets: UIEdgeInsets,
|
||||
@ -80,6 +82,7 @@ final class VideoChatParticipantVideoComponent: Component {
|
||||
self.isMyPeer = isMyPeer
|
||||
self.isPresentation = isPresentation
|
||||
self.isSpeaking = isSpeaking
|
||||
self.maxVideoQuality = maxVideoQuality
|
||||
self.isExpanded = isExpanded
|
||||
self.isUIHidden = isUIHidden
|
||||
self.contentInsets = contentInsets
|
||||
@ -104,6 +107,9 @@ final class VideoChatParticipantVideoComponent: Component {
|
||||
if lhs.isSpeaking != rhs.isSpeaking {
|
||||
return false
|
||||
}
|
||||
if lhs.maxVideoQuality != rhs.maxVideoQuality {
|
||||
return false
|
||||
}
|
||||
if lhs.isExpanded != rhs.isExpanded {
|
||||
return false
|
||||
}
|
||||
@ -413,7 +419,7 @@ final class VideoChatParticipantVideoComponent: Component {
|
||||
alphaTransition.setAlpha(view: titleView, alpha: controlsAlpha)
|
||||
}
|
||||
|
||||
let videoDescription = component.isPresentation ? component.participant.presentationDescription : component.participant.videoDescription
|
||||
let videoDescription: GroupCallParticipantsContext.Participant.VideoDescription? = component.maxVideoQuality == 0 ? nil : (component.isPresentation ? component.participant.presentationDescription : component.participant.videoDescription)
|
||||
|
||||
var isEffectivelyPaused = false
|
||||
if let videoDescription, videoDescription.isPaused {
|
||||
|
@ -129,6 +129,7 @@ final class VideoChatParticipantsComponent: Component {
|
||||
let participants: Participants?
|
||||
let speakingParticipants: Set<EnginePeer.Id>
|
||||
let expandedVideoState: ExpandedVideoState?
|
||||
let maxVideoQuality: Int
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let layout: Layout
|
||||
@ -147,6 +148,7 @@ final class VideoChatParticipantsComponent: Component {
|
||||
participants: Participants?,
|
||||
speakingParticipants: Set<EnginePeer.Id>,
|
||||
expandedVideoState: ExpandedVideoState?,
|
||||
maxVideoQuality: Int,
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
layout: Layout,
|
||||
@ -164,6 +166,7 @@ final class VideoChatParticipantsComponent: Component {
|
||||
self.participants = participants
|
||||
self.speakingParticipants = speakingParticipants
|
||||
self.expandedVideoState = expandedVideoState
|
||||
self.maxVideoQuality = maxVideoQuality
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.layout = layout
|
||||
@ -188,6 +191,9 @@ final class VideoChatParticipantsComponent: Component {
|
||||
if lhs.expandedVideoState != rhs.expandedVideoState {
|
||||
return false
|
||||
}
|
||||
if lhs.maxVideoQuality != rhs.maxVideoQuality {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
@ -1022,6 +1028,7 @@ final class VideoChatParticipantsComponent: Component {
|
||||
isMyPeer: videoParticipant.participant.peer.id == component.participants?.myPeerId,
|
||||
isPresentation: videoParticipant.isPresentation,
|
||||
isSpeaking: component.speakingParticipants.contains(videoParticipant.participant.peer.id),
|
||||
maxVideoQuality: component.maxVideoQuality,
|
||||
isExpanded: isItemExpanded,
|
||||
isUIHidden: isItemUIHidden || self.isPinchToZoomActive,
|
||||
contentInsets: itemContentInsets,
|
||||
@ -1376,6 +1383,7 @@ final class VideoChatParticipantsComponent: Component {
|
||||
component: AnyComponent(VideoChatExpandedParticipantThumbnailsComponent(
|
||||
call: component.call,
|
||||
theme: component.theme,
|
||||
displayVideo: component.maxVideoQuality != 0,
|
||||
participants: thumbnailParticipants,
|
||||
selectedParticipant: component.expandedVideoState.flatMap { expandedVideoState in
|
||||
return VideoChatExpandedParticipantThumbnailsComponent.Participant.Key(id: expandedVideoState.mainParticipant.id, isPresentation: expandedVideoState.mainParticipant.isPresentation)
|
||||
@ -1762,12 +1770,18 @@ final class VideoChatParticipantsComponent: Component {
|
||||
}
|
||||
|
||||
var requestedVideo: [PresentationGroupCallRequestedVideo] = []
|
||||
if let participants = component.participants {
|
||||
if let participants = component.participants, component.maxVideoQuality != 0 {
|
||||
for participant in participants.participants {
|
||||
var maxVideoQuality: PresentationGroupCallRequestedVideo.Quality = .medium
|
||||
if let expandedVideoState = component.expandedVideoState {
|
||||
if expandedVideoState.mainParticipant.id == participant.peer.id, !expandedVideoState.mainParticipant.isPresentation {
|
||||
maxVideoQuality = .full
|
||||
if component.maxVideoQuality == Int.max {
|
||||
maxVideoQuality = .full
|
||||
} else if component.maxVideoQuality == 360 {
|
||||
maxVideoQuality = .medium
|
||||
} else {
|
||||
maxVideoQuality = .thumbnail
|
||||
}
|
||||
} else {
|
||||
maxVideoQuality = .thumbnail
|
||||
}
|
||||
@ -1776,15 +1790,27 @@ final class VideoChatParticipantsComponent: Component {
|
||||
var maxPresentationQuality: PresentationGroupCallRequestedVideo.Quality = .medium
|
||||
if let expandedVideoState = component.expandedVideoState {
|
||||
if expandedVideoState.mainParticipant.id == participant.peer.id, expandedVideoState.mainParticipant.isPresentation {
|
||||
maxPresentationQuality = .full
|
||||
if component.maxVideoQuality == Int.max {
|
||||
maxVideoQuality = .full
|
||||
} else if component.maxVideoQuality == 360 {
|
||||
maxVideoQuality = .medium
|
||||
} else {
|
||||
maxVideoQuality = .thumbnail
|
||||
}
|
||||
} else {
|
||||
maxPresentationQuality = .thumbnail
|
||||
}
|
||||
}
|
||||
|
||||
if component.layout.videoColumn != nil && gridParticipants.count == 1 {
|
||||
maxVideoQuality = .full
|
||||
maxPresentationQuality = .full
|
||||
if component.maxVideoQuality == Int.max {
|
||||
maxVideoQuality = .full
|
||||
} else if component.maxVideoQuality == 360 {
|
||||
maxVideoQuality = .medium
|
||||
} else {
|
||||
maxVideoQuality = .thumbnail
|
||||
}
|
||||
maxPresentationQuality = maxVideoQuality
|
||||
}
|
||||
|
||||
if let videoChannel = participant.requestedVideoChannel(minQuality: .thumbnail, maxQuality: maxVideoQuality) {
|
||||
|
@ -119,6 +119,8 @@ final class VideoChatScreenComponent: Component {
|
||||
let updateAvatarDisposable = MetaDisposable()
|
||||
var currentUpdatingAvatar: (TelegramMediaImageRepresentation, Float)?
|
||||
|
||||
var maxVideoQuality: Int = Int.max
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.containerView = UIView()
|
||||
self.containerView.clipsToBounds = true
|
||||
@ -1506,6 +1508,7 @@ final class VideoChatScreenComponent: Component {
|
||||
participants: mappedParticipants,
|
||||
speakingParticipants: self.members?.speakingParticipants ?? Set(),
|
||||
expandedVideoState: self.expandedParticipantsVideoState,
|
||||
maxVideoQuality: self.maxVideoQuality,
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
layout: participantsLayout,
|
||||
|
@ -152,6 +152,56 @@ extension VideoChatScreenComponent.View {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let qualityList: [(Int, String)] = [
|
||||
(0, "Audio Only"),
|
||||
(180, "180p"),
|
||||
(360, "360p"),
|
||||
(Int.max, "720p")
|
||||
]
|
||||
|
||||
let videoQualityTitle = qualityList.first(where: { $0.0 == self.maxVideoQuality })?.1 ?? ""
|
||||
items.append(.action(ContextMenuActionItem(text: "Receive Video Quality", textColor: .primary, textLayout: .secondLineWithValue(videoQualityTitle), icon: { _ in
|
||||
return nil
|
||||
}, action: { [weak self] c, _ in
|
||||
guard let self else {
|
||||
c?.dismiss(completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
items.append(.action(ContextMenuActionItem(text: environment.strings.Common_Back, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor)
|
||||
}, iconPosition: .left, action: { (c, _) in
|
||||
c?.popItems()
|
||||
})))
|
||||
items.append(.separator)
|
||||
|
||||
for (quality, title) in qualityList {
|
||||
let isSelected = self.maxVideoQuality == quality
|
||||
items.append(.action(ContextMenuActionItem(text: title, icon: { _ in
|
||||
if isSelected {
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.maxVideoQuality != quality {
|
||||
self.maxVideoQuality = quality
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
c?.pushItems(items: .single(ContextController.Items(content: .list(items))))
|
||||
})))
|
||||
|
||||
if callState.isVideoEnabled && (callState.muteState?.canUnmute ?? true) {
|
||||
if component.call.hasScreencast {
|
||||
items.append(.action(ContextMenuActionItem(text: environment.strings.VoiceChat_StopScreenSharing, icon: { theme in
|
||||
|
@ -669,6 +669,8 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
||||
dismissNotice: { _ in
|
||||
},
|
||||
editPeer: { _ in
|
||||
},
|
||||
openWebApp: { _ in
|
||||
}
|
||||
)
|
||||
self.chatListNodeInteraction = chatListNodeInteraction
|
||||
|
@ -185,6 +185,7 @@ public final class LoadingOverlayNode: ASDisplayNode {
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _, _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { _ in
|
||||
})
|
||||
|
||||
let items = (0 ..< 1).map { _ -> ChatListItem in
|
||||
@ -539,6 +540,8 @@ private final class PeerInfoScreenPersonalChannelItemNode: PeerInfoScreenItemNod
|
||||
dismissNotice: { _ in
|
||||
},
|
||||
editPeer: { _ in
|
||||
},
|
||||
openWebApp: { _ in
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -207,6 +207,8 @@ final class GreetingMessageListItemComponent: Component {
|
||||
dismissNotice: { _ in
|
||||
},
|
||||
editPeer: { _ in
|
||||
},
|
||||
openWebApp: { _ in
|
||||
}
|
||||
)
|
||||
self.chatListNodeInteraction = chatListNodeInteraction
|
||||
|
@ -228,6 +228,8 @@ final class QuickReplySetupScreenComponent: Component {
|
||||
if let itemId = item.id {
|
||||
parentView.openEditShortcut(id: itemId, currentValue: item.shortcut)
|
||||
}
|
||||
},
|
||||
openWebApp: { _ in
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -873,6 +873,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, ASScrollViewDelegate
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { _ in
|
||||
})
|
||||
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
||||
|
@ -294,14 +294,14 @@ extension ChatControllerImpl {
|
||||
})
|
||||
}
|
||||
|
||||
func presentEmojiList(references: [StickerPackReference]) {
|
||||
func presentEmojiList(references: [StickerPackReference], previewIconFile: TelegramMediaFile? = nil) {
|
||||
guard let packReference = references.first else {
|
||||
return
|
||||
}
|
||||
self.chatDisplayNode.dismissTextInput()
|
||||
|
||||
let presentationData = self.presentationData
|
||||
let controller = StickerPackScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mainStickerPack: packReference, stickerPacks: Array(references), parentNavigationController: self.effectiveNavigationController, sendEmoji: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] text, attribute in
|
||||
let controller = StickerPackScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mainStickerPack: packReference, stickerPacks: Array(references), previewIconFile: previewIconFile, parentNavigationController: self.effectiveNavigationController, sendEmoji: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] text, attribute in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.sendEmoji(text, attribute, false)
|
||||
}
|
||||
|
@ -9026,7 +9026,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if let stickerPackReference = stickerPackReference {
|
||||
self.presentEmojiList(references: [stickerPackReference])
|
||||
self.presentEmojiList(references: [stickerPackReference], previewIconFile: file)
|
||||
|
||||
/*let _ = (self.context.engine.stickers.loadedStickerPack(reference: stickerPackReference, forceActualized: false)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] stickerPack in
|
||||
|
@ -293,6 +293,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, ASScrollViewDe
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
}, openWebApp: { _ in
|
||||
})
|
||||
interaction.searchTextHighightState = searchQuery
|
||||
self.interaction = interaction
|
||||
|
@ -177,6 +177,8 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable {
|
||||
dismissNotice: { _ in
|
||||
},
|
||||
editPeer: { _ in
|
||||
},
|
||||
openWebApp: { _ in
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -181,8 +181,8 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
||||
controller.navigationPresentation = .modal
|
||||
params.navigationController?.pushViewController(controller)
|
||||
return true
|
||||
case let .stickerPack(reference):
|
||||
let controller = StickerPackScreen(context: params.context, updatedPresentationData: params.updatedPresentationData, mainStickerPack: reference, stickerPacks: [reference], parentNavigationController: params.navigationController, sendSticker: params.sendSticker, sendEmoji: params.sendEmoji, actionPerformed: { actions in
|
||||
case let .stickerPack(reference, previewIconFile):
|
||||
let controller = StickerPackScreen(context: params.context, updatedPresentationData: params.updatedPresentationData, mainStickerPack: reference, stickerPacks: [reference], previewIconFile: previewIconFile, parentNavigationController: params.navigationController, sendSticker: params.sendSticker, sendEmoji: params.sendEmoji, actionPerformed: { actions in
|
||||
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
if actions.count > 1, let first = actions.first {
|
||||
|
Loading…
x
Reference in New Issue
Block a user