mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Voice Over Improvements
This commit is contained in:
parent
660234168d
commit
0ac2026d5a
BIN
Telegram/Telegram-iOS/Resources/Channel.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/Channel.tgs
Normal file
Binary file not shown.
@ -6069,6 +6069,8 @@ Sorry for the inconvenience.";
|
||||
"BroadcastGroups.ConfirmationAlert.Convert" = "Convert";
|
||||
|
||||
"BroadcastGroups.LimitAlert.Title" = "Limit Reached";
|
||||
"BroadcastGroups.LimitAlert.Text" = "Your group has reached a limit of **%@** members. You can increase this limit by converting the group to a **broadcast group** where only admins can post. Interested?";
|
||||
"BroadcastGroups.LimitAlert.Text" = "Your group has reached a limit of **%@** members.\n\nYou can increase this limit by converting the group to a **broadcast group** where only admins can post. Interested?";
|
||||
"BroadcastGroups.LimitAlert.LearnMore" = "Learn More";
|
||||
"BroadcastGroups.LimitAlert.SettingsTip" = "If you change your mind, go to the settings of your group.";
|
||||
|
||||
"VoiceOver.AuthSessions.CurrentSession" = "Current Session";
|
||||
|
@ -78,6 +78,8 @@ private final class ItemNode: ASDisplayNode {
|
||||
private var deleteButtonNode: ItemNodeDeleteButtonNode?
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
private var selectionFraction: CGFloat = 0.0
|
||||
private(set) var unreadCount: Int = 0
|
||||
|
||||
@ -136,6 +138,8 @@ private final class ItemNode: ASDisplayNode {
|
||||
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.extractedContainerNode.contentNode.addSubnode(self.extractedBackgroundNode)
|
||||
@ -154,6 +158,8 @@ private final class ItemNode: ASDisplayNode {
|
||||
self.containerNode.addSubnode(self.extractedContainerNode)
|
||||
self.containerNode.targetNodeForActivationProgress = self.extractedContainerNode.contentNode
|
||||
self.addSubnode(self.containerNode)
|
||||
|
||||
self.addSubnode(self.activateArea)
|
||||
|
||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
@ -184,7 +190,7 @@ private final class ItemNode: ASDisplayNode {
|
||||
self.pressed()
|
||||
}
|
||||
|
||||
func updateText(title: String, shortTitle: String, unreadCount: Int, unreadHasUnmuted: Bool, isNoFilter: Bool, selectionFraction: CGFloat, isEditing: Bool, isAllChats: Bool, isReordering: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
||||
func updateText(strings: PresentationStrings, title: String, shortTitle: String, unreadCount: Int, unreadHasUnmuted: Bool, isNoFilter: Bool, selectionFraction: CGFloat, isEditing: Bool, isAllChats: Bool, isReordering: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
||||
if self.theme !== presentationData.theme {
|
||||
self.theme = presentationData.theme
|
||||
|
||||
@ -192,6 +198,13 @@ private final class ItemNode: ASDisplayNode {
|
||||
self.badgeBackgroundInactiveNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.chatList.unreadBadgeInactiveBackgroundColor)
|
||||
}
|
||||
|
||||
self.activateArea.accessibilityLabel = title
|
||||
if unreadCount > 0 {
|
||||
self.activateArea.accessibilityValue = strings.VoiceOver_Chat_UnreadMessages(Int32(unreadCount))
|
||||
} else {
|
||||
self.activateArea.accessibilityValue = ""
|
||||
}
|
||||
|
||||
self.containerNode.isGestureEnabled = !isEditing && !isReordering
|
||||
self.buttonNode.isUserInteractionEnabled = !isEditing && !isReordering
|
||||
|
||||
@ -308,11 +321,12 @@ private final class ItemNode: ASDisplayNode {
|
||||
transition.updateAlpha(node: self.shortTitleContainer, alpha: useShortTitle ? 1.0 : 0.0)
|
||||
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(x: -sideInset, y: 0.0), size: CGSize(width: size.width + sideInset * 2.0, height: size.height))
|
||||
|
||||
|
||||
self.extractedContainerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.extractedContainerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.extractedContainerNode.contentRect = CGRect(origin: CGPoint(x: self.extractedBackgroundNode.frame.minX, y: 0.0), size: CGSize(width:self.extractedBackgroundNode.frame.width, height: size.height))
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.activateArea.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
self.hitTestSlop = UIEdgeInsets(top: 0.0, left: -sideInset, bottom: 0.0, right: -sideInset)
|
||||
self.extractedContainerNode.hitTestSlop = self.hitTestSlop
|
||||
@ -738,7 +752,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
||||
selectionFraction = 0.0
|
||||
}
|
||||
|
||||
itemNode.updateText(title: filter.title(strings: presentationData.strings), shortTitle: filter.shortTitle(strings: presentationData.strings), unreadCount: unreadCount, unreadHasUnmuted: unreadHasUnmuted, isNoFilter: isNoFilter, selectionFraction: selectionFraction, isEditing: false, isAllChats: isNoFilter, isReordering: isEditing || isReordering, presentationData: presentationData, transition: itemNodeTransition)
|
||||
itemNode.updateText(strings: presentationData.strings, title: filter.title(strings: presentationData.strings), shortTitle: filter.shortTitle(strings: presentationData.strings), unreadCount: unreadCount, unreadHasUnmuted: unreadHasUnmuted, isNoFilter: isNoFilter, selectionFraction: selectionFraction, isEditing: false, isAllChats: isNoFilter, isReordering: isEditing || isReordering, presentationData: presentationData, transition: itemNodeTransition)
|
||||
}
|
||||
var removeKeys: [ChatListFilterTabEntryId] = []
|
||||
for (id, _) in self.itemNodes {
|
||||
|
@ -4024,7 +4024,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
}
|
||||
}
|
||||
|
||||
public func ensureItemNodeVisible(_ node: ListViewItemNode, animated: Bool = true, overflow: CGFloat = 0.0, curve: ListViewAnimationCurve = .Default(duration: 0.25)) {
|
||||
public func ensureItemNodeVisible(_ node: ListViewItemNode, animated: Bool = true, overflow: CGFloat = 0.0, allowIntersection: Bool = false, curve: ListViewAnimationCurve = .Default(duration: 0.25)) {
|
||||
if let index = node.index {
|
||||
if node.apparentHeight > self.visibleSize.height - self.insets.top - self.insets.bottom {
|
||||
if node.frame.maxY > self.visibleSize.height - self.insets.bottom {
|
||||
@ -4037,9 +4037,13 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: ListViewScrollToItem(index: index, position: ListViewScrollPosition.visible, animated: animated, curve: ListViewAnimationCurve.Default(duration: nil), directionHint: ListViewScrollToItemDirectionHint.Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
} else {
|
||||
if node.frame.minY < self.insets.top {
|
||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: ListViewScrollToItem(index: index, position: ListViewScrollPosition.top(overflow), animated: animated, curve: curve, directionHint: ListViewScrollToItemDirectionHint.Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
if !allowIntersection || node.frame.maxY < self.insets.top {
|
||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: ListViewScrollToItem(index: index, position: ListViewScrollPosition.top(overflow), animated: animated, curve: curve, directionHint: ListViewScrollToItemDirectionHint.Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
}
|
||||
} else if node.frame.maxY > self.visibleSize.height - self.insets.bottom {
|
||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: ListViewScrollToItem(index: index, position: ListViewScrollPosition.bottom(-overflow), animated: animated, curve: curve, directionHint: ListViewScrollToItemDirectionHint.Down), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
if !allowIntersection || node.frame.minY > self.visibleSize.height - self.insets.bottom {
|
||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: ListViewScrollToItem(index: index, position: ListViewScrollPosition.bottom(-overflow), animated: animated, curve: curve, directionHint: ListViewScrollToItemDirectionHint.Down), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -542,7 +542,7 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
|
||||
}
|
||||
|
||||
override open func accessibilityElementDidBecomeFocused() {
|
||||
(self.supernode as? ListView)?.ensureItemNodeVisible(self, animated: false, overflow: 22.0)
|
||||
(self.supernode as? ListView)?.ensureItemNodeVisible(self, animated: false, overflow: 22.0, allowIntersection: true)
|
||||
}
|
||||
|
||||
public func updateFrame(_ frame: CGRect, within containerSize: CGSize) {
|
||||
|
@ -150,8 +150,6 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
|
||||
strongSelf.activateArea.accessibilityLabel = attributedText.string
|
||||
|
||||
strongSelf.activateArea.accessibilityLabel = attributedText.string
|
||||
|
||||
let _ = titleApply()
|
||||
|
||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: topInset), size: titleLayout.size)
|
||||
|
@ -113,6 +113,8 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
||||
private let locationNode: TextNode
|
||||
private let labelNode: TextNode
|
||||
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
private var layoutParams: (ItemListRecentSessionItem, ListViewItemLayoutParams, ItemListNeighbors)?
|
||||
|
||||
private var editableControlNode: ItemListEditableControlNode?
|
||||
@ -152,12 +154,16 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.isLayerBacked = true
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.appNode)
|
||||
self.addSubnode(self.locationNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
|
||||
self.addSubnode(self.activateArea)
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: ItemListRecentSessionItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
||||
@ -268,6 +274,33 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode {
|
||||
if let strongSelf = self {
|
||||
strongSelf.layoutParams = (item, params, neighbors)
|
||||
|
||||
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
|
||||
|
||||
var label = ""
|
||||
if item.session.isCurrent {
|
||||
label = item.presentationData.strings.VoiceOver_AuthSessions_CurrentSession
|
||||
label += ", "
|
||||
}
|
||||
label += titleAttributedString?.string ?? ""
|
||||
strongSelf.activateArea.accessibilityLabel = label
|
||||
|
||||
var value = ""
|
||||
if let string = appAttributedString?.string {
|
||||
value += string
|
||||
}
|
||||
if let string = locationAttributedString?.string {
|
||||
if !value.isEmpty {
|
||||
value += "\n"
|
||||
}
|
||||
value += string
|
||||
}
|
||||
|
||||
if item.enabled {
|
||||
strongSelf.activateArea.accessibilityTraits = []
|
||||
} else {
|
||||
strongSelf.activateArea.accessibilityTraits = .notEnabled
|
||||
}
|
||||
|
||||
if let _ = updatedTheme {
|
||||
strongSelf.topStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
@ -30,7 +30,6 @@ public func channelAdmins(account: Account, peerId: PeerId) -> Signal<[RenderedC
|
||||
if let peer = peers[participant.peerId] {
|
||||
items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presences: presences))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> [RenderedChannelParticipant] in
|
||||
|
@ -119,9 +119,9 @@ public final class PermissionContentNode: ASDisplayNode {
|
||||
secondaryText = true
|
||||
}
|
||||
|
||||
self.textNode.textAlignment = secondaryText ? .natural : .center
|
||||
self.textNode.textAlignment = secondaryButtonTitle != nil ? .natural : .center
|
||||
|
||||
let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: secondaryText ? theme.list.itemSecondaryTextColor : theme.list.itemPrimaryTextColor)
|
||||
let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: secondaryButtonTitle != nil ? theme.list.itemSecondaryTextColor : theme.list.itemPrimaryTextColor)
|
||||
let link = MarkdownAttributeSet(font: Font.regular(16.0), textColor: theme.list.itemAccentColor, additionalAttributes: [TelegramTextAttributes.URL: ""])
|
||||
self.textNode.attributedText = parseMarkdownIntoAttributedString(text.replacingOccurrences(of: "]", with: "]()"), attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: secondaryText ? .natural : .center)
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -94,6 +94,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
|
||||
let labelNode: TextNode
|
||||
let backgroundNode: ASImageNode
|
||||
let stickBackgroundNode: ASImageNode
|
||||
let activateArea: AccessibilityAreaNode
|
||||
|
||||
private let localTimestamp: Int32
|
||||
private var presentationData: ChatPresentationData
|
||||
@ -157,6 +158,9 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
|
||||
}
|
||||
self.text = text
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
self.activateArea.accessibilityTraits = .staticText
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: true, isRotated: true, seeThrough: false)
|
||||
|
||||
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
|
||||
@ -170,11 +174,15 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
|
||||
self.addSubnode(self.activateArea)
|
||||
|
||||
let titleFont = Font.medium(min(18.0, floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0)))
|
||||
|
||||
let attributedString = NSAttributedString(string: text, font: titleFont, textColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: presentationData.theme.wallpaper))
|
||||
let labelLayout = TextNode.asyncLayout(self.labelNode)
|
||||
|
||||
self.activateArea.accessibilityLabel = text
|
||||
|
||||
let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let _ = apply()
|
||||
self.labelNode.frame = CGRect(origin: CGPoint(), size: size.size)
|
||||
@ -230,6 +238,8 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
|
||||
self.stickBackgroundNode.frame = CGRect(origin: CGPoint(), size: backgroundFrame.size)
|
||||
self.backgroundNode.frame = backgroundFrame
|
||||
self.labelNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + chatDateInset, y: backgroundFrame.origin.y + floorToScreenPixels((backgroundSize.height - labelSize.height) / 2.0)), size: labelSize)
|
||||
|
||||
self.activateArea.frame = backgroundFrame
|
||||
}
|
||||
|
||||
override func updateStickDistanceFactor(_ factor: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
|
@ -59,6 +59,8 @@ class ChatUnreadItemNode: ListViewItemNode {
|
||||
let backgroundNode: ASImageNode
|
||||
let labelNode: TextNode
|
||||
|
||||
let activateArea: AccessibilityAreaNode
|
||||
|
||||
private var theme: ChatPresentationThemeData?
|
||||
|
||||
private let layoutConstants = ChatMessageItemLayoutConstants.default
|
||||
@ -71,12 +73,17 @@ class ChatUnreadItemNode: ListViewItemNode {
|
||||
self.labelNode = TextNode()
|
||||
self.labelNode.isUserInteractionEnabled = false
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
self.activateArea.accessibilityTraits = .staticText
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: true, rotated: true)
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
self.addSubnode(self.labelNode)
|
||||
|
||||
self.addSubnode(self.activateArea)
|
||||
|
||||
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
|
||||
|
||||
self.scrollPositioningInsets = UIEdgeInsets(top: 5.0, left: 0.0, bottom: 6.0, right: 0.0)
|
||||
@ -114,7 +121,8 @@ class ChatUnreadItemNode: ListViewItemNode {
|
||||
updatedBackgroundImage = PresentationResourcesChat.chatUnreadBarBackgroundImage(item.presentationData.theme.theme)
|
||||
}
|
||||
|
||||
let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Conversation_UnreadMessages, font: titleFont, textColor: item.presentationData.theme.theme.chat.serviceMessage.unreadBarTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let string = item.presentationData.strings.Conversation_UnreadMessages
|
||||
let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: string, font: titleFont, textColor: item.presentationData.theme.theme.chat.serviceMessage.unreadBarTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let backgroundSize = CGSize(width: params.width, height: 25.0)
|
||||
|
||||
@ -129,6 +137,9 @@ class ChatUnreadItemNode: ListViewItemNode {
|
||||
|
||||
let _ = apply()
|
||||
|
||||
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: backgroundSize)
|
||||
strongSelf.activateArea.accessibilityLabel = string
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backgroundSize)
|
||||
strongSelf.labelNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - size.size.width) / 2.0), y: floorToScreenPixels((backgroundSize.height - size.size.height) / 2.0)), size: size.size)
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ public class ComposeControllerImpl: ViewController, ComposeController {
|
||||
if let strongSelf = self {
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = PermissionController(context: strongSelf.context, splashScreen: true)
|
||||
controller.setState(.custom(icon: .animation("Channels"), title: presentationData.strings.ChannelIntro_Title, subtitle: nil, text: presentationData.strings.ChannelIntro_Text, buttonTitle: presentationData.strings.ChannelIntro_CreateChannel, secondaryButtonTitle: nil, footerText: nil), animated: false)
|
||||
controller.setState(.custom(icon: .animation("Channel"), title: presentationData.strings.ChannelIntro_Title, subtitle: nil, text: presentationData.strings.ChannelIntro_Text, buttonTitle: presentationData.strings.ChannelIntro_CreateChannel, secondaryButtonTitle: nil, footerText: nil), animated: false)
|
||||
controller.proceed = { [weak self] result in
|
||||
if let strongSelf = self {
|
||||
(strongSelf.navigationController as? NavigationController)?.replaceTopController(createChannelController(context: strongSelf.context), animated: true)
|
||||
|
Loading…
x
Reference in New Issue
Block a user