Various fixes and improvements

This commit is contained in:
Ilya Laktyushin 2021-02-23 23:01:09 +04:00
parent 0ecc138ff6
commit b14e81b767
49 changed files with 4616 additions and 4342 deletions

View File

@ -6151,3 +6151,5 @@ Sorry for the inconvenience.";
"VoiceOver.ScrollStatus" = "Row %1$@ of %2$@";
"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.";

View File

@ -162,6 +162,7 @@ public enum ResolvedUrlSettingsSection {
public enum ResolvedUrl {
case externalUrl(String)
case urlAuth(String)
case peer(PeerId?, ChatControllerInteractionNavigateToPeer)
case inaccessiblePeer
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>, messages: [MessageId: Message], peers: [PeerId: Peer]) -> Signal<ChatAvailableMessageActions, 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 openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void)
func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void)

View File

@ -527,6 +527,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
self.titleView.theme = self.presentationData.theme
self.titleView.strings = self.presentationData.strings
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))

View File

@ -166,8 +166,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peerId, navigation in
// self?.openPeer(peerId: peerId, navigation: navigation)
}, sendFile: nil,
sendSticker: nil,
present: { c, a in
sendSticker: nil,
requestMessageActionUrlAuth: nil,
present: { c, a in
present(c, a)
}, dismissInput: {
self?.dismissInput()

View File

@ -511,7 +511,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
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 selection: ChatHistoryMessageSelection = selected.flatMap { .selectable(selected: $0) } ?? .none
if let tagMask = tagMask, tagMask != .photoOrVideo {

View File

@ -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) {
self.theme = theme

View File

@ -132,9 +132,7 @@ public class InviteContactsController: ViewController, MFMessageComposeViewContr
if let strongSelf = self {
let url = strongSelf.presentationData.strings.InviteText_URL
let body = strongSelf.presentationData.strings.InviteText_SingleContact(url).0
let shareController = ShareController(context: strongSelf.context, subject: .text(body), externalShare: true, immediateExternalShare: true)
strongSelf.present(shareController, in: .window(.root))
presentExternalShare(context: strongSelf.context, text: body, parentController: strongSelf)
strongSelf.contactsNode.listNode.clearHighlightAnimated(true)
}

View File

@ -1249,11 +1249,16 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
}
}
public var forceHidden = false {
didSet {
self.updateItemNodeVisibilititesAndScrolling()
}
}
private func updateItemNodeVisibilititesAndScrolling() {
let visibleRect = self.scrollView.bounds
let isScrolling = self.scrollView.isDragging || self.scrollView.isDecelerating
for (_, itemNode) in self.itemNodes {
let visible = itemNode.frame.intersects(visibleRect)
let visible = itemNode.frame.intersects(visibleRect) && !self.forceHidden
if itemNode.isVisibleInGrid != visible {
itemNode.isVisibleInGrid = visible
}

View File

@ -29,6 +29,8 @@ public final class PeekController: ViewController {
private let content: PeekControllerContent
var sourceNode: () -> ASDisplayNode?
public var visibilityUpdated: ((Bool) -> Void)?
private var animatedIn = false
public init(theme: PeekControllerTheme, content: PeekControllerContent, sourceNode: @escaping () -> ASDisplayNode?) {
@ -67,6 +69,8 @@ public final class PeekController: ViewController {
if !self.animatedIn {
self.animatedIn = true
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) {
self.visibilityUpdated?(false)
self.controllerNode.animateOut(to: self.getSourceRect(), completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
})

View File

@ -504,6 +504,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
if !displayInfo {
authorNameText = ""
dateText = ""
canEdit = false
}
var messageText = NSAttributedString(string: "")

View File

@ -665,7 +665,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
playing = true
case let .buffering(_, whilePlaying, _, display):
displayProgress = display
initialBuffering = true
initialBuffering = !whilePlaying
isPaused = !whilePlaying
var isStreaming = false
if let fetchStatus = strongSelf.fetchStatus {

View File

@ -1198,6 +1198,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}, sendFile: nil,
sendSticker: nil,
requestMessageActionUrlAuth: nil,
present: { c, a in
self?.present(c, a)
}, dismissInput: {
@ -1275,6 +1276,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
fromPlayingVideo = true
entries.append(InstantPageGalleryEntry(index: Int32(media.index), pageId: webPage.webpageId, media: media, caption: media.caption, credit: media.credit, location: nil))
} else {
fromPlayingVideo = true
var medias: [InstantPageMedia] = mediasFromItems(items)
medias = medias.filter {
return $0.media is TelegramMediaImage || $0.media is TelegramMediaFile

View File

@ -615,24 +615,26 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
})))
}
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextEdit, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
}, action: { _, f in
f(.default)
let controller = inviteLinkEditController(context: context, peerId: peerId, invite: invite, completion: { invite in
if let invite = invite {
if invite.isRevoked {
invitesContext.remove(invite)
revokedInvitesContext.add(invite.withUpdated(isRevoked: true))
} else {
invitesContext.update(invite)
if !invite.isPermanent {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextEdit, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
}, action: { _, f in
f(.default)
let controller = inviteLinkEditController(context: context, peerId: peerId, invite: invite, completion: { invite in
if let invite = invite {
if invite.isRevoked {
invitesContext.remove(invite)
revokedInvitesContext.add(invite.withUpdated(isRevoked: true))
} else {
invitesContext.update(invite)
}
}
}
})
controller.navigationPresentation = .modal
pushControllerImpl?(controller)
})))
})
controller.navigationPresentation = .modal
pushControllerImpl?(controller)
})))
}
}
if invite.isRevoked {

View File

@ -79,10 +79,36 @@ public struct ItemListToolbarItem {
}
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 {
let theme: PresentationTheme
let strings: PresentationStrings
let entries: ItemListNodeEntryTransition
let updateStyle: ItemListStyle?
let emptyStateItem: ItemListControllerEmptyStateItem?
@ -348,7 +374,7 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
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
if let strongSelf = self {
@ -452,6 +478,10 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
searchNode.updateLayout(layout: layout, navigationBarHeight: navigationBarHeight, transition: transition)
}
if let toolbarNode = self.toolbarNode {
// toolbarNode.updateLayout(size: layout.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: UIEdgeInsets(), bottomInset: 0.0, toolbar: <#T##Toolbar#>, transition: transition)
}
let dequeue = self.validLayout == nil
self.validLayout = (layout, navigationBarHeight)
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
if let strongSelf = self {
if !strongSelf.didSetReady {

View File

@ -61,7 +61,7 @@ const CGFloat TGPhotoPaintSettingsPadPickerWidth = 360.0f;
_eyedropperButton.exclusiveTouch = true;
[_eyedropperButton setImage:TGTintedImage([UIImage imageNamed:@"Editor/Eyedropper"], [UIColor whiteColor]) forState:UIControlStateNormal];
[_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.exclusiveTouch = true;
@ -211,8 +211,7 @@ const CGFloat TGPhotoPaintSettingsPadPickerWidth = 360.0f;
- (void)layoutSubviews
{
CGFloat leftInset = 23.0f;
CGFloat rightInset = 66.0f;
CGFloat inset = 66.0f;
CGFloat colorPickerHeight = 10.0f;
if (self.frame.size.width > self.frame.size.height)
{
@ -223,14 +222,14 @@ const CGFloat TGPhotoPaintSettingsPadPickerWidth = 360.0f;
}
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);
_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
{
_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);
_settingsButton.frame = CGRectMake(floor((self.frame.size.width - _settingsButton.frame.size.width) / 2.0f), 10.0f, _settingsButton.frame.size.width, _settingsButton.frame.size.height);
}

View File

@ -148,7 +148,7 @@ private enum ChannelDiscussionGroupSetupControllerEntry: ItemListNodeEntry {
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.plusIconImage(theme), title: text, sectionId: self.section, editing: false, action: {
arguments.createGroup()
})
case let .group(_, theme, strings, peer, nameOrder):
case let .group(_, _, strings, peer, nameOrder):
let text: String
if let peer = peer as? TelegramChannel, let addressName = peer.addressName, !addressName.isEmpty {
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: {
arguments.selectGroup(peer.id)
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in })
case let .groupsInfo(theme, title):
case let .groupsInfo(_, title):
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: {
arguments.unlinkGroup()
})

View File

@ -20,6 +20,7 @@ import Markdown
public enum PeerReportSubject {
case peer(PeerId)
case messages([MessageId])
case profilePhoto(PeerId, Int64)
}
public enum PeerReportOption {
@ -60,7 +61,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
}, action: { [weak parent] _, f in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var reportReason: ReportReason?
let reportReason: ReportReason
switch option {
case .spam:
reportReason = .spam
@ -75,30 +76,60 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
case .copyright:
reportReason = .copyright
case .other:
break
reportReason = .custom
}
if let reportReason = reportReason {
switch subject {
case let .peer(peerId):
let _ = (reportPeer(account: context.account, peerId: peerId, 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)
})
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)
})
let displaySuccess = {
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)
}
} 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)
})))
}
@ -162,17 +193,21 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe
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 {
case let .peer(peerId):
if passthrough {
completion(reportReason, true)
} 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: {
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)
}
displaySuccess()
completion(nil, false)
})
}
@ -180,11 +215,19 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe
if passthrough {
completion(reportReason, true)
} 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: {
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)
}
displaySuccess()
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)
})
}
@ -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: {
dismissAction()
action()
action(message)
}))
controller.setItemGroups([
@ -214,7 +257,7 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe
])
present(controller, nil)
} else {
action()
action("")
}
} else {
push(peerReportController(context: context, subject: subject, completion: completion))
@ -369,16 +412,21 @@ private func peerReportController(context: AccountContext, subject: PeerReportSu
dismissImpl?()
}
switch subject {
case let .peer(peerId):
reportDisposable.set((reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: text)
|> deliverOnMainQueue).start(completed: {
completed()
}))
case let .messages(messageIds):
reportDisposable.set((reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: text)
|> deliverOnMainQueue).start(completed: {
completed()
}))
case let .peer(peerId):
reportDisposable.set((reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: text)
|> deliverOnMainQueue).start(completed: {
completed()
}))
case let .messages(messageIds):
reportDisposable.set((reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: text)
|> deliverOnMainQueue).start(completed: {
completed()
}))
case let .profilePhoto(peerId, photoId):
reportDisposable.set((reportPeerPhoto(account: context.account, peerId: peerId, reason: reportReason, message: text)
|> deliverOnMainQueue).start(completed: {
completed()
}))
}
}
})

View File

@ -1438,6 +1438,7 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
}, sendFile: nil,
sendSticker: nil,
requestMessageActionUrlAuth: nil,
present: { c, a in
presentControllerImpl?(c, a)
}, dismissInput: {

View File

@ -531,8 +531,7 @@ public func proxySettingsController(accountManager: AccountManager, context: Acc
result += string
}
let controller = ShareController(context: context, subject: .text(result), preferredAction: .default, showInChat: nil, externalShare: true, immediateExternalShare: true, switchableAccounts: [])
presentControllerImpl?(controller, nil)
presentExternalShare(context: context, text: result, parentController: strongController)
})
}

View File

@ -378,19 +378,15 @@ func proxyServerSettingsController(context: AccountContext? = nil, presentationD
}
shareImpl = { [weak controller] in
let state = stateValue.with { $0 }
guard let server = proxyServerSettings(with: state), let strongController = controller else {
guard let server = proxyServerSettings(with: state) else {
return
}
let link = shareLink(for: server)
controller?.view.endEditing(true)
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
let controller = ShareProxyServerActionSheetController(presentationData: presentationData, updatedPresentationData: updatedPresentationData, link: link)
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)
}
let controller = ShareProxyServerActionSheetController(presentationData: presentationData, updatedPresentationData: updatedPresentationData, link: link)
presentControllerImpl?(controller, nil)
}
return controller

View File

@ -180,7 +180,7 @@ public func logoutOptionsController(context: AccountContext, navigationControlle
dismissImpl?()
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)
}, dismissInput: {}, contentContext: nil)
})

View File

@ -897,7 +897,7 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList
let _ = (cachedFaqInstantPage(context: context)
|> deliverOnMainQueue).start(next: { resolvedUrl 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)
}, dismissInput: {}, contentContext: nil)
})

View File

@ -1080,3 +1080,13 @@ private class ShareToInstagramActivity: UIActivity {
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)
}

View File

@ -43,7 +43,7 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese
private let openMentionDisposable = MetaDisposable()
private var presentationDataDisposable: Disposable?
public var sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)? {
didSet {
if self.isNodeLoaded {

View File

@ -240,6 +240,11 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
let controller = PeekController(theme: PeekControllerTheme(presentationTheme: strongSelf.presentationData.theme), content: content, sourceNode: {
return sourceNode
})
controller.visibilityUpdated = { [weak self] visible in
if let strongSelf = self {
strongSelf.contentGridNode.forceHidden = visible
}
}
strongSelf.presentInGlobalOverlay?(controller, nil)
return controller
}

View File

@ -133,9 +133,9 @@ func managedConsumePersonalMessagesActions(postbox: Postbox, network: Network, s
for (entry, disposable) in beginValidateOperations {
let signal = synchronizeUnseenPersonalMentionsTag(postbox: postbox, network: network, entry: entry)
|> then(postbox.transaction { transaction -> Void in
transaction.removeInvalidatedMessageHistoryTagsSummaryEntry(entry)
})
|> then(postbox.transaction { transaction -> Void in
transaction.removeInvalidatedMessageHistoryTagsSummaryEntry(entry)
})
disposable.set(signal.start())
}
})

View File

@ -126,6 +126,22 @@ public func reportPeer(account: Account, peerId: PeerId, reason: ReportReason, m
} |> 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> {
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
let groupedIds = messagesIdsGroupedByPeerId(messageIds)
@ -149,22 +165,6 @@ public func reportPeerMessages(account: Account, messageIds: [MessageId], reason
} |> 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> {
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in

View File

@ -174,62 +174,89 @@ public enum MessageActionUrlAuthResult {
case request(String, Peer, Bool)
}
public func requestMessageActionUrlAuth(account: Account, messageId: MessageId, buttonId: Int32) -> Signal<MessageActionUrlAuthResult, NoError> {
return account.postbox.loadedPeerWithId(messageId.peerId)
|> take(1)
|> mapToSignal { peer in
if let inputPeer = apiInputPeer(peer) {
return account.network.request(Api.functions.messages.requestUrlAuth(peer: inputPeer, msgId: messageId.id, buttonId: buttonId))
public enum MessageActionUrlSubject {
case message(id: MessageId, buttonId: Int32)
case url(String)
}
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)
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
return .single(nil)
}
|> map { result -> MessageActionUrlAuthResult in
guard let result = result else {
return .default
}
switch result {
case .urlAuthResultDefault:
return .default
case let .urlAuthResultAccepted(url):
return .accepted(url)
case let .urlAuthResultRequest(flags, bot, domain):
return .request(domain, TelegramUser(user: bot), (flags & (1 << 0)) != 0)
}
}
} else {
return .single(.default)
}
return request
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
return .single(nil)
}
|> map { result -> MessageActionUrlAuthResult in
guard let result = result else {
return .default
}
switch result {
case .urlAuthResultDefault:
return .default
case let .urlAuthResultAccepted(url):
return .accepted(url)
case let .urlAuthResultRequest(flags, bot, domain):
return .request(domain, TelegramUser(user: bot), (flags & (1 << 0)) != 0)
}
}
}
public func acceptMessageActionUrlAuth(account: Account, messageId: MessageId, buttonId: Int32, allowWriteAccess: Bool) -> Signal<MessageActionUrlAuthResult, NoError> {
return account.postbox.loadedPeerWithId(messageId.peerId)
|> take(1)
|> mapToSignal { peer in
if let inputPeer = apiInputPeer(peer) {
var flags: Int32 = 0
if allowWriteAccess {
flags |= Int32(1 << 0)
public func acceptMessageActionUrlAuth(account: Account, subject: MessageActionUrlSubject, allowWriteAccess: Bool) -> Signal<MessageActionUrlAuthResult, NoError> {
var flags: Int32 = 0
if allowWriteAccess {
flags |= Int32(1 << 0)
}
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.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)
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
return .single(nil)
}
|> map { result -> MessageActionUrlAuthResult in
guard let result = result else {
return .default
}
switch result {
case let .urlAuthResultAccepted(url):
return .accepted(url)
default:
return .default
}
}
} else {
return .single(.default)
}
return request
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
return .single(nil)
}
|> map { result -> MessageActionUrlAuthResult in
guard let result = result else {
return .default
}
switch result {
case let .urlAuthResultAccepted(url):
return .accepted(url)
default:
return .default
}
}
}

View File

@ -27,8 +27,8 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
super.init()
self.containerNode.addSubnode(self.avatarNode)
self.addSubnode(self.containerNode)
self.containerNode.addSubnode(self.avatarNode)
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self else {

View File

@ -209,7 +209,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
break
case let .urlAuth(url, buttonId):
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):
self.controllerInteraction.openPollCreation(isQuiz)

View File

@ -306,6 +306,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private var automaticMediaDownloadSettings: MediaAutoDownloadSettings
private var automaticMediaDownloadSettingsDisposable: Disposable?
private var disableStickerAnimationsPromise = ValuePromise<Bool>(false)
private var disableStickerAnimations = false
private var stickerSettings: ChatInterfaceStickerSettings
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 {
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))
return
}
if let _ = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) {
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()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
return $0.updatedTitlePanelContext {
if !$0.contains(where: {
switch $0 {
case .requestInProgress:
return true
default:
return false
}
return $0
}) {
var updatedContexts = $0
updatedContexts.append(.requestInProgress)
return updatedContexts.sorted()
}
})
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.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
}
})
strongSelf.messageActionUrlAuthDisposable.set(((combineLatest(strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.context.account.peerId), requestMessageActionUrlAuth(account: strongSelf.context.account, subject: subject) |> 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: { 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
guard let strongSelf = self else {
@ -2578,7 +2578,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
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
@ -3440,20 +3442,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
self.stickerSettingsDisposable = (context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings])
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
self.stickerSettingsDisposable = combineLatest(queue: Queue.mainQueue(), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]), self.disableStickerAnimationsPromise.get()).start(next: { [weak self] sharedData, disableStickerAnimations in
var stickerSettings = StickerSettings.defaultSettings
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings {
stickerSettings = value
}
let chatStickerSettings = ChatInterfaceStickerSettings(stickerSettings: stickerSettings)
if let strongSelf = self, strongSelf.stickerSettings != chatStickerSettings {
if let strongSelf = self, strongSelf.stickerSettings != chatStickerSettings || strongSelf.disableStickerAnimations != disableStickerAnimations {
strongSelf.stickerSettings = chatStickerSettings
strongSelf.disableStickerAnimations = disableStickerAnimations
strongSelf.controllerInteraction?.stickerSettings = chatStickerSettings
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) {
self.disableStickerAnimationsPromise.set(!isInFocus)
self.chatDisplayNode.inFocusUpdated(isInFocus: isInFocus)
}
@ -8497,6 +8499,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self {
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 fileTypes: (music: Bool, other: Bool) = (false, false)
if results.count > 1 {
@ -11082,6 +11091,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}, sendFile: nil,
sendSticker: { [weak self] f, sourceNode, sourceRect in
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
self?.present(c, in: .window(.root), with: a)
}, dismissInput: { [weak self] in

View File

@ -64,7 +64,7 @@ public final class ChatControllerInteraction {
let sendGif: (FileMediaReference, ASDisplayNode, CGRect) -> Bool
let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool
let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void
let requestMessageActionUrlAuth: (String, MessageId, Int32) -> Void
let requestMessageActionUrlAuth: (String, MessageActionUrlSubject) -> Void
let activateSwitchInline: (PeerId?, String) -> Void
let openUrl: (String, Bool, Bool?, Message?) -> Void
let shareCurrentLocation: () -> Void
@ -154,7 +154,7 @@ public final class ChatControllerInteraction {
sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool,
sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool,
requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void,
requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void,
requestMessageActionUrlAuth: @escaping (String, MessageActionUrlSubject) -> Void,
activateSwitchInline: @escaping (PeerId?, String) -> Void,
openUrl: @escaping (String, Bool, Bool?, Message?) -> Void,
shareCurrentLocation: @escaping () -> Void,
@ -298,7 +298,7 @@ public final class ChatControllerInteraction {
static var `default`: ChatControllerInteraction {
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: {
return nil
}, chatControllerNode: {

View File

@ -2086,10 +2086,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.historyNode.prefetchManager.updateAutoDownloadSettings(settings)
}
func updateStickerSettings(_ settings: ChatInterfaceStickerSettings) {
func updateStickerSettings(_ settings: ChatInterfaceStickerSettings, forceStopAnimations: Bool) {
self.historyNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView {
itemNode.updateStickerSettings()
itemNode.updateStickerSettings(forceStopAnimations: forceStopAnimations)
}
}
}

View File

@ -810,6 +810,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
})))
}
var isUnremovableAction = false
if messages.count == 1 {
let message = messages[0]
@ -826,6 +827,14 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
if !hasAutoremove {
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 file.isVideo {
if file.isAnimated {
@ -937,7 +946,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
interfaceInteraction.deleteMessages(selectAll ? messages : [message], controller, f)
}
}), false))
} else {
} else if !isUnremovableAction {
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)
}, action: { controller, f in

View File

@ -1242,6 +1242,9 @@ final class ChatMediaInputNode: ChatInputNode {
let controller = PeekController(theme: PeekControllerTheme(presentationTheme: strongSelf.theme), content: content, sourceNode: {
return sourceNode
})
controller.visibilityUpdated = { [weak self] visible in
}
strongSelf.controllerInteraction.presentGlobalOverlayController(controller, nil)
return controller
}

View File

@ -178,6 +178,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
private var highlightedState: Bool = false
private var forceStopAnimations = false
private var haptic: EmojiHaptic?
private var mediaPlayer: MediaPlayer?
private let mediaStatusDisposable = MetaDisposable()
@ -489,7 +491,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
if let animationNode = self.animationNode as? AnimatedStickerNode {
let isPlaying = self.visibilityStatus
let isPlaying = self.visibilityStatus && !self.forceStopAnimations
if 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()
}

View File

@ -758,7 +758,7 @@ public class ChatMessageItemView: ListViewItemNode {
func updateAutomaticMediaDownloadSettings() {
}
func updateStickerSettings() {
func updateStickerSettings(forceStopAnimations: Bool) {
}
func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
@ -814,7 +814,7 @@ public class ChatMessageItemView: ListViewItemNode {
case .payment:
item.controllerInteraction.openCheckoutOrReceipt(item.message.id)
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:
break
}

View File

@ -261,7 +261,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
self?.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame)
}, 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: { [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)
}, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message, associatedData in
if let strongSelf = self, let navigationController = strongSelf.getNavigationController() {
@ -852,6 +852,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
self?.view.endEditing(true)
})
}
case .urlAuth:
break
case let .peer(peerId, _):
if let peerId = peerId {
strongSelf.openPeer(peerId: peerId, peer: nil)
@ -890,6 +892,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}
}, sendFile: nil,
sendSticker: nil,
requestMessageActionUrlAuth: nil,
present: { c, a in
self?.presentController(c, a)
}, dismissInput: {

View File

@ -110,7 +110,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
self.controllerInteraction = 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: { 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: {
return nil
}, chatControllerNode: {

View File

@ -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 }
switch resolvedUrl {
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)
case let .urlAuth(url):
requestMessageActionUrlAuth?(.url(url))
dismissInput()
break
case let .peer(peerId, navigation):
if let peerId = peerId {
openPeer(peerId, defaultNavigationForPeerId(peerId, navigation: navigation))

View File

@ -213,6 +213,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
}
}, sendFile: nil,
sendSticker: nil,
requestMessageActionUrlAuth: nil,
present: { c, a in
context.sharedContext.applicationBindings.dismissNativeController()

View File

@ -84,7 +84,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}, sendBotContextResultAsGif: { _, _, _, _ in
return false
}, requestMessageActionCallback: { _, _, _, _ in
}, requestMessageActionUrlAuth: { _, _, _ in
}, requestMessageActionUrlAuth: { _, _ in
}, activateSwitchInline: { _, _ in
}, openUrl: { _, _, _, _ in
}, shareCurrentLocation: {

View File

@ -1255,8 +1255,10 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
let context: AccountContext
let avatarNode: AvatarNode
private let containerNode: ContextControllerSourceNode
let avatarNode: AvatarNode
fileprivate var videoNode: UniversalVideoNode?
private var videoContent: NativeVideoContent?
private var videoStartTimestamp: Double?
@ -1271,6 +1273,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
}
var tapped: (() -> Void)?
var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
private var isFirstAvatarLoading = true
var item: PeerInfoAvatarListItem?
@ -1279,15 +1282,29 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
init(context: AccountContext) {
self.context = context
self.containerNode = ContextControllerSourceNode()
let avatarFont = avatarPlaceholderFont(size: floor(100.0 * 16.0 / 37.0))
self.avatarNode = AvatarNode(font: avatarFont)
super.init()
self.addSubnode(self.avatarNode)
self.avatarNode.frame = CGRect(origin: CGPoint(x: -50.0, y: -50.0), size: CGSize(width: 100.0, height: 100.0))
self.addSubnode(self.containerNode)
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 {
@ -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.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))
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) {
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)
@ -1411,7 +1431,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
shape.path = maskPath.cgPath
videoNode.layer.mask = shape
self.addSubnode(videoNode)
self.containerNode.addSubnode(videoNode)
}
} else if let videoNode = self.videoNode {
self.videoContent = nil
@ -1424,6 +1444,8 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
self.videoNode = nil
videoNode.removeFromSupernode()
self.containerNode.isGestureEnabled = false
}
if let videoNode = self.videoNode {
@ -2568,6 +2590,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var cancelUpload: (() -> Void)?
var requestUpdateLayout: (() -> Void)?
var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)?
var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)?
var navigationTransition: PeerInfoHeaderNavigationTransition?
@ -2663,6 +2686,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.avatarListNode.avatarContainerNode.tapped = { [weak self] in
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?.initiateAvatarExpansion(gallery: true, first: true)
}

View File

@ -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
if let count = data.invitations?.count, count > 0 {
invitesText = "\(count)"
@ -1987,7 +1987,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}, sendBotContextResultAsGif: { _, _, _, _ in
return false
}, requestMessageActionCallback: { _, _, _, _ in
}, requestMessageActionUrlAuth: { _, _, _ in
}, requestMessageActionUrlAuth: { _, _ in
}, activateSwitchInline: { _, _ in
}, openUrl: { [weak self] url, concealed, external, _ in
guard let strongSelf = self else {
@ -2726,6 +2726,29 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
} else {
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
@ -3024,10 +3047,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}, sendFile: nil,
sendSticker: { [weak self] f, sourceNode, sourceRect in
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)
}, dismissInput: { [weak self] in
self?.view.endEditing(true)
}, 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
self?.openPeer(peerId: peerId, navigation: navigation)
}, sendFile: nil,
sendSticker: nil,
present: { c, a in
sendSticker: nil,
requestMessageActionUrlAuth: nil,
present: { c, a in
self?.controller?.present(c, in: .window(.root), with: a)
}, dismissInput: {
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
}, sendFile: nil,
sendSticker: nil,
requestMessageActionUrlAuth: nil,
present: { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a)
}, dismissInput: { [weak controller] in
@ -4921,7 +4946,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
resolvedUrl = .instantView(webPage, customAnchor)
}
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)
}, dismissInput: {}, contentContext: nil)
}

View File

@ -1142,8 +1142,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
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?) {
openResolvedUrlImpl(resolvedUrl, context: context, urlContext: urlContext, navigationController: navigationController, openPeer: openPeer, sendFile: sendFile, sendSticker: sendSticker, present: present, dismissInput: dismissInput, contentContext: contentContext)
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, requestMessageActionUrlAuth: requestMessageActionUrlAuth, present: present, dismissInput: dismissInput, contentContext: contentContext)
}
public func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController {
@ -1221,7 +1221,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
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
}, 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: {
return nil
}, chatControllerNode: {

View File

@ -44,6 +44,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
}
}, sendFile: nil,
sendSticker: nil,
requestMessageActionUrlAuth: nil,
present: presentImpl, dismissInput: {}, contentContext: nil)
}

View File

@ -494,20 +494,22 @@ public func parseWallpaperUrl(_ url: String) -> WallpaperUrlParameter? {
private struct UrlHandlingConfiguration {
static var defaultValue: UrlHandlingConfiguration {
return UrlHandlingConfiguration(token: nil, domains: [])
return UrlHandlingConfiguration(token: nil, domains: [], urlAuthDomains: [])
}
public let token: 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.domains = domains
self.urlAuthDomains = urlAuthDomains
}
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] {
return UrlHandlingConfiguration(token: token, domains: domains)
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, urlAuthDomains: urlAuthDomains)
} else {
return .defaultValue
}
@ -525,12 +527,16 @@ public func resolveUrlImpl(account: Account, url: String) -> Signal<ResolvedUrl,
if !(url.hasPrefix("http") || url.hasPrefix("https")) {
url = "http://\(url)"
}
if let urlValue = URL(string: url), let host = urlValue.host, urlHandlingConfiguration.domains.contains(host.lowercased()), var components = URLComponents(string: url) {
components.scheme = "https"
var queryItems = components.queryItems ?? []
queryItems.append(URLQueryItem(name: "autologin_token", value: urlHandlingConfiguration.token))
components.queryItems = queryItems
url = components.url?.absoluteString ?? url
if let urlValue = URL(string: url), let host = urlValue.host?.lowercased() {
if urlHandlingConfiguration.domains.contains(host), var components = URLComponents(string: url) {
components.scheme = "https"
var queryItems = components.queryItems ?? []
queryItems.append(URLQueryItem(name: "autologin_token", value: urlHandlingConfiguration.token))
components.queryItems = queryItems
url = components.url?.absoluteString ?? url
} else if urlHandlingConfiguration.urlAuthDomains.contains(host) {
return .single(.urlAuth(url))
}
}
for basePath in baseTelegramMePaths {