mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-03 21:16:35 +00:00
Various fixes and improvements
This commit is contained in:
parent
0ecc138ff6
commit
b14e81b767
@ -6151,3 +6151,5 @@ Sorry for the inconvenience.";
|
|||||||
"VoiceOver.ScrollStatus" = "Row %1$@ of %2$@";
|
"VoiceOver.ScrollStatus" = "Row %1$@ of %2$@";
|
||||||
|
|
||||||
"Conversation.UsersTooMuchError" = "Sorry, this group is full.";
|
"Conversation.UsersTooMuchError" = "Sorry, this group is full.";
|
||||||
|
|
||||||
|
"Conversation.UploadFileTooLarge" = "File could not be sent, because it is larger than 2 GB.\n\nYou can send as many files as you like, but each must be smaller than 2 GB.";
|
||||||
|
|||||||
@ -162,6 +162,7 @@ public enum ResolvedUrlSettingsSection {
|
|||||||
|
|
||||||
public enum ResolvedUrl {
|
public enum ResolvedUrl {
|
||||||
case externalUrl(String)
|
case externalUrl(String)
|
||||||
|
case urlAuth(String)
|
||||||
case peer(PeerId?, ChatControllerInteractionNavigateToPeer)
|
case peer(PeerId?, ChatControllerInteractionNavigateToPeer)
|
||||||
case inaccessiblePeer
|
case inaccessiblePeer
|
||||||
case botStart(peerId: PeerId, payload: String)
|
case botStart(peerId: PeerId, payload: String)
|
||||||
@ -603,7 +604,7 @@ public protocol SharedAccountContext: class {
|
|||||||
func chatAvailableMessageActions(postbox: Postbox, accountPeerId: PeerId, messageIds: Set<MessageId>) -> Signal<ChatAvailableMessageActions, NoError>
|
func chatAvailableMessageActions(postbox: Postbox, accountPeerId: PeerId, messageIds: Set<MessageId>) -> Signal<ChatAvailableMessageActions, NoError>
|
||||||
func chatAvailableMessageActions(postbox: Postbox, accountPeerId: PeerId, messageIds: Set<MessageId>, messages: [MessageId: Message], peers: [PeerId: Peer]) -> Signal<ChatAvailableMessageActions, NoError>
|
func chatAvailableMessageActions(postbox: Postbox, accountPeerId: PeerId, messageIds: Set<MessageId>, messages: [MessageId: Message], peers: [PeerId: Peer]) -> Signal<ChatAvailableMessageActions, NoError>
|
||||||
func resolveUrl(account: Account, url: String) -> Signal<ResolvedUrl, NoError>
|
func resolveUrl(account: Account, url: String) -> Signal<ResolvedUrl, NoError>
|
||||||
func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?)
|
func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?)
|
||||||
func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void)
|
func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void)
|
||||||
func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void)
|
func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void)
|
||||||
func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void)
|
func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void)
|
||||||
|
|||||||
@ -527,6 +527,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.titleView.theme = self.presentationData.theme
|
self.titleView.theme = self.presentationData.theme
|
||||||
|
self.titleView.strings = self.presentationData.strings
|
||||||
|
|
||||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
||||||
|
|||||||
@ -166,8 +166,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peerId, navigation in
|
context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peerId, navigation in
|
||||||
// self?.openPeer(peerId: peerId, navigation: navigation)
|
// self?.openPeer(peerId: peerId, navigation: navigation)
|
||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: nil,
|
sendSticker: nil,
|
||||||
present: { c, a in
|
requestMessageActionUrlAuth: nil,
|
||||||
|
present: { c, a in
|
||||||
present(c, a)
|
present(c, a)
|
||||||
}, dismissInput: {
|
}, dismissInput: {
|
||||||
self?.dismissInput()
|
self?.dismissInput()
|
||||||
|
|||||||
@ -511,7 +511,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
|||||||
peerContextAction(peer.peer, .search(nil), node, gesture)
|
peerContextAction(peer.peer, .search(nil), node, gesture)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
case let .message(message, peer, readState, presentationData, totalCount, selected, displayCustomHeader):
|
case let .message(message, peer, readState, presentationData, _, selected, displayCustomHeader):
|
||||||
let header = ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
|
let header = ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
|
||||||
let selection: ChatHistoryMessageSelection = selected.flatMap { .selectable(selected: $0) } ?? .none
|
let selection: ChatHistoryMessageSelection = selected.flatMap { .selectable(selected: $0) } ?? .none
|
||||||
if let tagMask = tagMask, tagMask != .photoOrVideo {
|
if let tagMask = tagMask, tagMask != .photoOrVideo {
|
||||||
|
|||||||
@ -68,7 +68,11 @@ final class ChatListTitleView: UIView, NavigationBarTitleView, NavigationBarTitl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var strings: PresentationStrings
|
var strings: PresentationStrings {
|
||||||
|
didSet {
|
||||||
|
self.proxyButton.accessibilityLabel = self.strings.VoiceOver_Navigation_ProxySettings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init(theme: PresentationTheme, strings: PresentationStrings) {
|
init(theme: PresentationTheme, strings: PresentationStrings) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
|||||||
@ -132,9 +132,7 @@ public class InviteContactsController: ViewController, MFMessageComposeViewContr
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let url = strongSelf.presentationData.strings.InviteText_URL
|
let url = strongSelf.presentationData.strings.InviteText_URL
|
||||||
let body = strongSelf.presentationData.strings.InviteText_SingleContact(url).0
|
let body = strongSelf.presentationData.strings.InviteText_SingleContact(url).0
|
||||||
|
presentExternalShare(context: strongSelf.context, text: body, parentController: strongSelf)
|
||||||
let shareController = ShareController(context: strongSelf.context, subject: .text(body), externalShare: true, immediateExternalShare: true)
|
|
||||||
strongSelf.present(shareController, in: .window(.root))
|
|
||||||
|
|
||||||
strongSelf.contactsNode.listNode.clearHighlightAnimated(true)
|
strongSelf.contactsNode.listNode.clearHighlightAnimated(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1249,11 +1249,16 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var forceHidden = false {
|
||||||
|
didSet {
|
||||||
|
self.updateItemNodeVisibilititesAndScrolling()
|
||||||
|
}
|
||||||
|
}
|
||||||
private func updateItemNodeVisibilititesAndScrolling() {
|
private func updateItemNodeVisibilititesAndScrolling() {
|
||||||
let visibleRect = self.scrollView.bounds
|
let visibleRect = self.scrollView.bounds
|
||||||
let isScrolling = self.scrollView.isDragging || self.scrollView.isDecelerating
|
let isScrolling = self.scrollView.isDragging || self.scrollView.isDecelerating
|
||||||
for (_, itemNode) in self.itemNodes {
|
for (_, itemNode) in self.itemNodes {
|
||||||
let visible = itemNode.frame.intersects(visibleRect)
|
let visible = itemNode.frame.intersects(visibleRect) && !self.forceHidden
|
||||||
if itemNode.isVisibleInGrid != visible {
|
if itemNode.isVisibleInGrid != visible {
|
||||||
itemNode.isVisibleInGrid = visible
|
itemNode.isVisibleInGrid = visible
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,8 @@ public final class PeekController: ViewController {
|
|||||||
private let content: PeekControllerContent
|
private let content: PeekControllerContent
|
||||||
var sourceNode: () -> ASDisplayNode?
|
var sourceNode: () -> ASDisplayNode?
|
||||||
|
|
||||||
|
public var visibilityUpdated: ((Bool) -> Void)?
|
||||||
|
|
||||||
private var animatedIn = false
|
private var animatedIn = false
|
||||||
|
|
||||||
public init(theme: PeekControllerTheme, content: PeekControllerContent, sourceNode: @escaping () -> ASDisplayNode?) {
|
public init(theme: PeekControllerTheme, content: PeekControllerContent, sourceNode: @escaping () -> ASDisplayNode?) {
|
||||||
@ -67,6 +69,8 @@ public final class PeekController: ViewController {
|
|||||||
if !self.animatedIn {
|
if !self.animatedIn {
|
||||||
self.animatedIn = true
|
self.animatedIn = true
|
||||||
self.controllerNode.animateIn(from: self.getSourceRect())
|
self.controllerNode.animateIn(from: self.getSourceRect())
|
||||||
|
|
||||||
|
self.visibilityUpdated?(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +81,7 @@ public final class PeekController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||||
|
self.visibilityUpdated?(false)
|
||||||
self.controllerNode.animateOut(to: self.getSourceRect(), completion: { [weak self] in
|
self.controllerNode.animateOut(to: self.getSourceRect(), completion: { [weak self] in
|
||||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -504,6 +504,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
if !displayInfo {
|
if !displayInfo {
|
||||||
authorNameText = ""
|
authorNameText = ""
|
||||||
dateText = ""
|
dateText = ""
|
||||||
|
canEdit = false
|
||||||
}
|
}
|
||||||
|
|
||||||
var messageText = NSAttributedString(string: "")
|
var messageText = NSAttributedString(string: "")
|
||||||
|
|||||||
@ -665,7 +665,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
playing = true
|
playing = true
|
||||||
case let .buffering(_, whilePlaying, _, display):
|
case let .buffering(_, whilePlaying, _, display):
|
||||||
displayProgress = display
|
displayProgress = display
|
||||||
initialBuffering = true
|
initialBuffering = !whilePlaying
|
||||||
isPaused = !whilePlaying
|
isPaused = !whilePlaying
|
||||||
var isStreaming = false
|
var isStreaming = false
|
||||||
if let fetchStatus = strongSelf.fetchStatus {
|
if let fetchStatus = strongSelf.fetchStatus {
|
||||||
|
|||||||
@ -1198,6 +1198,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: nil,
|
sendSticker: nil,
|
||||||
|
requestMessageActionUrlAuth: nil,
|
||||||
present: { c, a in
|
present: { c, a in
|
||||||
self?.present(c, a)
|
self?.present(c, a)
|
||||||
}, dismissInput: {
|
}, dismissInput: {
|
||||||
@ -1275,6 +1276,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
fromPlayingVideo = true
|
fromPlayingVideo = true
|
||||||
entries.append(InstantPageGalleryEntry(index: Int32(media.index), pageId: webPage.webpageId, media: media, caption: media.caption, credit: media.credit, location: nil))
|
entries.append(InstantPageGalleryEntry(index: Int32(media.index), pageId: webPage.webpageId, media: media, caption: media.caption, credit: media.credit, location: nil))
|
||||||
} else {
|
} else {
|
||||||
|
fromPlayingVideo = true
|
||||||
var medias: [InstantPageMedia] = mediasFromItems(items)
|
var medias: [InstantPageMedia] = mediasFromItems(items)
|
||||||
medias = medias.filter {
|
medias = medias.filter {
|
||||||
return $0.media is TelegramMediaImage || $0.media is TelegramMediaFile
|
return $0.media is TelegramMediaImage || $0.media is TelegramMediaFile
|
||||||
|
|||||||
@ -615,24 +615,26 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextEdit, icon: { theme in
|
if !invite.isPermanent {
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextEdit, icon: { theme in
|
||||||
}, action: { _, f in
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||||
f(.default)
|
}, action: { _, f in
|
||||||
|
f(.default)
|
||||||
let controller = inviteLinkEditController(context: context, peerId: peerId, invite: invite, completion: { invite in
|
|
||||||
if let invite = invite {
|
let controller = inviteLinkEditController(context: context, peerId: peerId, invite: invite, completion: { invite in
|
||||||
if invite.isRevoked {
|
if let invite = invite {
|
||||||
invitesContext.remove(invite)
|
if invite.isRevoked {
|
||||||
revokedInvitesContext.add(invite.withUpdated(isRevoked: true))
|
invitesContext.remove(invite)
|
||||||
} else {
|
revokedInvitesContext.add(invite.withUpdated(isRevoked: true))
|
||||||
invitesContext.update(invite)
|
} else {
|
||||||
|
invitesContext.update(invite)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
controller.navigationPresentation = .modal
|
||||||
controller.navigationPresentation = .modal
|
pushControllerImpl?(controller)
|
||||||
pushControllerImpl?(controller)
|
})))
|
||||||
})))
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if invite.isRevoked {
|
if invite.isRevoked {
|
||||||
|
|||||||
@ -79,10 +79,36 @@ public struct ItemListToolbarItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let actions: [Action]
|
let actions: [Action]
|
||||||
|
|
||||||
|
var toolbar: Toolbar {
|
||||||
|
var leftAction: ToolbarAction?
|
||||||
|
var middleAction: ToolbarAction?
|
||||||
|
var rightAction: ToolbarAction?
|
||||||
|
|
||||||
|
if self.actions.count == 1 {
|
||||||
|
if let action = self.actions.first {
|
||||||
|
middleAction = ToolbarAction(title: action.title, isEnabled: action.isEnabled)
|
||||||
|
}
|
||||||
|
} else if 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 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return Toolbar(leftAction: leftAction, rightAction: rightAction, middleAction: middleAction)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct ItemListNodeTransition {
|
private struct ItemListNodeTransition {
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
|
let strings: PresentationStrings
|
||||||
let entries: ItemListNodeEntryTransition
|
let entries: ItemListNodeEntryTransition
|
||||||
let updateStyle: ItemListStyle?
|
let updateStyle: ItemListStyle?
|
||||||
let emptyStateItem: ItemListControllerEmptyStateItem?
|
let emptyStateItem: ItemListControllerEmptyStateItem?
|
||||||
@ -348,7 +374,7 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
scrollToItem = state.initialScrollToItem
|
scrollToItem = state.initialScrollToItem
|
||||||
}
|
}
|
||||||
|
|
||||||
return ItemListNodeTransition(theme: presentationData.theme, entries: transition, updateStyle: updatedStyle, emptyStateItem: state.emptyStateItem, searchItem: state.searchItem, toolbarItem: state.toolbarItem, focusItemTag: state.focusItemTag, ensureVisibleItemTag: state.ensureVisibleItemTag, scrollToItem: scrollToItem, firstTime: previous == nil, animated: previous != nil && state.animateChanges, animateAlpha: previous != nil && state.animateChanges, crossfade: state.crossfadeState, mergedEntries: state.entries, scrollEnabled: state.scrollEnabled)
|
return ItemListNodeTransition(theme: presentationData.theme, strings: presentationData.strings, entries: transition, updateStyle: updatedStyle, emptyStateItem: state.emptyStateItem, searchItem: state.searchItem, toolbarItem: state.toolbarItem, focusItemTag: state.focusItemTag, ensureVisibleItemTag: state.ensureVisibleItemTag, scrollToItem: scrollToItem, firstTime: previous == nil, animated: previous != nil && state.animateChanges, animateAlpha: previous != nil && state.animateChanges, crossfade: state.crossfadeState, mergedEntries: state.entries, scrollEnabled: state.scrollEnabled)
|
||||||
})
|
})
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] transition in
|
|> deliverOnMainQueue).start(next: { [weak self] transition in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -452,6 +478,10 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
searchNode.updateLayout(layout: layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
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
|
let dequeue = self.validLayout == nil
|
||||||
self.validLayout = (layout, navigationBarHeight)
|
self.validLayout = (layout, navigationBarHeight)
|
||||||
if dequeue {
|
if dequeue {
|
||||||
@ -610,6 +640,47 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.listNode.accessibilityPageScrolledString = { row, count in
|
||||||
|
return transition.strings.VoiceOver_ScrollStatus(row, count).0
|
||||||
|
}
|
||||||
|
|
||||||
|
let toolbarFrame = CGRect()
|
||||||
|
let layoutTransition: ContainedViewLayoutTransition = .immediate
|
||||||
|
if let toolbarItem = transition.toolbarItem, let (layout, _) = self.validLayout {
|
||||||
|
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), left: {
|
||||||
|
toolbarItem.actions[0].action()
|
||||||
|
}, right: {
|
||||||
|
if toolbarItem.actions.count == 2 {
|
||||||
|
toolbarItem.actions[1].action()
|
||||||
|
} else if toolbarItem.actions.count == 3 {
|
||||||
|
toolbarItem.actions[2].action()
|
||||||
|
}
|
||||||
|
}, middle: {
|
||||||
|
if toolbarItem.actions.count == 1 {
|
||||||
|
toolbarItem.actions[0].action()
|
||||||
|
} else if toolbarItem.actions.count == 3 {
|
||||||
|
toolbarItem.actions[1].action()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let toolbarNode = self.toolbarNode {
|
||||||
|
self.toolbarNode = nil
|
||||||
|
layoutTransition.updateAlpha(node: toolbarNode, alpha: 0.0, completion: { [weak toolbarNode] _ in
|
||||||
|
toolbarNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
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
|
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
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if !strongSelf.didSetReady {
|
if !strongSelf.didSetReady {
|
||||||
|
|||||||
@ -61,7 +61,7 @@ const CGFloat TGPhotoPaintSettingsPadPickerWidth = 360.0f;
|
|||||||
_eyedropperButton.exclusiveTouch = true;
|
_eyedropperButton.exclusiveTouch = true;
|
||||||
[_eyedropperButton setImage:TGTintedImage([UIImage imageNamed:@"Editor/Eyedropper"], [UIColor whiteColor]) forState:UIControlStateNormal];
|
[_eyedropperButton setImage:TGTintedImage([UIImage imageNamed:@"Editor/Eyedropper"], [UIColor whiteColor]) forState:UIControlStateNormal];
|
||||||
[_eyedropperButton addTarget:self action:@selector(eyedropperButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
[_eyedropperButton addTarget:self action:@selector(eyedropperButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||||
// [self addSubview:_eyedropperButton];
|
[self addSubview:_eyedropperButton];
|
||||||
|
|
||||||
_settingsButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 44.0f, 44.0f)];
|
_settingsButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 44.0f, 44.0f)];
|
||||||
_settingsButton.exclusiveTouch = true;
|
_settingsButton.exclusiveTouch = true;
|
||||||
@ -211,8 +211,7 @@ const CGFloat TGPhotoPaintSettingsPadPickerWidth = 360.0f;
|
|||||||
|
|
||||||
- (void)layoutSubviews
|
- (void)layoutSubviews
|
||||||
{
|
{
|
||||||
CGFloat leftInset = 23.0f;
|
CGFloat inset = 66.0f;
|
||||||
CGFloat rightInset = 66.0f;
|
|
||||||
CGFloat colorPickerHeight = 10.0f;
|
CGFloat colorPickerHeight = 10.0f;
|
||||||
if (self.frame.size.width > self.frame.size.height)
|
if (self.frame.size.width > self.frame.size.height)
|
||||||
{
|
{
|
||||||
@ -223,14 +222,14 @@ const CGFloat TGPhotoPaintSettingsPadPickerWidth = 360.0f;
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_colorPicker.frame = CGRectMake(leftInset, ceil((self.frame.size.height - colorPickerHeight) / 2.0f), self.frame.size.width - leftInset - rightInset, colorPickerHeight);
|
_colorPicker.frame = CGRectMake(inset, ceil((self.frame.size.height - colorPickerHeight) / 2.0f), self.frame.size.width - inset - inset, colorPickerHeight);
|
||||||
_eyedropperButton.frame = CGRectMake(10.0f, floor((self.frame.size.height - _eyedropperButton.frame.size.height) / 2.0f) + 1.0f, _eyedropperButton.frame.size.width, _eyedropperButton.frame.size.height);
|
_eyedropperButton.frame = CGRectMake(10.0f, floor((self.frame.size.height - _eyedropperButton.frame.size.height) / 2.0f) + 1.0f, _eyedropperButton.frame.size.width, _eyedropperButton.frame.size.height);
|
||||||
_settingsButton.frame = CGRectMake(self.frame.size.width - _settingsButton.frame.size.width - 10.0f, floor((self.frame.size.height - _settingsButton.frame.size.height) / 2.0f) + 1.0f, _settingsButton.frame.size.width, _settingsButton.frame.size.height);
|
_settingsButton.frame = CGRectMake(self.frame.size.width - _settingsButton.frame.size.width - 10.0f, floor((self.frame.size.height - _settingsButton.frame.size.height) / 2.0f) + 1.0f, _settingsButton.frame.size.width, _settingsButton.frame.size.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_colorPicker.frame = CGRectMake(ceil((self.frame.size.width - colorPickerHeight) / 2.0f), rightInset, colorPickerHeight, self.frame.size.height - leftInset - rightInset);
|
_colorPicker.frame = CGRectMake(ceil((self.frame.size.width - colorPickerHeight) / 2.0f), inset, colorPickerHeight, self.frame.size.height - inset - inset);
|
||||||
_eyedropperButton.frame = CGRectMake(floor((self.frame.size.width - _eyedropperButton.frame.size.width) / 2.0f), self.frame.size.height - _eyedropperButton.frame.size.height - 10.0, _eyedropperButton.frame.size.width, _eyedropperButton.frame.size.height);
|
_eyedropperButton.frame = CGRectMake(floor((self.frame.size.width - _eyedropperButton.frame.size.width) / 2.0f), self.frame.size.height - _eyedropperButton.frame.size.height - 10.0, _eyedropperButton.frame.size.width, _eyedropperButton.frame.size.height);
|
||||||
_settingsButton.frame = CGRectMake(floor((self.frame.size.width - _settingsButton.frame.size.width) / 2.0f), 10.0f, _settingsButton.frame.size.width, _settingsButton.frame.size.height);
|
_settingsButton.frame = CGRectMake(floor((self.frame.size.width - _settingsButton.frame.size.width) / 2.0f), 10.0f, _settingsButton.frame.size.width, _settingsButton.frame.size.height);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -148,7 +148,7 @@ private enum ChannelDiscussionGroupSetupControllerEntry: ItemListNodeEntry {
|
|||||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.plusIconImage(theme), title: text, sectionId: self.section, editing: false, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.plusIconImage(theme), title: text, sectionId: self.section, editing: false, action: {
|
||||||
arguments.createGroup()
|
arguments.createGroup()
|
||||||
})
|
})
|
||||||
case let .group(_, theme, strings, peer, nameOrder):
|
case let .group(_, _, strings, peer, nameOrder):
|
||||||
let text: String
|
let text: String
|
||||||
if let peer = peer as? TelegramChannel, let addressName = peer.addressName, !addressName.isEmpty {
|
if let peer = peer as? TelegramChannel, let addressName = peer.addressName, !addressName.isEmpty {
|
||||||
text = "@\(addressName)"
|
text = "@\(addressName)"
|
||||||
@ -160,9 +160,9 @@ private enum ChannelDiscussionGroupSetupControllerEntry: ItemListNodeEntry {
|
|||||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .regular, dateFormat: .monthFirst, dateSeparator: ".", decimalSeparator: ".", groupingSeparator: "."), nameDisplayOrder: nameOrder, context: arguments.context, peer: peer, aliasHandling: .standard, nameStyle: .plain, presence: nil, text: .text(text, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
|
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .regular, dateFormat: .monthFirst, dateSeparator: ".", decimalSeparator: ".", groupingSeparator: "."), nameDisplayOrder: nameOrder, context: arguments.context, peer: peer, aliasHandling: .standard, nameStyle: .plain, presence: nil, text: .text(text, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
|
||||||
arguments.selectGroup(peer.id)
|
arguments.selectGroup(peer.id)
|
||||||
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in })
|
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in })
|
||||||
case let .groupsInfo(theme, title):
|
case let .groupsInfo(_, title):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(title), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(title), sectionId: self.section)
|
||||||
case let .unlink(theme, title):
|
case let .unlink(_, title):
|
||||||
return ItemListActionItem(presentationData: presentationData, title: title, kind: .destructive, alignment: .center, sectionId: self.section, style: .blocks, action: {
|
return ItemListActionItem(presentationData: presentationData, title: title, kind: .destructive, alignment: .center, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.unlinkGroup()
|
arguments.unlinkGroup()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import Markdown
|
|||||||
public enum PeerReportSubject {
|
public enum PeerReportSubject {
|
||||||
case peer(PeerId)
|
case peer(PeerId)
|
||||||
case messages([MessageId])
|
case messages([MessageId])
|
||||||
|
case profilePhoto(PeerId, Int64)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PeerReportOption {
|
public enum PeerReportOption {
|
||||||
@ -60,7 +61,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
|
|||||||
}, action: { [weak parent] _, f in
|
}, action: { [weak parent] _, f in
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
var reportReason: ReportReason?
|
let reportReason: ReportReason
|
||||||
switch option {
|
switch option {
|
||||||
case .spam:
|
case .spam:
|
||||||
reportReason = .spam
|
reportReason = .spam
|
||||||
@ -75,30 +76,60 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
|
|||||||
case .copyright:
|
case .copyright:
|
||||||
reportReason = .copyright
|
reportReason = .copyright
|
||||||
case .other:
|
case .other:
|
||||||
break
|
reportReason = .custom
|
||||||
}
|
}
|
||||||
if let reportReason = reportReason {
|
|
||||||
switch subject {
|
let displaySuccess = {
|
||||||
case let .peer(peerId):
|
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
|
||||||
let _ = (reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: "")
|
parent?.present(UndoOverlayController(presentationData: presentationData, content: .emoji(path: path, text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current)
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|
||||||
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
|
|
||||||
parent?.present(UndoOverlayController(presentationData: presentationData, content: .emoji(path: path, text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current)
|
|
||||||
}
|
|
||||||
completion(reportReason, true)
|
|
||||||
})
|
|
||||||
case let .messages(messageIds):
|
|
||||||
let _ = (reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: "")
|
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|
||||||
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
|
|
||||||
parent?.present(UndoOverlayController(presentationData: presentationData, content: .emoji(path: path, text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current)
|
|
||||||
}
|
|
||||||
completion(reportReason, true)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
parent?.push(peerReportController(context: context, subject: subject, completion: completion))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let action: (String) -> Void = { message in
|
||||||
|
switch subject {
|
||||||
|
case let .peer(peerId):
|
||||||
|
let _ = (reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: "")
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
displaySuccess()
|
||||||
|
completion(reportReason, true)
|
||||||
|
})
|
||||||
|
case let .messages(messageIds):
|
||||||
|
let _ = (reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: "")
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
displaySuccess()
|
||||||
|
completion(reportReason, true)
|
||||||
|
})
|
||||||
|
case let .profilePhoto(peerId, photoId):
|
||||||
|
let _ = (reportPeerPhoto(account: context.account, peerId: peerId, reason: reportReason, message: "")
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
displaySuccess()
|
||||||
|
completion(reportReason, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = ActionSheetController(presentationData: presentationData, allowInputInset: true)
|
||||||
|
let dismissAction: () -> Void = { [weak controller] in
|
||||||
|
controller?.dismissAnimated()
|
||||||
|
}
|
||||||
|
var message = ""
|
||||||
|
var items: [ActionSheetItem] = []
|
||||||
|
items.append(ReportPeerHeaderActionSheetItem(context: context, text: presentationData.strings.Report_AdditionalDetailsText))
|
||||||
|
items.append(ReportPeerDetailsActionSheetItem(context: context, placeholderText: presentationData.strings.Report_AdditionalDetailsPlaceholder, textUpdated: { text in
|
||||||
|
message = text
|
||||||
|
}))
|
||||||
|
items.append(ActionSheetButtonItem(title: presentationData.strings.Report_Report, color: .accent, font: .bold, enabled: true, action: {
|
||||||
|
dismissAction()
|
||||||
|
|
||||||
|
action(message)
|
||||||
|
}))
|
||||||
|
|
||||||
|
controller.setItemGroups([
|
||||||
|
ActionSheetItemGroup(items: items),
|
||||||
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||||
|
])
|
||||||
|
parent?.present(controller, in: .window(.root))
|
||||||
|
|
||||||
f(.dismissWithoutContent)
|
f(.dismissWithoutContent)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
@ -162,17 +193,21 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe
|
|||||||
passthrough = false
|
passthrough = false
|
||||||
}
|
}
|
||||||
|
|
||||||
let action = {
|
let displaySuccess = {
|
||||||
|
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
|
||||||
|
present(UndoOverlayController(presentationData: presentationData, content: .emoji(path: path, text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let action: (String) -> Void = { message in
|
||||||
switch subject {
|
switch subject {
|
||||||
case let .peer(peerId):
|
case let .peer(peerId):
|
||||||
if passthrough {
|
if passthrough {
|
||||||
completion(reportReason, true)
|
completion(reportReason, true)
|
||||||
} else {
|
} else {
|
||||||
let _ = (reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: "")
|
let _ = (reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: message)
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
|
displaySuccess()
|
||||||
present(UndoOverlayController(presentationData: presentationData, content: .emoji(path: path, text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), nil)
|
|
||||||
}
|
|
||||||
completion(nil, false)
|
completion(nil, false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -180,11 +215,19 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe
|
|||||||
if passthrough {
|
if passthrough {
|
||||||
completion(reportReason, true)
|
completion(reportReason, true)
|
||||||
} else {
|
} else {
|
||||||
let _ = (reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: "")
|
let _ = (reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: message)
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
|
displaySuccess()
|
||||||
present(UndoOverlayController(presentationData: presentationData, content: .emoji(path: path, text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), nil)
|
completion(nil, false)
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
case let .profilePhoto(peerId, photoId):
|
||||||
|
if passthrough {
|
||||||
|
completion(reportReason, true)
|
||||||
|
} else {
|
||||||
|
let _ = (reportPeerPhoto(account: context.account, peerId: peerId, reason: reportReason, message: message)
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
displaySuccess()
|
||||||
completion(nil, false)
|
completion(nil, false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -205,7 +248,7 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe
|
|||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Report_Report, color: .accent, font: .bold, enabled: true, action: {
|
items.append(ActionSheetButtonItem(title: presentationData.strings.Report_Report, color: .accent, font: .bold, enabled: true, action: {
|
||||||
dismissAction()
|
dismissAction()
|
||||||
|
|
||||||
action()
|
action(message)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
controller.setItemGroups([
|
controller.setItemGroups([
|
||||||
@ -214,7 +257,7 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe
|
|||||||
])
|
])
|
||||||
present(controller, nil)
|
present(controller, nil)
|
||||||
} else {
|
} else {
|
||||||
action()
|
action("")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
push(peerReportController(context: context, subject: subject, completion: completion))
|
push(peerReportController(context: context, subject: subject, completion: completion))
|
||||||
@ -369,16 +412,21 @@ private func peerReportController(context: AccountContext, subject: PeerReportSu
|
|||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
}
|
}
|
||||||
switch subject {
|
switch subject {
|
||||||
case let .peer(peerId):
|
case let .peer(peerId):
|
||||||
reportDisposable.set((reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: text)
|
reportDisposable.set((reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: text)
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
completed()
|
completed()
|
||||||
}))
|
}))
|
||||||
case let .messages(messageIds):
|
case let .messages(messageIds):
|
||||||
reportDisposable.set((reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: text)
|
reportDisposable.set((reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: text)
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
completed()
|
completed()
|
||||||
}))
|
}))
|
||||||
|
case let .profilePhoto(peerId, photoId):
|
||||||
|
reportDisposable.set((reportPeerPhoto(account: context.account, peerId: peerId, reason: reportReason, message: text)
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
completed()
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1438,6 +1438,7 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
|
|||||||
|
|
||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: nil,
|
sendSticker: nil,
|
||||||
|
requestMessageActionUrlAuth: nil,
|
||||||
present: { c, a in
|
present: { c, a in
|
||||||
presentControllerImpl?(c, a)
|
presentControllerImpl?(c, a)
|
||||||
}, dismissInput: {
|
}, dismissInput: {
|
||||||
|
|||||||
@ -531,8 +531,7 @@ public func proxySettingsController(accountManager: AccountManager, context: Acc
|
|||||||
result += string
|
result += string
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = ShareController(context: context, subject: .text(result), preferredAction: .default, showInChat: nil, externalShare: true, immediateExternalShare: true, switchableAccounts: [])
|
presentExternalShare(context: context, text: result, parentController: strongController)
|
||||||
presentControllerImpl?(controller, nil)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -378,19 +378,15 @@ func proxyServerSettingsController(context: AccountContext? = nil, presentationD
|
|||||||
}
|
}
|
||||||
shareImpl = { [weak controller] in
|
shareImpl = { [weak controller] in
|
||||||
let state = stateValue.with { $0 }
|
let state = stateValue.with { $0 }
|
||||||
guard let server = proxyServerSettings(with: state), let strongController = controller else {
|
guard let server = proxyServerSettings(with: state) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let link = shareLink(for: server)
|
let link = shareLink(for: server)
|
||||||
controller?.view.endEditing(true)
|
controller?.view.endEditing(true)
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
|
||||||
let controller = ShareProxyServerActionSheetController(presentationData: presentationData, updatedPresentationData: updatedPresentationData, link: link)
|
let controller = ShareProxyServerActionSheetController(presentationData: presentationData, updatedPresentationData: updatedPresentationData, link: link)
|
||||||
presentControllerImpl?(controller, nil)
|
presentControllerImpl?(controller, nil)
|
||||||
} else if let context = context {
|
|
||||||
let controller = ShareController(context: context, subject: .url(link), preferredAction: .default, showInChat: nil, externalShare: true, immediateExternalShare: true, switchableAccounts: [])
|
|
||||||
presentControllerImpl?(controller, nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
|
|||||||
@ -180,7 +180,7 @@ public func logoutOptionsController(context: AccountContext, navigationControlle
|
|||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
|
|
||||||
context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peer, navigation in
|
context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peer, navigation in
|
||||||
}, sendFile: nil, sendSticker: nil, present: { controller, arguments in
|
}, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, present: { controller, arguments in
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
}, dismissInput: {}, contentContext: nil)
|
}, dismissInput: {}, contentContext: nil)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -897,7 +897,7 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList
|
|||||||
let _ = (cachedFaqInstantPage(context: context)
|
let _ = (cachedFaqInstantPage(context: context)
|
||||||
|> deliverOnMainQueue).start(next: { resolvedUrl in
|
|> deliverOnMainQueue).start(next: { resolvedUrl in
|
||||||
context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peer, navigation in
|
context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peer, navigation in
|
||||||
}, sendFile: nil, sendSticker: nil, present: { controller, arguments in
|
}, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, present: { controller, arguments in
|
||||||
present(.push, controller)
|
present(.push, controller)
|
||||||
}, dismissInput: {}, contentContext: nil)
|
}, dismissInput: {}, contentContext: nil)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1080,3 +1080,13 @@ private class ShareToInstagramActivity: UIActivity {
|
|||||||
activityDidFinish(true)
|
activityDidFinish(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public func presentExternalShare(context: AccountContext, text: String, parentController: ViewController) {
|
||||||
|
let activityController = UIActivityViewController(activityItems: [text], applicationActivities: nil)
|
||||||
|
if let window = parentController.view.window {
|
||||||
|
activityController.popoverPresentationController?.sourceView = window
|
||||||
|
activityController.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint(x: window.bounds.width / 2.0, y: window.bounds.size.height - 1.0), size: CGSize(width: 1.0, height: 1.0))
|
||||||
|
}
|
||||||
|
context.sharedContext.applicationBindings.presentNativeController(activityController)
|
||||||
|
}
|
||||||
|
|||||||
@ -43,7 +43,7 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese
|
|||||||
private let openMentionDisposable = MetaDisposable()
|
private let openMentionDisposable = MetaDisposable()
|
||||||
|
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
|
|
||||||
public var sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)? {
|
public var sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)? {
|
||||||
didSet {
|
didSet {
|
||||||
if self.isNodeLoaded {
|
if self.isNodeLoaded {
|
||||||
|
|||||||
@ -240,6 +240,11 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
|||||||
let controller = PeekController(theme: PeekControllerTheme(presentationTheme: strongSelf.presentationData.theme), content: content, sourceNode: {
|
let controller = PeekController(theme: PeekControllerTheme(presentationTheme: strongSelf.presentationData.theme), content: content, sourceNode: {
|
||||||
return sourceNode
|
return sourceNode
|
||||||
})
|
})
|
||||||
|
controller.visibilityUpdated = { [weak self] visible in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.contentGridNode.forceHidden = visible
|
||||||
|
}
|
||||||
|
}
|
||||||
strongSelf.presentInGlobalOverlay?(controller, nil)
|
strongSelf.presentInGlobalOverlay?(controller, nil)
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|||||||
@ -133,9 +133,9 @@ func managedConsumePersonalMessagesActions(postbox: Postbox, network: Network, s
|
|||||||
|
|
||||||
for (entry, disposable) in beginValidateOperations {
|
for (entry, disposable) in beginValidateOperations {
|
||||||
let signal = synchronizeUnseenPersonalMentionsTag(postbox: postbox, network: network, entry: entry)
|
let signal = synchronizeUnseenPersonalMentionsTag(postbox: postbox, network: network, entry: entry)
|
||||||
|> then(postbox.transaction { transaction -> Void in
|
|> then(postbox.transaction { transaction -> Void in
|
||||||
transaction.removeInvalidatedMessageHistoryTagsSummaryEntry(entry)
|
transaction.removeInvalidatedMessageHistoryTagsSummaryEntry(entry)
|
||||||
})
|
})
|
||||||
disposable.set(signal.start())
|
disposable.set(signal.start())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -126,6 +126,22 @@ public func reportPeer(account: Account, peerId: PeerId, reason: ReportReason, m
|
|||||||
} |> switchToLatest
|
} |> switchToLatest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func reportPeerPhoto(account: Account, peerId: PeerId, reason: ReportReason, message: String) -> Signal<Void, NoError> {
|
||||||
|
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||||
|
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||||
|
return account.network.request(Api.functions.account.reportProfilePhoto(peer: inputPeer, photoId: .inputPhotoEmpty, reason: reason.apiReason, message: message))
|
||||||
|
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||||
|
return .single(.boolFalse)
|
||||||
|
}
|
||||||
|
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
} |> switchToLatest
|
||||||
|
}
|
||||||
|
|
||||||
public func reportPeerMessages(account: Account, messageIds: [MessageId], reason: ReportReason, message: String) -> Signal<Void, NoError> {
|
public func reportPeerMessages(account: Account, messageIds: [MessageId], reason: ReportReason, message: String) -> Signal<Void, NoError> {
|
||||||
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||||
let groupedIds = messagesIdsGroupedByPeerId(messageIds)
|
let groupedIds = messagesIdsGroupedByPeerId(messageIds)
|
||||||
@ -149,22 +165,6 @@ public func reportPeerMessages(account: Account, messageIds: [MessageId], reason
|
|||||||
} |> switchToLatest
|
} |> switchToLatest
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reportSupergroupPeer(account: Account, peerId: PeerId, memberId: PeerId, messageIds: [MessageId]) -> Signal<Void, NoError> {
|
|
||||||
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
|
||||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputChannel(peer), let memberPeer = transaction.getPeer(memberId), let inputMember = apiInputUser(memberPeer) {
|
|
||||||
return account.network.request(Api.functions.channels.reportSpam(channel: inputPeer, userId: inputMember, id: messageIds.map({$0.id})))
|
|
||||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
|
||||||
return .single(.boolFalse)
|
|
||||||
}
|
|
||||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
|
||||||
return .complete()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return .complete()
|
|
||||||
}
|
|
||||||
} |> switchToLatest
|
|
||||||
}
|
|
||||||
|
|
||||||
public func dismissPeerStatusOptions(account: Account, peerId: PeerId) -> Signal<Void, NoError> {
|
public func dismissPeerStatusOptions(account: Account, peerId: PeerId) -> Signal<Void, NoError> {
|
||||||
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||||
|
|||||||
@ -174,62 +174,89 @@ public enum MessageActionUrlAuthResult {
|
|||||||
case request(String, Peer, Bool)
|
case request(String, Peer, Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func requestMessageActionUrlAuth(account: Account, messageId: MessageId, buttonId: Int32) -> Signal<MessageActionUrlAuthResult, NoError> {
|
public enum MessageActionUrlSubject {
|
||||||
return account.postbox.loadedPeerWithId(messageId.peerId)
|
case message(id: MessageId, buttonId: Int32)
|
||||||
|> take(1)
|
case url(String)
|
||||||
|> mapToSignal { peer in
|
}
|
||||||
if let inputPeer = apiInputPeer(peer) {
|
|
||||||
return account.network.request(Api.functions.messages.requestUrlAuth(peer: inputPeer, msgId: messageId.id, buttonId: buttonId))
|
public func requestMessageActionUrlAuth(account: Account, subject: MessageActionUrlSubject) -> Signal<MessageActionUrlAuthResult, NoError> {
|
||||||
|
let request: Signal<Api.UrlAuthResult?, MTRpcError>
|
||||||
|
switch subject {
|
||||||
|
case let .message(messageId, buttonId):
|
||||||
|
request = account.postbox.loadedPeerWithId(messageId.peerId)
|
||||||
|
|> take(1)
|
||||||
|
|> castError(MTRpcError.self)
|
||||||
|
|> mapToSignal { peer -> Signal<Api.UrlAuthResult?, MTRpcError> in
|
||||||
|
if let inputPeer = apiInputPeer(peer) {
|
||||||
|
return account.network.request(Api.functions.messages.requestUrlAuth(peer: inputPeer, msgId: messageId.id, buttonId: buttonId))
|
||||||
|
|> map(Optional.init)
|
||||||
|
} else {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case let .url(url):
|
||||||
|
request = account.network.request(Api.functions.messages.requestUrlAuth(peer: .inputPeerEmpty, msgId: 0, buttonId: 0))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
|
}
|
||||||
return .single(nil)
|
|
||||||
}
|
return request
|
||||||
|> map { result -> MessageActionUrlAuthResult in
|
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
|
||||||
guard let result = result else {
|
return .single(nil)
|
||||||
return .default
|
}
|
||||||
}
|
|> map { result -> MessageActionUrlAuthResult in
|
||||||
switch result {
|
guard let result = result else {
|
||||||
case .urlAuthResultDefault:
|
return .default
|
||||||
return .default
|
}
|
||||||
case let .urlAuthResultAccepted(url):
|
switch result {
|
||||||
return .accepted(url)
|
case .urlAuthResultDefault:
|
||||||
case let .urlAuthResultRequest(flags, bot, domain):
|
return .default
|
||||||
return .request(domain, TelegramUser(user: bot), (flags & (1 << 0)) != 0)
|
case let .urlAuthResultAccepted(url):
|
||||||
}
|
return .accepted(url)
|
||||||
}
|
case let .urlAuthResultRequest(flags, bot, domain):
|
||||||
} else {
|
return .request(domain, TelegramUser(user: bot), (flags & (1 << 0)) != 0)
|
||||||
return .single(.default)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func acceptMessageActionUrlAuth(account: Account, messageId: MessageId, buttonId: Int32, allowWriteAccess: Bool) -> Signal<MessageActionUrlAuthResult, NoError> {
|
public func acceptMessageActionUrlAuth(account: Account, subject: MessageActionUrlSubject, allowWriteAccess: Bool) -> Signal<MessageActionUrlAuthResult, NoError> {
|
||||||
return account.postbox.loadedPeerWithId(messageId.peerId)
|
var flags: Int32 = 0
|
||||||
|> take(1)
|
if allowWriteAccess {
|
||||||
|> mapToSignal { peer in
|
flags |= Int32(1 << 0)
|
||||||
if let inputPeer = apiInputPeer(peer) {
|
}
|
||||||
var flags: Int32 = 0
|
|
||||||
if allowWriteAccess {
|
let request: Signal<Api.UrlAuthResult?, MTRpcError>
|
||||||
flags |= Int32(1 << 0)
|
switch subject {
|
||||||
|
case let .message(messageId, buttonId):
|
||||||
|
request = account.postbox.loadedPeerWithId(messageId.peerId)
|
||||||
|
|> take(1)
|
||||||
|
|> castError(MTRpcError.self)
|
||||||
|
|> mapToSignal { peer -> Signal<Api.UrlAuthResult?, MTRpcError> in
|
||||||
|
if let inputPeer = apiInputPeer(peer) {
|
||||||
|
return account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: inputPeer, msgId: messageId.id, buttonId: buttonId))
|
||||||
|
|> map(Optional.init)
|
||||||
|
} else {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: inputPeer, msgId: messageId.id, buttonId: buttonId))
|
case let .url(url):
|
||||||
|
request = account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: .inputPeerEmpty, msgId: 0, buttonId: 0))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
|
}
|
||||||
return .single(nil)
|
|
||||||
}
|
|
||||||
|> map { result -> MessageActionUrlAuthResult in
|
return request
|
||||||
guard let result = result else {
|
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
|
||||||
return .default
|
return .single(nil)
|
||||||
}
|
}
|
||||||
switch result {
|
|> map { result -> MessageActionUrlAuthResult in
|
||||||
case let .urlAuthResultAccepted(url):
|
guard let result = result else {
|
||||||
return .accepted(url)
|
return .default
|
||||||
default:
|
}
|
||||||
return .default
|
switch result {
|
||||||
}
|
case let .urlAuthResultAccepted(url):
|
||||||
}
|
return .accepted(url)
|
||||||
} else {
|
default:
|
||||||
return .single(.default)
|
return .default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -27,8 +27,8 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.containerNode.addSubnode(self.avatarNode)
|
|
||||||
self.addSubnode(self.containerNode)
|
self.addSubnode(self.containerNode)
|
||||||
|
self.containerNode.addSubnode(self.avatarNode)
|
||||||
|
|
||||||
self.containerNode.activated = { [weak self] gesture, _ in
|
self.containerNode.activated = { [weak self] gesture, _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
|
|||||||
@ -209,7 +209,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
|
|||||||
break
|
break
|
||||||
case let .urlAuth(url, buttonId):
|
case let .urlAuth(url, buttonId):
|
||||||
if let message = self.message {
|
if let message = self.message {
|
||||||
self.controllerInteraction.requestMessageActionUrlAuth(url, message.id, buttonId)
|
self.controllerInteraction.requestMessageActionUrlAuth(url, .message(id: message.id, buttonId: buttonId))
|
||||||
}
|
}
|
||||||
case let .setupPoll(isQuiz):
|
case let .setupPoll(isQuiz):
|
||||||
self.controllerInteraction.openPollCreation(isQuiz)
|
self.controllerInteraction.openPollCreation(isQuiz)
|
||||||
|
|||||||
@ -306,6 +306,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
private var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
private var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
||||||
private var automaticMediaDownloadSettingsDisposable: Disposable?
|
private var automaticMediaDownloadSettingsDisposable: Disposable?
|
||||||
|
|
||||||
|
private var disableStickerAnimationsPromise = ValuePromise<Bool>(false)
|
||||||
|
private var disableStickerAnimations = false
|
||||||
private var stickerSettings: ChatInterfaceStickerSettings
|
private var stickerSettings: ChatInterfaceStickerSettings
|
||||||
private var stickerSettingsDisposable: Disposable?
|
private var stickerSettingsDisposable: Disposable?
|
||||||
|
|
||||||
@ -1163,124 +1165,122 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, requestMessageActionUrlAuth: { [weak self] defaultUrl, messageId, buttonId in
|
}, requestMessageActionUrlAuth: { [weak self] defaultUrl, subject in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
guard strongSelf.presentationInterfaceState.subject != .scheduledMessages else {
|
guard strongSelf.presentationInterfaceState.subject != .scheduledMessages else {
|
||||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let _ = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) {
|
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
return $0.updatedTitlePanelContext {
|
||||||
return $0.updatedTitlePanelContext {
|
if !$0.contains(where: {
|
||||||
if !$0.contains(where: {
|
switch $0 {
|
||||||
switch $0 {
|
case .requestInProgress:
|
||||||
case .requestInProgress:
|
return true
|
||||||
return true
|
default:
|
||||||
default:
|
return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
var updatedContexts = $0
|
|
||||||
updatedContexts.append(.requestInProgress)
|
|
||||||
return updatedContexts.sorted()
|
|
||||||
}
|
}
|
||||||
return $0
|
}) {
|
||||||
|
var updatedContexts = $0
|
||||||
|
updatedContexts.append(.requestInProgress)
|
||||||
|
return updatedContexts.sorted()
|
||||||
}
|
}
|
||||||
})
|
return $0
|
||||||
strongSelf.messageActionUrlAuthDisposable.set(((combineLatest(strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.context.account.peerId), requestMessageActionUrlAuth(account: strongSelf.context.account, messageId: messageId, buttonId: buttonId) |> afterDisposed {
|
}
|
||||||
Queue.mainQueue().async {
|
})
|
||||||
if let strongSelf = self {
|
strongSelf.messageActionUrlAuthDisposable.set(((combineLatest(strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.context.account.peerId), requestMessageActionUrlAuth(account: strongSelf.context.account, subject: subject) |> afterDisposed {
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
Queue.mainQueue().async {
|
||||||
return $0.updatedTitlePanelContext {
|
if let strongSelf = self {
|
||||||
if let index = $0.firstIndex(where: {
|
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||||
switch $0 {
|
return $0.updatedTitlePanelContext {
|
||||||
case .requestInProgress:
|
if let index = $0.firstIndex(where: {
|
||||||
return true
|
switch $0 {
|
||||||
default:
|
case .requestInProgress:
|
||||||
return false
|
return true
|
||||||
}
|
default:
|
||||||
}) {
|
return false
|
||||||
var updatedContexts = $0
|
}
|
||||||
updatedContexts.remove(at: index)
|
}) {
|
||||||
return updatedContexts
|
var updatedContexts = $0
|
||||||
|
updatedContexts.remove(at: index)
|
||||||
|
return updatedContexts
|
||||||
|
}
|
||||||
|
return $0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})) |> deliverOnMainQueue).start(next: { peer, result in
|
||||||
|
if let strongSelf = self {
|
||||||
|
switch result {
|
||||||
|
case .default:
|
||||||
|
strongSelf.openUrl(defaultUrl, concealed: false)
|
||||||
|
case let .request(domain, bot, requestWriteAccess):
|
||||||
|
let controller = chatMessageActionUrlAuthController(context: strongSelf.context, defaultUrl: defaultUrl, domain: domain, bot: bot, requestWriteAccess: requestWriteAccess, displayName: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), open: { [weak self] authorize, allowWriteAccess in
|
||||||
|
if let strongSelf = self {
|
||||||
|
if authorize {
|
||||||
|
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||||
|
return $0.updatedTitlePanelContext {
|
||||||
|
if !$0.contains(where: {
|
||||||
|
switch $0 {
|
||||||
|
case .requestInProgress:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
var updatedContexts = $0
|
||||||
|
updatedContexts.append(.requestInProgress)
|
||||||
|
return updatedContexts.sorted()
|
||||||
|
}
|
||||||
|
return $0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
strongSelf.messageActionUrlAuthDisposable.set(((acceptMessageActionUrlAuth(account: strongSelf.context.account, subject: subject, allowWriteAccess: allowWriteAccess) |> afterDisposed {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||||
|
return $0.updatedTitlePanelContext {
|
||||||
|
if let index = $0.firstIndex(where: {
|
||||||
|
switch $0 {
|
||||||
|
case .requestInProgress:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
var updatedContexts = $0
|
||||||
|
updatedContexts.remove(at: index)
|
||||||
|
return updatedContexts
|
||||||
|
}
|
||||||
|
return $0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) |> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
|
if let strongSelf = self {
|
||||||
|
switch result {
|
||||||
|
case let .accepted(url):
|
||||||
|
strongSelf.openUrl(url, concealed: false)
|
||||||
|
default:
|
||||||
|
strongSelf.openUrl(defaultUrl, concealed: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
strongSelf.openUrl(defaultUrl, concealed: false)
|
||||||
}
|
}
|
||||||
return $0
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
strongSelf.chatDisplayNode.dismissInput()
|
||||||
|
strongSelf.present(controller, in: .window(.root))
|
||||||
|
case let .accepted(url):
|
||||||
|
strongSelf.openUrl(url, concealed: false)
|
||||||
}
|
}
|
||||||
})) |> deliverOnMainQueue).start(next: { peer, result in
|
}
|
||||||
if let strongSelf = self {
|
}))
|
||||||
switch result {
|
|
||||||
case .default:
|
|
||||||
strongSelf.openUrl(defaultUrl, concealed: false)
|
|
||||||
case let .request(domain, bot, requestWriteAccess):
|
|
||||||
let controller = chatMessageActionUrlAuthController(context: strongSelf.context, defaultUrl: defaultUrl, domain: domain, bot: bot, requestWriteAccess: requestWriteAccess, displayName: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), open: { [weak self] authorize, allowWriteAccess in
|
|
||||||
if let strongSelf = self {
|
|
||||||
if authorize {
|
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
|
||||||
return $0.updatedTitlePanelContext {
|
|
||||||
if !$0.contains(where: {
|
|
||||||
switch $0 {
|
|
||||||
case .requestInProgress:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
var updatedContexts = $0
|
|
||||||
updatedContexts.append(.requestInProgress)
|
|
||||||
return updatedContexts.sorted()
|
|
||||||
}
|
|
||||||
return $0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
strongSelf.messageActionUrlAuthDisposable.set(((acceptMessageActionUrlAuth(account: strongSelf.context.account, messageId: messageId, buttonId: buttonId, allowWriteAccess: allowWriteAccess) |> afterDisposed {
|
|
||||||
Queue.mainQueue().async {
|
|
||||||
if let strongSelf = self {
|
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
|
||||||
return $0.updatedTitlePanelContext {
|
|
||||||
if let index = $0.firstIndex(where: {
|
|
||||||
switch $0 {
|
|
||||||
case .requestInProgress:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
var updatedContexts = $0
|
|
||||||
updatedContexts.remove(at: index)
|
|
||||||
return updatedContexts
|
|
||||||
}
|
|
||||||
return $0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) |> deliverOnMainQueue).start(next: { [weak self] result in
|
|
||||||
if let strongSelf = self {
|
|
||||||
switch result {
|
|
||||||
case let .accepted(url):
|
|
||||||
strongSelf.openUrl(url, concealed: false)
|
|
||||||
default:
|
|
||||||
strongSelf.openUrl(defaultUrl, concealed: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
strongSelf.openUrl(defaultUrl, concealed: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
strongSelf.chatDisplayNode.dismissInput()
|
|
||||||
strongSelf.present(controller, in: .window(.root))
|
|
||||||
case let .accepted(url):
|
|
||||||
strongSelf.openUrl(url, concealed: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, activateSwitchInline: { [weak self] peerId, inputString in
|
}, activateSwitchInline: { [weak self] peerId, inputString in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -2578,7 +2578,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.chatTitleView?.longPressed = { [weak self] in
|
self.chatTitleView?.longPressed = { [weak self] in
|
||||||
self?.interfaceInteraction?.beginMessageSearch(.everything, "")
|
if let strongSelf = self, let peerView = strongSelf.peerView, let peer = peerView.peers[peerView.peerId], peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil && !strongSelf.presentationInterfaceState.isNotAccessible {
|
||||||
|
strongSelf.interfaceInteraction?.beginMessageSearch(.everything, "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let chatInfoButtonItem: UIBarButtonItem
|
let chatInfoButtonItem: UIBarButtonItem
|
||||||
@ -3440,20 +3442,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
self.stickerSettingsDisposable = (context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings])
|
self.stickerSettingsDisposable = combineLatest(queue: Queue.mainQueue(), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]), self.disableStickerAnimationsPromise.get()).start(next: { [weak self] sharedData, disableStickerAnimations in
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
|
|
||||||
var stickerSettings = StickerSettings.defaultSettings
|
var stickerSettings = StickerSettings.defaultSettings
|
||||||
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings {
|
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings {
|
||||||
stickerSettings = value
|
stickerSettings = value
|
||||||
}
|
}
|
||||||
|
|
||||||
let chatStickerSettings = ChatInterfaceStickerSettings(stickerSettings: stickerSettings)
|
let chatStickerSettings = ChatInterfaceStickerSettings(stickerSettings: stickerSettings)
|
||||||
|
if let strongSelf = self, strongSelf.stickerSettings != chatStickerSettings || strongSelf.disableStickerAnimations != disableStickerAnimations {
|
||||||
if let strongSelf = self, strongSelf.stickerSettings != chatStickerSettings {
|
|
||||||
strongSelf.stickerSettings = chatStickerSettings
|
strongSelf.stickerSettings = chatStickerSettings
|
||||||
|
strongSelf.disableStickerAnimations = disableStickerAnimations
|
||||||
strongSelf.controllerInteraction?.stickerSettings = chatStickerSettings
|
strongSelf.controllerInteraction?.stickerSettings = chatStickerSettings
|
||||||
if strongSelf.isNodeLoaded {
|
if strongSelf.isNodeLoaded {
|
||||||
strongSelf.chatDisplayNode.updateStickerSettings(chatStickerSettings)
|
strongSelf.chatDisplayNode.updateStickerSettings(chatStickerSettings, forceStopAnimations: disableStickerAnimations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -7144,6 +7145,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func inFocusUpdated(isInFocus: Bool) {
|
override public func inFocusUpdated(isInFocus: Bool) {
|
||||||
|
self.disableStickerAnimationsPromise.set(!isInFocus)
|
||||||
self.chatDisplayNode.inFocusUpdated(isInFocus: isInFocus)
|
self.chatDisplayNode.inFocusUpdated(isInFocus: isInFocus)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8497,6 +8499,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
|
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
|
||||||
|
|
||||||
|
for item in results {
|
||||||
|
if let item = item, item.fileSize > 2000 * 1024 * 1024 {
|
||||||
|
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Conversation_UploadFileTooLarge, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var groupingKey: Int64?
|
var groupingKey: Int64?
|
||||||
var fileTypes: (music: Bool, other: Bool) = (false, false)
|
var fileTypes: (music: Bool, other: Bool) = (false, false)
|
||||||
if results.count > 1 {
|
if results.count > 1 {
|
||||||
@ -11082,6 +11091,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: { [weak self] f, sourceNode, sourceRect in
|
sendSticker: { [weak self] f, sourceNode, sourceRect in
|
||||||
return self?.interfaceInteraction?.sendSticker(f, sourceNode, sourceRect) ?? false
|
return self?.interfaceInteraction?.sendSticker(f, sourceNode, sourceRect) ?? false
|
||||||
|
}, requestMessageActionUrlAuth: { [weak self] subject in
|
||||||
|
if case let .url(url) = subject {
|
||||||
|
self?.controllerInteraction?.requestMessageActionUrlAuth(url, subject)
|
||||||
|
}
|
||||||
}, present: { [weak self] c, a in
|
}, present: { [weak self] c, a in
|
||||||
self?.present(c, in: .window(.root), with: a)
|
self?.present(c, in: .window(.root), with: a)
|
||||||
}, dismissInput: { [weak self] in
|
}, dismissInput: { [weak self] in
|
||||||
|
|||||||
@ -64,7 +64,7 @@ public final class ChatControllerInteraction {
|
|||||||
let sendGif: (FileMediaReference, ASDisplayNode, CGRect) -> Bool
|
let sendGif: (FileMediaReference, ASDisplayNode, CGRect) -> Bool
|
||||||
let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool
|
let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool
|
||||||
let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void
|
let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void
|
||||||
let requestMessageActionUrlAuth: (String, MessageId, Int32) -> Void
|
let requestMessageActionUrlAuth: (String, MessageActionUrlSubject) -> Void
|
||||||
let activateSwitchInline: (PeerId?, String) -> Void
|
let activateSwitchInline: (PeerId?, String) -> Void
|
||||||
let openUrl: (String, Bool, Bool?, Message?) -> Void
|
let openUrl: (String, Bool, Bool?, Message?) -> Void
|
||||||
let shareCurrentLocation: () -> Void
|
let shareCurrentLocation: () -> Void
|
||||||
@ -154,7 +154,7 @@ public final class ChatControllerInteraction {
|
|||||||
sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool,
|
sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool,
|
||||||
sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool,
|
sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool,
|
||||||
requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void,
|
requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void,
|
||||||
requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void,
|
requestMessageActionUrlAuth: @escaping (String, MessageActionUrlSubject) -> Void,
|
||||||
activateSwitchInline: @escaping (PeerId?, String) -> Void,
|
activateSwitchInline: @escaping (PeerId?, String) -> Void,
|
||||||
openUrl: @escaping (String, Bool, Bool?, Message?) -> Void,
|
openUrl: @escaping (String, Bool, Bool?, Message?) -> Void,
|
||||||
shareCurrentLocation: @escaping () -> Void,
|
shareCurrentLocation: @escaping () -> Void,
|
||||||
@ -298,7 +298,7 @@ public final class ChatControllerInteraction {
|
|||||||
|
|
||||||
static var `default`: ChatControllerInteraction {
|
static var `default`: ChatControllerInteraction {
|
||||||
return ChatControllerInteraction(openMessage: { _, _ in
|
return ChatControllerInteraction(openMessage: { _, _ in
|
||||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||||
}, presentController: { _, _ in }, navigationController: {
|
}, presentController: { _, _ in }, navigationController: {
|
||||||
return nil
|
return nil
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
|
|||||||
@ -2086,10 +2086,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.historyNode.prefetchManager.updateAutoDownloadSettings(settings)
|
self.historyNode.prefetchManager.updateAutoDownloadSettings(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateStickerSettings(_ settings: ChatInterfaceStickerSettings) {
|
func updateStickerSettings(_ settings: ChatInterfaceStickerSettings, forceStopAnimations: Bool) {
|
||||||
self.historyNode.forEachItemNode { itemNode in
|
self.historyNode.forEachItemNode { itemNode in
|
||||||
if let itemNode = itemNode as? ChatMessageItemView {
|
if let itemNode = itemNode as? ChatMessageItemView {
|
||||||
itemNode.updateStickerSettings()
|
itemNode.updateStickerSettings(forceStopAnimations: forceStopAnimations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -810,6 +810,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isUnremovableAction = false
|
||||||
if messages.count == 1 {
|
if messages.count == 1 {
|
||||||
let message = messages[0]
|
let message = messages[0]
|
||||||
|
|
||||||
@ -826,6 +827,14 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
|
|
||||||
if !hasAutoremove {
|
if !hasAutoremove {
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
|
if media is TelegramMediaAction {
|
||||||
|
if let channel = message.peers[message.id.peerId] as? TelegramChannel {
|
||||||
|
if channel.flags.contains(.isCreator) || (channel.adminRights?.rights.contains(.canDeleteMessages) == true) {
|
||||||
|
} else {
|
||||||
|
isUnremovableAction = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if let file = media as? TelegramMediaFile {
|
if let file = media as? TelegramMediaFile {
|
||||||
if file.isVideo {
|
if file.isVideo {
|
||||||
if file.isAnimated {
|
if file.isAnimated {
|
||||||
@ -937,7 +946,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
interfaceInteraction.deleteMessages(selectAll ? messages : [message], controller, f)
|
interfaceInteraction.deleteMessages(selectAll ? messages : [message], controller, f)
|
||||||
}
|
}
|
||||||
}), false))
|
}), false))
|
||||||
} else {
|
} else if !isUnremovableAction {
|
||||||
actions.append(.action(ContextMenuActionItem(text: title, textColor: .destructive, icon: { theme in
|
actions.append(.action(ContextMenuActionItem(text: title, textColor: .destructive, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: isSending ? "Chat/Context Menu/Clear" : "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: isSending ? "Chat/Context Menu/Clear" : "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||||
}, action: { controller, f in
|
}, action: { controller, f in
|
||||||
|
|||||||
@ -1242,6 +1242,9 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
let controller = PeekController(theme: PeekControllerTheme(presentationTheme: strongSelf.theme), content: content, sourceNode: {
|
let controller = PeekController(theme: PeekControllerTheme(presentationTheme: strongSelf.theme), content: content, sourceNode: {
|
||||||
return sourceNode
|
return sourceNode
|
||||||
})
|
})
|
||||||
|
controller.visibilityUpdated = { [weak self] visible in
|
||||||
|
|
||||||
|
}
|
||||||
strongSelf.controllerInteraction.presentGlobalOverlayController(controller, nil)
|
strongSelf.controllerInteraction.presentGlobalOverlayController(controller, nil)
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|||||||
@ -178,6 +178,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
private var highlightedState: Bool = false
|
private var highlightedState: Bool = false
|
||||||
|
|
||||||
|
private var forceStopAnimations = false
|
||||||
|
|
||||||
private var haptic: EmojiHaptic?
|
private var haptic: EmojiHaptic?
|
||||||
private var mediaPlayer: MediaPlayer?
|
private var mediaPlayer: MediaPlayer?
|
||||||
private let mediaStatusDisposable = MetaDisposable()
|
private let mediaStatusDisposable = MetaDisposable()
|
||||||
@ -489,7 +491,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let animationNode = self.animationNode as? AnimatedStickerNode {
|
if let animationNode = self.animationNode as? AnimatedStickerNode {
|
||||||
let isPlaying = self.visibilityStatus
|
let isPlaying = self.visibilityStatus && !self.forceStopAnimations
|
||||||
if self.isPlaying != isPlaying {
|
if self.isPlaying != isPlaying {
|
||||||
self.isPlaying = isPlaying
|
self.isPlaying = isPlaying
|
||||||
|
|
||||||
@ -550,7 +552,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func updateStickerSettings() {
|
override func updateStickerSettings(forceStopAnimations: Bool) {
|
||||||
|
self.forceStopAnimations = forceStopAnimations
|
||||||
self.updateVisibility()
|
self.updateVisibility()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -758,7 +758,7 @@ public class ChatMessageItemView: ListViewItemNode {
|
|||||||
func updateAutomaticMediaDownloadSettings() {
|
func updateAutomaticMediaDownloadSettings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateStickerSettings() {
|
func updateStickerSettings(forceStopAnimations: Bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
||||||
@ -814,7 +814,7 @@ public class ChatMessageItemView: ListViewItemNode {
|
|||||||
case .payment:
|
case .payment:
|
||||||
item.controllerInteraction.openCheckoutOrReceipt(item.message.id)
|
item.controllerInteraction.openCheckoutOrReceipt(item.message.id)
|
||||||
case let .urlAuth(url, buttonId):
|
case let .urlAuth(url, buttonId):
|
||||||
item.controllerInteraction.requestMessageActionUrlAuth(url, item.message.id, buttonId)
|
item.controllerInteraction.requestMessageActionUrlAuth(url, .message(id: item.message.id, buttonId: buttonId))
|
||||||
case .setupPoll:
|
case .setupPoll:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@ -261,7 +261,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
self?.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame)
|
self?.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame)
|
||||||
}, openMessageContextActions: { _, _, _, _ in
|
}, openMessageContextActions: { _, _, _, _ in
|
||||||
}, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
}, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
||||||
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _, _ in
|
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _, _ in
|
||||||
self?.openUrl(url)
|
self?.openUrl(url)
|
||||||
}, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message, associatedData in
|
}, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message, associatedData in
|
||||||
if let strongSelf = self, let navigationController = strongSelf.getNavigationController() {
|
if let strongSelf = self, let navigationController = strongSelf.getNavigationController() {
|
||||||
@ -852,6 +852,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
self?.view.endEditing(true)
|
self?.view.endEditing(true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
case .urlAuth:
|
||||||
|
break
|
||||||
case let .peer(peerId, _):
|
case let .peer(peerId, _):
|
||||||
if let peerId = peerId {
|
if let peerId = peerId {
|
||||||
strongSelf.openPeer(peerId: peerId, peer: nil)
|
strongSelf.openPeer(peerId: peerId, peer: nil)
|
||||||
@ -890,6 +892,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: nil,
|
sendSticker: nil,
|
||||||
|
requestMessageActionUrlAuth: nil,
|
||||||
present: { c, a in
|
present: { c, a in
|
||||||
self?.presentController(c, a)
|
self?.presentController(c, a)
|
||||||
}, dismissInput: {
|
}, dismissInput: {
|
||||||
|
|||||||
@ -110,7 +110,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
self.controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
|
self.controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
|
||||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
||||||
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, _, node, rect in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, _, node, rect in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||||
}, presentController: { _, _ in }, navigationController: {
|
}, presentController: { _, _ in }, navigationController: {
|
||||||
return nil
|
return nil
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
|
|||||||
@ -38,11 +38,15 @@ private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatContr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) {
|
func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)? = nil, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
switch resolvedUrl {
|
switch resolvedUrl {
|
||||||
case let .externalUrl(url):
|
case let .externalUrl(url):
|
||||||
context.sharedContext.openExternalUrl(context: context, urlContext: urlContext, url: url, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: dismissInput)
|
context.sharedContext.openExternalUrl(context: context, urlContext: urlContext, url: url, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: dismissInput)
|
||||||
|
case let .urlAuth(url):
|
||||||
|
requestMessageActionUrlAuth?(.url(url))
|
||||||
|
dismissInput()
|
||||||
|
break
|
||||||
case let .peer(peerId, navigation):
|
case let .peer(peerId, navigation):
|
||||||
if let peerId = peerId {
|
if let peerId = peerId {
|
||||||
openPeer(peerId, defaultNavigationForPeerId(peerId, navigation: navigation))
|
openPeer(peerId, defaultNavigationForPeerId(peerId, navigation: navigation))
|
||||||
|
|||||||
@ -213,6 +213,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
}
|
}
|
||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: nil,
|
sendSticker: nil,
|
||||||
|
requestMessageActionUrlAuth: nil,
|
||||||
present: { c, a in
|
present: { c, a in
|
||||||
context.sharedContext.applicationBindings.dismissNativeController()
|
context.sharedContext.applicationBindings.dismissNativeController()
|
||||||
|
|
||||||
|
|||||||
@ -84,7 +84,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
|||||||
}, sendBotContextResultAsGif: { _, _, _, _ in
|
}, sendBotContextResultAsGif: { _, _, _, _ in
|
||||||
return false
|
return false
|
||||||
}, requestMessageActionCallback: { _, _, _, _ in
|
}, requestMessageActionCallback: { _, _, _, _ in
|
||||||
}, requestMessageActionUrlAuth: { _, _, _ in
|
}, requestMessageActionUrlAuth: { _, _ in
|
||||||
}, activateSwitchInline: { _, _ in
|
}, activateSwitchInline: { _, _ in
|
||||||
}, openUrl: { _, _, _, _ in
|
}, openUrl: { _, _, _, _ in
|
||||||
}, shareCurrentLocation: {
|
}, shareCurrentLocation: {
|
||||||
|
|||||||
@ -1255,8 +1255,10 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let avatarNode: AvatarNode
|
|
||||||
|
|
||||||
|
private let containerNode: ContextControllerSourceNode
|
||||||
|
|
||||||
|
let avatarNode: AvatarNode
|
||||||
fileprivate var videoNode: UniversalVideoNode?
|
fileprivate var videoNode: UniversalVideoNode?
|
||||||
private var videoContent: NativeVideoContent?
|
private var videoContent: NativeVideoContent?
|
||||||
private var videoStartTimestamp: Double?
|
private var videoStartTimestamp: Double?
|
||||||
@ -1271,6 +1273,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tapped: (() -> Void)?
|
var tapped: (() -> Void)?
|
||||||
|
var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
|
|
||||||
private var isFirstAvatarLoading = true
|
private var isFirstAvatarLoading = true
|
||||||
var item: PeerInfoAvatarListItem?
|
var item: PeerInfoAvatarListItem?
|
||||||
@ -1279,15 +1282,29 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
init(context: AccountContext) {
|
init(context: AccountContext) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
self.containerNode = ContextControllerSourceNode()
|
||||||
|
|
||||||
let avatarFont = avatarPlaceholderFont(size: floor(100.0 * 16.0 / 37.0))
|
let avatarFont = avatarPlaceholderFont(size: floor(100.0 * 16.0 / 37.0))
|
||||||
self.avatarNode = AvatarNode(font: avatarFont)
|
self.avatarNode = AvatarNode(font: avatarFont)
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.addSubnode(self.avatarNode)
|
self.addSubnode(self.containerNode)
|
||||||
self.avatarNode.frame = CGRect(origin: CGPoint(x: -50.0, y: -50.0), size: CGSize(width: 100.0, height: 100.0))
|
self.containerNode.addSubnode(self.avatarNode)
|
||||||
|
self.containerNode.frame = CGRect(origin: CGPoint(x: -50.0, y: -50.0), size: CGSize(width: 100.0, height: 100.0))
|
||||||
|
self.avatarNode.frame = self.containerNode.bounds
|
||||||
|
|
||||||
self.avatarNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
||||||
|
self.avatarNode.view.addGestureRecognizer(tapGestureRecognizer)
|
||||||
|
|
||||||
|
self.containerNode.activated = { [weak self] gesture, _ in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tapGestureRecognizer.isEnabled = false
|
||||||
|
tapGestureRecognizer.isEnabled = true
|
||||||
|
strongSelf.contextAction?(strongSelf.containerNode, gesture)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -1337,7 +1354,8 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
self.avatarNode.setPeer(context: self.context, theme: theme, peer: peer, overrideImage: overrideImage, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true)
|
self.avatarNode.setPeer(context: self.context, theme: theme, peer: peer, overrideImage: overrideImage, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true)
|
||||||
self.isFirstAvatarLoading = false
|
self.isFirstAvatarLoading = false
|
||||||
|
|
||||||
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
|
self.containerNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
|
||||||
|
self.avatarNode.frame = self.containerNode.bounds
|
||||||
self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0))
|
self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0))
|
||||||
|
|
||||||
if let item = item {
|
if let item = item {
|
||||||
@ -1365,6 +1383,8 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.containerNode.isGestureEnabled = true
|
||||||
|
|
||||||
if let video = videoRepresentations.last, let peerReference = PeerReference(peer) {
|
if let video = videoRepresentations.last, let peerReference = PeerReference(peer) {
|
||||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
|
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
|
||||||
let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
|
let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
|
||||||
@ -1411,7 +1431,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
shape.path = maskPath.cgPath
|
shape.path = maskPath.cgPath
|
||||||
videoNode.layer.mask = shape
|
videoNode.layer.mask = shape
|
||||||
|
|
||||||
self.addSubnode(videoNode)
|
self.containerNode.addSubnode(videoNode)
|
||||||
}
|
}
|
||||||
} else if let videoNode = self.videoNode {
|
} else if let videoNode = self.videoNode {
|
||||||
self.videoContent = nil
|
self.videoContent = nil
|
||||||
@ -1424,6 +1444,8 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
self.videoNode = nil
|
self.videoNode = nil
|
||||||
|
|
||||||
videoNode.removeFromSupernode()
|
videoNode.removeFromSupernode()
|
||||||
|
|
||||||
|
self.containerNode.isGestureEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if let videoNode = self.videoNode {
|
if let videoNode = self.videoNode {
|
||||||
@ -2568,6 +2590,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
var cancelUpload: (() -> Void)?
|
var cancelUpload: (() -> Void)?
|
||||||
var requestUpdateLayout: (() -> Void)?
|
var requestUpdateLayout: (() -> Void)?
|
||||||
|
|
||||||
|
var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)?
|
var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)?
|
||||||
|
|
||||||
var navigationTransition: PeerInfoHeaderNavigationTransition?
|
var navigationTransition: PeerInfoHeaderNavigationTransition?
|
||||||
@ -2663,6 +2686,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
self.avatarListNode.avatarContainerNode.tapped = { [weak self] in
|
self.avatarListNode.avatarContainerNode.tapped = { [weak self] in
|
||||||
self?.initiateAvatarExpansion(gallery: false, first: false)
|
self?.initiateAvatarExpansion(gallery: false, first: false)
|
||||||
}
|
}
|
||||||
|
self.avatarListNode.avatarContainerNode.contextAction = { [weak self] node, gesture in
|
||||||
|
self?.displayAvatarContextMenu?(node, gesture)
|
||||||
|
}
|
||||||
|
|
||||||
self.editingContentNode.avatarNode.tapped = { [weak self] confirm in
|
self.editingContentNode.avatarNode.tapped = { [weak self] confirm in
|
||||||
self?.initiateAvatarExpansion(gallery: true, first: true)
|
self?.initiateAvatarExpansion(gallery: true, first: true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1204,7 +1204,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel.flags.contains(.isCreator) && (channel.username?.isEmpty ?? true)) || (channel.adminRights?.rights.contains(.canInviteUsers) == true) {
|
if (channel.flags.contains(.isCreator) && (channel.username?.isEmpty ?? true)) || (!channel.flags.contains(.isCreator) && channel.adminRights?.rights.contains(.canInviteUsers) == true) {
|
||||||
let invitesText: String
|
let invitesText: String
|
||||||
if let count = data.invitations?.count, count > 0 {
|
if let count = data.invitations?.count, count > 0 {
|
||||||
invitesText = "\(count)"
|
invitesText = "\(count)"
|
||||||
@ -1987,7 +1987,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}, sendBotContextResultAsGif: { _, _, _, _ in
|
}, sendBotContextResultAsGif: { _, _, _, _ in
|
||||||
return false
|
return false
|
||||||
}, requestMessageActionCallback: { _, _, _, _ in
|
}, requestMessageActionCallback: { _, _, _, _ in
|
||||||
}, requestMessageActionUrlAuth: { _, _, _ in
|
}, requestMessageActionUrlAuth: { _, _ in
|
||||||
}, activateSwitchInline: { _, _ in
|
}, activateSwitchInline: { _, _ in
|
||||||
}, openUrl: { [weak self] url, concealed, external, _ in
|
}, openUrl: { [weak self] url, concealed, external, _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -2726,6 +2726,29 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
screenData = peerInfoScreenData(context: context, peerId: peerId, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, isSettings: self.isSettings, ignoreGroupInCommon: ignoreGroupInCommon)
|
screenData = peerInfoScreenData(context: context, peerId: peerId, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, isSettings: self.isSettings, ignoreGroupInCommon: ignoreGroupInCommon)
|
||||||
|
|
||||||
|
self.headerNode.displayAvatarContextMenu = { [weak self] node, gesture in
|
||||||
|
guard let strongSelf = self, let peer = strongSelf.data?.peer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let items: [ContextMenuItem] = [
|
||||||
|
.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ReportProfilePhoto, icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.actionSheet.primaryTextColor)
|
||||||
|
}, action: { [weak self] c, f in
|
||||||
|
if let strongSelf = self, let parent = strongSelf.controller {
|
||||||
|
presentPeerReportOptions(context: context, parent: parent, contextController: c, subject: .profilePhoto(peer.id, 0), completion: { _, _ in })
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
|
||||||
|
let galleryController = AvatarGalleryController(context: strongSelf.context, peer: peer, remoteEntries: nil, replaceRootController: { controller, ready in
|
||||||
|
}, synchronousLoad: true)
|
||||||
|
galleryController.setHintWillBePresentedInPreviewingContext(true)
|
||||||
|
|
||||||
|
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
|
||||||
|
strongSelf.controller?.presentInGlobalOverlay(contextController)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.headerNode.avatarListNode.listContainerNode.currentIndexUpdated = { [weak self] in
|
self.headerNode.avatarListNode.listContainerNode.currentIndexUpdated = { [weak self] in
|
||||||
@ -3024,10 +3047,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: { [weak self] f, sourceNode, sourceRect in
|
sendSticker: { [weak self] f, sourceNode, sourceRect in
|
||||||
return false
|
return false
|
||||||
}, present: { [weak self] c, a in
|
}, requestMessageActionUrlAuth: nil, present: { [weak self] c, a in
|
||||||
self?.controller?.present(c, in: .window(.root), with: a)
|
self?.controller?.present(c, in: .window(.root), with: a)
|
||||||
}, dismissInput: { [weak self] in
|
}, dismissInput: { [weak self] in
|
||||||
|
self?.view.endEditing(true)
|
||||||
}, contentContext: nil)
|
}, contentContext: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3044,8 +3067,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, openPeer: { peerId, navigation in
|
strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, openPeer: { peerId, navigation in
|
||||||
self?.openPeer(peerId: peerId, navigation: navigation)
|
self?.openPeer(peerId: peerId, navigation: navigation)
|
||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: nil,
|
sendSticker: nil,
|
||||||
present: { c, a in
|
requestMessageActionUrlAuth: nil,
|
||||||
|
present: { c, a in
|
||||||
self?.controller?.present(c, in: .window(.root), with: a)
|
self?.controller?.present(c, in: .window(.root), with: a)
|
||||||
}, dismissInput: {
|
}, dismissInput: {
|
||||||
self?.view.endEditing(true)
|
self?.view.endEditing(true)
|
||||||
@ -4091,6 +4115,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
context.sharedContext.openResolvedUrl(.groupBotStart(peerId: peerId, payload: ""), context: self.context, urlContext: .generic, navigationController: controller.navigationController as? NavigationController, openPeer: { id, navigation in
|
context.sharedContext.openResolvedUrl(.groupBotStart(peerId: peerId, payload: ""), context: self.context, urlContext: .generic, navigationController: controller.navigationController as? NavigationController, openPeer: { id, navigation in
|
||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: nil,
|
sendSticker: nil,
|
||||||
|
requestMessageActionUrlAuth: nil,
|
||||||
present: { [weak controller] c, a in
|
present: { [weak controller] c, a in
|
||||||
controller?.present(c, in: .window(.root), with: a)
|
controller?.present(c, in: .window(.root), with: a)
|
||||||
}, dismissInput: { [weak controller] in
|
}, dismissInput: { [weak controller] in
|
||||||
@ -4921,7 +4946,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
resolvedUrl = .instantView(webPage, customAnchor)
|
resolvedUrl = .instantView(webPage, customAnchor)
|
||||||
}
|
}
|
||||||
strongSelf.context.sharedContext.openResolvedUrl(resolvedUrl, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, openPeer: { peer, navigation in
|
strongSelf.context.sharedContext.openResolvedUrl(resolvedUrl, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, openPeer: { peer, navigation in
|
||||||
}, sendFile: nil, sendSticker: nil, present: { [weak self] controller, arguments in
|
}, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, present: { [weak self] controller, arguments in
|
||||||
self?.controller?.push(controller)
|
self?.controller?.push(controller)
|
||||||
}, dismissInput: {}, contentContext: nil)
|
}, dismissInput: {}, contentContext: nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1142,8 +1142,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return resolveUrlImpl(account: account, url: url)
|
return resolveUrlImpl(account: account, url: url)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) {
|
public func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) {
|
||||||
openResolvedUrlImpl(resolvedUrl, context: context, urlContext: urlContext, navigationController: navigationController, openPeer: openPeer, sendFile: sendFile, sendSticker: sendSticker, present: present, dismissInput: dismissInput, contentContext: contentContext)
|
openResolvedUrlImpl(resolvedUrl, context: context, urlContext: urlContext, navigationController: navigationController, openPeer: openPeer, sendFile: sendFile, sendSticker: sendSticker, requestMessageActionUrlAuth: requestMessageActionUrlAuth, present: present, dismissInput: dismissInput, contentContext: contentContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController {
|
public func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController {
|
||||||
@ -1221,7 +1221,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
clickThroughMessage?()
|
clickThroughMessage?()
|
||||||
}, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in
|
}, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in
|
||||||
return false
|
return false
|
||||||
}, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
}, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||||
}, presentController: { _, _ in }, navigationController: {
|
}, presentController: { _, _ in }, navigationController: {
|
||||||
return nil
|
return nil
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
|
|||||||
@ -44,6 +44,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
|
|||||||
}
|
}
|
||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: nil,
|
sendSticker: nil,
|
||||||
|
requestMessageActionUrlAuth: nil,
|
||||||
present: presentImpl, dismissInput: {}, contentContext: nil)
|
present: presentImpl, dismissInput: {}, contentContext: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -494,20 +494,22 @@ public func parseWallpaperUrl(_ url: String) -> WallpaperUrlParameter? {
|
|||||||
|
|
||||||
private struct UrlHandlingConfiguration {
|
private struct UrlHandlingConfiguration {
|
||||||
static var defaultValue: UrlHandlingConfiguration {
|
static var defaultValue: UrlHandlingConfiguration {
|
||||||
return UrlHandlingConfiguration(token: nil, domains: [])
|
return UrlHandlingConfiguration(token: nil, domains: [], urlAuthDomains: [])
|
||||||
}
|
}
|
||||||
|
|
||||||
public let token: String?
|
public let token: String?
|
||||||
public let domains: [String]
|
public let domains: [String]
|
||||||
|
public let urlAuthDomains: [String]
|
||||||
|
|
||||||
fileprivate init(token: String?, domains: [String]) {
|
fileprivate init(token: String?, domains: [String], urlAuthDomains: [String]) {
|
||||||
self.token = token
|
self.token = token
|
||||||
self.domains = domains
|
self.domains = domains
|
||||||
|
self.urlAuthDomains = urlAuthDomains
|
||||||
}
|
}
|
||||||
|
|
||||||
static func with(appConfiguration: AppConfiguration) -> UrlHandlingConfiguration {
|
static func with(appConfiguration: AppConfiguration) -> UrlHandlingConfiguration {
|
||||||
if let data = appConfiguration.data, let token = data["autologin_token"] as? String, let domains = data["autologin_domains"] as? [String] {
|
if let data = appConfiguration.data, let token = data["autologin_token"] as? String, let domains = data["autologin_domains"] as? [String], let urlAuthDomains = data["url_auth_domains"] as? [String] {
|
||||||
return UrlHandlingConfiguration(token: token, domains: domains)
|
return UrlHandlingConfiguration(token: token, domains: domains, urlAuthDomains: urlAuthDomains)
|
||||||
} else {
|
} else {
|
||||||
return .defaultValue
|
return .defaultValue
|
||||||
}
|
}
|
||||||
@ -525,12 +527,16 @@ public func resolveUrlImpl(account: Account, url: String) -> Signal<ResolvedUrl,
|
|||||||
if !(url.hasPrefix("http") || url.hasPrefix("https")) {
|
if !(url.hasPrefix("http") || url.hasPrefix("https")) {
|
||||||
url = "http://\(url)"
|
url = "http://\(url)"
|
||||||
}
|
}
|
||||||
if let urlValue = URL(string: url), let host = urlValue.host, urlHandlingConfiguration.domains.contains(host.lowercased()), var components = URLComponents(string: url) {
|
if let urlValue = URL(string: url), let host = urlValue.host?.lowercased() {
|
||||||
components.scheme = "https"
|
if urlHandlingConfiguration.domains.contains(host), var components = URLComponents(string: url) {
|
||||||
var queryItems = components.queryItems ?? []
|
components.scheme = "https"
|
||||||
queryItems.append(URLQueryItem(name: "autologin_token", value: urlHandlingConfiguration.token))
|
var queryItems = components.queryItems ?? []
|
||||||
components.queryItems = queryItems
|
queryItems.append(URLQueryItem(name: "autologin_token", value: urlHandlingConfiguration.token))
|
||||||
url = components.url?.absoluteString ?? url
|
components.queryItems = queryItems
|
||||||
|
url = components.url?.absoluteString ?? url
|
||||||
|
} else if urlHandlingConfiguration.urlAuthDomains.contains(host) {
|
||||||
|
return .single(.urlAuth(url))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for basePath in baseTelegramMePaths {
|
for basePath in baseTelegramMePaths {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user