mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
4a7d5ccb67
commit
b4f89a7e6c
@ -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";
|
||||
|
@ -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 {
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)))
|
||||
|
||||
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ final class VoiceChatParticipantItem: ListViewItem {
|
||||
case none
|
||||
case microphone(Bool, UIColor)
|
||||
case invite(Bool)
|
||||
case wantsToSpeak
|
||||
}
|
||||
|
||||
struct RevealOption {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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))
|
||||
|
File diff suppressed because it is too large
Load Diff
12
submodules/TelegramUI/Images.xcassets/Call/Accounts.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Call/Accounts.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_chooseaccountbig.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Call/Accounts.imageset/ic_chooseaccountbig.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Call/Accounts.imageset/ic_chooseaccountbig.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Call/Context Menu/Accounts.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Call/Context Menu/Accounts.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_accounts.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Call/Context Menu/Accounts.imageset/ic_accounts.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Call/Context Menu/Accounts.imageset/ic_accounts.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Call/Context Menu/AllowToSpeak.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Call/Context Menu/AllowToSpeak.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_allowspeak.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mute2d.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mute2d.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_mute2days.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mute2d.imageset/ic_mute2days.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mute2d.imageset/ic_mute2days.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mute2h.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mute2h.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_mute2hours.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mute2h.imageset/ic_mute2hours.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mute2h.imageset/ic_mute2hours.pdf
vendored
Normal file
Binary file not shown.
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Muted.imageset/ic_mute.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Muted.imageset/ic_mute.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Pencil.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Pencil.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_edit.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Pencil.imageset/ic_edit.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Pencil.imageset/ic_edit.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Settings.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Settings.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_customize.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Settings.imageset/ic_customize.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Settings.imageset/ic_customize.pdf
vendored
Normal file
Binary file not shown.
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
Binary file not shown.
@ -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?()
|
||||
|
||||
|
@ -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? {
|
||||
|
@ -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
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user