Various Voice Chats improvements

This commit is contained in:
Ilya Laktyushin 2021-03-05 20:00:00 +04:00
parent 556002df68
commit 13fd591ee0
19 changed files with 3307 additions and 3112 deletions

View File

@ -6221,3 +6221,8 @@ Sorry for the inconvenience.";
"VoiceChat.InviteLink.InviteListeners_any" = "[%@] Invite Listeners"; "VoiceChat.InviteLink.InviteListeners_any" = "[%@] Invite Listeners";
"Conversation.JoinVoiceChat" = "JOIN VOICE CHAT"; "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.CancelForwardCancelForward" = "Cancel Forwarding";
"Conversation.CancelForwardSelectChat" = "Select Chat";

View File

@ -34,10 +34,11 @@ private struct ArchivedStickersNoticeEntry: Comparable, Identifiable {
} }
func item(account: Account, presentationData: PresentationData) -> ListViewItem { func item(account: Account, presentationData: PresentationData) -> ListViewItem {
return ItemListStickerPackItem(presentationData: ItemListPresentationData(presentationData), account: account, packInfo: info, itemCount: self.count, topItem: topItem, unread: false, control: .none, editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, playAnimatedStickers: true, sectionId: 0, action: { return ItemListStickerPackItem(presentationData: ItemListPresentationData(presentationData), account: account, packInfo: info, itemCount: self.count, topItem: topItem, unread: false, control: .none, editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false, selectable: false), enabled: true, playAnimatedStickers: true, sectionId: 0, action: {
}, setPackIdWithRevealedOptions: { current, previous in }, setPackIdWithRevealedOptions: { current, previous in
}, addPack: { }, addPack: {
}, removePack: { }, removePack: {
}, toggleSelected: {
}) })
} }
} }

View File

@ -296,8 +296,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
return .like return .like
case .unlike: case .unlike:
return .unlike return .unlike
default:
return nil
} }
} }
if strongSelf.highlightedReaction != highlightedReaction { if strongSelf.highlightedReaction != highlightedReaction {
@ -431,8 +429,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
reactionSelected(.like) reactionSelected(.like)
case .unlike: case .unlike:
reactionSelected(.unlike) reactionSelected(.unlike)
default:
break
} }
} }
} }
@ -632,7 +628,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
if let contentNode = self.contentContainerNode.contentNode { if let contentNode = self.contentContainerNode.contentNode {
switch contentNode { switch contentNode {
case let .reference(referenceNode): case .reference:
let springDuration: Double = 0.42 * animationDurationFactor let springDuration: Double = 0.42 * animationDurationFactor
let springDamping: CGFloat = 104.0 let springDamping: CGFloat = 104.0
@ -758,10 +754,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.scrollNode.view.setContentOffset(self.scrollNode.view.contentOffset, animated: false) self.scrollNode.view.setContentOffset(self.scrollNode.view.contentOffset, animated: false)
var completedEffect = false
var completedContentNode = false
var completedActionsNode = false
if let transitionInfo = transitionInfo, let parentSupernode = referenceNode.supernode { if let transitionInfo = transitionInfo, let parentSupernode = referenceNode.supernode {
self.originalProjectedContentViewFrame = (convertFrame(referenceNode.frame, from: parentSupernode.view, to: self.view), convertFrame(referenceNode.bounds, from: referenceNode.view, to: self.view)) self.originalProjectedContentViewFrame = (convertFrame(referenceNode.frame, from: parentSupernode.view, to: self.view), convertFrame(referenceNode.bounds, from: referenceNode.view, to: self.view))
@ -794,8 +786,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame { if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
let localSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.scrollNode.view) let localSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.scrollNode.view)
let localContentSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.contentContainerNode.view.superview)
self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true) self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true)
} }
case let .extracted(source): case let .extracted(source):
@ -864,7 +854,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
propertyAnimator?.stopAnimation(true) propertyAnimator?.stopAnimation(true)
} }
self.propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration * UIView.animationDurationFactor(), curve: .easeInOut, animations: { [weak self] in self.propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration * UIView.animationDurationFactor(), curve: .easeInOut, animations: {
//self?.effectView.effect = nil //self?.effectView.effect = nil
}) })
} }

View File

@ -446,7 +446,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil) presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}, mainLinkContextAction: { invite, node, gesture in }, mainLinkContextAction: { invite, node, gesture in
guard let node = node as? ContextExtractedContentContainingNode, let controller = getControllerImpl?(), let invite = invite else { guard let node = node as? ContextReferenceContentNode, let controller = getControllerImpl?(), let invite = invite else {
return return
} }
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
@ -551,7 +551,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
}))) })))
} }
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, blurBackground: false)), items: .single(items), reactionItems: [], gesture: gesture) let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController) presentInGlobalOverlayImpl?(contextController)
}, createLink: { }, createLink: {
let controller = inviteLinkEditController(context: context, peerId: peerId, invite: nil, completion: { invite in let controller = inviteLinkEditController(context: context, peerId: peerId, invite: nil, completion: { invite in
@ -899,3 +899,17 @@ final class InviteLinkContextExtractedContentSource: ContextExtractedContentSour
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
} }
} }
final class InviteLinkContextReferenceContentSource: ContextReferenceContentSource {
private let controller: ViewController
private let sourceNode: ContextReferenceContentNode
init(controller: ViewController, sourceNode: ContextReferenceContentNode) {
self.controller = controller
self.sourceNode = sourceNode
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -122,7 +122,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
private let fieldNode: ASImageNode private let fieldNode: ASImageNode
private let addressNode: TextNode private let addressNode: TextNode
private let fieldButtonNode: HighlightTrackingButtonNode private let fieldButtonNode: HighlightTrackingButtonNode
private let extractedContainerNode: ContextExtractedContentContainingNode private let referenceContainerNode: ContextReferenceContentNode
private let containerNode: ContextControllerSourceNode private let containerNode: ContextControllerSourceNode
private let addressButtonNode: HighlightTrackingButtonNode private let addressButtonNode: HighlightTrackingButtonNode
private let addressButtonIconNode: ASImageNode private let addressButtonIconNode: ASImageNode
@ -172,7 +172,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
self.fieldButtonNode = HighlightTrackingButtonNode() self.fieldButtonNode = HighlightTrackingButtonNode()
self.addressButtonNode = HighlightTrackingButtonNode() self.addressButtonNode = HighlightTrackingButtonNode()
self.extractedContainerNode = ContextExtractedContentContainingNode() self.referenceContainerNode = ContextReferenceContentNode()
self.containerNode = ContextControllerSourceNode() self.containerNode = ContextControllerSourceNode()
self.containerNode.isGestureEnabled = false self.containerNode.isGestureEnabled = false
self.addressButtonIconNode = ASImageNode() self.addressButtonIconNode = ASImageNode()
@ -196,9 +196,8 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
self.addSubnode(self.invitedPeersNode) self.addSubnode(self.invitedPeersNode)
self.addSubnode(self.avatarsButtonNode) self.addSubnode(self.avatarsButtonNode)
self.containerNode.addSubnode(self.extractedContainerNode) self.containerNode.addSubnode(self.referenceContainerNode)
self.extractedContainerNode.contentNode.addSubnode(self.addressButtonIconNode) self.referenceContainerNode.addSubnode(self.addressButtonIconNode)
self.containerNode.targetNodeForActivationProgress = self.extractedContainerNode.contentNode
self.addressButtonNode.addSubnode(self.containerNode) self.addressButtonNode.addSubnode(self.containerNode)
self.addSubnode(self.addressButtonNode) self.addSubnode(self.addressButtonNode)
@ -260,7 +259,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
@objc private func addressButtonPressed() { @objc private func addressButtonPressed() {
if let item = self.item { if let item = self.item {
item.contextAction?(self.extractedContainerNode) item.contextAction?(self.referenceContainerNode)
} }
} }
@ -432,8 +431,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
strongSelf.addressNode.frame = CGRect(origin: CGPoint(x: fieldFrame.minX + (alignCentrally ? floorToScreenPixels((fieldFrame.width - addressLayout.size.width) / 2.0) : 14.0), y: fieldFrame.minY + floorToScreenPixels((fieldFrame.height - addressLayout.size.height) / 2.0) + 1.0), size: addressLayout.size) strongSelf.addressNode.frame = CGRect(origin: CGPoint(x: fieldFrame.minX + (alignCentrally ? floorToScreenPixels((fieldFrame.width - addressLayout.size.width) / 2.0) : 14.0), y: fieldFrame.minY + floorToScreenPixels((fieldFrame.height - addressLayout.size.height) / 2.0) + 1.0), size: addressLayout.size)
strongSelf.addressButtonNode.frame = CGRect(origin: CGPoint(x: params.width - rightInset - 38.0 - 14.0, y: verticalInset), size: CGSize(width: 52.0, height: 52.0)) strongSelf.addressButtonNode.frame = CGRect(origin: CGPoint(x: params.width - rightInset - 38.0 - 14.0, y: verticalInset), size: CGSize(width: 52.0, height: 52.0))
strongSelf.extractedContainerNode.frame = strongSelf.addressButtonNode.bounds strongSelf.referenceContainerNode.frame = strongSelf.addressButtonNode.bounds
strongSelf.extractedContainerNode.contentRect = strongSelf.addressButtonNode.bounds
strongSelf.addressButtonIconNode.frame = strongSelf.addressButtonNode.bounds strongSelf.addressButtonIconNode.frame = strongSelf.addressButtonNode.bounds
let shareButtonNode: SolidRoundedButtonNode let shareButtonNode: SolidRoundedButtonNode

View File

@ -19,12 +19,14 @@ public struct ItemListStickerPackItemEditing: Equatable {
public var editing: Bool public var editing: Bool
public var revealed: Bool public var revealed: Bool
public var reorderable: Bool public var reorderable: Bool
public var selectable: Bool
public init(editable: Bool, editing: Bool, revealed: Bool, reorderable: Bool) { public init(editable: Bool, editing: Bool, revealed: Bool, reorderable: Bool, selectable: Bool) {
self.editable = editable self.editable = editable
self.editing = editing self.editing = editing
self.revealed = revealed self.revealed = revealed
self.reorderable = reorderable self.reorderable = reorderable
self.selectable = selectable
} }
} }
@ -51,8 +53,9 @@ public final class ItemListStickerPackItem: ListViewItem, ItemListItem {
let setPackIdWithRevealedOptions: (ItemCollectionId?, ItemCollectionId?) -> Void let setPackIdWithRevealedOptions: (ItemCollectionId?, ItemCollectionId?) -> Void
let addPack: () -> Void let addPack: () -> Void
let removePack: () -> Void let removePack: () -> Void
let toggleSelected: () -> Void
public init(presentationData: ItemListPresentationData, account: Account, packInfo: StickerPackCollectionInfo, itemCount: String, topItem: StickerPackItem?, unread: Bool, control: ItemListStickerPackItemControl, editing: ItemListStickerPackItemEditing, enabled: Bool, playAnimatedStickers: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, addPack: @escaping () -> Void, removePack: @escaping () -> Void) { public init(presentationData: ItemListPresentationData, account: Account, packInfo: StickerPackCollectionInfo, itemCount: String, topItem: StickerPackItem?, unread: Bool, control: ItemListStickerPackItemControl, editing: ItemListStickerPackItemEditing, enabled: Bool, playAnimatedStickers: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, addPack: @escaping () -> Void, removePack: @escaping () -> Void, toggleSelected: @escaping () -> Void) {
self.presentationData = presentationData self.presentationData = presentationData
self.account = account self.account = account
self.packInfo = packInfo self.packInfo = packInfo
@ -68,6 +71,7 @@ public final class ItemListStickerPackItem: ListViewItem, ItemListItem {
self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions
self.addPack = addPack self.addPack = addPack
self.removePack = removePack self.removePack = removePack
self.toggleSelected = toggleSelected
} }
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) { public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
@ -160,6 +164,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
private var layoutParams: (ItemListStickerPackItem, ListViewItemLayoutParams, ItemListNeighbors)? private var layoutParams: (ItemListStickerPackItem, ListViewItemLayoutParams, ItemListNeighbors)?
private var selectableControlNode: ItemListSelectableControlNode?
private var editableControlNode: ItemListEditableControlNode? private var editableControlNode: ItemListEditableControlNode?
private var reorderControlNode: ItemListEditableReorderControlNode? private var reorderControlNode: ItemListEditableReorderControlNode?
@ -168,7 +173,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
private let fetchDisposable = MetaDisposable() private let fetchDisposable = MetaDisposable()
override var canBeSelected: Bool { override var canBeSelected: Bool {
if self.editableControlNode != nil || self.disabledOverlayNode != nil { if self.selectableControlNode != nil || self.editableControlNode != nil || self.disabledOverlayNode != nil {
return false return false
} }
if let item = self.layoutParams?.0, item.action != nil { if let item = self.layoutParams?.0, item.action != nil {
@ -309,12 +314,20 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
} }
} }
override func tapped() {
guard let item = self.layoutParams?.0, item.editing.editing && item.editing.selectable else {
return
}
item.toggleSelected()
}
func asyncLayout() -> (_ item: ItemListStickerPackItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) { func asyncLayout() -> (_ item: ItemListStickerPackItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) {
let makeImageLayout = self.imageNode.asyncLayout() let makeImageLayout = self.imageNode.asyncLayout()
let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let makeStatusLayout = TextNode.asyncLayout(self.statusNode) let makeStatusLayout = TextNode.asyncLayout(self.statusNode)
let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode) let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode)
let reorderControlLayout = ItemListEditableReorderControlNode.asyncLayout(self.reorderControlNode) let reorderControlLayout = ItemListEditableReorderControlNode.asyncLayout(self.reorderControlNode)
let selectableControlLayout = ItemListSelectableControlNode.asyncLayout(self.selectableControlNode)
let previousThumbnailItem = self.currentThumbnailItem let previousThumbnailItem = self.currentThumbnailItem
var currentDisabledOverlayNode = self.disabledOverlayNode var currentDisabledOverlayNode = self.disabledOverlayNode
@ -358,8 +371,8 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
case .selection: case .selection:
rightInset += 16.0 rightInset += 16.0
checkImage = PresentationResourcesItemList.checkIconImage(item.presentationData.theme) checkImage = PresentationResourcesItemList.checkIconImage(item.presentationData.theme)
case .check: default:
rightInset += 16.0 break
} }
var unreadImage: UIImage? var unreadImage: UIImage?
@ -380,14 +393,25 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)? var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)?
var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)? var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)?
var selectableControlSizeAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)?
var editingOffset: CGFloat = 0.0 var editingOffset: CGFloat = 0.0
var reorderInset: CGFloat = 0.0 var reorderInset: CGFloat = 0.0
if item.editing.editing { if item.editing.editing {
let sizeAndApply = editableControlLayout(item.presentationData.theme, false) if item.editing.selectable {
editableControlSizeAndApply = sizeAndApply var selected = false
editingOffset = sizeAndApply.0 if case let .check(checked) = item.control {
selected = checked
}
let sizeAndApply = selectableControlLayout(item.presentationData.theme.list.itemCheckColors.strokeColor, item.presentationData.theme.list.itemCheckColors.fillColor, item.presentationData.theme.list.itemCheckColors.foregroundColor, selected, true)
selectableControlSizeAndApply = sizeAndApply
editingOffset = sizeAndApply.0
} else {
let sizeAndApply = editableControlLayout(item.presentationData.theme, false)
editableControlSizeAndApply = sizeAndApply
editingOffset = sizeAndApply.0
}
if item.editing.reorderable { if item.editing.reorderable {
let sizeAndApply = reorderControlLayout(item.presentationData.theme) let sizeAndApply = reorderControlLayout(item.presentationData.theme)
@ -547,6 +571,31 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
}) })
} }
if let selectableControlSizeAndApply = selectableControlSizeAndApply {
let selectableControlSize = CGSize(width: selectableControlSizeAndApply.0, height: layout.contentSize.height)
let selectableControlFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset, y: 0.0), size: selectableControlSize)
if strongSelf.selectableControlNode == nil {
let selectableControlNode = selectableControlSizeAndApply.1(selectableControlSize, false)
strongSelf.selectableControlNode = selectableControlNode
strongSelf.addSubnode(selectableControlNode)
selectableControlNode.frame = selectableControlFrame
transition.animatePosition(node: selectableControlNode, from: CGPoint(x: -selectableControlFrame.size.width / 2.0, y: selectableControlFrame.midY))
selectableControlNode.alpha = 0.0
transition.updateAlpha(node: selectableControlNode, alpha: 1.0)
} else if let selectableControlNode = strongSelf.selectableControlNode {
transition.updateFrame(node: selectableControlNode, frame: selectableControlFrame)
let _ = selectableControlSizeAndApply.1(selectableControlSize, transition.isAnimated)
}
} else if let selectableControlNode = strongSelf.selectableControlNode {
var selectableControlFrame = selectableControlNode.frame
selectableControlFrame.origin.x = -selectableControlFrame.size.width
strongSelf.selectableControlNode = nil
transition.updateAlpha(node: selectableControlNode, alpha: 0.0)
transition.updateFrame(node: selectableControlNode, frame: selectableControlFrame, completion: { [weak selectableControlNode] _ in
selectableControlNode?.removeFromSupernode()
})
}
if let reorderControlSizeAndApply = reorderControlSizeAndApply { if let reorderControlSizeAndApply = reorderControlSizeAndApply {
if strongSelf.reorderControlNode == nil { if strongSelf.reorderControlNode == nil {
let reorderControlNode = reorderControlSizeAndApply.1(layout.contentSize.height, false, .immediate) let reorderControlNode = reorderControlSizeAndApply.1(layout.contentSize.height, false, .immediate)

View File

@ -76,10 +76,20 @@ public struct ItemListToolbarItem {
public let title: String public let title: String
public let isEnabled: Bool public let isEnabled: Bool
public let action: () -> Void public let action: () -> Void
public init(title: String, isEnabled: Bool, action: @escaping () -> Void) {
self.title = title
self.isEnabled = isEnabled
self.action = action
}
} }
let actions: [Action] let actions: [Action]
public init(actions: [Action]) {
self.actions = actions
}
var toolbar: Toolbar { var toolbar: Toolbar {
var leftAction: ToolbarAction? var leftAction: ToolbarAction?
var middleAction: ToolbarAction? var middleAction: ToolbarAction?
@ -644,9 +654,25 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
return transition.strings.VoiceOver_ScrollStatus(row, count).0 return transition.strings.VoiceOver_ScrollStatus(row, count).0
} }
let toolbarFrame = CGRect()
let layoutTransition: ContainedViewLayoutTransition = .immediate let layoutTransition: ContainedViewLayoutTransition = .immediate
if let toolbarItem = transition.toolbarItem, let (layout, _) = self.validLayout { 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 { if let toolbarNode = self.toolbarNode {
layoutTransition.updateFrame(node: toolbarNode, frame: toolbarFrame) 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) 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)

View File

@ -228,7 +228,7 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
case let .packsTitle(_, text): case let .packsTitle(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .pack(_, _, _, info, topItem, count, playAnimatedStickers, selected): case let .pack(_, _, _, info, topItem, count, playAnimatedStickers, selected):
return ItemListStickerPackItem(presentationData: presentationData, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: selected ? .selection : .none, editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, playAnimatedStickers: playAnimatedStickers, sectionId: self.section, action: { return ItemListStickerPackItem(presentationData: presentationData, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: selected ? .selection : .none, editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false, selectable: false), enabled: true, playAnimatedStickers: playAnimatedStickers, sectionId: self.section, action: {
if selected { if selected {
arguments.openStickerPack(info) arguments.openStickerPack(info)
} else { } else {
@ -237,6 +237,7 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
}, setPackIdWithRevealedOptions: { _, _ in }, setPackIdWithRevealedOptions: { _, _ in
}, addPack: { }, addPack: {
}, removePack: { }, removePack: {
}, toggleSelected: {
}) })
case let .currentPack(_, theme, strings, content): case let .currentPack(_, theme, strings, content):
return GroupStickerPackCurrentItem(theme: theme, strings: strings, account: arguments.account, content: content, sectionId: self.section, action: { return GroupStickerPackCurrentItem(theme: theme, strings: strings, account: arguments.account, content: content, sectionId: self.section, action: {

View File

@ -169,6 +169,7 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
arguments.addPack(info) arguments.addPack(info)
}, removePack: { }, removePack: {
arguments.removePack(info) arguments.removePack(info)
}, toggleSelected: {
}) })
} }
} }
@ -232,7 +233,7 @@ private func archivedStickerPacksControllerEntries(presentationData: Presentatio
var index: Int32 = 0 var index: Int32 = 0
for item in packs { for item in packs {
if !installedIds.contains(item.info.id) { if !installedIds.contains(item.info.id) {
entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, item.topItems.first, presentationData.strings.StickerPack_StickerCount(item.info.count), stickerSettings.loopAnimatedStickers, !state.removingPackIds.contains(item.info.id), ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == item.info.id, reorderable: false))) entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, item.topItems.first, presentationData.strings.StickerPack_StickerCount(item.info.count), stickerSettings.loopAnimatedStickers, !state.removingPackIds.contains(item.info.id), ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == item.info.id, reorderable: false, selectable: true)))
index += 1 index += 1
} }
} }

View File

@ -121,12 +121,13 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry {
let arguments = arguments as! FeaturedStickerPacksControllerArguments let arguments = arguments as! FeaturedStickerPacksControllerArguments
switch self { switch self {
case let .pack(_, theme, strings, info, unread, topItem, count, playAnimatedStickers, installed): case let .pack(_, theme, strings, info, unread, topItem, count, playAnimatedStickers, installed):
return ItemListStickerPackItem(presentationData: presentationData, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: unread, control: .installation(installed: installed), editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, playAnimatedStickers: playAnimatedStickers, sectionId: self.section, action: { return ItemListStickerPackItem(presentationData: presentationData, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: unread, control: .installation(installed: installed), editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false, selectable: false), enabled: true, playAnimatedStickers: playAnimatedStickers, sectionId: self.section, action: {
arguments.openStickerPack(info) arguments.openStickerPack(info)
}, setPackIdWithRevealedOptions: { _, _ in }, setPackIdWithRevealedOptions: { _, _ in
}, addPack: { }, addPack: {
arguments.addPack(info) arguments.addPack(info)
}, removePack: { }, removePack: {
}, toggleSelected: {
}) })
} }
} }

View File

@ -14,6 +14,7 @@ import AccountContext
import StickerPackPreviewUI import StickerPackPreviewUI
import ItemListStickerPackItem import ItemListStickerPackItem
import UndoUI import UndoUI
import ShareController
private final class InstalledStickerPacksControllerArguments { private final class InstalledStickerPacksControllerArguments {
let account: Account let account: Account
@ -27,8 +28,9 @@ private final class InstalledStickerPacksControllerArguments {
let openArchived: ([ArchivedStickerPackItem]?) -> Void let openArchived: ([ArchivedStickerPackItem]?) -> Void
let openSuggestOptions: () -> Void let openSuggestOptions: () -> Void
let toggleAnimatedStickers: (Bool) -> Void let toggleAnimatedStickers: (Bool) -> Void
let togglePackSelected: (ItemCollectionId) -> Void
init(account: Account, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, removePack: @escaping (ArchivedStickerPackItem) -> Void, openStickersBot: @escaping () -> Void, openMasks: @escaping () -> Void, openFeatured: @escaping () -> Void, openArchived: @escaping ([ArchivedStickerPackItem]?) -> Void, openSuggestOptions: @escaping () -> Void, toggleAnimatedStickers: @escaping (Bool) -> Void) { init(account: Account, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, removePack: @escaping (ArchivedStickerPackItem) -> Void, openStickersBot: @escaping () -> Void, openMasks: @escaping () -> Void, openFeatured: @escaping () -> Void, openArchived: @escaping ([ArchivedStickerPackItem]?) -> Void, openSuggestOptions: @escaping () -> Void, toggleAnimatedStickers: @escaping (Bool) -> Void, togglePackSelected: @escaping (ItemCollectionId) -> Void) {
self.account = account self.account = account
self.openStickerPack = openStickerPack self.openStickerPack = openStickerPack
self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions
@ -39,6 +41,7 @@ private final class InstalledStickerPacksControllerArguments {
self.openArchived = openArchived self.openArchived = openArchived
self.openSuggestOptions = openSuggestOptions self.openSuggestOptions = openSuggestOptions
self.toggleAnimatedStickers = toggleAnimatedStickers self.toggleAnimatedStickers = toggleAnimatedStickers
self.togglePackSelected = togglePackSelected
} }
} }
@ -99,7 +102,7 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
case animatedStickers(PresentationTheme, String, Bool) case animatedStickers(PresentationTheme, String, Bool)
case animatedStickersInfo(PresentationTheme, String) case animatedStickersInfo(PresentationTheme, String)
case packsTitle(PresentationTheme, String) case packsTitle(PresentationTheme, String)
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing) case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing, Bool?)
case packsInfo(PresentationTheme, String) case packsInfo(PresentationTheme, String)
var section: ItemListSectionId { var section: ItemListSectionId {
@ -127,7 +130,7 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
return .index(5) return .index(5)
case .packsTitle: case .packsTitle:
return .index(6) return .index(6)
case let .pack(_, _, _, info, _, _, _, _, _): case let .pack(_, _, _, info, _, _, _, _, _, _):
return .pack(info.id) return .pack(info.id)
case .packsInfo: case .packsInfo:
return .index(7) return .index(7)
@ -178,8 +181,8 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsAnimatedStickers, lhsEnabled, lhsEditing): case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsAnimatedStickers, lhsEnabled, lhsEditing, lhsSelected):
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsAnimatedStickers, rhsEnabled, rhsEditing) = rhs { if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsAnimatedStickers, rhsEnabled, rhsEditing, rhsSelected) = rhs {
if lhsIndex != rhsIndex { if lhsIndex != rhsIndex {
return false return false
} }
@ -207,6 +210,9 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
if lhsEditing != rhsEditing { if lhsEditing != rhsEditing {
return false return false
} }
if lhsSelected != rhsSelected {
return false
}
return true return true
} else { } else {
return false return false
@ -271,9 +277,9 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
default: default:
return true return true
} }
case let .pack(lhsIndex, _, _, _, _, _, _, _, _): case let .pack(lhsIndex, _, _, _, _, _, _, _, _, _):
switch rhs { switch rhs {
case let .pack(rhsIndex, _, _, _, _, _, _, _, _): case let .pack(rhsIndex, _, _, _, _, _, _, _, _, _):
return lhsIndex < rhsIndex return lhsIndex < rhsIndex
case .packsInfo: case .packsInfo:
return true return true
@ -317,14 +323,16 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .packsTitle(_, text): case let .packsTitle(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .pack(_, _, strings, info, topItem, count, animatedStickers, enabled, editing): case let .pack(_, _, _, info, topItem, count, animatedStickers, enabled, editing, selected):
return ItemListStickerPackItem(presentationData: presentationData, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: .none, editing: editing, enabled: enabled, playAnimatedStickers: animatedStickers, sectionId: self.section, action: { return ItemListStickerPackItem(presentationData: presentationData, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: editing.editing ? .check(checked: selected ?? false) : .none, editing: editing, enabled: enabled, playAnimatedStickers: animatedStickers, sectionId: self.section, action: {
arguments.openStickerPack(info) arguments.openStickerPack(info)
}, setPackIdWithRevealedOptions: { current, previous in }, setPackIdWithRevealedOptions: { current, previous in
arguments.setPackIdWithRevealedOptions(current, previous) arguments.setPackIdWithRevealedOptions(current, previous)
}, addPack: { }, addPack: {
}, removePack: { }, removePack: {
arguments.removePack(ArchivedStickerPackItem(info: info, topItems: topItem != nil ? [topItem!] : [])) arguments.removePack(ArchivedStickerPackItem(info: info, topItems: topItem != nil ? [topItem!] : []))
}, toggleSelected: {
arguments.togglePackSelected(info.id)
}) })
case let .packsInfo(_, text): case let .packsInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section, linkAction: { _ in return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section, linkAction: { _ in
@ -336,7 +344,7 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry {
private struct InstalledStickerPacksControllerState: Equatable { private struct InstalledStickerPacksControllerState: Equatable {
let editing: Bool let editing: Bool
let selectedPackIds: Set<Int64>? let selectedPackIds: Set<ItemCollectionId>?
let packIdWithRevealedOptions: ItemCollectionId? let packIdWithRevealedOptions: ItemCollectionId?
init() { init() {
@ -345,7 +353,7 @@ private struct InstalledStickerPacksControllerState: Equatable {
self.packIdWithRevealedOptions = nil self.packIdWithRevealedOptions = nil
} }
init(editing: Bool, selectedPackIds: Set<Int64>?, packIdWithRevealedOptions: ItemCollectionId?) { init(editing: Bool, selectedPackIds: Set<ItemCollectionId>?, packIdWithRevealedOptions: ItemCollectionId?) {
self.editing = editing self.editing = editing
self.selectedPackIds = selectedPackIds self.selectedPackIds = selectedPackIds
self.packIdWithRevealedOptions = packIdWithRevealedOptions self.packIdWithRevealedOptions = packIdWithRevealedOptions
@ -369,7 +377,7 @@ private struct InstalledStickerPacksControllerState: Equatable {
return InstalledStickerPacksControllerState(editing: editing, selectedPackIds: self.selectedPackIds, packIdWithRevealedOptions: self.packIdWithRevealedOptions) return InstalledStickerPacksControllerState(editing: editing, selectedPackIds: self.selectedPackIds, packIdWithRevealedOptions: self.packIdWithRevealedOptions)
} }
func withUpdatedSelectedPackIds(_ selectedPackIds: Set<Int64>) -> InstalledStickerPacksControllerState { func withUpdatedSelectedPackIds(_ selectedPackIds: Set<ItemCollectionId>?) -> InstalledStickerPacksControllerState {
return InstalledStickerPacksControllerState(editing: editing, selectedPackIds: selectedPackIds, packIdWithRevealedOptions: self.packIdWithRevealedOptions) return InstalledStickerPacksControllerState(editing: editing, selectedPackIds: selectedPackIds, packIdWithRevealedOptions: self.packIdWithRevealedOptions)
} }
@ -454,7 +462,7 @@ private func installedStickerPacksControllerEntries(presentationData: Presentati
var index: Int32 = 0 var index: Int32 = 0
for entry in sortedPacks { for entry in sortedPacks {
if let info = entry.info as? StickerPackCollectionInfo { if let info = entry.info as? StickerPackCollectionInfo {
entries.append(.pack(index, presentationData.theme, presentationData.strings, info, entry.firstItem as? StickerPackItem, presentationData.strings.StickerPack_StickerCount(info.count == 0 ? entry.count : info.count), stickerSettings.loopAnimatedStickers, true, ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == entry.id, reorderable: true))) entries.append(.pack(index, presentationData.theme, presentationData.strings, info, entry.firstItem as? StickerPackItem, presentationData.strings.StickerPack_StickerCount(info.count == 0 ? entry.count : info.count), stickerSettings.loopAnimatedStickers, true, ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == entry.id, reorderable: true, selectable: true), state.selectedPackIds?.contains(info.id)))
index += 1 index += 1
} }
} }
@ -485,7 +493,7 @@ public enum InstalledStickerPacksControllerMode {
} }
public func installedStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, archivedPacks: [ArchivedStickerPackItem]? = nil, updatedPacks: @escaping ([ArchivedStickerPackItem]?) -> Void = { _ in }, focusOnItemTag: InstalledStickerPacksEntryTag? = nil) -> ViewController { public func installedStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, archivedPacks: [ArchivedStickerPackItem]? = nil, updatedPacks: @escaping ([ArchivedStickerPackItem]?) -> Void = { _ in }, focusOnItemTag: InstalledStickerPacksEntryTag? = nil) -> ViewController {
let initialState = InstalledStickerPacksControllerState().withUpdatedEditing(mode == .modal) let initialState = InstalledStickerPacksControllerState().withUpdatedEditing(mode == .modal).withUpdatedSelectedPackIds(mode == .modal ? Set() : nil)
let statePromise = ValuePromise(initialState, ignoreRepeated: true) let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let stateValue = Atomic(value: initialState) let stateValue = Atomic(value: initialState)
let updateState: ((InstalledStickerPacksControllerState) -> InstalledStickerPacksControllerState) -> Void = { f in let updateState: ((InstalledStickerPacksControllerState) -> InstalledStickerPacksControllerState) -> Void = { f in
@ -624,6 +632,19 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
let _ = updateStickerSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in let _ = updateStickerSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return current.withUpdatedLoopAnimatedStickers(value) return current.withUpdatedLoopAnimatedStickers(value)
}).start() }).start()
}, togglePackSelected: { packId in
updateState { state in
if var selectedPackIds = state.selectedPackIds {
if selectedPackIds.contains(packId) {
selectedPackIds.remove(packId)
} else {
selectedPackIds.insert(packId)
}
return state.withUpdatedSelectedPackIds(selectedPackIds)
} else {
return state
}
}
}) })
let stickerPacks = Promise<CombinedView>() let stickerPacks = Promise<CombinedView>()
stickerPacks.set(context.account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [namespaceForMode(mode)])])) stickerPacks.set(context.account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [namespaceForMode(mode)])]))
@ -669,16 +690,32 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
if state.editing { if state.editing {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: { rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
updateState { updateState {
$0.withUpdatedEditing(false) $0.withUpdatedEditing(false).withUpdatedSelectedPackIds(nil)
} }
if case .modal = mode { if case .modal = mode {
dismissImpl?() dismissImpl?()
} }
}) })
let enabled = (state.selectedPackIds?.count ?? 0) > 0
toolbarItem = ItemListToolbarItem(actions: [.init(title: "Delete", isEnabled: enabled, action: {
updateState {
$0.withUpdatedEditing(false).withUpdatedSelectedPackIds(nil)
}
}), .init(title: "Archive", isEnabled: enabled, action: {
updateState {
$0.withUpdatedEditing(false).withUpdatedSelectedPackIds(nil)
}
}), .init(title: "Share", isEnabled: enabled, action: {
updateState {
$0.withUpdatedEditing(false).withUpdatedSelectedPackIds(nil)
}
let shareController = ShareController(context: context, subject: .text("test"), externalShare: true)
presentControllerImpl?(shareController, nil)
})])
} else { } else {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: { rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: {
updateState { updateState {
$0.withUpdatedEditing(true) $0.withUpdatedEditing(true).withUpdatedSelectedPackIds(Set())
} }
}) })
} }
@ -711,7 +748,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
} }
controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [InstalledStickerPacksEntry]) -> Signal<Bool, NoError> in controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [InstalledStickerPacksEntry]) -> Signal<Bool, NoError> in
let fromEntry = entries[fromIndex] let fromEntry = entries[fromIndex]
guard case let .pack(_, _, _, fromPackInfo, _, _, _, _, _) = fromEntry else { guard case let .pack(_, _, _, fromPackInfo, _, _, _, _, _, _) = fromEntry else {
return .single(false) return .single(false)
} }
var referenceId: ItemCollectionId? var referenceId: ItemCollectionId?
@ -719,7 +756,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
var afterAll = false var afterAll = false
if toIndex < entries.count { if toIndex < entries.count {
switch entries[toIndex] { switch entries[toIndex] {
case let .pack(_, _, _, toPackInfo, _, _, _, _, _): case let .pack(_, _, _, toPackInfo, _, _, _, _, _, _):
referenceId = toPackInfo.id referenceId = toPackInfo.id
default: default:
if entries[toIndex] < fromEntry { if entries[toIndex] < fromEntry {

View File

@ -743,7 +743,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
activityRank: nil, activityRank: nil,
muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
volume: nil, volume: nil,
about: about about: about,
raiseHandRating: nil
)) ))
participants.sort() participants.sort()
} }
@ -901,7 +902,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
strongSelf.requestDisposable.set((joinGroupCall( strongSelf.requestDisposable.set((joinGroupCall(
account: strongSelf.account, account: strongSelf.account,
peerId: strongSelf.peerId, peerId: strongSelf.peerId,
joinAs: strongSelf.joinAsPeerId == strongSelf.account.peerId ? nil : strongSelf.joinAsPeerId, joinAs: strongSelf.joinAsPeerId,
callId: callInfo.id, callId: callInfo.id,
accessHash: callInfo.accessHash, accessHash: callInfo.accessHash,
preferMuted: true, preferMuted: true,
@ -1456,7 +1457,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if id == peerId { if id == peerId {
self.callContext?.setVolume(ssrc: ssrc, volume: Double(volume) / 10000.0) self.callContext?.setVolume(ssrc: ssrc, volume: Double(volume) / 10000.0)
if sync { if sync {
self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, volume: volume) self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, volume: volume, raiseHand: nil)
} }
break break
} }
@ -1564,19 +1565,19 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
canThenUnmute = true canThenUnmute = true
} }
let muteState = isMuted ? GroupCallParticipantsContext.Participant.MuteState(canUnmute: canThenUnmute, mutedByYou: mutedByYou) : nil let muteState = isMuted ? GroupCallParticipantsContext.Participant.MuteState(canUnmute: canThenUnmute, mutedByYou: mutedByYou) : nil
self.participantsContext?.updateMuteState(peerId: peerId, muteState: muteState, volume: nil) self.participantsContext?.updateMuteState(peerId: peerId, muteState: muteState, volume: nil, raiseHand: nil)
return muteState return muteState
} else { } else {
if peerId == self.joinAsPeerId { if peerId == self.joinAsPeerId {
self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, volume: nil) self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, volume: nil, raiseHand: nil)
return nil return nil
} else if self.stateValue.canManageCall || self.stateValue.adminIds.contains(self.accountContext.account.peerId) { } else if self.stateValue.canManageCall || self.stateValue.adminIds.contains(self.accountContext.account.peerId) {
let muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false) let muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false)
self.participantsContext?.updateMuteState(peerId: peerId, muteState: muteState, volume: nil) self.participantsContext?.updateMuteState(peerId: peerId, muteState: muteState, volume: nil, raiseHand: nil)
return muteState return muteState
} else { } else {
self.setVolume(peerId: peerId, volume: 10000, sync: true) self.setVolume(peerId: peerId, volume: 10000, sync: true)
self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, volume: nil) self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, volume: nil, raiseHand: nil)
return nil return nil
} }
} }

View File

@ -1769,7 +1769,11 @@ public func cachedGroupCallDisplayAsAvailablePeers(account: Account) -> Signal<[
var peers: [FoundPeer] = [] var peers: [FoundPeer] = []
for peerId in cached.peerIds { for peerId in cached.peerIds {
if let peer = transaction.getPeer(peerId) { if let peer = transaction.getPeer(peerId) {
peers.append(FoundPeer(peer: peer, subscribers: nil)) var subscribers: Int32?
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData {
subscribers = cachedData.participantsSummary.memberCount
}
peers.append(FoundPeer(peer: peer, subscribers: subscribers))
} }
} }
return (peers, cached.timestamp) return (peers, cached.timestamp)

View File

@ -563,7 +563,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
statusController?.dismiss() statusController?.dismiss()
} }
strongSelf.present(statusController, in: .window(.root)) strongSelf.present(statusController, in: .window(.root))
strongSelf.createVoiceChatDisposable.set((createGroupCall(account: strongSelf.context.account, peerId: message.id.peerId, joinAs: nil) strongSelf.createVoiceChatDisposable.set((createGroupCall(account: strongSelf.context.account, peerId: message.id.peerId, joinAs: strongSelf.context.account.peerId)
|> deliverOnMainQueue).start(next: { [weak self] info in |> deliverOnMainQueue).start(next: { [weak self] info in
guard let strongSelf = self else { guard let strongSelf = self else {
return return

View File

@ -9,6 +9,8 @@ import Display
import TelegramPresentationData import TelegramPresentationData
import AccountContext import AccountContext
import LocalizedPeerData import LocalizedPeerData
import AlertUI
import PresentationDataUtils
func textStringForForwardedMessage(_ message: Message, strings: PresentationStrings) -> (String, Bool) { func textStringForForwardedMessage(_ message: Message, strings: PresentationStrings) -> (String, Bool) {
for media in message.media { for media in message.media {
@ -81,11 +83,15 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode {
private let actionArea: AccessibilityAreaNode private let actionArea: AccessibilityAreaNode
let context: AccountContext
var theme: PresentationTheme var theme: PresentationTheme
var strings: PresentationStrings
init(context: AccountContext, messageIds: [MessageId], theme: PresentationTheme, strings: PresentationStrings) { init(context: AccountContext, messageIds: [MessageId], theme: PresentationTheme, strings: PresentationStrings) {
self.context = context
self.messageIds = messageIds self.messageIds = messageIds
self.theme = theme self.theme = theme
self.strings = strings
self.closeButton = ASButtonNode() self.closeButton = ASButtonNode()
self.closeButton.accessibilityLabel = strings.VoiceOver_DiscardPreparedContent self.closeButton.accessibilityLabel = strings.VoiceOver_DiscardPreparedContent
@ -167,8 +173,9 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode {
} }
override func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { override func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
if self.theme !== theme { if self.theme !== theme || self.strings !== strings {
self.theme = theme self.theme = theme
self.strings = strings
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(theme), for: []) self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(theme), for: [])
@ -215,9 +222,12 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode {
} }
@objc func closePressed() { @objc func closePressed() {
if let dismiss = self.dismiss { let alertController = textAlertController(context: self.context, title: self.strings.Conversation_CancelForwardTitle, text: self.strings.Conversation_CancelForwardText, actions: [TextAlertAction(type: .genericAction, title: self.strings.Conversation_CancelForwardSelectChat, action: { [weak self] in
dismiss() self?.interfaceInteraction?.forwardCurrentForwardMessages()
} }), TextAlertAction(type: .defaultAction, title: self.strings.Conversation_CancelForwardCancelForward, action: { [weak self] in
self?.dismiss?()
})], actionLayout: .vertical)
self.interfaceInteraction?.presentController(alertController, nil)
} }
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) { @objc func tapGesture(_ recognizer: UITapGestureRecognizer) {

View File

@ -3828,7 +3828,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
statusController?.dismiss() statusController?.dismiss()
} }
strongSelf.controller?.present(statusController, in: .window(.root)) strongSelf.controller?.present(statusController, in: .window(.root))
strongSelf.activeActionDisposable.set((createGroupCall(account: strongSelf.context.account, peerId: peerId, joinAs: joinAsPeerId) strongSelf.activeActionDisposable.set((createGroupCall(account: strongSelf.context.account, peerId: peerId, joinAs: joinAsPeerId ?? strongSelf.context.account.peerId)
|> deliverOnMainQueue).start(next: { [weak self] info in |> deliverOnMainQueue).start(next: { [weak self] info in
guard let strongSelf = self else { guard let strongSelf = self else {
return return

View File

@ -28,6 +28,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
private let animationNode: AnimationNode? private let animationNode: AnimationNode?
private var animatedStickerNode: AnimatedStickerNode? private var animatedStickerNode: AnimatedStickerNode?
private var slotMachineNode: SlotMachineAnimationNode? private var slotMachineNode: SlotMachineAnimationNode?
private var recordingIconNode: RecordingIconNode?
private var stillStickerNode: TransformImageNode? private var stillStickerNode: TransformImageNode?
private var stickerImageSize: CGSize? private var stickerImageSize: CGSize?
private var stickerOffset: CGPoint? private var stickerOffset: CGPoint?
@ -555,9 +556,11 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.avatarNode = nil self.avatarNode = nil
self.iconNode = nil self.iconNode = nil
self.iconCheckNode = nil self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_linkrevoked", colors: [:], scale: 0.066) self.animationNode = nil
self.animatedStickerNode = nil self.animatedStickerNode = nil
self.recordingIconNode = RecordingIconNode()
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white) let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(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) let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .natural)
@ -609,6 +612,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode) self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
self.slotMachineNode.flatMap(self.panelWrapperNode.addSubnode) self.slotMachineNode.flatMap(self.panelWrapperNode.addSubnode)
self.avatarNode.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.titleNode)
self.panelWrapperNode.addSubnode(self.textNode) self.panelWrapperNode.addSubnode(self.textNode)
self.panelWrapperNode.addSubnode(self.buttonNode) self.panelWrapperNode.addSubnode(self.buttonNode)
@ -834,6 +838,12 @@ 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) 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) 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)) 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)) 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))
@ -909,3 +919,46 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
return super.hitTest(point, with: event) 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)
}
}