Various improvements

This commit is contained in:
Isaac 2025-03-13 00:34:59 +01:00
parent 950c3d9f1e
commit bb015f2bfa
16 changed files with 671 additions and 210 deletions

View File

@ -116,13 +116,14 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
if case let .search(search) = source {
switch search {
case .recentPeers:
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromRecents, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
break
/*items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromRecents, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
let _ = (context.engine.peers.removeRecentPeer(peerId: peerId)
|> deliverOnMainQueue).startStandalone(completed: {
f(.default)
})
})))
items.append(.separator)
items.append(.separator)*/
case .recentSearch:
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromRecents, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
let _ = (context.engine.peers.removeRecentlySearchedPeer(peerId: peerId)
@ -535,6 +536,23 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
}
f(.default)
})))
} else if case let .search(search) = source {
switch search {
case .recentPeers, .search:
if peerGroup != nil {
if !items.isEmpty {
items.append(.separator)
}
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
if let chatListController = chatListController {
chatListController.deletePeerChat(peerId: peerId, joined: joined)
}
f(.default)
})))
}
default:
break
}
}
}

View File

@ -544,6 +544,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
}
private struct ChatListFilterPresetControllerState: Equatable {
var isExisting: Bool
var name: ChatFolderTitle
var changedName: Bool
var nameInputMode: ListComposePollOptionComponent.InputMode = .keyboard
@ -564,6 +565,10 @@ private struct ChatListFilterPresetControllerState: Equatable {
return false
}
if self.isExisting {
return true
}
let defaultCategories: ChatListFilterPeerCategories = .all
let defaultExcludeArchived = true
let defaultExcludeMuted = false
@ -1388,6 +1393,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi
initialName = ChatFolderTitle(text: "", entities: [], enableAnimations: true)
}
var initialState = ChatListFilterPresetControllerState(
isExisting: initialPreset?.id != nil,
name: initialName,
changedName: initialPreset != nil,
color: initialPreset?.data?.color,

View File

@ -2048,11 +2048,31 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
return result
}
let updatedLocalPeers = context.engine.contacts.searchLocalPeers(query: query.lowercased())
|> mapToSignal { peers -> Signal<[EngineRenderedPeer], NoError> in
return context.engine.data.subscribe(
EngineDataMap(peers.map { peer in
return TelegramEngine.EngineData.Item.Messages.ChatListIndex(id: peer.peerId)
})
)
|> map { chatListIndices -> [EngineRenderedPeer] in
return peers.filter { peer in
if peer.peerId.namespace == Namespaces.Peer.CloudUser || peer.peerId.namespace == Namespaces.Peer.SecretChat {
return true
}
if let maybeIndex = chatListIndices[peer.peerId], maybeIndex != nil {
return true
}
return false
}
}
}
foundLocalPeers = combineLatest(
context.engine.contacts.searchLocalPeers(query: query.lowercased()),
updatedLocalPeers,
fixedOrRemovedRecentlySearchedPeers
)
|> mapToSignal { local, allRecentlySearched -> Signal<([EnginePeer.Id: Optional<EnginePeer.NotificationSettings>], [EnginePeer.Id: Int], [EngineRenderedPeer], Set<EnginePeer.Id>, EngineGlobalNotificationSettings), NoError> in
|> mapToSignal { local, allRecentlySearched -> Signal<([EnginePeer.Id: Optional<EnginePeer.NotificationSettings>], [EnginePeer.Id: TelegramEngine.EngineData.Item.Messages.PeerUnreadState.Result], [EngineRenderedPeer], Set<EnginePeer.Id>, EngineGlobalNotificationSettings), NoError> in
let recentlySearched = allRecentlySearched.filter { peer in
guard let peer = peer.peer.peer else {
return false
@ -2083,8 +2103,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}
),
EngineDataMap(
peerIds.map { peerId -> TelegramEngine.EngineData.Item.Messages.PeerUnreadCount in
return TelegramEngine.EngineData.Item.Messages.PeerUnreadCount(id: peerId)
peerIds.map { peerId -> TelegramEngine.EngineData.Item.Messages.PeerUnreadState in
return TelegramEngine.EngineData.Item.Messages.PeerUnreadState(id: peerId)
}
),
TelegramEngine.EngineData.Item.NotificationSettings.Global()
@ -2118,8 +2138,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}
}
let unreadCount = unreadCounts[peer.peerId]
if let unreadCount = unreadCount, unreadCount > 0 {
unread[peer.peerId] = (Int32(unreadCount), isMuted)
if let unreadCount = unreadCount, (unreadCount.count > 0 || unreadCount.isMarkedUnread) {
unread[peer.peerId] = (Int32(unreadCount.count), isMuted)
}
}
return (peers: peers, unread: unread, recentlySearchedPeerIds: recentlySearchedPeerIds)

View File

@ -955,6 +955,33 @@ public struct ComponentTransition {
}
}
public func setShadowPath(layer: CALayer, path: CGPath, completion: ((Bool) -> Void)? = nil) {
switch self.animation {
case .none:
layer.shadowPath = path
completion?(true)
case let .curve(duration, curve):
if let previousPath = layer.shadowPath, previousPath != path {
layer.animate(
from: previousPath,
to: path,
keyPath: "shadowPath",
duration: duration,
delay: 0.0,
curve: curve,
removeOnCompletion: true,
additive: false,
completion: completion
)
layer.shadowPath = path
} else {
layer.shadowPath = path
completion?(true)
}
}
}
public func setShapeLayerPath(layer: CAShapeLayer, path: CGPath, completion: ((Bool) -> Void)? = nil) {
switch self.animation {
case .none:

View File

@ -37,7 +37,7 @@ public final class BlurredBackgroundComponent: Component {
private var vibrancyEffectView: UIVisualEffectView?
public func update(component: BlurredBackgroundComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize {
self.updateColor(color: component.color, transition: transition.containedViewLayoutTransition)
self.updateColor(color: component.color, forceKeepBlur: true, transition: transition.containedViewLayoutTransition)
self.update(size: availableSize, cornerRadius: component.cornerRadius, transition: transition.containedViewLayoutTransition)

View File

@ -208,6 +208,20 @@ public final class UnreadMessageCountsView: PostboxView {
}
return nil
}
public func countOrUnread(for item: UnreadMessageCountsItem) -> (Int32, Bool)? {
for entry in self.entries {
switch entry {
case .total, .totalInGroup:
break
case let .peer(peerId, state):
if case .peer(peerId, _) = item {
return (state?.count ?? 0, state?.markedUnread ?? false)
}
}
}
return nil
}
}
final class MutableCombinedReadStateView: MutablePostboxView {

View File

@ -116,6 +116,7 @@ swift_library(
"//submodules/TelegramUI/Components/Stories/PeerListItemComponent",
"//submodules/TelegramUI/Components/BackButtonComponent",
"//submodules/TelegramUI/Components/AlertComponent",
"//submodules/Components/BlurredBackgroundComponent",
"//submodules/DirectMediaImageCache",
"//submodules/FastBlur",
],

View File

@ -0,0 +1,377 @@
import Foundation
import UIKit
import Display
import ComponentFlow
import MultilineTextComponent
import BalancedTextComponent
import TelegramPresentationData
final class VideoChatEncryptionKeyComponent: Component {
let theme: PresentationTheme
let strings: PresentationStrings
let emoji: [String]
let isExpanded: Bool
let tapAction: () -> Void
init(
theme: PresentationTheme,
strings: PresentationStrings,
emoji: [String],
isExpanded: Bool,
tapAction: @escaping () -> Void
) {
self.theme = theme
self.strings = strings
self.emoji = emoji
self.isExpanded = isExpanded
self.tapAction = tapAction
}
static func ==(lhs: VideoChatEncryptionKeyComponent, rhs: VideoChatEncryptionKeyComponent) -> Bool {
if lhs.theme !== rhs.theme {
return false
}
if lhs.strings !== rhs.strings {
return false
}
if lhs.emoji != rhs.emoji {
return false
}
if lhs.isExpanded != rhs.isExpanded {
return false
}
return true
}
final class View: UIView {
private let containerView: UIView
private var emojiItems: [ComponentView<Empty>] = []
private let background = ComponentView<Empty>()
private let backgroundShadowLayer = SimpleLayer()
private let collapsedText = ComponentView<Empty>()
private let expandedText = ComponentView<Empty>()
private let expandedSeparatorLayer = SimpleLayer()
private let expandedButtonText = ComponentView<Empty>()
private var component: VideoChatEncryptionKeyComponent?
private var isUpdating: Bool = false
private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer?
override init(frame: CGRect) {
self.containerView = UIView()
self.containerView.clipsToBounds = true
super.init(frame: frame)
self.addSubview(self.containerView)
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
self.addGestureRecognizer(tapRecognizer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func tapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
guard let component = self.component else {
return
}
if case .ended = recognizer.state {
component.tapAction()
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.containerView.frame.contains(point) {
return self
} else {
return nil
}
}
func update(component: VideoChatEncryptionKeyComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
self.isUpdating = true
defer {
self.isUpdating = false
}
self.component = component
let alphaTransition: ComponentTransition
if transition.animation.isImmediate {
alphaTransition = .immediate
} else {
alphaTransition = .easeInOut(duration: 0.25)
}
let collapsedSideInset: CGFloat = 7.0
let collapsedVerticalInset: CGFloat = 8.0
let collapsedEmojiSpacing: CGFloat = 0.0
let collapsedEmojiTextSpacing: CGFloat = 5.0
let expandedTopInset: CGFloat = 8.0
let expandedSideInset: CGFloat = 14.0
var expandedEmojiSpacing: CGFloat = 10.0
let expandedEmojiTextSpacing: CGFloat = 10.0
let expandedTextButtonSpacing: CGFloat = 10.0
let expandedButtonTopInset: CGFloat = 12.0
let expandedButtonBottomInset: CGFloat = 13.0
let emojiItemSizes = (0 ..< component.emoji.count).map { i -> CGSize in
let emojiItem: ComponentView<Empty>
if self.emojiItems.count > i {
emojiItem = self.emojiItems[i]
} else {
emojiItem = ComponentView()
self.emojiItems.append(emojiItem)
}
return emojiItem.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: component.emoji[i], font: Font.regular(40.0), textColor: .white))
)),
environment: {},
containerSize: CGSize(width: 200.0, height: 200.0)
)
}
//TODO:localize
let collapsedTextSize = self.collapsedText.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: "End-to-end encrypted", font: Font.semibold(12.0), textColor: component.theme.list.itemPrimaryTextColor))
)),
environment: {},
containerSize: CGSize(width: 1000.0, height: 1000.0)
)
let expandedTextSize = self.expandedText.update(
transition: .immediate,
component: AnyComponent(BalancedTextComponent(
text: .plain(NSAttributedString(string: "These four emojis represent the call's encryption key. They must match for all participants and change when someone joins or leaves.", font: Font.regular(12.0), textColor: component.theme.list.itemPrimaryTextColor)),
maximumNumberOfLines: 0,
lineSpacing: 0.3
)),
environment: {},
containerSize: CGSize(width: availableSize.width - expandedSideInset * 2.0, height: 1000.0)
)
let expandedButtonTextSize = self.expandedButtonText.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: "Close", font: Font.regular(17.0), textColor: component.theme.list.itemPrimaryTextColor))
)),
environment: {},
containerSize: CGSize(width: availableSize.width - expandedSideInset * 2.0, height: 1000.0)
)
let collapsedEmojiFactor: CGFloat = 20.0 / 40.0
let collapsedEmojiSizes = emojiItemSizes.map { CGSize(width: floor($0.width * collapsedEmojiFactor), height: floor($0.height * collapsedEmojiFactor)) }
let collapsedSize = CGSize(width: collapsedTextSize.width + collapsedSideInset * 2.0 + collapsedEmojiTextSpacing * 2.0 + collapsedEmojiSpacing + collapsedEmojiSizes.reduce(into: 0.0, { $0 += $1.width }), height: collapsedTextSize.height + collapsedVerticalInset * 2.0)
var expandedEmojiWidth = expandedEmojiSpacing * CGFloat(self.emojiItems.count - 1) + emojiItemSizes.reduce(into: 0.0, { $0 += $1.width })
var expandedSize = CGSize(width: expandedSideInset * 2.0, height: 0.0)
expandedSize.width += max(expandedTextSize.width, expandedEmojiWidth)
expandedSize.height += expandedTopInset
expandedSize.height += emojiItemSizes[0].height
expandedSize.height += expandedEmojiTextSpacing
expandedSize.height += expandedTextSize.height
expandedSize.height += expandedTextButtonSpacing
expandedSize.height += expandedButtonTopInset
expandedSize.height += expandedButtonTextSize.height
expandedSize.height += expandedButtonBottomInset
if expandedEmojiWidth < expandedSize.width - expandedSideInset * 2.0 {
expandedEmojiWidth = expandedSize.width - expandedSideInset * 2.0
let cleanEmojiWidth = emojiItemSizes.reduce(into: 0.0, { $0 += $1.width })
expandedEmojiSpacing = floorToScreenPixels((expandedEmojiWidth - cleanEmojiWidth) / CGFloat(self.emojiItems.count - 1))
expandedEmojiSpacing = min(expandedEmojiSpacing, 24.0)
expandedEmojiWidth = expandedEmojiSpacing * CGFloat(self.emojiItems.count - 1) + emojiItemSizes.reduce(into: 0.0, { $0 += $1.width })
}
let backgroundSize = component.isExpanded ? expandedSize : collapsedSize
let backgroundCornerRadius: CGFloat = component.isExpanded ? 10.0 : collapsedSize.height * 0.5
let _ = self.background.update(
transition: transition,
component: AnyComponent(FilledRoundedRectangleComponent(
color: component.theme.list.itemBlocksBackgroundColor,
cornerRadius: .value(backgroundCornerRadius), smoothCorners: false
)),
environment: {},
containerSize: backgroundSize
)
let backgroundFrame = CGRect(origin: CGPoint(), size: backgroundSize)
if self.backgroundShadowLayer.superlayer == nil {
self.backgroundShadowLayer.backgroundColor = UIColor.clear.cgColor
self.containerView.layer.addSublayer(self.backgroundShadowLayer)
}
self.backgroundShadowLayer.shadowOpacity = 0.3
self.backgroundShadowLayer.shadowColor = UIColor.black.cgColor
self.backgroundShadowLayer.shadowRadius = 5.0
self.backgroundShadowLayer.shadowOffset = CGSize(width: 0.0, height: 2.0)
alphaTransition.setAlpha(layer: self.backgroundShadowLayer, alpha: component.isExpanded ? 1.0 : 0.0)
transition.setFrame(layer: self.backgroundShadowLayer, frame: backgroundFrame)
transition.setCornerRadius(layer: self.backgroundShadowLayer, cornerRadius: backgroundCornerRadius)
transition.setShadowPath(layer: self.backgroundShadowLayer, path: UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: backgroundFrame.size), cornerRadius: backgroundCornerRadius).cgPath)
if let backgroundView = self.background.view {
if backgroundView.superview == nil {
self.containerView.addSubview(backgroundView)
}
transition.setFrame(view: backgroundView, frame: backgroundFrame)
}
var collapsedEmojiLeftOffset = collapsedSideInset
var collapsedEmojiRightOffset = collapsedSize.width - collapsedSideInset
for i in 0 ..< self.emojiItems.count {
let mappedIndex: Int
if i < 2 {
mappedIndex = i
} else {
mappedIndex = self.emojiItems.count - (i - 1)
}
var collapsedItemFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((collapsedSize.height - collapsedEmojiSizes[mappedIndex].height) * 0.5)), size: collapsedEmojiSizes[mappedIndex])
if i < 2 {
if mappedIndex != 0 {
collapsedEmojiLeftOffset += collapsedEmojiSpacing
}
collapsedItemFrame.origin.x = collapsedEmojiLeftOffset
collapsedEmojiLeftOffset += collapsedEmojiSizes[mappedIndex].width
} else {
if mappedIndex != 0 {
collapsedEmojiRightOffset -= collapsedEmojiSpacing
}
collapsedItemFrame.origin.x = collapsedEmojiRightOffset - collapsedEmojiSizes[mappedIndex].width
collapsedEmojiRightOffset -= collapsedEmojiSizes[mappedIndex].width
}
var expandedItemFrame = CGRect(origin: CGPoint(x: floor((backgroundFrame.width - expandedEmojiWidth) * 0.5), y: expandedTopInset), size: emojiItemSizes[mappedIndex])
expandedItemFrame.origin.x += CGFloat(mappedIndex) * (emojiItemSizes[0].width + expandedEmojiSpacing)
let itemFrame: CGRect
if component.isExpanded {
itemFrame = expandedItemFrame
} else {
itemFrame = collapsedItemFrame
}
if let itemView = self.emojiItems[mappedIndex].view {
if itemView.superview == nil {
self.containerView.addSubview(itemView)
}
transition.setPosition(view: itemView, position: itemFrame.center)
itemView.bounds = CGRect(origin: CGPoint(), size: emojiItemSizes[mappedIndex])
transition.setScale(view: itemView, scale: itemFrame.height / emojiItemSizes[mappedIndex].height)
}
}
var collapsedTextFrame = CGRect(origin: CGPoint(x: collapsedEmojiLeftOffset + collapsedEmojiTextSpacing, y: floor((collapsedSize.height - collapsedTextSize.height) * 0.5)), size: collapsedTextSize)
var expandedTextFrame = CGRect(origin: CGPoint(x: floor((backgroundSize.width - expandedTextSize.width) * 0.5), y: expandedTopInset + emojiItemSizes[0].height + expandedEmojiTextSpacing), size: expandedTextSize)
if component.isExpanded {
collapsedTextFrame.origin = expandedTextFrame.origin
} else {
expandedTextFrame.origin = collapsedTextFrame.origin
}
if let collapsedTextView = self.collapsedText.view {
if collapsedTextView.superview == nil {
collapsedTextView.layer.anchorPoint = CGPoint()
self.containerView.addSubview(collapsedTextView)
} else {
if collapsedTextView.alpha != (component.isExpanded ? 0.0 : 1.0) {
if let blurFilter = CALayer.blur() {
let maxBlur: CGFloat = 8.0
if !component.isExpanded {
blurFilter.setValue(0.0 as NSNumber, forKey: "inputRadius")
collapsedTextView.layer.filters = [blurFilter]
collapsedTextView.layer.animate(from: maxBlur as NSNumber, to: 0.0 as NSNumber, keyPath: "filters.gaussianBlur.inputRadius", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, removeOnCompletion: false, completion: { [weak collapsedTextView] flag in
if flag, let collapsedTextView {
collapsedTextView.layer.filters = nil
}
})
} else {
blurFilter.setValue(maxBlur as NSNumber, forKey: "inputRadius")
collapsedTextView.layer.filters = [blurFilter]
collapsedTextView.layer.animate(from: 0.0 as NSNumber, to: maxBlur as NSNumber, keyPath: "filters.gaussianBlur.inputRadius", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, removeOnCompletion: true)
}
}
}
}
transition.setPosition(view: collapsedTextView, position: collapsedTextFrame.origin)
collapsedTextView.bounds = CGRect(origin: CGPoint(), size: collapsedTextFrame.size)
alphaTransition.setAlpha(view: collapsedTextView, alpha: component.isExpanded ? 0.0 : 1.0)
}
if let expandedTextView = self.expandedText.view {
if expandedTextView.superview == nil {
expandedTextView.layer.anchorPoint = CGPoint()
self.containerView.addSubview(expandedTextView)
} else {
if expandedTextView.alpha != (component.isExpanded ? 1.0 : 0.0) {
if let blurFilter = CALayer.blur() {
let maxBlur: CGFloat = 8.0
if component.isExpanded {
blurFilter.setValue(0.0 as NSNumber, forKey: "inputRadius")
expandedTextView.layer.filters = [blurFilter]
expandedTextView.layer.animate(from: maxBlur as NSNumber, to: 0.0 as NSNumber, keyPath: "filters.gaussianBlur.inputRadius", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, removeOnCompletion: false, completion: { [weak expandedTextView] flag in
if flag, let expandedTextView {
expandedTextView.layer.filters = nil
}
})
} else {
blurFilter.setValue(maxBlur as NSNumber, forKey: "inputRadius")
expandedTextView.layer.filters = [blurFilter]
expandedTextView.layer.animate(from: 0.0 as NSNumber, to: maxBlur as NSNumber, keyPath: "filters.gaussianBlur.inputRadius", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, removeOnCompletion: true)
}
}
}
}
transition.setPosition(view: expandedTextView, position: expandedTextFrame.origin)
expandedTextView.bounds = CGRect(origin: CGPoint(), size: expandedTextFrame.size)
alphaTransition.setAlpha(view: expandedTextView, alpha: component.isExpanded ? 1.0 : 0.0)
}
let expandedButtonOffset: CGFloat = component.isExpanded ? 0.0 : (expandedButtonBottomInset + expandedButtonTextSize.height + expandedButtonTopInset)
if self.expandedSeparatorLayer.superlayer == nil {
self.containerView.layer.addSublayer(self.expandedSeparatorLayer)
}
self.expandedSeparatorLayer.backgroundColor = component.theme.list.itemBlocksSeparatorColor.cgColor
transition.setFrame(layer: self.expandedSeparatorLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: backgroundSize.height - expandedButtonBottomInset - expandedButtonTextSize.height - expandedButtonTopInset + expandedButtonOffset), size: CGSize(width: backgroundSize.width, height: UIScreenPixel)))
alphaTransition.setAlpha(layer: self.expandedSeparatorLayer, alpha: component.isExpanded ? 1.0 : 0.0)
if let expandedButtonTextView = self.expandedButtonText.view {
if expandedButtonTextView.superview == nil {
self.containerView.addSubview(expandedButtonTextView)
}
transition.setFrame(view: expandedButtonTextView, frame: CGRect(origin: CGPoint(x: floor((backgroundSize.width - expandedButtonTextSize.width) * 0.5), y: backgroundSize.height - expandedButtonBottomInset - expandedButtonTextSize.height + expandedButtonOffset), size: expandedButtonTextSize))
alphaTransition.setAlpha(view: expandedButtonTextView, alpha: component.isExpanded ? 1.0 : 0.0)
}
transition.setFrame(view: self.containerView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backgroundSize))
return CGSize(width: backgroundSize.width, height: collapsedSize.height)
}
}
func makeView() -> View {
return View()
}
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View File

@ -23,6 +23,7 @@ import AvatarNode
import TelegramAudio
import LegacyComponents
import TooltipUI
import BlurredBackgroundComponent
extension VideoChatCall {
var myAudioLevelAndSpeaking: Signal<(Float, Bool), NoError> {
@ -219,6 +220,9 @@ final class VideoChatScreenComponent: Component {
let navigationLeftButton = ComponentView<Empty>()
let navigationRightButton = ComponentView<Empty>()
var navigationSidebarButton: ComponentView<Empty>?
var encryptionKeyBackground: ComponentView<Empty>?
var encryptionKey: ComponentView<Empty>?
var isEncryptionKeyExpanded: Bool = false
let videoButton = ComponentView<Empty>()
let leaveButton = ComponentView<Empty>()
@ -406,6 +410,12 @@ final class VideoChatScreenComponent: Component {
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let encryptionKeyBackgroundView = self.encryptionKeyBackground?.view, let _ = encryptionKeyBackgroundView.hitTest(self.convert(point, to: encryptionKeyBackgroundView), with: event) {
if let encryptionKeyView = self.encryptionKey?.view {
return encryptionKeyView
}
}
guard let result = super.hitTest(point, with: event) else {
return nil
}
@ -1744,7 +1754,7 @@ final class VideoChatScreenComponent: Component {
let topInset: CGFloat = environment.statusBarHeight + 2.0
let navigationBarHeight: CGFloat = 61.0
let navigationHeight = topInset + navigationBarHeight
var navigationHeight = topInset + navigationBarHeight
let navigationButtonAreaWidth: CGFloat = 40.0
let navigationButtonDiameter: CGFloat = 28.0
@ -1950,6 +1960,52 @@ final class VideoChatScreenComponent: Component {
alphaTransition.setAlpha(view: titleView, alpha: self.isAnimatedOutFromPrivateCall ? 0.0 : 1.0)
}
var encryptionKeyFrame: CGRect?
if component.initialCall.accountContext.sharedContext.immediateExperimentalUISettings.conferenceDebug {
navigationHeight -= 2.0
let encryptionKey: ComponentView<Empty>
var encryptionKeyTransition = transition
if let current = self.encryptionKey {
encryptionKey = current
} else {
encryptionKeyTransition = encryptionKeyTransition.withAnimation(.none)
encryptionKey = ComponentView()
self.encryptionKey = encryptionKey
}
let encryptionKeySize = encryptionKey.update(
transition: encryptionKeyTransition,
component: AnyComponent(VideoChatEncryptionKeyComponent(
theme: environment.theme,
strings: environment.strings,
emoji: ["👌", "🧡", "🌹", "🤷"],
isExpanded: self.isEncryptionKeyExpanded,
tapAction: { [weak self] in
guard let self else {
return
}
self.isEncryptionKeyExpanded = !self.isEncryptionKeyExpanded
if !self.isUpdating {
self.state?.updated(transition: .spring(duration: 0.4))
}
}
)),
environment: {},
containerSize: CGSize(width: min(400.0, availableSize.width - sideInset * 2.0 - 16.0 * 2.0), height: 10000.0)
)
let encryptionKeyFrameValue = CGRect(origin: CGPoint(x: floor((availableSize.width - encryptionKeySize.width) * 0.5), y: navigationHeight), size: encryptionKeySize)
encryptionKeyFrame = encryptionKeyFrameValue
navigationHeight += encryptionKeySize.height
navigationHeight += 16.0
} else if let encryptionKey = self.encryptionKey {
self.encryptionKey = nil
encryptionKey.view?.removeFromSuperview()
self.encryptionKeyBackground?.view?.removeFromSuperview()
self.encryptionKeyBackground = nil
}
let areButtonsCollapsed: Bool
let mainColumnWidth: CGFloat
let mainColumnSideInset: CGFloat
@ -2226,6 +2282,51 @@ final class VideoChatScreenComponent: Component {
alphaTransition.setAlpha(view: participantsView, alpha: participantsAlpha)
}
if let encryptionKeyView = self.encryptionKey?.view, let encryptionKeyFrame {
if encryptionKeyView.superview == nil {
self.containerView.addSubview(encryptionKeyView)
}
transition.setFrame(view: encryptionKeyView, frame: encryptionKeyFrame)
alphaTransition.setAlpha(view: encryptionKeyView, alpha: self.isAnimatedOutFromPrivateCall ? 0.0 : 1.0)
if self.isEncryptionKeyExpanded {
let encryptionKeyBackground: ComponentView<Empty>
var encryptionKeyBackgroundTransition = transition
if let current = self.encryptionKeyBackground {
encryptionKeyBackground = current
} else {
encryptionKeyBackgroundTransition = encryptionKeyBackgroundTransition.withAnimation(.none)
encryptionKeyBackground = ComponentView()
self.encryptionKeyBackground = encryptionKeyBackground
}
let _ = encryptionKeyBackground.update(
transition: encryptionKeyBackgroundTransition,
component: AnyComponent(BlurredBackgroundComponent(
color: .clear,
tintContainerView: nil,
cornerRadius: 0.0
)),
environment: {},
containerSize: availableSize
)
if let encryptionKeyBackgroundView = encryptionKeyBackground.view {
if encryptionKeyBackgroundView.superview == nil {
self.containerView.insertSubview(encryptionKeyBackgroundView, belowSubview: encryptionKeyView)
encryptionKeyBackgroundView.alpha = 0.0
}
alphaTransition.setAlpha(view: encryptionKeyBackgroundView, alpha: 1.0)
encryptionKeyBackgroundTransition.setFrame(view: encryptionKeyBackgroundView, frame: CGRect(origin: CGPoint(), size: availableSize))
}
} else if let encryptionKeyBackground = self.encryptionKeyBackground {
self.encryptionKeyBackground = nil
if let encryptionKeyBackgroundView = encryptionKeyBackground.view {
alphaTransition.setAlpha(view: encryptionKeyBackgroundView, alpha: 0.0, completion: { [weak encryptionKeyBackgroundView] _ in
encryptionKeyBackgroundView?.removeFromSuperview()
})
}
}
}
if let callState = self.callState, let scheduleTimestamp = callState.scheduleTimestamp {
let scheduleInfo: ComponentView<Empty>
var scheduleInfoTransition = transition

View File

@ -226,6 +226,42 @@ public extension TelegramEngine.EngineData.Item {
return Int(view.count(for: .peer(id: self.id, handleThreads: true)) ?? 0)
}
}
public struct PeerUnreadState: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public struct Result: Equatable {
public var count: Int
public var isMarkedUnread: Bool
public init(count: Int, isMarkedUnread: Bool) {
self.count = count
self.isMarkedUnread = isMarkedUnread
}
}
fileprivate let id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
var key: PostboxViewKey {
return .unreadCounts(items: [.peer(id: self.id, handleThreads: true)])
}
public init(id: EnginePeer.Id) {
self.id = id
}
func extract(view: PostboxView) -> Result {
guard let view = view as? UnreadMessageCountsView else {
preconditionFailure()
}
if let (value, isUnread) = view.countOrUnread(for: .peer(id: self.id, handleThreads: true)) {
return Result(count: Int(value), isMarkedUnread: isUnread)
}
return Result(count: 0, isMarkedUnread: false)
}
}
public struct TotalReadCounters: TelegramEngineDataItem, PostboxViewDataItem {
public typealias Result = EngineTotalReadCounters

View File

@ -17,7 +17,7 @@ public enum CreateChannelMode {
case supergroup(isForum: Bool)
}
private func createChannel(postbox: Postbox, network: Network, stateManager: AccountStateManager, title: String, description: String?, username: String?, mode: CreateChannelMode, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false) -> Signal<PeerId, CreateChannelError> {
private func createChannel(postbox: Postbox, network: Network, stateManager: AccountStateManager, title: String, description: String?, username: String?, mode: CreateChannelMode, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false, ttlPeriod: Int32?) -> Signal<PeerId, CreateChannelError> {
return postbox.transaction { transaction -> Signal<PeerId, CreateChannelError> in
var flags: Int32 = 0
switch mode {
@ -43,7 +43,11 @@ private func createChannel(postbox: Postbox, network: Network, stateManager: Acc
transaction.clearItemCacheCollection(collectionId: Namespaces.CachedItemCollection.cachedGroupCallDisplayAsPeers)
return network.request(Api.functions.channels.createChannel(flags: flags, title: title, about: description ?? "", geoPoint: geoPoint, address: address, ttlPeriod: nil), automaticFloodWait: false)
if ttlPeriod != nil {
flags |= (1 << 4)
}
return network.request(Api.functions.channels.createChannel(flags: flags, title: title, about: description ?? "", geoPoint: geoPoint, address: address, ttlPeriod: ttlPeriod), automaticFloodWait: false)
|> mapError { error -> CreateChannelError in
if error.errorCode == 406 {
return .serverProvided(error.errorDescription)
@ -91,11 +95,11 @@ private func createChannel(postbox: Postbox, network: Network, stateManager: Acc
}
func _internal_createChannel(account: Account, title: String, description: String?, username: String?) -> Signal<PeerId, CreateChannelError> {
return createChannel(postbox: account.postbox, network: account.network, stateManager: account.stateManager, title: title, description: description, username: nil, mode: .channel)
return createChannel(postbox: account.postbox, network: account.network, stateManager: account.stateManager, title: title, description: description, username: nil, mode: .channel, ttlPeriod: nil)
}
public func _internal_createSupergroup(postbox: Postbox, network: Network, stateManager: AccountStateManager, title: String, description: String?, username: String?, isForum: Bool, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false) -> Signal<PeerId, CreateChannelError> {
return createChannel(postbox: postbox, network: network, stateManager: stateManager, title: title, description: description, username: username, mode: .supergroup(isForum: isForum), location: location, isForHistoryImport: isForHistoryImport)
public func _internal_createSupergroup(postbox: Postbox, network: Network, stateManager: AccountStateManager, title: String, description: String?, username: String?, isForum: Bool, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false, ttlPeriod: Int32? = nil) -> Signal<PeerId, CreateChannelError> {
return createChannel(postbox: postbox, network: network, stateManager: stateManager, title: title, description: description, username: username, mode: .supergroup(isForum: isForum), location: location, isForHistoryImport: isForHistoryImport, ttlPeriod: ttlPeriod)
}
public enum DeleteChannelError {

View File

@ -466,8 +466,8 @@ public extension TelegramEngine {
return _internal_createChannel(account: self.account, title: title, description: description, username: username)
}
public func createSupergroup(title: String, description: String?, username: String? = nil, isForum: Bool = false, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false) -> Signal<PeerId, CreateChannelError> {
return _internal_createSupergroup(postbox: self.account.postbox, network: self.account.network, stateManager: account.stateManager, title: title, description: description, username: username, isForum: isForum, location: location, isForHistoryImport: isForHistoryImport)
public func createSupergroup(title: String, description: String?, username: String? = nil, isForum: Bool = false, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false, ttlPeriod: Int32? = nil) -> Signal<PeerId, CreateChannelError> {
return _internal_createSupergroup(postbox: self.account.postbox, network: self.account.network, stateManager: account.stateManager, title: title, description: description, username: username, isForum: isForum, location: location, isForHistoryImport: isForHistoryImport, ttlPeriod: ttlPeriod)
}
public func deleteChannel(peerId: PeerId) -> Signal<Void, DeleteChannelError> {

View File

@ -467,8 +467,9 @@ public final class MessageInputPanelComponent: Component {
private let gradientView: UIImageView
private let bottomGradientView: UIView
private let placeholder = ComponentView<Empty>()
private let vibrancyPlaceholder = ComponentView<Empty>()
private var currentPlaceholderType: Bool?
private var placeholder = ComponentView<Empty>()
private var vibrancyPlaceholder = ComponentView<Empty>()
private let counter = ComponentView<Empty>()
private var header: ComponentView<Empty>?
@ -868,7 +869,17 @@ public final class MessageInputPanelComponent: Component {
let placeholderTransition: ComponentTransition = (previousPlaceholder != nil && previousPlaceholder != component.placeholder) ? ComponentTransition(animation: .curve(duration: 0.3, curve: .spring)) : .immediate
let placeholderSize: CGSize
if case let .plain(string) = component.placeholder, string.contains("#") {
let placeholderType = false
if let currentPlaceholderType = self.currentPlaceholderType, currentPlaceholderType != placeholderType {
self.placeholder.view?.removeFromSuperview()
self.placeholder = ComponentView()
self.vibrancyPlaceholder.view?.removeFromSuperview()
self.vibrancyPlaceholder = ComponentView()
}
let attributedPlaceholder = NSMutableAttributedString(string: string, font:Font.regular(17.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.4))
if let range = attributedPlaceholder.string.range(of: "#") {
attributedPlaceholder.addAttribute(.attachment, value: PresentationResourcesChat.chatPlaceholderStarIcon(component.theme)!, range: NSRange(range, in: attributedPlaceholder.string))
@ -897,6 +908,15 @@ public final class MessageInputPanelComponent: Component {
containerSize: availableTextFieldSize
)
} else {
let placeholderType = true
if let currentPlaceholderType = self.currentPlaceholderType, currentPlaceholderType != placeholderType {
self.placeholder.view?.removeFromSuperview()
self.placeholder = ComponentView()
self.vibrancyPlaceholder.view?.removeFromSuperview()
self.vibrancyPlaceholder = ComponentView()
}
var placeholderItems: [AnimatedTextComponent.Item] = []
switch component.placeholder {
case let .plain(string):

View File

@ -155,6 +155,8 @@ swift_library(
"//submodules/TelegramUI/Components/CameraScreen",
"//submodules/TelegramUI/Components/PeerInfo/VerifyAlertController",
"//submodules/TelegramUI/Components/Gifts/GiftViewScreen",
"//submodules/TelegramUI/Components/BatchVideoRendering",
"//submodules/TelegramUI/Components/GifVideoLayer",
],
visibility = [
"//visibility:public",

View File

@ -17,106 +17,8 @@ import SoftwareVideo
import ChatControllerInteraction
import PeerInfoVisualMediaPaneNode
import PeerInfoPaneNode
private final class FrameSequenceThumbnailNode: ASDisplayNode {
private let context: AccountContext
private let file: FileMediaReference
private let imageNode: ASImageNode
private var isPlaying: Bool = false
private var isPlayingInternal: Bool = false
private var frames: [Int: UIImage] = [:]
private var frameTimes: [Double] = []
private var sources: [UniversalSoftwareVideoSource] = []
private var disposables: [Int: Disposable] = [:]
private var currentFrameIndex: Int = 0
private var timer: SwiftSignalKit.Timer?
init(
context: AccountContext,
userLocation: MediaResourceUserLocation,
file: FileMediaReference
) {
self.context = context
self.file = file
self.imageNode = ASImageNode()
self.imageNode.isUserInteractionEnabled = false
self.imageNode.contentMode = .scaleAspectFill
self.imageNode.clipsToBounds = true
if let duration = file.media.duration {
let frameCount = 5
let frameInterval: Double = Double(duration) / Double(frameCount)
for i in 0 ..< frameCount {
self.frameTimes.append(Double(i) * frameInterval)
}
}
super.init()
self.addSubnode(self.imageNode)
for i in 0 ..< self.frameTimes.count {
let framePts = self.frameTimes[i]
let index = i
let source = UniversalSoftwareVideoSource(
mediaBox: self.context.account.postbox.mediaBox,
source: .file(
userLocation: userLocation,
userContentType: .other,
fileReference: self.file
),
automaticallyFetchHeader: true
)
self.sources.append(source)
self.disposables[index] = (source.takeFrame(at: framePts)
|> deliverOnMainQueue).start(next: { [weak self] result in
guard let strongSelf = self else {
return
}
if case let .image(image) = result {
if let image = image {
strongSelf.frames[index] = image
}
}
})
}
}
deinit {
for (_, disposable) in self.disposables {
disposable.dispose()
}
self.timer?.invalidate()
}
func updateIsPlaying(_ isPlaying: Bool) {
if self.isPlaying == isPlaying {
return
}
self.isPlaying = isPlaying
}
func updateLayout(size: CGSize) {
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
}
func tick() {
let isPlayingInternal = self.isPlaying && self.frames.count == self.frameTimes.count
if isPlayingInternal {
self.currentFrameIndex = (self.currentFrameIndex + 1) % self.frames.count
if self.currentFrameIndex < self.frames.count {
self.imageNode.image = self.frames[self.currentFrameIndex]
}
}
}
}
import BatchVideoRendering
import GifVideoLayer
private let mediaBadgeBackgroundColor = UIColor(white: 0.0, alpha: 0.6)
private let mediaBadgeTextColor = UIColor.white
@ -142,14 +44,10 @@ private final class VisualMediaItemInteraction {
private final class VisualMediaItemNode: ASDisplayNode {
private let context: AccountContext
private let videoContext: BatchVideoRenderingContext
private let interaction: VisualMediaItemInteraction
private var videoLayerFrameManager: SoftwareVideoLayerFrameManager?
private var sampleBufferLayer: SampleBufferLayer?
private var displayLink: ConstantDisplayLinkAnimator?
private var displayLinkTimestamp: Double = 0.0
private var frameSequenceThumbnailNode: FrameSequenceThumbnailNode?
private var gifVideoLayer: GifVideoLayer?
private let containerNode: ContextControllerSourceNode
private let imageNode: TransformImageNode
@ -166,9 +64,10 @@ private final class VisualMediaItemNode: ASDisplayNode {
private var hasVisibility: Bool = false
init(context: AccountContext, interaction: VisualMediaItemInteraction) {
init(context: AccountContext, interaction: VisualMediaItemInteraction, videoContext: BatchVideoRenderingContext) {
self.context = context
self.interaction = interaction
self.videoContext = videoContext
self.containerNode = ContextControllerSourceNode()
self.imageNode = TransformImageNode()
@ -295,25 +194,16 @@ private final class VisualMediaItemNode: ASDisplayNode {
}
if let file = media as? TelegramMediaFile, file.isAnimated, self.context.sharedContext.energyUsageSettings.autoplayGif {
if self.videoLayerFrameManager == nil {
let sampleBufferLayer: SampleBufferLayer
if let current = self.sampleBufferLayer {
sampleBufferLayer = current
} else {
sampleBufferLayer = takeSampleBufferLayer()
self.sampleBufferLayer = sampleBufferLayer
self.imageNode.layer.addSublayer(sampleBufferLayer.layer)
}
self.videoLayerFrameManager = SoftwareVideoLayerFrameManager(account: self.context.account, userLocation: .peer(item.message.id.peerId), userContentType: .other, fileReference: FileMediaReference.message(message: MessageReference(item.message), media: file), layerHolder: sampleBufferLayer)
self.videoLayerFrameManager?.start()
if self.gifVideoLayer == nil {
let gifVideoLayer = GifVideoLayer(context: self.context, batchVideoContext: self.videoContext, userLocation: .peer(item.message.id.peerId), file: FileMediaReference.message(message: MessageReference(item.message), media: file), synchronousLoad: false)
self.gifVideoLayer = gifVideoLayer
self.imageNode.layer.addSublayer(gifVideoLayer)
}
} else {
if let sampleBufferLayer = self.sampleBufferLayer {
sampleBufferLayer.layer.removeFromSuperlayer()
self.sampleBufferLayer = nil
if let gifVideoLayer = self.gifVideoLayer {
gifVideoLayer.removeFromSuperlayer()
self.gifVideoLayer = nil
}
self.videoLayerFrameManager = nil
}
if let media = media, (self.item?.1 == nil || !media.isEqual(to: self.item!.1!)) {
@ -427,8 +317,8 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.containerNode.frame = imageFrame
self.imageNode.frame = imageFrame
if let sampleBufferLayer = self.sampleBufferLayer {
sampleBufferLayer.layer.frame = imageFrame
if let gifVideoLayer = self.gifVideoLayer {
gifVideoLayer.frame = imageFrame
}
if let mediaDimensions = mediaDimensions {
@ -442,54 +332,9 @@ private final class VisualMediaItemNode: ASDisplayNode {
func updateIsVisible(_ isVisible: Bool) {
self.hasVisibility = isVisible
if let _ = self.videoLayerFrameManager {
let displayLink: ConstantDisplayLinkAnimator
if let current = self.displayLink {
displayLink = current
} else {
displayLink = ConstantDisplayLinkAnimator { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.videoLayerFrameManager?.tick(timestamp: strongSelf.displayLinkTimestamp)
strongSelf.displayLinkTimestamp += 1.0 / 30.0
}
displayLink.frameInterval = 2
self.displayLink = displayLink
}
if let gifVideoLayer = self.gifVideoLayer {
gifVideoLayer.shouldBeAnimating = self.hasVisibility && !self.isHidden
}
self.displayLink?.isPaused = !self.hasVisibility || self.isHidden
/*if isVisible {
if let item = self.item?.0, let file = self.item?.1 as? TelegramMediaFile, !file.isAnimated {
if self.frameSequenceThumbnailNode == nil {
let frameSequenceThumbnailNode = FrameSequenceThumbnailNode(context: context, file: .message(message: MessageReference(item.message), media: file))
self.frameSequenceThumbnailNode = frameSequenceThumbnailNode
self.imageNode.addSubnode(frameSequenceThumbnailNode)
}
if let frameSequenceThumbnailNode = self.frameSequenceThumbnailNode {
let size = self.bounds.size
frameSequenceThumbnailNode.frame = CGRect(origin: CGPoint(), size: size)
frameSequenceThumbnailNode.updateLayout(size: size)
}
} else {
if let frameSequenceThumbnailNode = self.frameSequenceThumbnailNode {
self.frameSequenceThumbnailNode = nil
frameSequenceThumbnailNode.removeFromSupernode()
}
}
} else {
if let frameSequenceThumbnailNode = self.frameSequenceThumbnailNode {
self.frameSequenceThumbnailNode = nil
frameSequenceThumbnailNode.removeFromSupernode()
}
}*/
self.frameSequenceThumbnailNode?.updateIsPlaying(isVisible)
}
func tick() {
self.frameSequenceThumbnailNode?.tick()
}
func updateSelectionState(animated: Bool) {
@ -566,7 +411,7 @@ private final class VisualMediaItemNode: ASDisplayNode {
} else {
self.isHidden = false
}
self.displayLink?.isPaused = !self.hasVisibility || self.isHidden
self.gifVideoLayer?.shouldBeAnimating = self.hasVisibility && !self.isHidden
}
}
@ -774,6 +619,8 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScrollViewDe
private let chatControllerInteraction: ChatControllerInteraction
private let contentType: ContentType
private let videoContext: BatchVideoRenderingContext
weak var parentController: ViewController?
private let scrollNode: ASScrollNode
@ -806,8 +653,6 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScrollViewDe
private var isFirstHistoryView: Bool = true
private var decelerationAnimator: ConstantDisplayLinkAnimator?
private var animationTimer: SwiftSignalKit.Timer?
private let statusPromise = Promise<PeerInfoStatusData?>(nil)
var status: Signal<PeerInfoStatusData?, NoError> {
@ -831,6 +676,8 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScrollViewDe
self.floatingHeaderNode = FloatingHeaderNode()
self.floatingHeaderNode.alpha = 0.0
self.videoContext = BatchVideoRenderingContext(context: self.context)
super.init()
self._itemInteraction = VisualMediaItemInteraction(
@ -875,17 +722,6 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScrollViewDe
itemNode.updateHiddenMedia()
}
})
let animationTimer = SwiftSignalKit.Timer(timeout: 0.3, repeat: true, completion: { [weak self] in
guard let strongSelf = self else {
return
}
for (_, itemNode) in strongSelf.visibleMediaItems {
itemNode.tick()
}
}, queue: .mainQueue())
self.animationTimer = animationTimer
animationTimer.start()
self.statusPromise.set(context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, threadId: chatLocation.threadId, tag: tagMaskForType(self.contentType))
@ -910,7 +746,6 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScrollViewDe
deinit {
self.listDisposable.dispose()
self.hiddenMediaDisposable?.dispose()
self.animationTimer?.invalidate()
}
func ensureMessageIsVisible(id: MessageId) {
@ -1164,7 +999,7 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScrollViewDe
if let current = self.visibleMediaItems[stableId] {
itemNode = current
} else {
itemNode = VisualMediaItemNode(context: self.context, interaction: self.itemInteraction)
itemNode = VisualMediaItemNode(context: self.context, interaction: self.itemInteraction, videoContext: self.videoContext)
self.visibleMediaItems[stableId] = itemNode
self.scrollNode.addSubnode(itemNode)
}

View File

@ -675,7 +675,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
case .generic:
createSignal = context.engine.peers.createGroup(title: title, peerIds: peerIds, ttlPeriod: ttlPeriod)
case .supergroup:
createSignal = context.engine.peers.createSupergroup(title: title, description: nil)
createSignal = context.engine.peers.createSupergroup(title: title, description: nil, ttlPeriod: ttlPeriod)
|> map { peerId -> CreateGroupResult? in
return CreateGroupResult(peerId: peerId, result: TelegramInvitePeersResult(forbiddenPeers: []))
}
@ -730,7 +730,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
}
let createGroupSignal: (Bool) -> Signal<CreateGroupResult?, CreateGroupError> = { isForum in
return context.engine.peers.createSupergroup(title: title, description: nil, isForum: isForum)
return context.engine.peers.createSupergroup(title: title, description: nil, isForum: isForum, ttlPeriod: ttlPeriod)
|> map { peerId -> CreateGroupResult? in
return CreateGroupResult(peerId: peerId, result: TelegramInvitePeersResult(forbiddenPeers: []))
}
@ -767,7 +767,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
} else if isForum || group.userAdminRights != nil {
createSignal = createGroupSignal(isForum)
} else {
createSignal = context.engine.peers.createGroup(title: title, peerIds: peerIds, ttlPeriod: nil)
createSignal = context.engine.peers.createGroup(title: title, peerIds: peerIds, ttlPeriod: ttlPeriod)
}
if group.userAdminRights?.rights.contains(.canBeAnonymous) == true {