Various fixes

This commit is contained in:
Ilya Laktyushin 2021-03-09 12:00:10 +04:00
parent 4a7d5ccb67
commit b4f89a7e6c
53 changed files with 4761 additions and 4566 deletions

View File

@ -6200,6 +6200,7 @@ Sorry for the inconvenience.";
"VoiceChat.EditTitleTitle" = "Voice Chat Title";
"VoiceChat.EditTitleText" = "Edit a title of this voice chat.";
"VoiceChat.EditTitleSuccess" = "Voice chat title changed to **%@**.";
"VoiceChat.EditTitleRemoveSuccess" = "Voice chat title removed.";
"VoiceChat.InviteLink.Speaker" = "Speaker";
"VoiceChat.InviteLink.Listener" = "Listener";
@ -6223,9 +6224,9 @@ Sorry for the inconvenience.";
"Conversation.JoinVoiceChat" = "JOIN VOICE CHAT";
"Conversation.CancelForwardTitle" = "Cancel Forwarding";
"Conversation.CancelForwardText" = "Do you want to cancel this forwarding or would like to send this messages to another chat?";
"Conversation.CancelForwardText" = "Do you want to cancel forwarding or send messages to a different chat?";
"Conversation.CancelForwardCancelForward" = "Cancel Forwarding";
"Conversation.CancelForwardSelectChat" = "Select Chat";
"Conversation.CancelForwardSelectChat" = "Select Another Chat";
"StickerPacks.ActionDelete" = "Delete";
"StickerPacks.ActionArchive" = "Archive";

View File

@ -9,6 +9,8 @@ public final class ContextControllerSourceNode: ASDisplayNode {
self.contextGesture?.isEnabled = self.isGestureEnabled
}
}
public var animateScale: Bool = true
public var activated: ((ContextGesture, CGPoint) -> Void)?
public var shouldBegin: ((CGPoint) -> Bool)?
public var customActivationProgress: ((CGFloat, ContextGestureTransition) -> Void)?
@ -41,7 +43,7 @@ public final class ContextControllerSourceNode: ASDisplayNode {
}
if let customActivationProgress = strongSelf.customActivationProgress {
customActivationProgress(progress, update)
} else {
} else if strongSelf.animateScale {
let targetNode: ASDisplayNode
let targetContentRect: CGRect
if let targetNodeForActivationProgress = strongSelf.targetNodeForActivationProgress {

View File

@ -129,8 +129,8 @@ private enum InviteLinkInviteEntry: Comparable, Identifiable {
if let invite = invite {
interaction.shareLink(invite)
}
}, contextAction: { node in
interaction.mainLinkContextAction(invite, node, nil)
}, contextAction: { node, gesture in
interaction.mainLinkContextAction(invite, node, gesture)
}, viewAction: {
})
case let .manage(theme, text, standalone):

View File

@ -224,8 +224,8 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
if let invite = invite {
arguments.shareMainLink(invite)
}
}, contextAction: { node in
arguments.mainLinkContextAction(invite, node, nil)
}, contextAction: { node, gesture in
arguments.mainLinkContextAction(invite, node, gesture)
}, viewAction: {
if let invite = invite {
arguments.openLink(invite)

View File

@ -165,8 +165,8 @@ private enum InviteLinkViewEntry: Comparable, Identifiable {
} else {
interaction.shareLink(invite)
}
}, contextAction: { node in
interaction.contextAction(invite, node, nil)
}, contextAction: { node, gesture in
interaction.contextAction(invite, node, gesture)
}, viewAction: {
})
case let .creatorHeader(_, title):

View File

@ -39,7 +39,7 @@ public class ItemListPermanentInviteLinkItem: ListViewItem, ItemListItem {
let style: ItemListStyle
let copyAction: (() -> Void)?
let shareAction: (() -> Void)?
let contextAction: ((ASDisplayNode) -> Void)?
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
let viewAction: (() -> Void)?
public let tag: ItemListItemTag?
@ -56,7 +56,7 @@ public class ItemListPermanentInviteLinkItem: ListViewItem, ItemListItem {
style: ItemListStyle,
copyAction: (() -> Void)?,
shareAction: (() -> Void)?,
contextAction: ((ASDisplayNode) -> Void)?,
contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?,
viewAction: (() -> Void)?,
tag: ItemListItemTag? = nil
) {
@ -174,7 +174,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
self.addressButtonNode = HighlightTrackingButtonNode()
self.referenceContainerNode = ContextReferenceContentNode()
self.containerNode = ContextControllerSourceNode()
self.containerNode.isGestureEnabled = false
self.containerNode.animateScale = false
self.addressButtonIconNode = ASImageNode()
self.addressButtonIconNode.contentMode = .center
self.addressButtonIconNode.displaysAsynchronously = false
@ -203,6 +203,12 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
self.addSubnode(self.activateArea)
self.containerNode.activated = { [weak self] gesture, _ in
if let strongSelf = self, let item = strongSelf.item {
item.contextAction?(strongSelf.referenceContainerNode, gesture)
}
}
self.fieldButtonNode.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
@ -259,7 +265,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
@objc private func addressButtonPressed() {
if let item = self.item {
item.contextAction?(self.referenceContainerNode)
item.contextAction?(self.referenceContainerNode, nil)
}
}

View File

@ -71,7 +71,7 @@ public enum ItemListStyle {
case blocks
}
public struct ItemListToolbarItem {
open class ItemListToolbarItem {
public struct Action {
public let title: String
public let isEnabled: Bool
@ -90,6 +90,10 @@ public struct ItemListToolbarItem {
self.actions = actions
}
open func isEqual(to: ItemListToolbarItem) -> Bool {
return false
}
var toolbar: Toolbar {
var leftAction: ToolbarAction?
var middleAction: ToolbarAction?
@ -99,14 +103,14 @@ public struct ItemListToolbarItem {
if let action = self.actions.first {
middleAction = ToolbarAction(title: action.title, isEnabled: action.isEnabled)
}
} else if actions.count == 2 {
} else if self.actions.count == 2 {
if let action = self.actions.first {
leftAction = ToolbarAction(title: action.title, isEnabled: action.isEnabled)
}
if let action = self.actions.last {
rightAction = ToolbarAction(title: action.title, isEnabled: action.isEnabled)
}
} else if actions.count == 3 {
} else if self.actions.count == 3 {
leftAction = ToolbarAction(title: self.actions[0].title, isEnabled: self.actions[0].isEnabled)
middleAction = ToolbarAction(title: self.actions[1].title, isEnabled: self.actions[1].isEnabled)
rightAction = ToolbarAction(title: self.actions[2].title, isEnabled: self.actions[2].isEnabled)
@ -243,10 +247,12 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
private var searchItem: ItemListControllerSearch?
private var searchNode: ItemListControllerSearchNode?
private var toolbarItem: ItemListToolbarItem?
private let transitionDisposable = MetaDisposable()
private var enqueuedTransitions: [ItemListNodeTransition] = []
private var validLayout: (ContainerViewLayout, CGFloat)?
private var validLayout: (ContainerViewLayout, CGFloat, UIEdgeInsets)?
private var theme: PresentationTheme?
private var listStyle: ItemListStyle?
@ -463,11 +469,65 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
if let toolbarItem = self.toolbarItem {
var tabBarHeight: CGFloat
let bottomInset: CGFloat = insets.bottom
if !layout.safeInsets.left.isZero {
tabBarHeight = 34.0 + bottomInset
insets.bottom += 34.0
} else {
tabBarHeight = 49.0 + bottomInset
insets.bottom += 49.0
}
let toolbarFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - tabBarHeight), size: CGSize(width: layout.size.width, height: tabBarHeight))
if let toolbarNode = self.toolbarNode {
transition.updateFrame(node: toolbarNode, frame: toolbarFrame)
toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: layout.intrinsicInsets.bottom, toolbar: toolbarItem.toolbar, transition: transition)
} else if let theme = self.theme {
let toolbarNode = ToolbarNode(theme: TabBarControllerTheme(rootControllerTheme: theme), displaySeparator: true)
toolbarNode.frame = toolbarFrame
toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: layout.intrinsicInsets.bottom, toolbar: toolbarItem.toolbar, transition: .immediate)
self.addSubnode(toolbarNode)
self.toolbarNode = toolbarNode
if case let .animated(duration, curve) = transition {
toolbarNode.layer.animatePosition(from: CGPoint(x: 0.0, y: toolbarFrame.height / 2.0), to: CGPoint(), duration: duration, mediaTimingFunction: curve.mediaTimingFunction, additive: true)
}
}
self.toolbarNode?.left = {
toolbarItem.actions[0].action()
}
self.toolbarNode?.right = {
if toolbarItem.actions.count == 2 {
toolbarItem.actions[1].action()
} else if toolbarItem.actions.count == 3 {
toolbarItem.actions[2].action()
}
}
self.toolbarNode?.middle = {
if toolbarItem.actions.count == 1 {
toolbarItem.actions[0].action()
} else if toolbarItem.actions.count == 3 {
toolbarItem.actions[1].action()
}
}
} else if let toolbarNode = self.toolbarNode {
self.toolbarNode = nil
if case let .animated(duration, curve) = transition {
toolbarNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: toolbarNode.frame.size.height / 2.0), duration: duration, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: false, additive: true, completion: { [weak toolbarNode] _ in
toolbarNode?.removeFromSupernode()
})
} else {
toolbarNode.removeFromSupernode()
}
}
self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
self.listNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: curve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
self.leftOverlayNode.frame = CGRect(x: 0.0, y: 0.0, width: insets.left, height: layout.size.height)
@ -487,13 +547,9 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
searchNode.updateLayout(layout: layout, navigationBarHeight: navigationBarHeight, transition: transition)
}
if let toolbarNode = self.toolbarNode {
// toolbarNode.updateLayout(size: layout.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: UIEdgeInsets(), bottomInset: 0.0, toolbar: <#T##Toolbar#>, transition: transition)
}
let dequeue = self.validLayout == nil
self.validLayout = (layout, navigationBarHeight)
self.validLayout = (layout, navigationBarHeight, additionalInsets)
if dequeue {
self.dequeueTransitions()
}
@ -654,60 +710,14 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
return transition.strings.VoiceOver_ScrollStatus(row, count).0
}
let layoutTransition: ContainedViewLayoutTransition = .immediate
if let toolbarItem = transition.toolbarItem, let (layout, _) = self.validLayout {
var options: ContainerViewLayoutInsetOptions = []
if layout.metrics.widthClass == .regular {
options.insert(.input)
}
var tabBarHeight: CGFloat
let bottomInset: CGFloat = layout.insets(options: options).bottom
if !layout.safeInsets.left.isZero {
tabBarHeight = 34.0 + bottomInset
// insets.bottom += 34.0
} else {
tabBarHeight = 49.0 + bottomInset
// insets.bottom += 49.0
}
let toolbarFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - tabBarHeight), size: CGSize(width: layout.size.width, height: tabBarHeight))
if let toolbarNode = self.toolbarNode {
layoutTransition.updateFrame(node: toolbarNode, frame: toolbarFrame)
toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: layout.intrinsicInsets.bottom, toolbar: toolbarItem.toolbar, transition: layoutTransition)
} else {
let toolbarNode = ToolbarNode(theme: TabBarControllerTheme(rootControllerTheme: transition.theme), displaySeparator: true)
toolbarNode.frame = toolbarFrame
toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: layout.intrinsicInsets.bottom, toolbar: toolbarItem.toolbar, transition: .immediate)
self.addSubnode(toolbarNode)
self.toolbarNode = toolbarNode
if transition.animated {
toolbarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
self.toolbarNode?.left = {
toolbarItem.actions[0].action()
}
self.toolbarNode?.right = {
if toolbarItem.actions.count == 2 {
toolbarItem.actions[1].action()
} else if toolbarItem.actions.count == 3 {
toolbarItem.actions[2].action()
}
}
self.toolbarNode?.middle = {
if toolbarItem.actions.count == 1 {
toolbarItem.actions[0].action()
} else if toolbarItem.actions.count == 3 {
toolbarItem.actions[1].action()
}
}
} else if let toolbarNode = self.toolbarNode {
self.toolbarNode = nil
layoutTransition.updateAlpha(node: toolbarNode, alpha: 0.0, completion: { [weak toolbarNode] _ in
toolbarNode?.removeFromSupernode()
})
var updateToolbarItem = false
if let toolbarItem = self.toolbarItem, let updatedToolbarItem = transition.toolbarItem {
updateToolbarItem = !toolbarItem.isEqual(to: updatedToolbarItem)
} else if (self.toolbarItem != nil) != (transition.toolbarItem != nil) {
updateToolbarItem = true
}
if updateToolbarItem {
self.toolbarItem = transition.toolbarItem
}
self.listNode.transaction(deleteIndices: transition.entries.deletions, insertIndicesAndItems: transition.entries.insertions, updateIndicesAndItems: transition.entries.updates, options: options, scrollToItem: scrollToItem, updateOpaqueState: ItemListNodeOpaqueState(mergedEntries: transition.mergedEntries), completion: { [weak self] _ in
@ -799,6 +809,8 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
if updateSearchItem {
self.requestLayout?(.animated(duration: 0.3, curve: .spring))
} else if updateToolbarItem, let (layout, navigationBarHeight, additionalInsets) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .spring), additionalInsets: additionalInsets)
}
}
}

View File

@ -33,10 +33,10 @@ private final class ChannelVisibilityControllerArguments {
let revokePeerId: (PeerId) -> Void
let copyLink: (ExportedInvitation) -> Void
let shareLink: (ExportedInvitation) -> Void
let linkContextAction: (ASDisplayNode) -> Void
let linkContextAction: (ASDisplayNode, ContextGesture?) -> Void
let manageInviteLinks: () -> Void
init(context: AccountContext, updateCurrentType: @escaping (CurrentChannelType) -> Void, updatePublicLinkText: @escaping (String?, String) -> Void, scrollToPublicLinkText: @escaping () -> Void, displayPrivateLinkMenu: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, revokePeerId: @escaping (PeerId) -> Void, copyLink: @escaping (ExportedInvitation) -> Void, shareLink: @escaping (ExportedInvitation) -> Void, linkContextAction: @escaping (ASDisplayNode) -> Void, manageInviteLinks: @escaping () -> Void) {
init(context: AccountContext, updateCurrentType: @escaping (CurrentChannelType) -> Void, updatePublicLinkText: @escaping (String?, String) -> Void, scrollToPublicLinkText: @escaping () -> Void, displayPrivateLinkMenu: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, revokePeerId: @escaping (PeerId) -> Void, copyLink: @escaping (ExportedInvitation) -> Void, shareLink: @escaping (ExportedInvitation) -> Void, linkContextAction: @escaping (ASDisplayNode, ContextGesture?) -> Void, manageInviteLinks: @escaping () -> Void) {
self.context = context
self.updateCurrentType = updateCurrentType
self.updatePublicLinkText = updatePublicLinkText
@ -302,8 +302,8 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
if let invite = invite {
arguments.shareLink(invite)
}
}, contextAction: { node in
arguments.linkContextAction(node)
}, contextAction: { node, gesture in
arguments.linkContextAction(node, gesture)
}, viewAction: {
})
@ -923,7 +923,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
presentControllerImpl?(shareController, nil)
}, linkContextAction: { node in
}, linkContextAction: { node, gesture in
guard let node = node as? ContextExtractedContentContainingNode, let controller = getControllerImpl?() else {
return
}
@ -1048,7 +1048,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
})
})))
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: nil)
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController)
}, manageInviteLinks: {
let controller = inviteLinkListController(context: context, peerId: peerId, admin: nil)

View File

@ -700,7 +700,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
})
let selectedCount = Int32(state.selectedPackIds?.count ?? 0)
toolbarItem = ItemListToolbarItem(actions: [.init(title: presentationData.strings.StickerPacks_ActionDelete, isEnabled: selectedCount > 0, action: {
toolbarItem = StickersToolbarItem(selectedCount: selectedCount, actions: [.init(title: presentationData.strings.StickerPacks_ActionDelete, isEnabled: selectedCount > 0, action: {
let actionSheet = ActionSheetController(presentationData: presentationData)
var items: [ActionSheetItem] = []
items.append(ActionSheetButtonItem(title: presentationData.strings.StickerPacks_DeleteStickerPacksConfirmation(selectedCount), color: .destructive, action: { [weak actionSheet] in
@ -986,3 +986,20 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
return controller
}
private class StickersToolbarItem: ItemListToolbarItem {
private let selectedCount: Int32
init(selectedCount: Int32, actions: [Action]) {
self.selectedCount = selectedCount
super.init(actions: actions)
}
override func isEqual(to: ItemListToolbarItem) -> Bool {
if let other = to as? StickersToolbarItem {
return self.selectedCount == other.selectedCount
} else {
return false
}
}
}

View File

@ -639,8 +639,14 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
}
}
var animatingOut = false
var outCompletion: (() -> Void)?
func animateOut(shared: Bool, completion: @escaping () -> Void) {
guard !self.animatingOut else {
return
}
self.animatingOut = true
if self.contentNode != nil {
var dimCompleted = false
var offsetCompleted = false
@ -648,6 +654,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
let internalCompletion: () -> Void = { [weak self] in
if dimCompleted && offsetCompleted {
if let strongSelf = self {
strongSelf.animatingOut = false
strongSelf.isHidden = true
strongSelf.dimNode.layer.removeAllAnimations()
strongSelf.layer.removeAllAnimations()
@ -669,6 +676,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
internalCompletion()
})
} else {
self.animatingOut = false
self.outCompletion = completion
Queue.mainQueue().after(0.2) {
if let completion = self.outCompletion {

View File

@ -158,23 +158,32 @@ public final class CachedChannelData: CachedPeerData {
public struct ActiveCall: Equatable, PostboxCoding {
public var id: Int64
public var accessHash: Int64
public var title: String?
public init(
id: Int64,
accessHash: Int64
accessHash: Int64,
title: String?
) {
self.id = id
self.accessHash = accessHash
self.title = title
}
public init(decoder: PostboxDecoder) {
self.id = decoder.decodeInt64ForKey("id", orElse: 0)
self.accessHash = decoder.decodeInt64ForKey("accessHash", orElse: 0)
self.title = decoder.decodeOptionalStringForKey("title")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt64(self.id, forKey: "id")
encoder.encodeInt64(self.accessHash, forKey: "accessHash")
if let title = self.title {
encoder.encodeString(title, forKey: "title")
} else {
encoder.encodeNil(forKey: "title")
}
}
}

View File

@ -853,6 +853,6 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
}
open func joinGroupCall(peerId: PeerId, joinAsPeerId: PeerId?, info: GroupCallInfo) {
self.context.joinGroupCall(peerId: peerId, joinAsPeerId: joinAsPeerId, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
self.context.joinGroupCall(peerId: peerId, joinAsPeerId: joinAsPeerId, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title))
}
}

View File

@ -713,7 +713,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
})
if let initialCall = initialCall, let temporaryParticipantsContext = (self.accountContext.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl)?.impl.syncWith({ impl in
impl.get(account: accountContext.account, peerId: peerId, call: CachedChannelData.ActiveCall(id: initialCall.id, accessHash: initialCall.accessHash))
impl.get(account: accountContext.account, peerId: peerId, call: CachedChannelData.ActiveCall(id: initialCall.id, accessHash: initialCall.accessHash, title: initialCall.title))
}) {
if let participantsContext = temporaryParticipantsContext.context.participantsContext, let immediateState = participantsContext.immediateState {
self.switchToTemporaryParticipantsContext(sourceContext: participantsContext, initialState: immediateState, oldMyPeerId: self.joinAsPeerId)
@ -1779,6 +1779,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.checkCallDisposable = nil
self.stateValue.networkState = .connecting
self.stateValue.title = initialCall?.title
self.requestDisposable.set((currentOrRequestedCall
|> deliverOnMainQueue).start(next: { [weak self] value in
@ -1787,7 +1788,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
if let value = value {
strongSelf.initialCall = CachedChannelData.ActiveCall(id: value.id, accessHash: value.accessHash)
strongSelf.initialCall = CachedChannelData.ActiveCall(id: value.id, accessHash: value.accessHash, title: value.title)
strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl)
} else {

View File

@ -0,0 +1,110 @@
import Foundation
import UIKit
import Display
import Postbox
import TelegramCore
import SyncCore
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import AnimatedStickerNode
import AppBundle
public final class VoiceChatAccountHeaderActionSheetItem: ActionSheetItem {
let title: String
let text: String
public init(context: AccountContext, title: String, text: String) {
self.title = title
self.text = text
}
public func node(theme: ActionSheetControllerTheme) -> ActionSheetItemNode {
return VoiceChatAccountHeaderActionSheetItemNode(theme: theme, title: self.title, text: self.text)
}
public func updateNode(_ node: ActionSheetItemNode) {
}
}
private final class VoiceChatAccountHeaderActionSheetItemNode: ActionSheetItemNode {
private let theme: ActionSheetControllerTheme
private let iconBackgroundNode: ASImageNode
private let iconNode: ASImageNode
private let titleNode: ImmediateTextNode
private let textNode: ImmediateTextNode
private let accessibilityArea: AccessibilityAreaNode
init(theme: ActionSheetControllerTheme, title: String, text: String) {
self.theme = theme
let titleFont = Font.medium(floor(theme.baseFontSize * 13.0 / 17.0))
let textFont = Font.regular(floor(theme.baseFontSize * 13.0 / 17.0))
self.iconBackgroundNode = ASImageNode()
self.iconBackgroundNode.displaysAsynchronously = false
self.iconBackgroundNode.displayWithoutProcessing = true
self.iconNode = ASImageNode()
self.iconNode.displaysAsynchronously = false
self.iconNode.displayWithoutProcessing = true
self.titleNode = ImmediateTextNode()
self.titleNode.displaysAsynchronously = false
self.titleNode.maximumNumberOfLines = 0
self.titleNode.textAlignment = .center
self.titleNode.isAccessibilityElement = false
self.textNode = ImmediateTextNode()
self.textNode.displaysAsynchronously = false
self.textNode.maximumNumberOfLines = 0
self.textNode.textAlignment = .center
self.textNode.isAccessibilityElement = false
self.accessibilityArea = AccessibilityAreaNode()
super.init(theme: theme)
self.hasSeparator = false
self.addSubnode(self.backgroundNode)
self.addSubnode(self.iconNode)
self.addSubnode(self.titleNode)
self.addSubnode(self.textNode)
self.addSubnode(self.accessibilityArea)
self.titleNode.attributedText = NSAttributedString(string: title, font: titleFont, textColor: theme.primaryTextColor)
self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: theme.secondaryTextColor)
self.accessibilityArea.accessibilityLabel = text
self.accessibilityArea.accessibilityTraits = .staticText
}
public override func updateLayout(constrainedSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
let titleSize = self.titleNode.updateLayout(CGSize(width: constrainedSize.width - 120.0, height: .greatestFiniteMagnitude))
let textSize = self.textNode.updateLayout(CGSize(width: constrainedSize.width - 120.0, height: .greatestFiniteMagnitude))
let topInset: CGFloat = 26.0
let textSpacing: CGFloat = 17.0
let bottomInset: CGFloat = 15.0
let iconSize = CGSize(width: 72.0, height: 72.0)
let iconFrame = CGRect(origin: CGPoint(x: floor((constrainedSize.width - iconSize.width) / 2.0), y: topInset), size: iconSize)
self.iconBackgroundNode.frame = iconFrame
if let image = self.iconNode.image {
self.iconNode.frame = CGRect(origin: CGPoint(x: iconFrame.minX + floorToScreenPixels((iconSize.width - image.size.width) / 2.0), y: iconFrame.minY + floorToScreenPixels((iconSize.height - image.size.height) / 2.0)), size: image.size)
}
self.titleNode.frame = CGRect(origin: CGPoint(x: floor((constrainedSize.width - titleSize.width) / 2.0), y: topInset + iconSize.height + textSpacing), size: titleSize)
self.textNode.frame = CGRect(origin: CGPoint(x: floor((constrainedSize.width - textSize.width) / 2.0), y: topInset + iconSize.height + textSpacing + titleSize.height), size: textSize)
let size = CGSize(width: constrainedSize.width, height: topInset + iconSize.height + textSpacing + titleSize.height + textSize.height + bottomInset)
self.accessibilityArea.frame = CGRect(origin: CGPoint(), size: size)
self.updateInternalLayout(size, constrainedSize: constrainedSize)
return size
}
}

View File

@ -644,34 +644,51 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
}
}
func updateGlowAndGradientAnimations(active: Bool?, previousActive: Bool? = nil) {
let effectivePreviousActive = previousActive ?? false
enum Gradient {
case speaking
case active
case connecting
case muted
}
func updateGlowAndGradientAnimations(type: Gradient, previousType: Gradient? = nil) {
let effectivePreviousTyoe = previousType ?? .active
let initialScale: CGFloat = ((self.maskGradientLayer.value(forKeyPath: "presentationLayer.transform.scale.x") as? NSNumber)?.floatValue).flatMap({ CGFloat($0) }) ?? (((self.maskGradientLayer.value(forKeyPath: "transform.scale.x") as? NSNumber)?.floatValue).flatMap({ CGFloat($0) }) ?? (effectivePreviousActive ? 0.95 : 0.8))
let scale: CGFloat
if case .speaking = effectivePreviousTyoe {
scale = 0.95
} else {
scale = 0.8
}
let initialScale: CGFloat = ((self.maskGradientLayer.value(forKeyPath: "presentationLayer.transform.scale.x") as? NSNumber)?.floatValue).flatMap({ CGFloat($0) }) ?? (((self.maskGradientLayer.value(forKeyPath: "transform.scale.x") as? NSNumber)?.floatValue).flatMap({ CGFloat($0) }) ?? scale)
let initialColors = self.foregroundGradientLayer.colors
let outerColor: UIColor?
let targetColors: [CGColor]
let targetScale: CGFloat
if let active = active {
if active {
switch type {
case .speaking:
targetColors = [activeBlue.cgColor, green.cgColor, green.cgColor]
targetScale = 0.89
outerColor = UIColor(rgb: 0x21674f)
} else {
case .active:
targetColors = [lightBlue.cgColor, blue.cgColor, blue.cgColor]
targetScale = 0.85
outerColor = UIColor(rgb: 0x1d588d)
}
} else {
targetColors = [lightBlue.cgColor, blue.cgColor, blue.cgColor]
targetScale = 0.3
outerColor = nil
case .connecting:
targetColors = [lightBlue.cgColor, blue.cgColor, blue.cgColor]
targetScale = 0.3
outerColor = nil
case .muted:
targetColors = [pink.cgColor, purple.cgColor, purple.cgColor]
targetScale = 0.85
outerColor = UIColor(rgb: 0x1d588d)
}
self.updatedOuterColor?(outerColor)
self.maskGradientLayer.transform = CATransform3DMakeScale(targetScale, targetScale, 1.0)
if let _ = previousActive {
if let _ = previousType {
self.maskGradientLayer.animateScale(from: initialScale, to: targetScale, duration: 0.3)
} else {
self.maskGradientLayer.animateSpring(from: initialScale as NSNumber, to: targetScale as NSNumber, keyPath: "transform.scale", duration: 0.45)
@ -681,46 +698,23 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
self.foregroundGradientLayer.animate(from: initialColors as AnyObject, to: targetColors as AnyObject, keyPath: "colors", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.3)
}
private func playConnectionDisappearanceAnimation() {
let initialRotation: CGFloat = CGFloat((self.maskProgressLayer.value(forKeyPath: "presentationLayer.transform.rotation.z") as? NSNumber)?.floatValue ?? 0.0)
let initialStrokeEnd: CGFloat = CGFloat((self.maskProgressLayer.value(forKeyPath: "presentationLayer.strokeEnd") as? NSNumber)?.floatValue ?? 1.0)
self.maskProgressLayer.removeAnimation(forKey: "progressGrowth")
self.maskProgressLayer.removeAnimation(forKey: "progressRotation")
let duration: Double = (1.0 - Double(initialStrokeEnd)) * 0.6
let growthAnimation = CABasicAnimation(keyPath: "strokeEnd")
growthAnimation.fromValue = initialStrokeEnd
growthAnimation.toValue = 0.0
growthAnimation.duration = duration
growthAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotateAnimation.fromValue = initialRotation
rotateAnimation.toValue = initialRotation + CGFloat.pi * 2
rotateAnimation.isAdditive = true
rotateAnimation.duration = duration
rotateAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
let groupAnimation = CAAnimationGroup()
groupAnimation.animations = [growthAnimation, rotateAnimation]
groupAnimation.duration = duration
CATransaction.setCompletionBlock {
CATransaction.begin()
CATransaction.setDisableActions(true)
self.maskProgressLayer.isHidden = true
self.maskProgressLayer.removeAllAnimations()
CATransaction.commit()
}
self.maskProgressLayer.add(groupAnimation, forKey: "progressDisappearance")
CATransaction.commit()
private func playMuteAnimation() {
self.maskBlobView.startAnimating()
self.maskBlobView.layer.animateScale(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
if strongSelf.state != .connecting && strongSelf.state != .disabled {
return
}
strongSelf.maskBlobView.isHidden = true
strongSelf.maskBlobView.stopAnimating()
strongSelf.maskBlobView.layer.removeAllAnimations()
})
}
var animatingDisappearance = false
private func playBlobsDisappearanceAnimation() {
private func playDeactivationAnimation() {
if self.animatingDisappearance {
return
}
@ -732,7 +726,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
self.disableGlowAnimations = true
self.maskGradientLayer.removeAllAnimations()
self.updateGlowAndGradientAnimations(active: nil, previousActive: nil)
self.updateGlowAndGradientAnimations(type: .connecting, previousType: nil)
self.maskBlobView.startAnimating()
self.maskBlobView.layer.animateScale(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak self] _ in
@ -757,15 +751,15 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
CATransaction.setCompletionBlock {
self.animatingDisappearance = false
self.growingForegroundCircleLayer.isHidden = true
self.disableGlowAnimations = false
if self.state != .connecting && self.state != .disabled {
return
}
CATransaction.begin()
CATransaction.setDisableActions(true)
self.disableGlowAnimations = false
self.maskGradientLayer.isHidden = true
self.maskCircleLayer.isHidden = true
self.growingForegroundCircleLayer.isHidden = true
self.growingForegroundCircleLayer.removeAllAnimations()
CATransaction.commit()
}
@ -773,8 +767,8 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
self.growingForegroundCircleLayer.add(growthAnimation, forKey: "insideGrowth")
CATransaction.commit()
}
private func playBlobsAppearanceAnimation(active: Bool) {
private func playActivationAnimation(active: Bool) {
CATransaction.begin()
CATransaction.setDisableActions(true)
self.foregroundCircleLayer.isHidden = false
@ -785,7 +779,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
self.disableGlowAnimations = true
self.maskGradientLayer.removeAllAnimations()
self.updateGlowAndGradientAnimations(active: active, previousActive: nil)
self.updateGlowAndGradientAnimations(type: active ? .speaking : .active, previousType: nil)
self.maskBlobView.isHidden = false
self.maskBlobView.startAnimating()
@ -812,7 +806,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
CATransaction.commit()
}
private func playConnectionAnimation(active: Bool, completion: @escaping () -> Void) {
private func playConnectionAnimation(type: Gradient, completion: @escaping () -> Void) {
CATransaction.begin()
let initialRotation: CGFloat = CGFloat((self.maskProgressLayer.value(forKeyPath: "presentationLayer.transform.rotation.z") as? NSNumber)?.floatValue ?? 0.0)
let initialStrokeEnd: CGFloat = CGFloat((self.maskProgressLayer.value(forKeyPath: "presentationLayer.strokeEnd") as? NSNumber)?.floatValue ?? 1.0)
@ -840,7 +834,11 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
groupAnimation.duration = duration
CATransaction.setCompletionBlock {
if case .blob = self.state {
var active = true
if case .connecting = self.state {
active = false
}
if active {
CATransaction.begin()
CATransaction.setDisableActions(true)
self.foregroundCircleLayer.isHidden = false
@ -851,11 +849,13 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
completion()
self.updateGlowAndGradientAnimations(active: active, previousActive: nil)
self.updateGlowAndGradientAnimations(type: type, previousType: nil)
self.maskBlobView.isHidden = false
self.maskBlobView.startAnimating()
self.maskBlobView.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.45)
if case .blob = self.state {
self.maskBlobView.isHidden = false
self.maskBlobView.startAnimating()
self.maskBlobView.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.45)
}
self.updatedActive?(true)
@ -867,12 +867,10 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
shrinkAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
CATransaction.setCompletionBlock {
if case .blob = self.state {
CATransaction.begin()
CATransaction.setDisableActions(true)
self.foregroundCircleLayer.isHidden = true
CATransaction.commit()
}
CATransaction.begin()
CATransaction.setDisableActions(true)
self.foregroundCircleLayer.isHidden = true
CATransaction.commit()
}
self.foregroundCircleLayer.add(shrinkAnimation, forKey: "insideShrink")
@ -901,7 +899,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
if let transition = self.transition {
self.updateGlowScale(nil)
if case .blob = transition {
playBlobsDisappearanceAnimation()
self.playDeactivationAnimation()
}
self.transition = nil
}
@ -909,17 +907,18 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
self.isActive = false
case let .blob(newActive):
if let transition = self.transition {
let type: Gradient = newActive ? .speaking : .active
if transition == .connecting {
self.playConnectionAnimation(active: newActive) { [weak self] in
self.playConnectionAnimation(type: type) { [weak self] in
self?.isActive = newActive
}
} else if transition == .disabled {
self.playBlobsAppearanceAnimation(active: newActive)
self.playActivationAnimation(active: newActive)
self.transition = nil
self.isActive = newActive
self.updatedActive?(true)
} else if case let .blob(previousActive) = transition {
updateGlowAndGradientAnimations(active: newActive, previousActive: previousActive)
self.updateGlowAndGradientAnimations(type: type, previousType: previousActive ? .speaking : .active)
self.transition = nil
self.isActive = newActive
}
@ -930,13 +929,15 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
case .disabled:
self.updatedActive?(true)
self.isActive = false
self.updateGlowScale(nil)
if let transition = self.transition {
if case .connecting = transition {
playConnectionDisappearanceAnimation()
} else if case .blob = transition {
playBlobsDisappearanceAnimation()
self.playConnectionAnimation(type: .muted) { [weak self] in
self?.isActive = false
}
} else if case let .blob(previousActive) = transition {
self.updateGlowAndGradientAnimations(type: .muted, previousType: previousActive ? .speaking : .active)
self.playMuteAnimation()
}
self.transition = nil
}

View File

@ -552,7 +552,7 @@ public final class VoiceChatController: ViewController {
icon = .invite(true)
case .raisedHand:
text = .text(presentationData.strings.VoiceChat_StatusWantsToSpeak, .accent)
icon = .invite(true)
icon = .wantsToSpeak
}
let revealOptions: [VoiceChatParticipantItem.RevealOption] = []
@ -598,7 +598,6 @@ public final class VoiceChatController: ViewController {
private let topPanelNode: ASDisplayNode
private let topPanelEdgeNode: ASDisplayNode
private let topPanelBackgroundNode: ASDisplayNode
private let recButton: VoiceChatHeaderButton
private let optionsButton: VoiceChatHeaderButton
private let closeButton: VoiceChatHeaderButton
private let topCornersNode: ASImageNode
@ -724,9 +723,6 @@ public final class VoiceChatController: ViewController {
self.topPanelEdgeNode.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
}
self.recButton = VoiceChatHeaderButton(rec: true)
self.recButton.setImage(optionsBackgroundImage(dark: false))
self.recButton.isHidden = true
self.optionsButton = VoiceChatHeaderButton()
self.optionsButton.setImage(optionsButtonImage(dark: false))
self.closeButton = VoiceChatHeaderButton()
@ -1251,7 +1247,6 @@ public final class VoiceChatController: ViewController {
self.topPanelNode.addSubnode(self.topPanelEdgeNode)
self.topPanelNode.addSubnode(self.topPanelBackgroundNode)
self.topPanelNode.addSubnode(self.titleNode)
self.topPanelNode.addSubnode(self.recButton)
self.topPanelNode.addSubnode(self.optionsButton)
self.topPanelNode.addSubnode(self.closeButton)
self.topPanelNode.addSubnode(self.topCornersNode)
@ -1456,7 +1451,7 @@ public final class VoiceChatController: ViewController {
|> map { peers -> [ContextMenuItem] in
var items: [ContextMenuItem] = []
items.append(.custom(VoiceChatInfoContextItem(text: strongSelf.presentationData.strings.VoiceChat_DisplayAsInfo, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/Stickers"), color: theme.actionSheet.primaryTextColor)
return generateTintedImage(image: UIImage(bundleImageName: "Call/Context Menu/Accounts"), color: theme.actionSheet.primaryTextColor)
}), true))
for peer in peers {
@ -1581,7 +1576,7 @@ public final class VoiceChatController: ViewController {
}
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_EditTitle, icon: { theme -> UIImage? in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.actionSheet.primaryTextColor)
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pencil"), color: theme.actionSheet.primaryTextColor)
}, action: { [weak self] _, f in
f(.default)
@ -1589,7 +1584,7 @@ public final class VoiceChatController: ViewController {
if let strongSelf = self, let title = title {
strongSelf.call.updateTitle(title)
strongSelf.presentUndoOverlay(content: .succeed(text: strongSelf.presentationData.strings.VoiceChat_EditTitleSuccess(title).0), action: { _ in return false })
strongSelf.presentUndoOverlay(content: .voiceChatFlag(text: title.isEmpty ? strongSelf.presentationData.strings.VoiceChat_EditTitleRemoveSuccess : strongSelf.presentationData.strings.VoiceChat_EditTitleSuccess(title).0), action: { _ in return false })
}
})
self?.controller?.present(controller, in: .window(.root))
@ -1672,7 +1667,7 @@ public final class VoiceChatController: ViewController {
if let strongSelf = self, let title = title {
strongSelf.call.setShouldBeRecording(true, title: title)
strongSelf.presentUndoOverlay(content: .recording(text: strongSelf.presentationData.strings.VoiceChat_RecordingStarted), action: { _ in return false })
strongSelf.presentUndoOverlay(content: .voiceChatRecording(text: strongSelf.presentationData.strings.VoiceChat_RecordingStarted), action: { _ in return false })
}
})
self?.controller?.present(controller, in: .window(.root))
@ -1714,22 +1709,19 @@ public final class VoiceChatController: ViewController {
}
}
let optionsButton: VoiceChatHeaderButton = !strongSelf.recButton.isHidden ? strongSelf.recButton : strongSelf.optionsButton
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .reference(VoiceChatContextReferenceContentSource(controller: controller, sourceNode: optionsButton.referenceNode)), items: mainItemsImpl?() ?? .single([]), reactionItems: [], gesture: gesture)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .reference(VoiceChatContextReferenceContentSource(controller: controller, sourceNode: strongSelf.optionsButton.referenceNode)), items: mainItemsImpl?() ?? .single([]), reactionItems: [], gesture: gesture)
strongSelf.controller?.presentInGlobalOverlay(contextController)
}
self.recButton.contextAction = self.optionsButton.contextAction
self.optionsButton.addTarget(self, action: #selector(self.optionsPressed), forControlEvents: .touchUpInside)
self.recButton.addTarget(self, action: #selector(self.optionsPressed), forControlEvents: .touchUpInside)
self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: .touchUpInside)
self.actionButtonColorDisposable = (self.actionButton.outerColor
|> deliverOnMainQueue).start(next: { [weak self] color in
if let strongSelf = self {
let animated = strongSelf.currentAudioButtonColor != nil
strongSelf.currentAudioButtonColor = color
strongSelf.updateButtons(animated: true)
strongSelf.updateButtons(animated: animated)
}
})
@ -2249,7 +2241,6 @@ public final class VoiceChatController: ViewController {
}
self.bottomCornersNode.image = cornersImage(top: false, bottom: true, dark: isFullscreen)
self.recButton.setImage(optionsBackgroundImage(dark: isFullscreen), animated: transition.isAnimated)
self.optionsButton.setImage(optionsButtonImage(dark: isFullscreen), animated: transition.isAnimated)
self.closeButton.setImage(closeButtonImage(dark: isFullscreen), animated: transition.isAnimated)
@ -2357,7 +2348,6 @@ public final class VoiceChatController: ViewController {
self.updateTitle(transition: transition)
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 10.0), size: CGSize(width: size.width, height: 44.0)))
transition.updateFrame(node: self.recButton, frame: CGRect(origin: CGPoint(x: 20.0, y: 18.0), size: CGSize(width: 58.0, height: 28.0)))
transition.updateFrame(node: self.optionsButton, frame: CGRect(origin: CGPoint(x: 20.0, y: 18.0), size: CGSize(width: 28.0, height: 28.0)))
transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: size.width - 20.0 - 28.0, y: 18.0), size: CGSize(width: 28.0, height: 28.0)))

View File

@ -51,42 +51,21 @@ final class VoiceChatHeaderButton: HighlightableButtonNode {
var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
var textNode: ImmediateTextNode?
var dotNode: ASImageNode?
init(rec: Bool = false) {
init() {
self.referenceNode = ContextReferenceContentNode()
self.containerNode = ContextControllerSourceNode()
self.containerNode.isGestureEnabled = false
self.containerNode.animateScale = false
self.iconNode = ASImageNode()
self.iconNode.displaysAsynchronously = false
self.iconNode.displayWithoutProcessing = true
self.iconNode.contentMode = .scaleToFill
if rec {
self.textNode = ImmediateTextNode()
self.textNode?.attributedText = NSAttributedString(string: "REC", font: Font.regular(12.0), textColor: .white)
if let textNode = self.textNode {
let textSize = textNode.updateLayout(CGSize(width: 58.0, height: 28.0))
textNode.frame = CGRect(origin: CGPoint(), size: textSize)
}
self.dotNode = ASImageNode()
self.dotNode?.displaysAsynchronously = false
self.dotNode?.displayWithoutProcessing = true
self.dotNode?.image = generateFilledCircleImage(diameter: 8.0, color: UIColor(rgb: 0xff3b30))
}
super.init()
self.containerNode.addSubnode(self.referenceNode)
self.referenceNode.addSubnode(self.iconNode)
self.addSubnode(self.containerNode)
if rec, let textNode = self.textNode, let dotNode = self.dotNode {
self.referenceNode.addSubnode(textNode)
self.referenceNode.addSubnode(dotNode)
}
self.containerNode.shouldBegin = { [weak self] location in
guard let strongSelf = self, let _ = strongSelf.contextAction else {
return false
@ -102,7 +81,7 @@ final class VoiceChatHeaderButton: HighlightableButtonNode {
self.iconNode.image = optionsButtonImage(dark: false)
self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: rec ? 58.0 : 28.0, height: 28.0))
self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 28.0, height: 28.0))
self.referenceNode.frame = self.containerNode.bounds
self.iconNode.frame = self.containerNode.bounds
}
@ -122,31 +101,12 @@ final class VoiceChatHeaderButton: HighlightableButtonNode {
override func didLoad() {
super.didLoad()
self.view.isOpaque = false
if let dotNode = self.dotNode {
let animation = CAKeyframeAnimation(keyPath: "opacity")
animation.values = [1.0 as NSNumber, 1.0 as NSNumber, 0.0 as NSNumber]
animation.keyTimes = [0.0 as NSNumber, 0.4546 as NSNumber, 0.9091 as NSNumber, 1 as NSNumber]
animation.duration = 0.5
animation.autoreverses = true
animation.repeatCount = Float.infinity
dotNode.layer.add(animation, forKey: "recording")
}
}
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
return CGSize(width: self.dotNode != nil ? 58.0 : 28.0, height: 28.0)
return CGSize(width: 28.0, height: 28.0)
}
override func layout() {
super.layout()
if let dotNode = self.dotNode, let textNode = self.textNode {
dotNode.frame = CGRect(origin: CGPoint(x: 10.0, y: 10.0), size: CGSize(width: 8.0, height: 8.0))
textNode.frame = CGRect(origin: CGPoint(x: 22.0, y: 7.0), size: textNode.frame.size)
}
}
func onLayout() {
}
}

View File

@ -36,6 +36,7 @@ final class VoiceChatParticipantItem: ListViewItem {
case none
case microphone(Bool, UIColor)
case invite(Bool)
case wantsToSpeak
}
struct RevealOption {

View File

@ -2978,9 +2978,9 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
if let info = GroupCallInfo(call) {
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let current = current as? CachedChannelData {
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title))
} else if let current = current as? CachedGroupData {
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title))
} else {
return current
}

View File

@ -205,9 +205,9 @@ public func createGroupCall(account: Account, peerId: PeerId) -> Signal<GroupCal
return account.postbox.transaction { transaction -> GroupCallInfo in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash))
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title))
} else if let cachedData = cachedData as? CachedGroupData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash))
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title))
} else {
return cachedData
}

View File

@ -291,16 +291,8 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
if (chatFull.flags & 1 << 8) != 0 {
hasScheduledMessages = true
}
var updatedActiveCall: CachedChannelData.ActiveCall?
if let inputCall = chatFull.call {
switch inputCall {
case let .inputGroupCall(id, accessHash):
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash)
}
}
let groupcallDefaultJoinAs = chatFull.groupcallDefaultJoinAs
let groupCallDefaultJoinAs = chatFull.groupcallDefaultJoinAs
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
let previous: CachedGroupData
@ -310,6 +302,14 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
previous = CachedGroupData()
}
var updatedActiveCall: CachedChannelData.ActiveCall?
if let inputCall = chatFull.call {
switch inputCall {
case let .inputGroupCall(id, accessHash):
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title)
}
}
return previous.withUpdatedParticipants(participants)
.withUpdatedExportedInvitation(exportedInvitation)
.withUpdatedBotInfos(botInfos)
@ -320,7 +320,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
.withUpdatedInvitedBy(invitedBy)
.withUpdatedPhoto(photo)
.withUpdatedActiveCall(updatedActiveCall)
.withUpdatedCallJoinPeerId(groupcallDefaultJoinAs?.peerId)
.withUpdatedCallJoinPeerId(groupCallDefaultJoinAs?.peerId)
})
case .channelFull:
break
@ -409,15 +409,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
if let pinnedMsgId = pinnedMsgId {
pinnedMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: pinnedMsgId)
}
var updatedActiveCall: CachedChannelData.ActiveCall?
if let inputCall = inputCall {
switch inputCall {
case let .inputGroupCall(id, accessHash):
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash)
}
}
var minAvailableMessageId: MessageId?
if let minAvailableMsgId = minAvailableMsgId {
minAvailableMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: minAvailableMsgId)
@ -515,6 +507,14 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
minAvailableMessageIdUpdated = previous.minAvailableMessageId != minAvailableMessageId
var updatedActiveCall: CachedChannelData.ActiveCall?
if let inputCall = inputCall {
switch inputCall {
case let .inputGroupCall(id, accessHash):
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title)
}
}
return previous.withUpdatedFlags(channelFlags)
.withUpdatedAbout(about)
.withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: participantsCount, adminCount: adminsCount, bannedCount: bannedCount, kickedCount: kickedCount))

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_chooseaccountbig.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_accounts.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_allowspeak.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_mute2days.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_mute2hours.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,12 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_lt_muted.pdf"
"filename" : "ic_mute.pdf",
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_edit.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_customize.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -534,7 +534,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
case .groupPhoneCall, .inviteToGroupPhoneCall:
if let activeCall = strongSelf.presentationInterfaceState.activeGroupCallInfo?.activeCall {
strongSelf.context.joinGroupCall(peerId: message.id.peerId, joinAsPeerId: nil, activeCall: CachedChannelData.ActiveCall(id: activeCall.id, accessHash: activeCall.accessHash))
strongSelf.context.joinGroupCall(peerId: message.id.peerId, joinAsPeerId: nil, activeCall: CachedChannelData.ActiveCall(id: activeCall.id, accessHash: activeCall.accessHash, title: activeCall.title))
} else {
var canManageGroupCalls = false
if let channel = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel {
@ -568,7 +568,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self else {
return
}
strongSelf.context.joinGroupCall(peerId: message.id.peerId, joinAsPeerId: nil, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
strongSelf.context.joinGroupCall(peerId: message.id.peerId, joinAsPeerId: nil, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title))
}, error: { [weak self] error in
dismissStatus?()

View File

@ -48,7 +48,7 @@ enum PeerInfoHeaderButtonIcon {
final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
let key: PeerInfoHeaderButtonKey
private let action: (PeerInfoHeaderButtonNode) -> Void
private let action: (PeerInfoHeaderButtonNode, ContextGesture?) -> Void
let referenceNode: ContextReferenceContentNode
let containerNode: ContextControllerSourceNode
private let backgroundNode: ASImageNode
@ -58,13 +58,13 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
private var icon: PeerInfoHeaderButtonIcon?
private var isActive: Bool?
init(key: PeerInfoHeaderButtonKey, action: @escaping (PeerInfoHeaderButtonNode) -> Void) {
init(key: PeerInfoHeaderButtonKey, action: @escaping (PeerInfoHeaderButtonNode, ContextGesture?) -> Void) {
self.key = key
self.action = action
self.referenceNode = ContextReferenceContentNode()
self.containerNode = ContextControllerSourceNode()
self.containerNode.isGestureEnabled = false
self.containerNode.animateScale = false
self.backgroundNode = ASImageNode()
self.backgroundNode.displaysAsynchronously = false
@ -94,11 +94,17 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
}
}
self.containerNode.activated = { [weak self] gesture, _ in
if let strongSelf = self {
strongSelf.action(strongSelf, gesture)
}
}
self.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
}
@objc private func buttonPressed() {
self.action(self)
self.action(self, nil)
}
func update(size: CGSize, text: String, icon: PeerInfoHeaderButtonIcon, isActive: Bool, isExpanded: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
@ -2593,7 +2599,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let navigationSeparatorNode: ASDisplayNode
let navigationButtonContainer: PeerInfoHeaderNavigationButtonContainerNode
var performButtonAction: ((PeerInfoHeaderButtonKey) -> Void)?
var performButtonAction: ((PeerInfoHeaderButtonKey, ContextGesture?) -> Void)?
var requestAvatarExpansion: ((Bool, [AvatarGalleryEntry], AvatarGalleryEntry?, (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?) -> Void)?
var requestOpenAvatarForEditing: ((Bool) -> Void)?
var cancelUpload: (() -> Void)?
@ -3302,8 +3308,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
buttonNode = current
} else {
wasAdded = true
buttonNode = PeerInfoHeaderButtonNode(key: buttonKey, action: { [weak self] buttonNode in
self?.buttonPressed(buttonNode)
buttonNode = PeerInfoHeaderButtonNode(key: buttonKey, action: { [weak self] buttonNode, gesture in
self?.buttonPressed(buttonNode, gesture: gesture)
})
self.buttonNodes[buttonKey] = buttonNode
self.regularContentNode.addSubnode(buttonNode)
@ -3457,8 +3463,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
return resolvedHeight
}
private func buttonPressed(_ buttonNode: PeerInfoHeaderButtonNode) {
self.performButtonAction?(buttonNode.key)
private func buttonPressed(_ buttonNode: PeerInfoHeaderButtonNode, gesture: ContextGesture?) {
self.performButtonAction?(buttonNode.key, gesture)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

View File

@ -2264,8 +2264,8 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
}
self.headerNode.performButtonAction = { [weak self] key in
self?.performButtonAction(key: key)
self.headerNode.performButtonAction = { [weak self] key, gesture in
self?.performButtonAction(key: key, gesture: gesture)
}
self.headerNode.cancelUpload = { [weak self] in
@ -3267,7 +3267,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}))
}
private func performButtonAction(key: PeerInfoHeaderButtonKey) {
private func performButtonAction(key: PeerInfoHeaderButtonKey, gesture: ContextGesture?) {
guard let controller = self.controller else {
return
}
@ -3304,7 +3304,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
case .videoCall:
self.requestCall(isVideo: true)
case .voiceChat:
self.openVoiceChatDisplayAsPeerSelection(contextController: nil, result: nil, backAction: nil)
self.openVoiceChatDisplayAsPeerSelection(gesture: gesture, contextController: nil, result: nil, backAction: nil)
case .mute:
if let notificationSettings = self.data?.notificationSettings, case .muted = notificationSettings.muteState {
let _ = updatePeerMuteSetting(account: self.context.account, peerId: self.peerId, muteInterval: nil).start()
@ -3315,12 +3315,12 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
var items: [ContextMenuItem] = []
let muteValues: [Int32] = [
1 * 60 * 60,
2 * 24 * 60 * 60,
Int32.max
let muteValues: [(Int32, String)] = [
(1 * 60 * 60, "Chat/Context Menu/Mute2h"),
(2 * 24 * 60 * 60, "Chat/Context Menu/Mute2d"),
(Int32.max, "Chat/Context Menu/Muted")
]
for delay in muteValues {
for (delay, iconName) in muteValues {
let title: String
if delay == Int32.max {
title = self.presentationData.strings.MuteFor_Forever
@ -3329,7 +3329,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
items.append(.action(ContextMenuActionItem(text: title, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor)
return generateTintedImage(image: UIImage(bundleImageName: iconName), color: theme.contextMenu.primaryColor)
}, action: { _, f in
f(.dismissWithoutContent)
@ -3340,7 +3340,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_CustomizeNotifications, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Settings"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
@ -3388,7 +3388,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.view.endEditing(true)
if let sourceNode = self.headerNode.buttonNodes[.mute]?.referenceNode {
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(items), reactionItems: [], gesture: nil)
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(items), reactionItems: [], gesture: gesture)
contextController.dismissed = { [weak self] in
if let strongSelf = self {
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
@ -3489,7 +3489,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
if self.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_StartSecretChat, icon: { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor)
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Timer"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
@ -3525,7 +3525,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChannelInfo_CreateVoiceChat, icon: { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VoiceChat"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, f in
self?.openVoiceChatDisplayAsPeerSelection(contextController: c, result: f, backAction: { c in
self?.openVoiceChatDisplayAsPeerSelection(gesture: nil, contextController: c, result: f, backAction: { c in
if let mainItemsImpl = mainItemsImpl {
c.setItems(mainItemsImpl())
}
@ -3635,7 +3635,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChannelInfo_CreateVoiceChat, icon: { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VoiceChat"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, f in
self?.openVoiceChatDisplayAsPeerSelection(contextController: c, result: f, backAction: { c in
self?.openVoiceChatDisplayAsPeerSelection(gesture: nil, contextController: c, result: f, backAction: { c in
if let mainItemsImpl = mainItemsImpl {
c.setItems(mainItemsImpl())
}
@ -3663,7 +3663,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.view.endEditing(true)
if let sourceNode = self.headerNode.buttonNodes[.more]?.referenceNode {
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: mainItemsImpl?() ?? .single([]), reactionItems: [], gesture: nil)
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: mainItemsImpl?() ?? .single([]), reactionItems: [], gesture: gesture)
contextController.dismissed = { [weak self] in
if let strongSelf = self {
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
@ -3869,7 +3869,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
guard let strongSelf = self else {
return
}
strongSelf.context.joinGroupCall(peerId: peerId, joinAsPeerId: joinAsPeerId, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
strongSelf.context.joinGroupCall(peerId: peerId, joinAsPeerId: joinAsPeerId, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title))
}, error: { [weak self] error in
dismissStatus?()
@ -4188,7 +4188,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
controller.push(statsController)
}
private func openVoiceChatDisplayAsPeerSelection(contextController: ContextController?, result: ((ContextMenuActionResult) -> Void)?, backAction: ((ContextController) -> Void)?) {
private func openVoiceChatDisplayAsPeerSelection(gesture: ContextGesture?, contextController: ContextController?, result: ((ContextMenuActionResult) -> Void)?, backAction: ((ContextController) -> Void)?) {
let currentAccountPeer = self.context.account.postbox.loadedPeerWithId(context.account.peerId)
|> map { peer in
return [FoundPeer(peer: peer, subscribers: nil)]
@ -4209,7 +4209,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
var items: [ContextMenuItem] = []
items.append(.custom(VoiceChatInfoContextItem(text: strongSelf.presentationData.strings.VoiceChat_DisplayAsInfo, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/Stickers"), color: theme.actionSheet.primaryTextColor)
return generateTintedImage(image: UIImage(bundleImageName: "Call/Context Menu/Accounts"), color: theme.actionSheet.primaryTextColor)
}), true))
for peer in peers {
@ -4252,7 +4252,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
if let sourceNode = strongSelf.headerNode.buttonNodes[.voiceChat]?.referenceNode, let controller = strongSelf.controller {
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(items), reactionItems: [], gesture: nil)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(items), reactionItems: [], gesture: gesture)
contextController.dismissed = { [weak self] in
if let strongSelf = self {
strongSelf.state = strongSelf.state.withHighlightedButton(nil)

@ -1 +1 @@
Subproject commit 2c55abafe21cf67128dc3733844e37276fbcabb3
Subproject commit 18ed939bf6818139f7147cde1c34cf22eb5080a2

View File

@ -32,7 +32,9 @@ public enum UndoOverlayContent {
case autoDelete(isOn: Bool, title: String?, text: String)
case gigagroupConversion(text: String)
case linkRevoked(text: String)
case recording(text: String)
case voiceChatRecording(text: String)
case voiceChatFlag(text: String)
case voiceChatCanSpeak(text: String)
}
public enum UndoOverlayAction {

View File

@ -28,7 +28,6 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
private let animationNode: AnimationNode?
private var animatedStickerNode: AnimatedStickerNode?
private var slotMachineNode: SlotMachineAnimationNode?
private var recordingIconNode: RecordingIconNode?
private var stillStickerNode: TransformImageNode?
private var stickerImageSize: CGSize?
private var stickerOffset: CGPoint?
@ -552,14 +551,42 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
displayUndo = false
self.originalRemainingSeconds = 3
case let .recording(text):
case let .voiceChatRecording(text):
self.avatarNode = nil
self.iconNode = nil
self.iconCheckNode = nil
self.animationNode = nil
self.animationNode = AnimationNode(animation: "anim_vcrecord", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
self.recordingIconNode = RecordingIconNode()
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .natural)
self.textNode.attributedText = attributedText
self.textNode.maximumNumberOfLines = 2
displayUndo = false
self.originalRemainingSeconds = 3
case let .voiceChatFlag(text):
self.avatarNode = nil
self.iconNode = nil
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_vcflag", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .natural)
self.textNode.attributedText = attributedText
self.textNode.maximumNumberOfLines = 2
displayUndo = false
self.originalRemainingSeconds = 3
case let .voiceChatCanSpeak(text):
self.avatarNode = nil
self.iconNode = nil
self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_vcflag", colors: [:], scale: 0.066)
self.animatedStickerNode = nil
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@ -597,7 +624,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
switch content {
case .removedChat:
self.panelWrapperNode.addSubnode(self.timerTextNode)
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .recording:
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak:
break
case .dice:
self.panelWrapperNode.clipsToBounds = true
@ -612,7 +639,6 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
self.slotMachineNode.flatMap(self.panelWrapperNode.addSubnode)
self.avatarNode.flatMap(self.panelWrapperNode.addSubnode)
self.recordingIconNode.flatMap(self.panelWrapperNode.addSubnode)
self.panelWrapperNode.addSubnode(self.titleNode)
self.panelWrapperNode.addSubnode(self.textNode)
self.panelWrapperNode.addSubnode(self.buttonNode)
@ -838,13 +864,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
let iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0)), size: iconSize)
transition.updateFrame(node: slotMachineNode, frame: iconFrame)
}
if let recordingIconNode = self.recordingIconNode {
let iconSize = CGSize(width: 24.0, height: 24.0)
let iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0) + verticalOffset), size: iconSize)
transition.updateFrame(node: recordingIconNode, frame: iconFrame)
}
let timerTextSize = self.timerTextNode.updateLayout(CGSize(width: 100.0, height: 100.0))
transition.updateFrame(node: self.timerTextNode, frame: CGRect(origin: CGPoint(x: floor((leftInset - timerTextSize.width) / 2.0), y: floor((contentHeight - timerTextSize.height) / 2.0)), size: timerTextSize))
@ -919,46 +939,3 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
return super.hitTest(point, with: event)
}
}
private class RecordingIconNode: ASDisplayNode {
private let backgroundNode: ASImageNode
private let dotNode: ASImageNode
override init() {
let iconSize: CGFloat = 24.0
self.backgroundNode = ASImageNode()
self.backgroundNode.displaysAsynchronously = false
self.backgroundNode.displayWithoutProcessing = true
self.backgroundNode.image = generateCircleImage(diameter: iconSize, lineWidth: 1.0 + UIScreenPixel, color: UIColor.white, backgroundColor: nil)
self.dotNode = ASImageNode()
self.dotNode.displaysAsynchronously = false
self.dotNode.displayWithoutProcessing = true
self.dotNode.image = generateFilledCircleImage(diameter: 12.0, color: UIColor(rgb: 0xff3b30))
super.init()
self.addSubnode(self.backgroundNode)
self.addSubnode(self.dotNode)
}
override func didLoad() {
super.didLoad()
let animation = CAKeyframeAnimation(keyPath: "opacity")
animation.values = [1.0 as NSNumber, 1.0 as NSNumber, 0.0 as NSNumber]
animation.keyTimes = [0.0 as NSNumber, 0.4546 as NSNumber, 0.9091 as NSNumber, 1 as NSNumber]
animation.duration = 0.5
animation.autoreverses = true
animation.repeatCount = Float.infinity
self.dotNode.layer.add(animation, forKey: "recording")
}
override func layout() {
super.layout()
self.backgroundNode.frame = self.bounds
let dotSize = CGSize(width: 12.0, height: 12.0)
self.dotNode.frame = CGRect(origin: CGPoint(x: (self.bounds.width - dotSize.width) / 2.0, y: (self.bounds.height - dotSize.height) / 2.0), size: dotSize)
}
}