mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-06 11:30:40 +00:00
Merge branch 'beta'
This commit is contained in:
commit
1f69d24691
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
|||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
||||||
- name: Set active Xcode path
|
- name: Set active Xcode path
|
||||||
run: sudo xcode-select -s /Applications/Xcode_12.3.app/Contents/Developer
|
run: sudo xcode-select -s /Applications/Xcode_12.4.app/Contents/Developer
|
||||||
|
|
||||||
- name: Create canonical source directory
|
- name: Create canonical source directory
|
||||||
run: |
|
run: |
|
||||||
@ -40,8 +40,8 @@ jobs:
|
|||||||
# download bazel
|
# download bazel
|
||||||
mkdir -p $HOME/bazel-dist
|
mkdir -p $HOME/bazel-dist
|
||||||
pushd $HOME/bazel-dist
|
pushd $HOME/bazel-dist
|
||||||
curl -O -L https://github.com/bazelbuild/bazel/releases/download/3.7.0/bazel-3.7.0-darwin-x86_64
|
curl -O -L https://github.com/bazelbuild/bazel/releases/download/4.0.0/bazel-4.0.0-darwin-x86_64
|
||||||
mv bazel-3.7.0* bazel
|
mv bazel-4.0.0* bazel
|
||||||
chmod +x bazel
|
chmod +x bazel
|
||||||
./bazel --version
|
./bazel --version
|
||||||
popd
|
popd
|
||||||
|
@ -6151,3 +6151,10 @@ 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.";
|
||||||
|
|
||||||
|
"Channel.AddUserLeftError" = "Sorry, if a person is no longer part of a channel, you need to be in their Telegram contacts in order to add them back.\n\nNote that they can still join via the channel's invite link as long as they are not in the Removed Users list.";
|
||||||
|
|
||||||
|
"Message.ScamAccount" = "Scam";
|
||||||
|
"Message.FakeAccount" = "Fake";
|
||||||
|
@ -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)
|
||||||
|
@ -258,7 +258,7 @@ private final class ImportManager {
|
|||||||
if !pathExtension.isEmpty, let value = TGMimeTypeMap.mimeType(forExtension: pathExtension) {
|
if !pathExtension.isEmpty, let value = TGMimeTypeMap.mimeType(forExtension: pathExtension) {
|
||||||
mimeType = value
|
mimeType = value
|
||||||
}
|
}
|
||||||
return ChatHistoryImport.uploadMedia(account: account, session: session, file: tempFile, fileName: entry.0.path, mimeType: mimeType, type: entry.2)
|
return ChatHistoryImport.uploadMedia(account: account, session: session, file: tempFile, disposeFileAfterDone: true, fileName: entry.0.path, mimeType: mimeType, type: entry.2)
|
||||||
|> mapError { error -> ImportError in
|
|> mapError { error -> ImportError in
|
||||||
switch error {
|
switch error {
|
||||||
case .chatAdminRequired:
|
case .chatAdminRequired:
|
||||||
|
@ -332,7 +332,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
|||||||
if case .search = source {
|
if case .search = source {
|
||||||
if let _ = peer as? TelegramChannel {
|
if let _ = peer as? TelegramChannel {
|
||||||
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_JoinChannel, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_JoinChannel, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||||
var createSignal = context.peerChannelMemberCategoriesContextsManager.join(account: context.account, peerId: peerId)
|
var createSignal = context.peerChannelMemberCategoriesContextsManager.join(account: context.account, peerId: peerId, hash: nil)
|
||||||
var cancelImpl: (() -> Void)?
|
var cancelImpl: (() -> Void)?
|
||||||
let progressSignal = Signal<Never, NoError> { subscriber in
|
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
@ -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))
|
||||||
|
@ -167,6 +167,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
// self?.openPeer(peerId: peerId, navigation: navigation)
|
// self?.openPeer(peerId: peerId, navigation: navigation)
|
||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: nil,
|
sendSticker: nil,
|
||||||
|
requestMessageActionUrlAuth: nil,
|
||||||
present: { c, a in
|
present: { c, a in
|
||||||
present(c, a)
|
present(c, a)
|
||||||
}, dismissInput: {
|
}, 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
|
||||||
|
@ -1307,10 +1307,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _):
|
case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
if let peer = messages.last?.author {
|
if let peer = messages.last?.author {
|
||||||
if peer.isScam {
|
if peer.isScam {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, type: .regular)
|
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
|
||||||
credibilityIconOffset = 2.0
|
credibilityIconOffset = 2.0
|
||||||
} else if peer.isFake {
|
} else if peer.isFake {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, type: .regular)
|
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
|
||||||
credibilityIconOffset = 2.0
|
credibilityIconOffset = 2.0
|
||||||
} else if peer.isVerified {
|
} else if peer.isVerified {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
|
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
|
||||||
@ -1322,10 +1322,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
} else if case let .chat(itemPeer) = contentPeer, let peer = itemPeer.chatMainPeer {
|
} else if case let .chat(itemPeer) = contentPeer, let peer = itemPeer.chatMainPeer {
|
||||||
if peer.isScam {
|
if peer.isScam {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, type: .regular)
|
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
|
||||||
credibilityIconOffset = 2.0
|
credibilityIconOffset = 2.0
|
||||||
} else if peer.isFake {
|
} else if peer.isFake {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, type: .regular)
|
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
|
||||||
credibilityIconOffset = 2.0
|
credibilityIconOffset = 2.0
|
||||||
} else if peer.isVerified {
|
} else if peer.isVerified {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
|
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
|
||||||
|
@ -95,10 +95,17 @@ func contactContextMenuItems(context: AccountContext, peerId: PeerId, contactsCo
|
|||||||
if let navigationController = (contactsController?.navigationController as? NavigationController) {
|
if let navigationController = (contactsController?.navigationController as? NavigationController) {
|
||||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), peekData: nil))
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), peekData: nil))
|
||||||
}
|
}
|
||||||
}, error: { _ in
|
}, error: { error in
|
||||||
if let contactsController = contactsController {
|
if let contactsController = contactsController {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
contactsController.present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
let text: String
|
||||||
|
switch error {
|
||||||
|
case .limitExceeded:
|
||||||
|
text = presentationData.strings.TwoStepAuth_FloodError
|
||||||
|
default:
|
||||||
|
text = presentationData.strings.Login_UnknownError
|
||||||
|
}
|
||||||
|
contactsController.present(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -121,10 +128,17 @@ func contactContextMenuItems(context: AccountContext, peerId: PeerId, contactsCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if canCall {
|
if canCall {
|
||||||
items.append(.action(ContextMenuActionItem(text: strings.ContactList_Context_Call, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: strings.ContactList_Context_Call, icon: { theme in
|
||||||
if let contactsController = contactsController {
|
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor)
|
||||||
|
}, action: { _, f in
|
||||||
context.requestCall(peerId: peerId, isVideo: false, completion: {})
|
context.requestCall(peerId: peerId, isVideo: false, completion: {})
|
||||||
|
f(.default)
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
|
if canVideoCall {
|
||||||
|
items.append(.action(ContextMenuActionItem(text: strings.ContactList_Context_VideoCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VideoCall"), color: theme.contextMenu.primaryColor)
|
||||||
|
}, action: { _, f in
|
||||||
|
context.requestCall(peerId: peerId, isVideo: true, completion: {})
|
||||||
f(.default)
|
f(.default)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
})
|
})
|
||||||
|
@ -100,8 +100,9 @@ final class PeekControllerMenuItemNode: HighlightTrackingButtonNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func buttonPressed() {
|
@objc func buttonPressed() {
|
||||||
if self.item.action(self, self.bounds) {
|
|
||||||
self.activatedAction()
|
self.activatedAction()
|
||||||
|
if self.item.action(self, self.bounds) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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: "")
|
||||||
@ -910,15 +911,20 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
if let strongSelf = self, !messages.isEmpty {
|
if let strongSelf = self, !messages.isEmpty {
|
||||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
var generalMessageContentKind: MessageContentKind?
|
var generalMessageContentKind: MessageContentKind?
|
||||||
|
var beganContentKindScanning = false
|
||||||
|
var messageContentKinds = Set<MessageContentKindKey>()
|
||||||
|
|
||||||
for message in messages {
|
for message in messages {
|
||||||
let currentKind = messageContentKind(contentSettings: strongSelf.context.currentContentSettings.with { $0 }, message: message, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: strongSelf.context.account.peerId)
|
let currentKind = messageContentKind(contentSettings: strongSelf.context.currentContentSettings.with { $0 }, message: message, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: strongSelf.context.account.peerId)
|
||||||
if generalMessageContentKind == nil || generalMessageContentKind == currentKind {
|
if beganContentKindScanning && currentKind != generalMessageContentKind {
|
||||||
generalMessageContentKind = currentKind
|
|
||||||
} else {
|
|
||||||
generalMessageContentKind = nil
|
generalMessageContentKind = nil
|
||||||
break
|
} else if !beganContentKindScanning || currentKind == generalMessageContentKind {
|
||||||
|
beganContentKindScanning = true
|
||||||
|
generalMessageContentKind = currentKind
|
||||||
}
|
}
|
||||||
|
messageContentKinds.insert(currentKind.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
var preferredAction = ShareControllerPreferredAction.default
|
var preferredAction = ShareControllerPreferredAction.default
|
||||||
if let generalMessageContentKind = generalMessageContentKind {
|
if let generalMessageContentKind = generalMessageContentKind {
|
||||||
switch generalMessageContentKind {
|
switch generalMessageContentKind {
|
||||||
@ -927,6 +933,8 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
} else if messageContentKinds.count == 2 && messageContentKinds.contains(.image) && messageContentKinds.contains(.video) {
|
||||||
|
preferredAction = .saveToCameraRoll
|
||||||
}
|
}
|
||||||
|
|
||||||
if messages.count == 1 {
|
if messages.count == 1 {
|
||||||
|
@ -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 {
|
||||||
|
@ -164,7 +164,11 @@ open class ZoomableContentGalleryItemNode: GalleryItemNode, UIScrollViewDelegate
|
|||||||
self.centerScrollViewContents(transition: transition)
|
self.centerScrollViewContents(transition: transition)
|
||||||
self.ignoreZoom = false
|
self.ignoreZoom = false
|
||||||
|
|
||||||
|
let updatedZoomScale = self.scrollNode.view.zoomScale != self.scrollNode.view.minimumZoomScale
|
||||||
self.scrollNode.view.zoomScale = self.scrollNode.view.minimumZoomScale
|
self.scrollNode.view.zoomScale = self.scrollNode.view.minimumZoomScale
|
||||||
|
if !updatedZoomScale {
|
||||||
|
self.scrollViewDidZoom(self.scrollNode.view)
|
||||||
|
}
|
||||||
self.ignoreZoomTransition = nil
|
self.ignoreZoomTransition = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -309,7 +309,7 @@ final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode {
|
|||||||
@objc func joinPressed() {
|
@objc func joinPressed() {
|
||||||
if let peer = self.peer, case .notJoined = self.joinState {
|
if let peer = self.peer, case .notJoined = self.joinState {
|
||||||
self.updateJoinState(.inProgress)
|
self.updateJoinState(.inProgress)
|
||||||
self.joinDisposable.set((joinChannel(account: self.context.account, peerId: peer.id) |> deliverOnMainQueue).start(error: { [weak self] _ in
|
self.joinDisposable.set((joinChannel(account: self.context.account, peerId: peer.id, hash: nil) |> deliverOnMainQueue).start(error: { [weak self] _ in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if case .inProgress = strongSelf.joinState {
|
if case .inProgress = strongSelf.joinState {
|
||||||
strongSelf.updateJoinState(.notJoined)
|
strongSelf.updateJoinState(.notJoined)
|
||||||
|
@ -30,11 +30,11 @@ private final class InviteLinkListControllerArguments {
|
|||||||
let mainLinkContextAction: (ExportedInvitation?, ASDisplayNode, ContextGesture?) -> Void
|
let mainLinkContextAction: (ExportedInvitation?, ASDisplayNode, ContextGesture?) -> Void
|
||||||
let createLink: () -> Void
|
let createLink: () -> Void
|
||||||
let openLink: (ExportedInvitation) -> Void
|
let openLink: (ExportedInvitation) -> Void
|
||||||
let linkContextAction: (ExportedInvitation?, ASDisplayNode, ContextGesture?) -> Void
|
let linkContextAction: (ExportedInvitation?, Bool, ASDisplayNode, ContextGesture?) -> Void
|
||||||
let openAdmin: (ExportedInvitationCreator) -> Void
|
let openAdmin: (ExportedInvitationCreator) -> Void
|
||||||
let deleteAllRevokedLinks: () -> Void
|
let deleteAllRevokedLinks: () -> Void
|
||||||
|
|
||||||
init(context: AccountContext, shareMainLink: @escaping (ExportedInvitation) -> Void, openMainLink: @escaping (ExportedInvitation) -> Void, copyLink: @escaping (ExportedInvitation) -> Void, mainLinkContextAction: @escaping (ExportedInvitation?, ASDisplayNode, ContextGesture?) -> Void, createLink: @escaping () -> Void, openLink: @escaping (ExportedInvitation?) -> Void, linkContextAction: @escaping (ExportedInvitation?, ASDisplayNode, ContextGesture?) -> Void, openAdmin: @escaping (ExportedInvitationCreator) -> Void, deleteAllRevokedLinks: @escaping () -> Void) {
|
init(context: AccountContext, shareMainLink: @escaping (ExportedInvitation) -> Void, openMainLink: @escaping (ExportedInvitation) -> Void, copyLink: @escaping (ExportedInvitation) -> Void, mainLinkContextAction: @escaping (ExportedInvitation?, ASDisplayNode, ContextGesture?) -> Void, createLink: @escaping () -> Void, openLink: @escaping (ExportedInvitation?) -> Void, linkContextAction: @escaping (ExportedInvitation?, Bool, ASDisplayNode, ContextGesture?) -> Void, openAdmin: @escaping (ExportedInvitationCreator) -> Void, deleteAllRevokedLinks: @escaping () -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.shareMainLink = shareMainLink
|
self.shareMainLink = shareMainLink
|
||||||
self.openMainLink = openMainLink
|
self.openMainLink = openMainLink
|
||||||
@ -65,7 +65,7 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||||||
|
|
||||||
case linksHeader(PresentationTheme, String)
|
case linksHeader(PresentationTheme, String)
|
||||||
case linksCreate(PresentationTheme, String)
|
case linksCreate(PresentationTheme, String)
|
||||||
case link(Int32, PresentationTheme, ExportedInvitation?, Int32?)
|
case link(Int32, PresentationTheme, ExportedInvitation?, Bool, Int32?)
|
||||||
case linksInfo(PresentationTheme, String)
|
case linksInfo(PresentationTheme, String)
|
||||||
|
|
||||||
case revokedLinksHeader(PresentationTheme, String)
|
case revokedLinksHeader(PresentationTheme, String)
|
||||||
@ -104,7 +104,7 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||||||
return 4
|
return 4
|
||||||
case .linksCreate:
|
case .linksCreate:
|
||||||
return 5
|
return 5
|
||||||
case let .link(index, _, _, _):
|
case let .link(index, _, _, _, _):
|
||||||
return 6 + index
|
return 6 + index
|
||||||
case .linksInfo:
|
case .linksInfo:
|
||||||
return 10000
|
return 10000
|
||||||
@ -159,8 +159,8 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .link(lhsIndex, lhsTheme, lhsLink, lhsTick):
|
case let .link(lhsIndex, lhsTheme, lhsLink, lhsCanEdit, lhsTick):
|
||||||
if case let .link(rhsIndex, rhsTheme, rhsLink, rhsTick) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsLink == rhsLink, lhsTick == rhsTick {
|
if case let .link(rhsIndex, rhsTheme, rhsLink, rhsCanEdit, rhsTick) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsLink == rhsLink, lhsCanEdit == rhsCanEdit, lhsTick == rhsTick {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -239,11 +239,11 @@ private enum InviteLinksListEntry: 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.createLink()
|
arguments.createLink()
|
||||||
})
|
})
|
||||||
case let .link(_, _, invite, _):
|
case let .link(_, _, invite, canEdit, _):
|
||||||
return ItemListInviteLinkItem(presentationData: presentationData, invite: invite, share: false, sectionId: self.section, style: .blocks) { invite in
|
return ItemListInviteLinkItem(presentationData: presentationData, invite: invite, share: false, sectionId: self.section, style: .blocks) { invite in
|
||||||
arguments.openLink(invite)
|
arguments.openLink(invite)
|
||||||
} contextAction: { invite, node, gesture in
|
} contextAction: { invite, node, gesture in
|
||||||
arguments.linkContextAction(invite, node, gesture)
|
arguments.linkContextAction(invite, canEdit, node, gesture)
|
||||||
}
|
}
|
||||||
case let .linksInfo(_, text):
|
case let .linksInfo(_, text):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
@ -257,12 +257,12 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||||||
return ItemListInviteLinkItem(presentationData: presentationData, invite: invite, share: false, sectionId: self.section, style: .blocks) { invite in
|
return ItemListInviteLinkItem(presentationData: presentationData, invite: invite, share: false, sectionId: self.section, style: .blocks) { invite in
|
||||||
arguments.openLink(invite)
|
arguments.openLink(invite)
|
||||||
} contextAction: { invite, node, gesture in
|
} contextAction: { invite, node, gesture in
|
||||||
arguments.linkContextAction(invite, node, gesture)
|
arguments.linkContextAction(invite, false, node, gesture)
|
||||||
}
|
}
|
||||||
case let .adminsHeader(_, text):
|
case let .adminsHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .admin(_, _, creator):
|
case let .admin(_, _, creator):
|
||||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .regular, dateFormat: .monthFirst, dateSeparator: ".", decimalSeparator: ".", groupingSeparator: "."), nameDisplayOrder: .firstLast, context: arguments.context, peer: creator.peer.peer!, height: .peerList, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .none, label: .disclosure("\(creator.count)"), editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
|
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .regular, dateFormat: .monthFirst, dateSeparator: ".", decimalSeparator: ".", groupingSeparator: "."), nameDisplayOrder: .firstLast, context: arguments.context, peer: creator.peer.peer!, height: .peerList, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .none, label: creator.count > 1 ? .disclosure("\(creator.count)") : .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
|
||||||
arguments.openAdmin(creator)
|
arguments.openAdmin(creator)
|
||||||
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, contextAction: nil)
|
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, contextAction: nil)
|
||||||
}
|
}
|
||||||
@ -333,16 +333,21 @@ private func inviteLinkListControllerEntries(presentationData: PresentationData,
|
|||||||
entries.append(.linksCreate(presentationData.theme, presentationData.strings.InviteLink_Create))
|
entries.append(.linksCreate(presentationData.theme, presentationData.strings.InviteLink_Create))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var canEditLinks = true
|
||||||
|
if let peer = admin?.peer.peer as? TelegramUser, peer.botInfo != nil {
|
||||||
|
canEditLinks = false
|
||||||
|
}
|
||||||
|
|
||||||
if let additionalInvites = additionalInvites {
|
if let additionalInvites = additionalInvites {
|
||||||
var index: Int32 = 0
|
var index: Int32 = 0
|
||||||
for invite in additionalInvites {
|
for invite in additionalInvites {
|
||||||
entries.append(.link(index, presentationData.theme, invite, invite.expireDate != nil ? tick : nil))
|
entries.append(.link(index, presentationData.theme, invite, canEditLinks, invite.expireDate != nil ? tick : nil))
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
} else if let admin = admin, admin.count > 1 {
|
} else if let admin = admin, admin.count > 1 {
|
||||||
var index: Int32 = 0
|
var index: Int32 = 0
|
||||||
for _ in 0 ..< admin.count - 1 {
|
for _ in 0 ..< admin.count - 1 {
|
||||||
entries.append(.link(index, presentationData.theme, nil, nil))
|
entries.append(.link(index, presentationData.theme, nil, false, nil))
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -558,7 +563,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
|||||||
let controller = InviteLinkViewController(context: context, peerId: peerId, invite: invite, invitationsContext: invitesContext, revokedInvitationsContext: revokedInvitesContext, importersContext: nil)
|
let controller = InviteLinkViewController(context: context, peerId: peerId, invite: invite, invitationsContext: invitesContext, revokedInvitationsContext: revokedInvitesContext, importersContext: nil)
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
}
|
}
|
||||||
}, linkContextAction: { invite, node, gesture in
|
}, linkContextAction: { invite, canEdit, node, gesture in
|
||||||
guard let node = node as? ContextExtractedContentContainingNode, let controller = getControllerImpl?(), let invite = invite else {
|
guard let node = node as? ContextExtractedContentContainingNode, let controller = getControllerImpl?(), let invite = invite else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -615,6 +620,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !invite.isPermanent && canEdit {
|
||||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextEdit, icon: { theme in
|
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)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
@ -634,6 +640,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
|||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if invite.isRevoked {
|
if invite.isRevoked {
|
||||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextDelete, textColor: .destructive, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextDelete, textColor: .destructive, icon: { theme in
|
||||||
@ -685,8 +692,10 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
|
|||||||
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
|
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
|
||||||
dismissAction()
|
dismissAction()
|
||||||
|
|
||||||
revokeLinkDisposable.set((revokePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(completed: {
|
revokeLinkDisposable.set((revokePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(next: { result in
|
||||||
|
if case let .replace(_, newInvite) = result {
|
||||||
|
invitesContext.add(newInvite)
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
invitesContext.remove(invite)
|
invitesContext.remove(invite)
|
||||||
|
@ -537,8 +537,10 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
dismissAction()
|
dismissAction()
|
||||||
self?.controller?.dismiss()
|
self?.controller?.dismiss()
|
||||||
|
|
||||||
let _ = (revokePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(completed: {
|
let _ = (revokePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(next: { result in
|
||||||
|
if case let .replace(_, newInvite) = result {
|
||||||
|
self?.controller?.invitationsContext?.add(newInvite)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
self?.controller?.invitationsContext?.remove(invite)
|
self?.controller?.invitationsContext?.remove(invite)
|
||||||
|
@ -374,10 +374,10 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
|
|||||||
var credibilityIconOffset: CGFloat = 4.0
|
var credibilityIconOffset: CGFloat = 4.0
|
||||||
if let peer = item.peer {
|
if let peer = item.peer {
|
||||||
if peer.isScam {
|
if peer.isScam {
|
||||||
credibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, type: .regular)
|
credibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
|
||||||
credibilityIconOffset = 6.0
|
credibilityIconOffset = 6.0
|
||||||
} else if peer.isFake {
|
} else if peer.isFake {
|
||||||
credibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, type: .regular)
|
credibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
|
||||||
credibilityIconOffset = 2.0
|
credibilityIconOffset = 2.0
|
||||||
} else if peer.isVerified {
|
} else if peer.isVerified {
|
||||||
credibilityIconImage = PresentationResourcesItemList.verifiedPeerIcon(item.presentationData.theme)
|
credibilityIconImage = PresentationResourcesItemList.verifiedPeerIcon(item.presentationData.theme)
|
||||||
|
@ -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 {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#define GPUImageRotationSwapsWidthAndHeight(rotation) ((rotation) == kGPUImageRotateLeft || (rotation) == kGPUImageRotateRight || (rotation) == kGPUImageRotateRightFlipVertical || (rotation) == kGPUImageRotateRightFlipHorizontal)
|
#define GPUImageRotationSwapsWidthAndHeight(rotation) ((rotation) == kGPUImageRotateLeft || (rotation) == kGPUImageRotateRight || (rotation) == kGPUImageRotateRightFlipVertical || (rotation) == kGPUImageRotateRightFlipHorizontal)
|
||||||
|
|
||||||
typedef enum { kGPUImageNoRotation, kGPUImageRotateLeft, kGPUImageRotateRight, kGPUImageFlipVertical, kGPUImageFlipHorizonal, kGPUImageRotateRightFlipVertical, kGPUImageRotateRightFlipHorizontal, kGPUImageRotate180 } GPUImageRotationMode;
|
typedef enum { kGPUImageNoRotation, kGPUImageRotateLeft, kGPUImageRotateRight, kGPUImageFlipVertical, kGPUImageFlipHorizonal, kGPUImageRotateRightFlipVertical, kGPUImageRotateRightFlipHorizontal, kGPUImageRotate180, kGPUImageRotate180FlipHorizontal } GPUImageRotationMode;
|
||||||
|
|
||||||
@interface GPUImageContext : NSObject
|
@interface GPUImageContext : NSObject
|
||||||
|
|
||||||
|
@ -284,6 +284,13 @@ NSString *const kGPUImagePassthroughFragmentShaderString = SHADER_STRING
|
|||||||
0.0f, 0.0f,
|
0.0f, 0.0f,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const GLfloat rotate180HorizontalFlipTextureCoordinates[] = {
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1.0f, 1.0f,
|
||||||
|
0.0f, 0.0f,
|
||||||
|
1.0f, 0.0f,
|
||||||
|
};
|
||||||
|
|
||||||
switch(rotationMode)
|
switch(rotationMode)
|
||||||
{
|
{
|
||||||
case kGPUImageNoRotation: return noRotationTextureCoordinates;
|
case kGPUImageNoRotation: return noRotationTextureCoordinates;
|
||||||
@ -294,6 +301,7 @@ NSString *const kGPUImagePassthroughFragmentShaderString = SHADER_STRING
|
|||||||
case kGPUImageRotateRightFlipVertical: return rotateRightVerticalFlipTextureCoordinates;
|
case kGPUImageRotateRightFlipVertical: return rotateRightVerticalFlipTextureCoordinates;
|
||||||
case kGPUImageRotateRightFlipHorizontal: return rotateRightHorizontalFlipTextureCoordinates;
|
case kGPUImageRotateRightFlipHorizontal: return rotateRightHorizontalFlipTextureCoordinates;
|
||||||
case kGPUImageRotate180: return rotate180TextureCoordinates;
|
case kGPUImageRotate180: return rotate180TextureCoordinates;
|
||||||
|
case kGPUImageRotate180FlipHorizontal: return rotate180HorizontalFlipTextureCoordinates;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,6 +650,11 @@ NSString *const kGPUImagePassthroughFragmentShaderString = SHADER_STRING
|
|||||||
rotatedPoint.x = 1.0f - pointToRotate.x;
|
rotatedPoint.x = 1.0f - pointToRotate.x;
|
||||||
rotatedPoint.y = 1.0f - pointToRotate.y;
|
rotatedPoint.y = 1.0f - pointToRotate.y;
|
||||||
}; break;
|
}; break;
|
||||||
|
case kGPUImageRotate180FlipHorizontal:
|
||||||
|
{
|
||||||
|
rotatedPoint.x = pointToRotate.x;
|
||||||
|
rotatedPoint.y = 1.0f - pointToRotate.y;
|
||||||
|
}; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rotatedPoint;
|
return rotatedPoint;
|
||||||
|
@ -214,7 +214,7 @@
|
|||||||
_rotationMode = cropMirrored ? kGPUImageRotateRightFlipHorizontal : kGPUImageRotateRight;
|
_rotationMode = cropMirrored ? kGPUImageRotateRightFlipHorizontal : kGPUImageRotateRight;
|
||||||
break;
|
break;
|
||||||
case UIImageOrientationDown:
|
case UIImageOrientationDown:
|
||||||
_rotationMode = kGPUImageRotate180;
|
_rotationMode = cropMirrored ? kGPUImageRotate180FlipHorizontal : kGPUImageRotate180;
|
||||||
break;
|
break;
|
||||||
case UIImageOrientationUp:
|
case UIImageOrientationUp:
|
||||||
if (cropMirrored)
|
if (cropMirrored)
|
||||||
|
@ -285,6 +285,13 @@
|
|||||||
0.0f, 1.0f,
|
0.0f, 1.0f,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const GLfloat rotate180HorizontalFlipTextureCoordinates[] = {
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1.0f, 1.0f,
|
||||||
|
0.0f, 0.0f,
|
||||||
|
1.0f, 0.0f,
|
||||||
|
};
|
||||||
|
|
||||||
switch(rotationMode)
|
switch(rotationMode)
|
||||||
{
|
{
|
||||||
case kGPUImageNoRotation: return noRotationTextureCoordinates;
|
case kGPUImageNoRotation: return noRotationTextureCoordinates;
|
||||||
@ -295,6 +302,7 @@
|
|||||||
case kGPUImageRotateRightFlipVertical: return rotateRightVerticalFlipTextureCoordinates;
|
case kGPUImageRotateRightFlipVertical: return rotateRightVerticalFlipTextureCoordinates;
|
||||||
case kGPUImageRotateRightFlipHorizontal: return rotateRightHorizontalFlipTextureCoordinates;
|
case kGPUImageRotateRightFlipHorizontal: return rotateRightHorizontalFlipTextureCoordinates;
|
||||||
case kGPUImageRotate180: return rotate180TextureCoordinates;
|
case kGPUImageRotate180: return rotate180TextureCoordinates;
|
||||||
|
case kGPUImageRotate180FlipHorizontal: return rotate180HorizontalFlipTextureCoordinates;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,16 +118,20 @@ NSString *const kYUVVideoRangeConversionForLAFragmentShaderString = SHADER_STRIN
|
|||||||
|
|
||||||
- (GPUImageRotationMode)rotationForTrack:(AVAsset *)asset {
|
- (GPUImageRotationMode)rotationForTrack:(AVAsset *)asset {
|
||||||
AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
|
AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
|
||||||
CGAffineTransform trackTransform = [videoTrack preferredTransform];
|
CGAffineTransform t = [videoTrack preferredTransform];
|
||||||
|
|
||||||
if (trackTransform.a == -1 && trackTransform.d == -1) {
|
if (t.a == -1 && t.d == -1) {
|
||||||
return kGPUImageRotate180;
|
return kGPUImageRotate180;
|
||||||
} else if (trackTransform.a == 1 && trackTransform.d == 1) {
|
} else if (t.a == 1 && t.d == 1) {
|
||||||
return kGPUImageNoRotation;
|
return kGPUImageNoRotation;
|
||||||
} else if (trackTransform.b == -1 && trackTransform.c == 1) {
|
} else if (t.b == -1 && t.c == 1) {
|
||||||
return kGPUImageRotateLeft;
|
return kGPUImageRotateLeft;
|
||||||
|
} else if (t.a == -1 && t.d == 1) {
|
||||||
|
return kGPUImageFlipHorizonal;
|
||||||
|
} else if (t.a == 1 && t.d == -1) {
|
||||||
|
return kGPUImageRotate180FlipHorizontal;
|
||||||
} else {
|
} else {
|
||||||
if (trackTransform.c == 1) {
|
if (t.c == 1) {
|
||||||
return kGPUImageRotateRightFlipVertical;
|
return kGPUImageRotateRightFlipVertical;
|
||||||
} else {
|
} else {
|
||||||
return kGPUImageRotateRight;
|
return kGPUImageRotateRight;
|
||||||
|
@ -593,28 +593,30 @@ UIImageOrientation TGVideoOrientationForAsset(AVAsset *asset, bool *mirrored)
|
|||||||
{
|
{
|
||||||
AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
|
AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
|
||||||
CGAffineTransform t = videoTrack.preferredTransform;
|
CGAffineTransform t = videoTrack.preferredTransform;
|
||||||
double videoRotation = atan2((float)t.b, (float)t.a);
|
|
||||||
|
|
||||||
if (mirrored != NULL)
|
if (t.a == -1 && t.d == -1) {
|
||||||
{
|
|
||||||
CGFloat scaleX = sqrt(t.a * t.a + t.c * t.c);
|
|
||||||
CGFloat scaleY = sqrt(t.b * t.b + t.d * t.d);
|
|
||||||
CGSize scale = CGSizeMake(scaleX, scaleY);
|
|
||||||
|
|
||||||
*mirrored = (scale.width < 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fabs(videoRotation - M_PI) < FLT_EPSILON) {
|
|
||||||
return UIImageOrientationLeft;
|
return UIImageOrientationLeft;
|
||||||
} else if (fabs(videoRotation - M_PI_2) < FLT_EPSILON) {
|
} else if (t.a == 1 && t.d == 1) {
|
||||||
if (t.c == 1 && mirrored != NULL) {
|
return UIImageOrientationRight;
|
||||||
|
} else if (t.b == -1 && t.c == 1) {
|
||||||
|
return UIImageOrientationDown;
|
||||||
|
} else if (t.a == -1 && t.d == 1) {
|
||||||
|
if (mirrored != NULL) {
|
||||||
|
*mirrored = true;
|
||||||
|
}
|
||||||
|
return UIImageOrientationLeft;
|
||||||
|
} else if (t.a == 1 && t.d == -1) {
|
||||||
|
if (mirrored != NULL) {
|
||||||
*mirrored = true;
|
*mirrored = true;
|
||||||
}
|
}
|
||||||
return UIImageOrientationUp;
|
|
||||||
} else if (fabs(videoRotation + M_PI_2) < FLT_EPSILON) {
|
|
||||||
return UIImageOrientationDown;
|
|
||||||
} else {
|
|
||||||
return UIImageOrientationRight;
|
return UIImageOrientationRight;
|
||||||
|
} else {
|
||||||
|
if (t.c == 1) {
|
||||||
|
if (mirrored != NULL) {
|
||||||
|
*mirrored = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UIImageOrientationUp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,8 +696,8 @@ static void addRoundedRectToPath(CGContextRef context, CGRect rect, CGFloat oval
|
|||||||
(id)UIColorRGB(0x80c864).CGColor, //green
|
(id)UIColorRGB(0x80c864).CGColor, //green
|
||||||
(id)UIColorRGB(0xfcde65).CGColor, //yellow
|
(id)UIColorRGB(0xfcde65).CGColor, //yellow
|
||||||
(id)UIColorRGB(0xfc964d).CGColor, //orange
|
(id)UIColorRGB(0xfc964d).CGColor, //orange
|
||||||
(id)[UIColor blackColor].CGColor, //black
|
(id)UIColorRGB(0x000000).CGColor, //black
|
||||||
(id)[UIColor whiteColor].CGColor //white
|
(id)UIColorRGB(0xffffff).CGColor //white
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
return colors;
|
return colors;
|
||||||
|
@ -187,7 +187,7 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
|||||||
_style = style;
|
_style = style;
|
||||||
switch (_style) {
|
switch (_style) {
|
||||||
case TGPhotoPaintTextEntityStyleRegular:
|
case TGPhotoPaintTextEntityStyleRegular:
|
||||||
_textView.layer.shadowColor = [[UIColor blackColor] CGColor];
|
_textView.layer.shadowColor = [UIColorRGB(0x000000) CGColor];
|
||||||
_textView.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
|
_textView.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
|
||||||
_textView.layer.shadowOpacity = 0.4f;
|
_textView.layer.shadowOpacity = 0.4f;
|
||||||
_textView.layer.shadowRadius = 4.0f;
|
_textView.layer.shadowRadius = 4.0f;
|
||||||
@ -218,7 +218,7 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
|||||||
|
|
||||||
case TGPhotoPaintTextEntityStyleOutlined:
|
case TGPhotoPaintTextEntityStyleOutlined:
|
||||||
{
|
{
|
||||||
_textView.textColor = [UIColor whiteColor];
|
_textView.textColor = UIColorRGB(0xffffff);
|
||||||
_textView.strokeColor = _swatch.color;
|
_textView.strokeColor = _swatch.color;
|
||||||
_textView.frameColor = nil;
|
_textView.frameColor = nil;
|
||||||
}
|
}
|
||||||
@ -238,9 +238,9 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lightness > 0.87) {
|
if (lightness > 0.87) {
|
||||||
_textView.textColor = [UIColor blackColor];
|
_textView.textColor = UIColorRGB(0x000000);
|
||||||
} else {
|
} else {
|
||||||
_textView.textColor = [UIColor whiteColor];
|
_textView.textColor = UIColorRGB(0xffffff);
|
||||||
}
|
}
|
||||||
_textView.strokeColor = nil;
|
_textView.strokeColor = nil;
|
||||||
_textView.frameColor = _swatch.color;
|
_textView.frameColor = _swatch.color;
|
||||||
@ -484,6 +484,7 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
|||||||
@implementation TGPhotoTextView
|
@implementation TGPhotoTextView
|
||||||
{
|
{
|
||||||
UIFont *_font;
|
UIFont *_font;
|
||||||
|
UIColor *_forcedTextColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithFrame:(CGRect)frame
|
- (instancetype)initWithFrame:(CGRect)frame
|
||||||
@ -564,6 +565,11 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
|||||||
self.layoutManager.textContainers.firstObject.lineFragmentPadding = floor(font.pointSize * 0.3);
|
self.layoutManager.textContainers.firstObject.lineFragmentPadding = floor(font.pointSize * 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setTextColor:(UIColor *)textColor {
|
||||||
|
_forcedTextColor = textColor;
|
||||||
|
[super setTextColor:textColor];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)insertText:(NSString *)text {
|
- (void)insertText:(NSString *)text {
|
||||||
[self fixTypingAttributes];
|
[self fixTypingAttributes];
|
||||||
[super insertText:text];
|
[super insertText:text];
|
||||||
@ -577,9 +583,14 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)fixTypingAttributes {
|
- (void)fixTypingAttributes {
|
||||||
|
NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
|
||||||
if (_font != nil) {
|
if (_font != nil) {
|
||||||
self.typingAttributes = @{NSFontAttributeName: _font};
|
attributes[NSFontAttributeName] = _font;
|
||||||
}
|
}
|
||||||
|
if (_forcedTextColor != nil) {
|
||||||
|
attributes[NSForegroundColorAttributeName] = _forcedTextColor;
|
||||||
|
}
|
||||||
|
self.typingAttributes = attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -530,7 +530,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if estimatedSize > 5 * 1024 * 1024 {
|
if estimatedSize > 10 * 1024 * 1024 {
|
||||||
fileAttributes.append(.hintFileIsLarge)
|
fileAttributes.append(.hintFileIsLarge)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1049,11 +1049,20 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
|||||||
case .tooMuchJoined:
|
case .tooMuchJoined:
|
||||||
text = presentationData.strings.Group_ErrorSupergroupConversionNotPossible
|
text = presentationData.strings.Group_ErrorSupergroupConversionNotPossible
|
||||||
case .restricted:
|
case .restricted:
|
||||||
if let peer = adminView.peers[adminView.peerId] {
|
if let admin = adminView.peers[adminView.peerId] {
|
||||||
text = presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(peer.compactDisplayTitle, peer.compactDisplayTitle).0
|
switch channel.info {
|
||||||
|
case .broadcast:
|
||||||
|
text = presentationData.strings.Privacy_GroupsAndChannels_InviteToChannelError(admin.compactDisplayTitle, admin.compactDisplayTitle).0
|
||||||
|
case .group:
|
||||||
|
text = presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(admin.compactDisplayTitle, admin.compactDisplayTitle).0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case .notMutualContact:
|
case .notMutualContact:
|
||||||
|
if case .broadcast = channel.info {
|
||||||
|
text = presentationData.strings.Channel_AddUserLeftError
|
||||||
|
} else {
|
||||||
text = presentationData.strings.GroupInfo_AddUserLeftError
|
text = presentationData.strings.GroupInfo_AddUserLeftError
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1119,17 +1128,28 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
|||||||
return current.withUpdatedUpdating(true)
|
return current.withUpdatedUpdating(true)
|
||||||
}
|
}
|
||||||
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(account: context.account, peerId: peerId, memberId: adminId, adminRights: TelegramChatAdminRights(rights: updateFlags), rank: updateRank) |> deliverOnMainQueue).start(error: { error in
|
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(account: context.account, peerId: peerId, memberId: adminId, adminRights: TelegramChatAdminRights(rights: updateFlags), rank: updateRank) |> deliverOnMainQueue).start(error: { error in
|
||||||
if case let .addMemberError(error) = error, let admin = adminView.peers[adminView.peerId] {
|
if case let .addMemberError(addMemberError) = error, let admin = adminView.peers[adminView.peerId] {
|
||||||
if case .restricted = error {
|
var text = presentationData.strings.Login_UnknownError
|
||||||
var text = presentationData.strings.Privacy_GroupsAndChannels_InviteToChannelError(admin.compactDisplayTitle, admin.compactDisplayTitle).0
|
switch addMemberError {
|
||||||
if case .group = channel.info {
|
case .tooMuchJoined:
|
||||||
|
text = presentationData.strings.Group_ErrorSupergroupConversionNotPossible
|
||||||
|
case .restricted:
|
||||||
|
switch channel.info {
|
||||||
|
case .broadcast:
|
||||||
|
text = presentationData.strings.Privacy_GroupsAndChannels_InviteToChannelError(admin.compactDisplayTitle, admin.compactDisplayTitle).0
|
||||||
|
case .group:
|
||||||
text = presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(admin.compactDisplayTitle, admin.compactDisplayTitle).0
|
text = presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(admin.compactDisplayTitle, admin.compactDisplayTitle).0
|
||||||
}
|
}
|
||||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
case .notMutualContact:
|
||||||
} else if case .tooMuchJoined = error {
|
if case .broadcast = channel.info {
|
||||||
let text = presentationData.strings.Invite_ChannelsTooMuch
|
text = presentationData.strings.Channel_AddUserLeftError
|
||||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
} else {
|
||||||
|
text = presentationData.strings.GroupInfo_AddUserLeftError
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||||
} else if case .adminsTooMuch = error {
|
} else if case .adminsTooMuch = error {
|
||||||
let text: String
|
let text: String
|
||||||
if case .broadcast = channel.info {
|
if case .broadcast = channel.info {
|
||||||
@ -1146,7 +1166,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let group = channelView.peers[channelView.peerId] as? TelegramGroup {
|
} else if let _ = channelView.peers[channelView.peerId] as? TelegramGroup {
|
||||||
var updateFlags: TelegramChatAdminRightsFlags?
|
var updateFlags: TelegramChatAdminRightsFlags?
|
||||||
var updateRank: String?
|
var updateRank: String?
|
||||||
updateState { current in
|
updateState { current in
|
||||||
@ -1163,12 +1183,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
|
|||||||
}
|
}
|
||||||
|
|
||||||
let maskRightsFlags: TelegramChatAdminRightsFlags = .groupSpecific
|
let maskRightsFlags: TelegramChatAdminRightsFlags = .groupSpecific
|
||||||
let defaultFlags: TelegramChatAdminRightsFlags
|
let defaultFlags = maskRightsFlags.subtracting([.canBeAnonymous, .canAddAdmins])
|
||||||
if case .creator = group.role {
|
|
||||||
defaultFlags = maskRightsFlags.subtracting(.canBeAnonymous)
|
|
||||||
} else {
|
|
||||||
defaultFlags = maskRightsFlags.subtracting(.canAddAdmins).subtracting(.canBeAnonymous)
|
|
||||||
}
|
|
||||||
|
|
||||||
if updateFlags == nil {
|
if updateFlags == nil {
|
||||||
updateFlags = defaultFlags
|
updateFlags = defaultFlags
|
||||||
|
@ -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()
|
||||||
})
|
})
|
||||||
|
@ -370,6 +370,10 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
|
|||||||
}
|
}
|
||||||
}).start(error: { [weak contactsController] error in
|
}).start(error: { [weak contactsController] error in
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let _ = (context.account.postbox.transaction { transaction in
|
||||||
|
return transaction.getPeer(peerId)
|
||||||
|
}
|
||||||
|
|> deliverOnMainQueue).start(next: { peer in
|
||||||
let text: String
|
let text: String
|
||||||
switch error {
|
switch error {
|
||||||
case .limitExceeded:
|
case .limitExceeded:
|
||||||
@ -381,16 +385,15 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
|
|||||||
case .restricted:
|
case .restricted:
|
||||||
text = presentationData.strings.Channel_ErrorAddBlocked
|
text = presentationData.strings.Channel_ErrorAddBlocked
|
||||||
case .notMutualContact:
|
case .notMutualContact:
|
||||||
|
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||||
|
text = presentationData.strings.Channel_AddUserLeftError
|
||||||
|
} else {
|
||||||
text = presentationData.strings.GroupInfo_AddUserLeftError
|
text = presentationData.strings.GroupInfo_AddUserLeftError
|
||||||
case let .bot(memberId):
|
|
||||||
let _ = (context.account.postbox.transaction { transaction in
|
|
||||||
return transaction.getPeer(peerId)
|
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { peer in
|
case let .bot(memberId):
|
||||||
guard let peer = peer as? TelegramChannel else {
|
guard let peer = peer as? TelegramChannel else {
|
||||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||||
contactsController?.dismiss()
|
contactsController?.dismiss()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,7 +410,6 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
contactsController?.dismiss()
|
contactsController?.dismiss()
|
||||||
})
|
|
||||||
return
|
return
|
||||||
case .botDoesntSupportGroups:
|
case .botDoesntSupportGroups:
|
||||||
text = presentationData.strings.Channel_BotDoesntSupportGroups
|
text = presentationData.strings.Channel_BotDoesntSupportGroups
|
||||||
@ -416,6 +418,7 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
|
|||||||
}
|
}
|
||||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||||
contactsController?.dismiss()
|
contactsController?.dismiss()
|
||||||
|
})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
presentControllerImpl?(contactsController, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
presentControllerImpl?(contactsController, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
|
@ -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 {
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let action: (String) -> Void = { message in
|
||||||
switch subject {
|
switch subject {
|
||||||
case let .peer(peerId):
|
case let .peer(peerId):
|
||||||
let _ = (reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: "")
|
let _ = (reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: "")
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
|
displaySuccess()
|
||||||
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)
|
completion(reportReason, true)
|
||||||
})
|
})
|
||||||
case let .messages(messageIds):
|
case let .messages(messageIds):
|
||||||
let _ = (reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: "")
|
let _ = (reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: "")
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
|
displaySuccess()
|
||||||
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 .profilePhoto(peerId, photoId):
|
||||||
|
let _ = (reportPeerPhoto(account: context.account, peerId: peerId, reason: reportReason, message: "")
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
displaySuccess()
|
||||||
completion(reportReason, true)
|
completion(reportReason, true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
parent?.push(peerReportController(context: context, subject: subject, completion: completion))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
@ -379,6 +422,11 @@ private func peerReportController(context: AccountContext, subject: PeerReportSu
|
|||||||
|> 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()
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1420,10 +1420,17 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
|
|||||||
if let navigationController = (controller?.navigationController as? NavigationController) {
|
if let navigationController = (controller?.navigationController as? NavigationController) {
|
||||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
|
||||||
}
|
}
|
||||||
}, error: { [weak controller] _ in
|
}, error: { [weak controller] error in
|
||||||
if let controller = controller {
|
if let controller = controller {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
let text: String
|
||||||
|
switch error {
|
||||||
|
case .limitExceeded:
|
||||||
|
text = presentationData.strings.TwoStepAuth_FloodError
|
||||||
|
default:
|
||||||
|
text = presentationData.strings.Login_UnknownError
|
||||||
|
}
|
||||||
|
controller.present(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
})]), in: .window(.root))
|
})]), in: .window(.root))
|
||||||
@ -1438,6 +1445,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)
|
||||||
})
|
})
|
||||||
|
@ -469,7 +469,7 @@ public final class ShareController: ViewController {
|
|||||||
}
|
}
|
||||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
}, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare, immediatePeerId: self.immediatePeerId, shares: self.shares, fromForeignApp: self.fromForeignApp, forcedTheme: self.forcedTheme)
|
}, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare, immediatePeerId: self.immediatePeerId, shares: self.shares, fromForeignApp: self.fromForeignApp, forcedTheme: self.forcedTheme)
|
||||||
self.controllerNode.completed = completed
|
self.controllerNode.completed = self.completed
|
||||||
self.controllerNode.dismiss = { [weak self] shared in
|
self.controllerNode.dismiss = { [weak self] shared in
|
||||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||||
self?.dismissed?(shared)
|
self?.dismissed?(shared)
|
||||||
@ -721,13 +721,18 @@ public final class ShareController: ViewController {
|
|||||||
})
|
})
|
||||||
activities = [shareToInstagram]
|
activities = [shareToInstagram]
|
||||||
}
|
}
|
||||||
let activityController = UIActivityViewController(activityItems: activityItems, applicationActivities: activities)
|
|
||||||
|
|
||||||
if let window = strongSelf.view.window, let rootViewController = window.rootViewController {
|
let _ = (strongSelf.didAppearPromise.get()
|
||||||
|
|> filter { $0 }
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||||
|
let activityController = UIActivityViewController(activityItems: activityItems, applicationActivities: activities)
|
||||||
|
if let strongSelf = self, let window = strongSelf.view.window, let rootViewController = window.rootViewController {
|
||||||
activityController.popoverPresentationController?.sourceView = 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))
|
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))
|
||||||
rootViewController.present(activityController, animated: true, completion: nil)
|
rootViewController.present(activityController, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return .done
|
return .done
|
||||||
}
|
}
|
||||||
@ -785,14 +790,18 @@ public final class ShareController: ViewController {
|
|||||||
super.loadView()
|
super.loadView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let didAppearPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
override public func viewDidAppear(_ animated: Bool) {
|
override public func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
if !self.animatedIn {
|
if !self.animatedIn {
|
||||||
self.animatedIn = true
|
self.animatedIn = true
|
||||||
|
self.didAppearPromise.set(true)
|
||||||
|
if !self.immediateExternalShare {
|
||||||
self.controllerNode.animateIn()
|
self.controllerNode.animateIn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||||
self.controllerNode.view.endEditing(true)
|
self.controllerNode.view.endEditing(true)
|
||||||
@ -1080,3 +1089,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)
|
||||||
|
}
|
||||||
|
@ -21,10 +21,6 @@ enum ShareExternalState {
|
|||||||
case done
|
case done
|
||||||
}
|
}
|
||||||
|
|
||||||
func openExternalShare(state: () -> Signal<ShareExternalState, NoError>) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
||||||
private let sharedContext: SharedAccountContext
|
private let sharedContext: SharedAccountContext
|
||||||
private var context: AccountContext?
|
private var context: AccountContext?
|
||||||
@ -176,6 +172,8 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.isHidden = true
|
||||||
|
|
||||||
self.controllerInteraction = ShareControllerInteraction(togglePeer: { [weak self] peer, search in
|
self.controllerInteraction = ShareControllerInteraction(togglePeer: { [weak self] peer, search in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
var added = false
|
var added = false
|
||||||
@ -621,6 +619,11 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
func animateIn() {
|
func animateIn() {
|
||||||
|
if let completion = self.outCompletion {
|
||||||
|
self.outCompletion = nil
|
||||||
|
completion()
|
||||||
|
return
|
||||||
|
}
|
||||||
if self.contentNode != nil {
|
if self.contentNode != nil {
|
||||||
self.isHidden = false
|
self.isHidden = false
|
||||||
|
|
||||||
@ -635,6 +638,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var outCompletion: (() -> Void)?
|
||||||
func animateOut(shared: Bool, completion: @escaping () -> Void) {
|
func animateOut(shared: Bool, completion: @escaping () -> Void) {
|
||||||
if self.contentNode != nil {
|
if self.contentNode != nil {
|
||||||
var dimCompleted = false
|
var dimCompleted = false
|
||||||
@ -664,9 +668,15 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
internalCompletion()
|
internalCompletion()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
self.outCompletion = completion
|
||||||
|
Queue.mainQueue().after(0.2) {
|
||||||
|
if let completion = self.outCompletion {
|
||||||
|
self.outCompletion = nil
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updatePeers(context: AccountContext, switchableAccounts: [AccountWithInfo], peers: [(RenderedPeer, PeerPresence?)], accountPeer: Peer, defaultAction: ShareControllerAction?) {
|
func updatePeers(context: AccountContext, switchableAccounts: [AccountWithInfo], peers: [(RenderedPeer, PeerPresence?)], accountPeer: Peer, defaultAction: ShareControllerAction?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
@ -128,7 +128,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
|||||||
let estimatedSize = TGMediaVideoConverter.estimatedSize(for: preset, duration: finalDuration, hasAudio: true)
|
let estimatedSize = TGMediaVideoConverter.estimatedSize(for: preset, duration: finalDuration, hasAudio: true)
|
||||||
|
|
||||||
let resource = LocalFileVideoMediaResource(randomId: arc4random64(), path: asset.url.path, adjustments: resourceAdjustments)
|
let resource = LocalFileVideoMediaResource(randomId: arc4random64(), path: asset.url.path, adjustments: resourceAdjustments)
|
||||||
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: Int(finalDuration), size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags)], hintFileIsLarge: estimatedSize > 5 * 1024 * 1024)
|
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: Int(finalDuration), size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags)], hintFileIsLarge: estimatedSize > 10 * 1024 * 1024)
|
||||||
|> mapError { _ -> Void in
|
|> mapError { _ -> Void in
|
||||||
return Void()
|
return Void()
|
||||||
}
|
}
|
||||||
@ -192,7 +192,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
|||||||
mimeType = "animation/gif"
|
mimeType = "animation/gif"
|
||||||
attributes = [.ImageSize(size: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height))), .Animated, .FileName(fileName: fileName ?? "animation.gif")]
|
attributes = [.ImageSize(size: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height))), .Animated, .FileName(fileName: fileName ?? "animation.gif")]
|
||||||
}
|
}
|
||||||
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), mimeType: mimeType, attributes: attributes, hintFileIsLarge: data.count > 5 * 1024 * 1024)
|
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), mimeType: mimeType, attributes: attributes, hintFileIsLarge: data.count > 10 * 1024 * 1024)
|
||||||
|> mapError { _ -> Void in return Void() }
|
|> mapError { _ -> Void in return Void() }
|
||||||
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
|
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
|
||||||
switch event {
|
switch event {
|
||||||
@ -223,7 +223,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
|||||||
thumbnailData = jpegData
|
thumbnailData = jpegData
|
||||||
}
|
}
|
||||||
|
|
||||||
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), thumbnailData: thumbnailData, mimeType: mimeType, attributes: [.FileName(fileName: fileName ?? "file")], hintFileIsLarge: data.count > 5 * 1024 * 1024)
|
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), thumbnailData: thumbnailData, mimeType: mimeType, attributes: [.FileName(fileName: fileName ?? "file")], hintFileIsLarge: data.count > 10 * 1024 * 1024)
|
||||||
|> mapError { _ -> Void in return Void() }
|
|> mapError { _ -> Void in return Void() }
|
||||||
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
|
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
|
||||||
switch event {
|
switch event {
|
||||||
@ -241,13 +241,14 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
|||||||
let isVoice = ((value["isVoice"] as? NSNumber)?.boolValue ?? false)
|
let isVoice = ((value["isVoice"] as? NSNumber)?.boolValue ?? false)
|
||||||
let title = value["title"] as? String
|
let title = value["title"] as? String
|
||||||
let artist = value["artist"] as? String
|
let artist = value["artist"] as? String
|
||||||
|
let mimeType = value["mimeType"] as? String ?? "audio/ogg"
|
||||||
|
|
||||||
var waveform: MemoryBuffer?
|
var waveform: MemoryBuffer?
|
||||||
if let waveformData = TGItemProviderSignals.audioWaveform(url) {
|
if let waveformData = TGItemProviderSignals.audioWaveform(url) {
|
||||||
waveform = MemoryBuffer(data: waveformData)
|
waveform = MemoryBuffer(data: waveformData)
|
||||||
}
|
}
|
||||||
|
|
||||||
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(audioData), mimeType: "audio/ogg", attributes: [.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: artist, waveform: waveform), .FileName(fileName: fileName)], hintFileIsLarge: audioData.count > 5 * 1024 * 1024)
|
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(audioData), mimeType: mimeType, attributes: [.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: artist, waveform: waveform), .FileName(fileName: fileName)], hintFileIsLarge: audioData.count > 10 * 1024 * 1024)
|
||||||
|> mapError { _ -> Void in return Void() }
|
|> mapError { _ -> Void in return Void() }
|
||||||
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
|
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
|
||||||
switch event {
|
switch event {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,6 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != stickerItem || self.isEmpty != isEmpty {
|
if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != stickerItem || self.isEmpty != isEmpty {
|
||||||
if let stickerItem = stickerItem {
|
if let stickerItem = stickerItem {
|
||||||
if let _ = stickerItem.file.dimensions {
|
|
||||||
if stickerItem.file.isAnimatedSticker {
|
if stickerItem.file.isAnimatedSticker {
|
||||||
let dimensions = stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
let dimensions = stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: account.postbox, file: stickerItem.file, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))))
|
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: account.postbox, file: stickerItem.file, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))))
|
||||||
@ -167,7 +166,6 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
|||||||
self.imageNode.setSignal(chatMessageSticker(account: account, file: stickerItem.file, small: true))
|
self.imageNode.setSignal(chatMessageSticker(account: account, file: stickerItem.file, small: true))
|
||||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: chatMessageStickerResource(file: stickerItem.file, small: true)).start())
|
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: chatMessageStickerResource(file: stickerItem.file, small: true)).start())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if let placeholderNode = self.placeholderNode {
|
if let placeholderNode = self.placeholderNode {
|
||||||
if isEmpty {
|
if isEmpty {
|
||||||
|
@ -887,7 +887,11 @@ public final class VoiceChatController: ViewController {
|
|||||||
case .restricted:
|
case .restricted:
|
||||||
text = presentationData.strings.Channel_ErrorAddBlocked
|
text = presentationData.strings.Channel_ErrorAddBlocked
|
||||||
case .notMutualContact:
|
case .notMutualContact:
|
||||||
|
if case .broadcast = groupPeer.info {
|
||||||
|
text = presentationData.strings.Channel_AddUserLeftError
|
||||||
|
} else {
|
||||||
text = presentationData.strings.GroupInfo_AddUserLeftError
|
text = presentationData.strings.GroupInfo_AddUserLeftError
|
||||||
|
}
|
||||||
case .botDoesntSupportGroups:
|
case .botDoesntSupportGroups:
|
||||||
text = presentationData.strings.Channel_BotDoesntSupportGroups
|
text = presentationData.strings.Channel_BotDoesntSupportGroups
|
||||||
case .tooMuchBots:
|
case .tooMuchBots:
|
||||||
@ -952,15 +956,9 @@ public final class VoiceChatController: ViewController {
|
|||||||
self?.controller?.present(textAlertController(context: context, title: nil, text: presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(peer.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
self?.controller?.present(textAlertController(context: context, title: nil, text: presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(peer.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
})
|
})
|
||||||
case .notMutualContact:
|
case .notMutualContact:
|
||||||
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peer.id)
|
strongSelf.controller?.present(textAlertController(context: context, title: nil, text: presentationData.strings.GroupInfo_AddUserLeftError, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
|> deliverOnMainQueue).start(next: { peer in
|
|
||||||
self?.controller?.present(textAlertController(context: context, title: nil, text: presentationData.strings.GroupInfo_AddUserLeftError, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
|
||||||
})
|
|
||||||
case .tooManyChannels:
|
case .tooManyChannels:
|
||||||
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peer.id)
|
strongSelf.controller?.present(textAlertController(context: context, title: nil, text: presentationData.strings.Invite_ChannelsTooMuch, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
|> deliverOnMainQueue).start(next: { peer in
|
|
||||||
self?.controller?.present(textAlertController(context: context, title: nil, text: presentationData.strings.Invite_ChannelsTooMuch, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
|
||||||
})
|
|
||||||
case .groupFull, .generic:
|
case .groupFull, .generic:
|
||||||
strongSelf.controller?.present(textAlertController(context: strongSelf.context, forceTheme: strongSelf.darkTheme, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
strongSelf.controller?.present(textAlertController(context: strongSelf.context, forceTheme: strongSelf.darkTheme, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
}
|
}
|
||||||
|
@ -1202,12 +1202,12 @@ public class Account {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func allPeerInputActivities() -> Signal<[PeerActivitySpace: [PeerId: PeerInputActivity]], NoError> {
|
public func allPeerInputActivities() -> Signal<[PeerActivitySpace: [(PeerId, PeerInputActivity)]], NoError> {
|
||||||
return self.peerInputActivityManager.allActivities()
|
return self.peerInputActivityManager.allActivities()
|
||||||
|> map { activities in
|
|> map { activities in
|
||||||
var result: [PeerActivitySpace: [PeerId: PeerInputActivity]] = [:]
|
var result: [PeerActivitySpace: [(PeerId, PeerInputActivity)]] = [:]
|
||||||
for (chatPeerId, chatActivities) in activities {
|
for (chatPeerId, chatActivities) in activities {
|
||||||
result[chatPeerId] = chatActivities.mapValues({ $0.activity })
|
result[chatPeerId] = chatActivities.map { ($0.0, $0.1.activity) }
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ public enum ChatHistoryImport {
|
|||||||
case chatAdminRequired
|
case chatAdminRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func uploadMedia(account: Account, session: Session, file: TempBoxFile, fileName: String, mimeType: String, type: MediaType) -> Signal<Float, UploadMediaError> {
|
public static func uploadMedia(account: Account, session: Session, file: TempBoxFile, disposeFileAfterDone: Bool, fileName: String, mimeType: String, type: MediaType) -> Signal<Float, UploadMediaError> {
|
||||||
var forceNoBigParts = true
|
var forceNoBigParts = true
|
||||||
guard let size = fileSize(file.path), size != 0 else {
|
guard let size = fileSize(file.path), size != 0 else {
|
||||||
return .single(1.0)
|
return .single(1.0)
|
||||||
@ -160,6 +160,11 @@ public enum ChatHistoryImport {
|
|||||||
|> mapToSignal { result -> Signal<Float, UploadMediaError> in
|
|> mapToSignal { result -> Signal<Float, UploadMediaError> in
|
||||||
return .single(1.0)
|
return .single(1.0)
|
||||||
}
|
}
|
||||||
|
|> afterDisposed {
|
||||||
|
if disposeFileAfterDone {
|
||||||
|
TempBox.shared.dispose(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +197,7 @@ public enum ChatHistoryImport {
|
|||||||
case invalidChatType
|
case invalidChatType
|
||||||
case userBlocked
|
case userBlocked
|
||||||
case limitExceeded
|
case limitExceeded
|
||||||
case userIsNotMutualContact
|
case notMutualContact
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func checkPeerImport(account: Account, peerId: PeerId) -> Signal<CheckPeerImportResult, CheckPeerImportError> {
|
public static func checkPeerImport(account: Account, peerId: PeerId) -> Signal<CheckPeerImportResult, CheckPeerImportError> {
|
||||||
@ -217,7 +222,7 @@ public enum ChatHistoryImport {
|
|||||||
} else if error.errorDescription == "USER_IS_BLOCKED" {
|
} else if error.errorDescription == "USER_IS_BLOCKED" {
|
||||||
return .userBlocked
|
return .userBlocked
|
||||||
} else if error.errorDescription == "USER_NOT_MUTUAL_CONTACT" {
|
} else if error.errorDescription == "USER_NOT_MUTUAL_CONTACT" {
|
||||||
return .userBlocked
|
return .notMutualContact
|
||||||
} else if error.errorDescription == "FLOOD_WAIT" {
|
} else if error.errorDescription == "FLOOD_WAIT" {
|
||||||
return .limitExceeded
|
return .limitExceeded
|
||||||
} else {
|
} else {
|
||||||
|
@ -6,6 +6,7 @@ import MtProtoKit
|
|||||||
|
|
||||||
public enum CreateSecretChatError {
|
public enum CreateSecretChatError {
|
||||||
case generic
|
case generic
|
||||||
|
case limitExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
public func createSecretChat(account: Account, peerId: PeerId) -> Signal<PeerId, CreateSecretChatError> {
|
public func createSecretChat(account: Account, peerId: PeerId) -> Signal<PeerId, CreateSecretChatError> {
|
||||||
@ -29,10 +30,14 @@ public func createSecretChat(account: Account, peerId: PeerId) -> Signal<PeerId,
|
|||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.network.request(Api.functions.messages.requestEncryption(userId: inputUser, randomId: Int32(bitPattern: arc4random()), gA: Buffer(data: ga)))
|
return account.network.request(Api.functions.messages.requestEncryption(userId: inputUser, randomId: Int32(bitPattern: arc4random()), gA: Buffer(data: ga)), automaticFloodWait: false)
|
||||||
|> mapError { _ -> CreateSecretChatError in
|
|> mapError { error -> CreateSecretChatError in
|
||||||
|
if error.errorDescription.hasPrefix("FLOOD_WAIT_") {
|
||||||
|
return .limitExceeded
|
||||||
|
} else {
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|> mapToSignal { result -> Signal<PeerId, CreateSecretChatError> in
|
|> mapToSignal { result -> Signal<PeerId, CreateSecretChatError> in
|
||||||
return account.postbox.transaction { transaction -> PeerId in
|
return account.postbox.transaction { transaction -> PeerId in
|
||||||
updateSecretChat(encryptionProvider: account.network.encryptionProvider, accountPeerId: account.peerId, transaction: transaction, mediaBox: account.postbox.mediaBox, chat: result, requestData: SecretChatRequestData(g: config.g, p: config.p, a: a))
|
updateSecretChat(encryptionProvider: account.network.encryptionProvider, accountPeerId: account.peerId, transaction: transaction, mediaBox: account.postbox.mediaBox, chat: result, requestData: SecretChatRequestData(g: config.g, p: config.p, a: a))
|
||||||
|
@ -529,7 +529,7 @@ private final class PeerExportedInvitationsContextImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateCache() {
|
private func updateCache() {
|
||||||
guard self.hasLoadedOnce && !self.isLoadingMore else {
|
guard self.isMainList && self.hasLoadedOnce && !self.isLoadingMore else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import Foundation
|
|||||||
import Postbox
|
import Postbox
|
||||||
import TelegramApi
|
import TelegramApi
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
|
import MtProtoKit
|
||||||
|
|
||||||
import SyncCore
|
import SyncCore
|
||||||
|
|
||||||
@ -11,13 +12,19 @@ public enum JoinChannelError {
|
|||||||
case tooMuchUsers
|
case tooMuchUsers
|
||||||
}
|
}
|
||||||
|
|
||||||
public func joinChannel(account: Account, peerId: PeerId) -> Signal<RenderedChannelParticipant?, JoinChannelError> {
|
public func joinChannel(account: Account, peerId: PeerId, hash: String?) -> Signal<RenderedChannelParticipant?, JoinChannelError> {
|
||||||
return account.postbox.loadedPeerWithId(peerId)
|
return account.postbox.loadedPeerWithId(peerId)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> castError(JoinChannelError.self)
|
|> castError(JoinChannelError.self)
|
||||||
|> mapToSignal { peer -> Signal<RenderedChannelParticipant?, JoinChannelError> in
|
|> mapToSignal { peer -> Signal<RenderedChannelParticipant?, JoinChannelError> in
|
||||||
if let inputChannel = apiInputChannel(peer) {
|
if let inputChannel = apiInputChannel(peer) {
|
||||||
return account.network.request(Api.functions.channels.joinChannel(channel: inputChannel))
|
let request: Signal<Api.Updates, MTRpcError>
|
||||||
|
if let hash = hash {
|
||||||
|
request = account.network.request(Api.functions.messages.importChatInvite(hash: hash))
|
||||||
|
} else {
|
||||||
|
request = account.network.request(Api.functions.channels.joinChannel(channel: inputChannel))
|
||||||
|
}
|
||||||
|
return request
|
||||||
|> mapError { error -> JoinChannelError in
|
|> mapError { error -> JoinChannelError in
|
||||||
switch error.errorDescription {
|
switch error.errorDescription {
|
||||||
case "CHANNELS_TOO_MUCH":
|
case "CHANNELS_TOO_MUCH":
|
||||||
|
@ -30,13 +30,13 @@ struct PeerInputActivityRecord: Equatable {
|
|||||||
private final class ManagedLocalTypingActivitiesContext {
|
private final class ManagedLocalTypingActivitiesContext {
|
||||||
private var disposables: [PeerActivitySpace: (PeerInputActivityRecord, MetaDisposable)] = [:]
|
private var disposables: [PeerActivitySpace: (PeerInputActivityRecord, MetaDisposable)] = [:]
|
||||||
|
|
||||||
func update(activities: [PeerActivitySpace: [PeerId: PeerInputActivityRecord]]) -> (start: [(PeerActivitySpace, PeerInputActivityRecord?, MetaDisposable)], dispose: [MetaDisposable]) {
|
func update(activities: [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]]) -> (start: [(PeerActivitySpace, PeerInputActivityRecord?, MetaDisposable)], dispose: [MetaDisposable]) {
|
||||||
var start: [(PeerActivitySpace, PeerInputActivityRecord?, MetaDisposable)] = []
|
var start: [(PeerActivitySpace, PeerInputActivityRecord?, MetaDisposable)] = []
|
||||||
var dispose: [MetaDisposable] = []
|
var dispose: [MetaDisposable] = []
|
||||||
|
|
||||||
var validPeerIds = Set<PeerActivitySpace>()
|
var validPeerIds = Set<PeerActivitySpace>()
|
||||||
for (peerId, record) in activities {
|
for (peerId, record) in activities {
|
||||||
if let activity = record.values.first {
|
if let activity = record.first?.1 {
|
||||||
validPeerIds.insert(peerId)
|
validPeerIds.insert(peerId)
|
||||||
|
|
||||||
let currentRecord = self.disposables[peerId]
|
let currentRecord = self.disposables[peerId]
|
||||||
@ -76,7 +76,7 @@ private final class ManagedLocalTypingActivitiesContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func managedLocalTypingActivities(activities: Signal<[PeerActivitySpace: [PeerId: PeerInputActivityRecord]], NoError>, postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal<Void, NoError> {
|
func managedLocalTypingActivities(activities: Signal<[PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]], NoError>, postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal<Void, NoError> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let context = Atomic(value: ManagedLocalTypingActivitiesContext())
|
let context = Atomic(value: ManagedLocalTypingActivitiesContext())
|
||||||
let disposable = activities.start(next: { activities in
|
let disposable = activities.start(next: { activities in
|
||||||
|
@ -202,6 +202,8 @@ public func updateChannelAdminRights(account: Account, peerId: PeerId, adminId:
|
|||||||
}
|
}
|
||||||
|> map { [$0] }
|
|> map { [$0] }
|
||||||
)
|
)
|
||||||
|
} else if error.errorDescription == "USER_NOT_MUTUAL_CONTACT" {
|
||||||
|
return .fail(.addMemberError(.notMutualContact))
|
||||||
} else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
|
} else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
|
||||||
return .fail(.addMemberError(.restricted))
|
return .fail(.addMemberError(.restricted))
|
||||||
} else if error.errorDescription == "USER_CHANNELS_TOO_MUCH" {
|
} else if error.errorDescription == "USER_CHANNELS_TOO_MUCH" {
|
||||||
|
@ -178,9 +178,9 @@ private final class PeerInputActivityContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class PeerGlobalInputActivityContext {
|
private final class PeerGlobalInputActivityContext {
|
||||||
private let subscribers = Bag<([PeerActivitySpace: [PeerId: PeerInputActivityRecord]]) -> Void>()
|
private let subscribers = Bag<([PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]]) -> Void>()
|
||||||
|
|
||||||
func addSubscriber(_ subscriber: @escaping ([PeerActivitySpace: [PeerId: PeerInputActivityRecord]]) -> Void) -> Int {
|
func addSubscriber(_ subscriber: @escaping ([PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]]) -> Void) -> Int {
|
||||||
return self.subscribers.add(subscriber)
|
return self.subscribers.add(subscriber)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ private final class PeerGlobalInputActivityContext {
|
|||||||
return self.subscribers.isEmpty
|
return self.subscribers.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
func notify(_ activities: [PeerActivitySpace: [PeerId: PeerInputActivityRecord]]) {
|
func notify(_ activities: [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]]) {
|
||||||
for subscriber in self.subscribers.copyItems() {
|
for subscriber in self.subscribers.copyItems() {
|
||||||
subscriber(activities)
|
subscriber(activities)
|
||||||
}
|
}
|
||||||
@ -256,21 +256,17 @@ final class PeerInputActivityManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func collectActivities() -> [PeerActivitySpace: [PeerId: PeerInputActivityRecord]] {
|
private func collectActivities() -> [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]] {
|
||||||
assert(self.queue.isCurrent())
|
assert(self.queue.isCurrent())
|
||||||
|
|
||||||
var dict: [PeerActivitySpace: [PeerId: PeerInputActivityRecord]] = [:]
|
var dict: [PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]] = [:]
|
||||||
for (chatPeerId, context) in self.contexts {
|
for (chatPeerId, context) in self.contexts {
|
||||||
var chatDict: [PeerId: PeerInputActivityRecord] = [:]
|
dict[chatPeerId] = context.topActivities()
|
||||||
for (peerId, activity) in context.topActivities() {
|
|
||||||
chatDict[peerId] = activity
|
|
||||||
}
|
|
||||||
dict[chatPeerId] = chatDict
|
|
||||||
}
|
}
|
||||||
return dict
|
return dict
|
||||||
}
|
}
|
||||||
|
|
||||||
func allActivities() -> Signal<[PeerActivitySpace: [PeerId: PeerInputActivityRecord]], NoError> {
|
func allActivities() -> Signal<[PeerActivitySpace: [(PeerId, PeerInputActivityRecord)]], NoError> {
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
return Signal { [weak self] subscriber in
|
return Signal { [weak self] subscriber in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
@ -623,7 +623,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
|||||||
hintFileIsLarge = true
|
hintFileIsLarge = true
|
||||||
break loop
|
break loop
|
||||||
default:
|
default:
|
||||||
break loop
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,13 +174,32 @@ 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)
|
||||||
|
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)
|
|> take(1)
|
||||||
|> mapToSignal { peer in
|
|> castError(MTRpcError.self)
|
||||||
|
|> mapToSignal { peer -> Signal<Api.UrlAuthResult?, MTRpcError> in
|
||||||
if let inputPeer = apiInputPeer(peer) {
|
if let inputPeer = apiInputPeer(peer) {
|
||||||
return account.network.request(Api.functions.messages.requestUrlAuth(peer: inputPeer, msgId: messageId.id, buttonId: buttonId))
|
return account.network.request(Api.functions.messages.requestUrlAuth(peer: inputPeer, msgId: messageId.id, buttonId: buttonId))
|
||||||
|> map(Optional.init)
|
|> 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return request
|
||||||
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
|
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
@ -197,23 +216,35 @@ public func requestMessageActionUrlAuth(account: Account, messageId: MessageId,
|
|||||||
return .request(domain, TelegramUser(user: bot), (flags & (1 << 0)) != 0)
|
return .request(domain, TelegramUser(user: bot), (flags & (1 << 0)) != 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
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)
|
|
||||||
|> take(1)
|
|
||||||
|> mapToSignal { peer in
|
|
||||||
if let inputPeer = apiInputPeer(peer) {
|
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
if allowWriteAccess {
|
if allowWriteAccess {
|
||||||
flags |= Int32(1 << 0)
|
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))
|
return account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: inputPeer, msgId: messageId.id, buttonId: buttonId))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|
} else {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case let .url(url):
|
||||||
|
request = account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: .inputPeerEmpty, msgId: 0, buttonId: 0))
|
||||||
|
|> map(Optional.init)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return request
|
||||||
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
|
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
@ -228,8 +259,4 @@ public func acceptMessageActionUrlAuth(account: Account, messageId: MessageId, b
|
|||||||
return .default
|
return .default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return .single(.default)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -212,7 +212,7 @@ public struct PresentationResourcesChatList {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func scamIcon(_ theme: PresentationTheme, type: ScamIconType) -> UIImage? {
|
public static func scamIcon(_ theme: PresentationTheme, strings: PresentationStrings, type: ScamIconType) -> UIImage? {
|
||||||
let key: PresentationResourceKey
|
let key: PresentationResourceKey
|
||||||
let color: UIColor
|
let color: UIColor
|
||||||
switch type {
|
switch type {
|
||||||
@ -227,7 +227,10 @@ public struct PresentationResourcesChatList {
|
|||||||
color = theme.chat.serviceMessage.components.withDefaultWallpaper.scam
|
color = theme.chat.serviceMessage.components.withDefaultWallpaper.scam
|
||||||
}
|
}
|
||||||
return theme.image(key.rawValue, { theme in
|
return theme.image(key.rawValue, { theme in
|
||||||
return generateImage(CGSize(width: 37.0, height: 16.0), contextGenerator: { size, context in
|
let titleString = NSAttributedString(string: strings.Message_ScamAccount.uppercased(), font: Font.bold(10.0), textColor: color, paragraphAlignment: .center)
|
||||||
|
let stringRect = titleString.boundingRect(with: CGSize(width: 100.0, height: 16.0), options: .usesLineFragmentOrigin, context: nil)
|
||||||
|
|
||||||
|
return generateImage(CGSize(width: floor(stringRect.width) + 11.0, height: 16.0), contextGenerator: { size, context in
|
||||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
context.clear(bounds)
|
context.clear(bounds)
|
||||||
|
|
||||||
@ -240,7 +243,6 @@ public struct PresentationResourcesChatList {
|
|||||||
|
|
||||||
let titlePath = CGMutablePath()
|
let titlePath = CGMutablePath()
|
||||||
titlePath.addRect(bounds.offsetBy(dx: 0.0, dy: -2.0 + UIScreenPixel))
|
titlePath.addRect(bounds.offsetBy(dx: 0.0, dy: -2.0 + UIScreenPixel))
|
||||||
let titleString = NSAttributedString(string: "SCAM", font: Font.bold(10.0), textColor: color, paragraphAlignment: .center)
|
|
||||||
let titleFramesetter = CTFramesetterCreateWithAttributedString(titleString as CFAttributedString)
|
let titleFramesetter = CTFramesetterCreateWithAttributedString(titleString as CFAttributedString)
|
||||||
let titleFrame = CTFramesetterCreateFrame(titleFramesetter, CFRangeMake(0, titleString.length), titlePath, nil)
|
let titleFrame = CTFramesetterCreateFrame(titleFramesetter, CFRangeMake(0, titleString.length), titlePath, nil)
|
||||||
CTFrameDraw(titleFrame, context)
|
CTFrameDraw(titleFrame, context)
|
||||||
@ -248,7 +250,7 @@ public struct PresentationResourcesChatList {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func fakeIcon(_ theme: PresentationTheme, type: ScamIconType) -> UIImage? {
|
public static func fakeIcon(_ theme: PresentationTheme, strings: PresentationStrings, type: ScamIconType) -> UIImage? {
|
||||||
let key: PresentationResourceKey
|
let key: PresentationResourceKey
|
||||||
let color: UIColor
|
let color: UIColor
|
||||||
switch type {
|
switch type {
|
||||||
@ -263,7 +265,10 @@ public struct PresentationResourcesChatList {
|
|||||||
color = theme.chat.serviceMessage.components.withDefaultWallpaper.scam
|
color = theme.chat.serviceMessage.components.withDefaultWallpaper.scam
|
||||||
}
|
}
|
||||||
return theme.image(key.rawValue, { theme in
|
return theme.image(key.rawValue, { theme in
|
||||||
return generateImage(CGSize(width: 37.0, height: 16.0), contextGenerator: { size, context in
|
let titleString = NSAttributedString(string: strings.Message_FakeAccount.uppercased(), font: Font.bold(10.0), textColor: color, paragraphAlignment: .center)
|
||||||
|
let stringRect = titleString.boundingRect(with: CGSize(width: 100.0, height: 16.0), options: .usesLineFragmentOrigin, context: nil)
|
||||||
|
|
||||||
|
return generateImage(CGSize(width: floor(stringRect.width) + 11.0, height: 16.0), contextGenerator: { size, context in
|
||||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
context.clear(bounds)
|
context.clear(bounds)
|
||||||
|
|
||||||
@ -276,7 +281,6 @@ public struct PresentationResourcesChatList {
|
|||||||
|
|
||||||
let titlePath = CGMutablePath()
|
let titlePath = CGMutablePath()
|
||||||
titlePath.addRect(bounds.offsetBy(dx: 0.0, dy: -2.0 + UIScreenPixel))
|
titlePath.addRect(bounds.offsetBy(dx: 0.0, dy: -2.0 + UIScreenPixel))
|
||||||
let titleString = NSAttributedString(string: "FAKE", font: Font.bold(10.0), textColor: color, paragraphAlignment: .center)
|
|
||||||
let titleFramesetter = CTFramesetterCreateWithAttributedString(titleString as CFAttributedString)
|
let titleFramesetter = CTFramesetterCreateWithAttributedString(titleString as CFAttributedString)
|
||||||
let titleFrame = CTFramesetterCreateFrame(titleFramesetter, CFRangeMake(0, titleString.length), titlePath, nil)
|
let titleFrame = CTFramesetterCreateFrame(titleFramesetter, CFRangeMake(0, titleString.length), titlePath, nil)
|
||||||
CTFrameDraw(titleFrame, context)
|
CTFrameDraw(titleFrame, context)
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/VideoCall.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/VideoCall.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ic_menuvideocall.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/VideoCall.imageset/ic_menuvideocall.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/VideoCall.imageset/ic_menuvideocall.pdf
vendored
Normal file
Binary file not shown.
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)
|
||||||
|
@ -168,7 +168,7 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||||||
case .join:
|
case .join:
|
||||||
self.activityIndicator.isHidden = false
|
self.activityIndicator.isHidden = false
|
||||||
self.activityIndicator.startAnimating()
|
self.activityIndicator.startAnimating()
|
||||||
self.actionDisposable.set((context.peerChannelMemberCategoriesContextsManager.join(account: context.account, peerId: peer.id)
|
self.actionDisposable.set((context.peerChannelMemberCategoriesContextsManager.join(account: context.account, peerId: peer.id, hash: nil)
|
||||||
|> afterDisposed { [weak self] in
|
|> afterDisposed { [weak self] in
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
@ -306,6 +306,15 @@ 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 disableStickerAnimationsValue = false
|
||||||
|
var disableStickerAnimations: Bool {
|
||||||
|
get {
|
||||||
|
return self.disableStickerAnimationsValue
|
||||||
|
} set {
|
||||||
|
self.disableStickerAnimationsPromise.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
private var stickerSettings: ChatInterfaceStickerSettings
|
private var stickerSettings: ChatInterfaceStickerSettings
|
||||||
private var stickerSettingsDisposable: Disposable?
|
private var stickerSettingsDisposable: Disposable?
|
||||||
|
|
||||||
@ -1163,13 +1172,12 @@ 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: {
|
||||||
@ -1187,7 +1195,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return $0
|
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 {
|
strongSelf.messageActionUrlAuthDisposable.set(((combineLatest(strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.context.account.peerId), requestMessageActionUrlAuth(account: strongSelf.context.account, subject: subject) |> afterDisposed {
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||||
@ -1236,7 +1244,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
strongSelf.messageActionUrlAuthDisposable.set(((acceptMessageActionUrlAuth(account: strongSelf.context.account, messageId: messageId, buttonId: buttonId, allowWriteAccess: allowWriteAccess) |> afterDisposed {
|
strongSelf.messageActionUrlAuthDisposable.set(((acceptMessageActionUrlAuth(account: strongSelf.context.account, subject: subject, allowWriteAccess: allowWriteAccess) |> afterDisposed {
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||||
@ -1281,7 +1289,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, activateSwitchInline: { [weak self] peerId, inputString in
|
}, activateSwitchInline: { [weak self] peerId, inputString in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -2578,7 +2585,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 +3449,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.disableStickerAnimationsValue != disableStickerAnimations {
|
||||||
if let strongSelf = self, strongSelf.stickerSettings != chatStickerSettings {
|
|
||||||
strongSelf.stickerSettings = chatStickerSettings
|
strongSelf.stickerSettings = chatStickerSettings
|
||||||
|
strongSelf.disableStickerAnimationsValue = 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 +7152,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 +8506,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 {
|
||||||
@ -11079,6 +11095,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: {
|
||||||
|
@ -964,6 +964,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
if let inputMediaNode = inputNode as? ChatMediaInputNode, self.inputMediaNode == nil {
|
if let inputMediaNode = inputNode as? ChatMediaInputNode, self.inputMediaNode == nil {
|
||||||
self.inputMediaNode = inputMediaNode
|
self.inputMediaNode = inputMediaNode
|
||||||
|
inputMediaNode.requestDisableStickerAnimations = { [weak self] disabled in
|
||||||
|
self?.controller?.disableStickerAnimations = disabled
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if self.inputNode != inputNode {
|
if self.inputNode != inputNode {
|
||||||
dismissedInputNode = self.inputNode
|
dismissedInputNode = self.inputNode
|
||||||
@ -2086,10 +2089,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2170,6 +2173,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
inputNode.interfaceInteraction = interfaceInteraction
|
inputNode.interfaceInteraction = interfaceInteraction
|
||||||
|
inputNode.requestDisableStickerAnimations = { [weak self] disabled in
|
||||||
|
self?.controller?.disableStickerAnimations = disabled
|
||||||
|
}
|
||||||
self.inputMediaNode = inputNode
|
self.inputMediaNode = inputNode
|
||||||
if let (validLayout, _) = self.validLayout {
|
if let (validLayout, _) = self.validLayout {
|
||||||
let _ = inputNode.updateLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, bottomInset: validLayout.intrinsicInsets.bottom, standardInputHeight: validLayout.standardInputHeight, inputHeight: validLayout.inputHeight ?? 0.0, maximumHeight: validLayout.standardInputHeight, inputPanelHeight: 44.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: validLayout.deviceMetrics, isVisible: false)
|
let _ = inputNode.updateLayout(width: validLayout.size.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, bottomInset: validLayout.intrinsicInsets.bottom, standardInputHeight: validLayout.standardInputHeight, inputHeight: validLayout.inputHeight ?? 0.0, maximumHeight: validLayout.standardInputHeight, inputPanelHeight: 44.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: validLayout.deviceMetrics, isVisible: false)
|
||||||
|
@ -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
|
||||||
|
@ -448,6 +448,8 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
private var currentView: ItemCollectionsView?
|
private var currentView: ItemCollectionsView?
|
||||||
private let dismissedPeerSpecificStickerPack = Promise<Bool>()
|
private let dismissedPeerSpecificStickerPack = Promise<Bool>()
|
||||||
|
|
||||||
|
var requestDisableStickerAnimations: ((Bool) -> Void)?
|
||||||
|
|
||||||
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, ChatPresentationInterfaceState, DeviceMetrics, Bool)?
|
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, ChatPresentationInterfaceState, DeviceMetrics, Bool)?
|
||||||
private var paneArrangement: ChatMediaInputPaneArrangement
|
private var paneArrangement: ChatMediaInputPaneArrangement
|
||||||
private var initializedArrangement = false
|
private var initializedArrangement = false
|
||||||
@ -1242,6 +1244,10 @@ 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
|
||||||
|
self?.requestDisableStickerAnimations?(visible)
|
||||||
|
self?.simulateUpdateLayout(isVisible: !visible)
|
||||||
|
}
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1442,9 +1442,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
if case let .peer(peerId) = item.chatLocation, let authorPeerId = item.message.author?.id, authorPeerId == peerId {
|
if case let .peer(peerId) = item.chatLocation, let authorPeerId = item.message.author?.id, authorPeerId == peerId {
|
||||||
|
|
||||||
} else if effectiveAuthor.isScam {
|
} else if effectiveAuthor.isScam {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme.theme, type: incoming ? .regular : .outgoing)
|
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme.theme, strings: item.presentationData.strings, type: incoming ? .regular : .outgoing)
|
||||||
} else if effectiveAuthor.isFake {
|
} else if effectiveAuthor.isFake {
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme.theme, type: incoming ? .regular : .outgoing)
|
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme.theme, strings: item.presentationData.strings, type: incoming ? .regular : .outgoing)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -196,16 +196,16 @@ class ChatMessageForwardInfoNode: ASDisplayNode {
|
|||||||
if peer.isFake {
|
if peer.isFake {
|
||||||
switch type {
|
switch type {
|
||||||
case let .bubble(incoming):
|
case let .bubble(incoming):
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(presentationData.theme.theme, type: incoming ? .regular : .outgoing)
|
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(presentationData.theme.theme, strings: presentationData.strings, type: incoming ? .regular : .outgoing)
|
||||||
case .standalone:
|
case .standalone:
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(presentationData.theme.theme, type: .service)
|
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(presentationData.theme.theme, strings: presentationData.strings, type: .service)
|
||||||
}
|
}
|
||||||
} else if peer.isScam {
|
} else if peer.isScam {
|
||||||
switch type {
|
switch type {
|
||||||
case let .bubble(incoming):
|
case let .bubble(incoming):
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(presentationData.theme.theme, type: incoming ? .regular : .outgoing)
|
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(presentationData.theme.theme, strings: presentationData.strings, type: incoming ? .regular : .outgoing)
|
||||||
case .standalone:
|
case .standalone:
|
||||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(presentationData.theme.theme, type: .service)
|
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(presentationData.theme.theme, strings: presentationData.strings, type: .service)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentCredibilityIconImage = nil
|
currentCredibilityIconImage = nil
|
||||||
|
@ -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: {
|
||||||
|
@ -240,13 +240,13 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
|
|
||||||
if titleFakeIcon != self.titleFakeIcon {
|
if titleFakeIcon != self.titleFakeIcon {
|
||||||
self.titleFakeIcon = titleFakeIcon
|
self.titleFakeIcon = titleFakeIcon
|
||||||
self.titleCredibilityIconNode.image = titleFakeIcon ? PresentationResourcesChatList.fakeIcon(titleTheme, type: .regular) : nil
|
self.titleCredibilityIconNode.image = titleFakeIcon ? PresentationResourcesChatList.fakeIcon(titleTheme, strings: self.strings, type: .regular) : nil
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if titleScamIcon != self.titleScamIcon {
|
if titleScamIcon != self.titleScamIcon {
|
||||||
self.titleScamIcon = titleScamIcon
|
self.titleScamIcon = titleScamIcon
|
||||||
self.titleCredibilityIconNode.image = titleScamIcon ? PresentationResourcesChatList.scamIcon(titleTheme, type: .regular) : nil
|
self.titleCredibilityIconNode.image = titleScamIcon ? PresentationResourcesChatList.scamIcon(titleTheme, strings: self.strings, type: .regular) : nil
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,12 +166,18 @@ public class ComposeControllerImpl: ViewController, ComposeController {
|
|||||||
controller.displayNavigationActivity = false
|
controller.displayNavigationActivity = false
|
||||||
(controller.navigationController as? NavigationController)?.replaceAllButRootController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)), animated: true)
|
(controller.navigationController as? NavigationController)?.replaceAllButRootController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)), animated: true)
|
||||||
}
|
}
|
||||||
}, error: { _ in
|
}, error: { error in
|
||||||
if let strongSelf = self, let controller = controller {
|
if let strongSelf = self, let controller = controller {
|
||||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
controller.displayNavigationActivity = false
|
controller.displayNavigationActivity = false
|
||||||
controller.present(textAlertController(context: strongSelf.context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
let text: String
|
||||||
|
switch error {
|
||||||
|
case .limitExceeded:
|
||||||
|
text = presentationData.strings.TwoStepAuth_FloodError
|
||||||
|
default:
|
||||||
|
text = presentationData.strings.Login_UnknownError
|
||||||
|
}
|
||||||
|
controller.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -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: {
|
||||||
|
@ -984,7 +984,7 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
|
|||||||
if isOpenedFromChat {
|
if isOpenedFromChat {
|
||||||
result.append(.search)
|
result.append(.search)
|
||||||
}
|
}
|
||||||
if isSecretChat && !isContact {
|
if (isSecretChat && !isContact) || user.flags.contains(.isSupport) {
|
||||||
} else {
|
} else {
|
||||||
result.append(.more)
|
result.append(.more)
|
||||||
}
|
}
|
||||||
@ -1043,7 +1043,7 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
|
|||||||
if channel.isVerified || channel.adminRights != nil || channel.flags.contains(.isCreator) {
|
if channel.isVerified || channel.adminRights != nil || channel.flags.contains(.isCreator) {
|
||||||
canReport = false
|
canReport = false
|
||||||
}
|
}
|
||||||
if !canReport && !canViewStats && displayLeave {
|
if !canReport && !canViewStats {
|
||||||
displayMore = false
|
displayMore = false
|
||||||
}
|
}
|
||||||
if displayMore {
|
if displayMore {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
@ -2783,9 +2810,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
if let peer = peer {
|
if let peer = peer {
|
||||||
self.initializedCredibilityIcon = true
|
self.initializedCredibilityIcon = true
|
||||||
if peer.isFake {
|
if peer.isFake {
|
||||||
image = PresentationResourcesChatList.fakeIcon(presentationData.theme, type: .regular)
|
image = PresentationResourcesChatList.fakeIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
|
||||||
} else if peer.isScam {
|
} else if peer.isScam {
|
||||||
image = PresentationResourcesChatList.scamIcon(presentationData.theme, type: .regular)
|
image = PresentationResourcesChatList.scamIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
|
||||||
} else if peer.isVerified {
|
} else if peer.isVerified {
|
||||||
if let sourceImage = UIImage(bundleImageName: "Peer Info/VerifiedIcon") {
|
if let sourceImage = UIImage(bundleImageName: "Peer Info/VerifiedIcon") {
|
||||||
image = generateImage(sourceImage.size, contextGenerator: { size, context in
|
image = generateImage(sourceImage.size, contextGenerator: { size, context in
|
||||||
|
@ -563,6 +563,7 @@ private final class PeerInfoInteraction {
|
|||||||
let logoutAccount: (AccountRecordId) -> Void
|
let logoutAccount: (AccountRecordId) -> Void
|
||||||
let accountContextMenu: (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void
|
let accountContextMenu: (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void
|
||||||
let updateBio: (String) -> Void
|
let updateBio: (String) -> Void
|
||||||
|
let openDeletePeer: () -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
openUsername: @escaping (String) -> Void,
|
openUsername: @escaping (String) -> Void,
|
||||||
@ -599,7 +600,8 @@ private final class PeerInfoInteraction {
|
|||||||
switchToAccount: @escaping (AccountRecordId) -> Void,
|
switchToAccount: @escaping (AccountRecordId) -> Void,
|
||||||
logoutAccount: @escaping (AccountRecordId) -> Void,
|
logoutAccount: @escaping (AccountRecordId) -> Void,
|
||||||
accountContextMenu: @escaping (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void,
|
accountContextMenu: @escaping (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void,
|
||||||
updateBio: @escaping (String) -> Void
|
updateBio: @escaping (String) -> Void,
|
||||||
|
openDeletePeer: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
self.openUsername = openUsername
|
self.openUsername = openUsername
|
||||||
self.openPhone = openPhone
|
self.openPhone = openPhone
|
||||||
@ -636,6 +638,7 @@ private final class PeerInfoInteraction {
|
|||||||
self.logoutAccount = logoutAccount
|
self.logoutAccount = logoutAccount
|
||||||
self.accountContextMenu = accountContextMenu
|
self.accountContextMenu = accountContextMenu
|
||||||
self.updateBio = updateBio
|
self.updateBio = updateBio
|
||||||
|
self.openDeletePeer = openDeletePeer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1140,6 +1143,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||||||
case groupLocation
|
case groupLocation
|
||||||
case peerPublicSettings
|
case peerPublicSettings
|
||||||
case peerSettings
|
case peerSettings
|
||||||
|
case peerActions
|
||||||
}
|
}
|
||||||
|
|
||||||
var items: [Section: [PeerInfoScreenItem]] = [:]
|
var items: [Section: [PeerInfoScreenItem]] = [:]
|
||||||
@ -1147,33 +1151,6 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||||||
items[section] = []
|
items[section] = []
|
||||||
}
|
}
|
||||||
|
|
||||||
// if let data = data, let notificationSettings = data.notificationSettings {
|
|
||||||
// let notificationsLabel: String
|
|
||||||
// let soundLabel: String
|
|
||||||
// if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
|
||||||
// if until < Int32.max - 1 {
|
|
||||||
// notificationsLabel = stringForRemainingMuteInterval(strings: presentationData.strings, muteInterval: until)
|
|
||||||
// } else {
|
|
||||||
// notificationsLabel = presentationData.strings.UserInfo_NotificationsDisabled
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// notificationsLabel = presentationData.strings.UserInfo_NotificationsEnabled
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let globalNotificationSettings: GlobalNotificationSettings = data.globalNotificationSettings ?? GlobalNotificationSettings.defaultSettings
|
|
||||||
// soundLabel = localizedPeerNotificationSoundString(strings: presentationData.strings, sound: notificationSettings.messageSound, default: globalNotificationSettings.effective.privateChats.sound)
|
|
||||||
//
|
|
||||||
// items[.notifications]!.append(PeerInfoScreenDisclosureItem(id: 0, label: .text(notificationsLabel), text: presentationData.strings.GroupInfo_Notifications, action: {
|
|
||||||
// interaction.editingOpenNotificationSettings()
|
|
||||||
// }))
|
|
||||||
// items[.notifications]!.append(PeerInfoScreenDisclosureItem(id: 1, label: .text(soundLabel), text: presentationData.strings.GroupInfo_Sound, action: {
|
|
||||||
// interaction.editingOpenSoundSettings()
|
|
||||||
// }))
|
|
||||||
// items[.notifications]!.append(PeerInfoScreenSwitchItem(id: 2, text: presentationData.strings.Notification_Exceptions_PreviewAlwaysOn, value: notificationSettings.displayPreviews != .hide, toggled: { value in
|
|
||||||
// interaction.editingToggleShowMessageText(value)
|
|
||||||
// }))
|
|
||||||
// }
|
|
||||||
|
|
||||||
if let data = data {
|
if let data = data {
|
||||||
if let _ = data.peer as? TelegramUser {
|
if let _ = data.peer as? TelegramUser {
|
||||||
let ItemDelete = 0
|
let ItemDelete = 0
|
||||||
@ -1194,8 +1171,8 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||||||
|
|
||||||
if channel.flags.contains(.isCreator) {
|
if channel.flags.contains(.isCreator) {
|
||||||
let linkText: String
|
let linkText: String
|
||||||
if let username = channel.username {
|
if let _ = channel.username {
|
||||||
linkText = "@\(username)"
|
linkText = presentationData.strings.Channel_Setup_TypePublic
|
||||||
} else {
|
} else {
|
||||||
linkText = presentationData.strings.Channel_Setup_TypePrivate
|
linkText = presentationData.strings.Channel_Setup_TypePrivate
|
||||||
}
|
}
|
||||||
@ -1204,7 +1181,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)"
|
||||||
@ -1256,14 +1233,15 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||||||
let ItemLinkedChannel = 103
|
let ItemLinkedChannel = 103
|
||||||
let ItemPreHistory = 104
|
let ItemPreHistory = 104
|
||||||
let ItemStickerPack = 105
|
let ItemStickerPack = 105
|
||||||
let ItemPermissions = 106
|
let ItemMembers = 106
|
||||||
let ItemMembers = 107
|
let ItemPermissions = 107
|
||||||
let ItemAdmins = 108
|
let ItemAdmins = 108
|
||||||
let ItemRemovedUsers = 109
|
let ItemRemovedUsers = 109
|
||||||
let ItemLocationHeader = 110
|
let ItemLocationHeader = 110
|
||||||
let ItemLocation = 111
|
let ItemLocation = 111
|
||||||
let ItemLocationSetup = 112
|
let ItemLocationSetup = 112
|
||||||
let ItemAutoremove = 113
|
let ItemAutoremove = 113
|
||||||
|
let ItemDeleteGroup = 114
|
||||||
|
|
||||||
let isCreator = channel.flags.contains(.isCreator)
|
let isCreator = channel.flags.contains(.isCreator)
|
||||||
let isPublic = channel.username != nil
|
let isPublic = channel.username != nil
|
||||||
@ -1369,25 +1347,28 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||||||
activePermissionCount = count
|
activePermissionCount = count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMembers, label: .text(cachedData.participantsSummary.memberCount.flatMap { "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" } ?? ""), text: presentationData.strings.Group_Info_Members, icon: UIImage(bundleImageName: "Chat/Info/GroupMembersIcon"), action: {
|
||||||
|
interaction.openParticipantsSection(.members)
|
||||||
|
}))
|
||||||
if !channel.flags.contains(.isGigagroup) {
|
if !channel.flags.contains(.isGigagroup) {
|
||||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPermissions, label: .text(activePermissionCount.flatMap({ "\($0)/\(allGroupPermissionList.count)" }) ?? ""), text: presentationData.strings.GroupInfo_Permissions, icon: UIImage(bundleImageName: "Settings/MenuIcons/SetPasscode"), action: {
|
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPermissions, label: .text(activePermissionCount.flatMap({ "\($0)/\(allGroupPermissionList.count)" }) ?? ""), text: presentationData.strings.GroupInfo_Permissions, icon: UIImage(bundleImageName: "Settings/MenuIcons/SetPasscode"), action: {
|
||||||
interaction.openPermissions()
|
interaction.openPermissions()
|
||||||
}))
|
}))
|
||||||
} else {
|
|
||||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMembers, label: .text(cachedData.participantsSummary.memberCount.flatMap { "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" } ?? ""), text: presentationData.strings.Group_Info_Members, icon: UIImage(bundleImageName: "Chat/Info/GroupMembersIcon"), action: {
|
|
||||||
interaction.openParticipantsSection(.members)
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, label: .text(cachedData.participantsSummary.adminCount.flatMap { "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" } ?? ""), text: presentationData.strings.GroupInfo_Administrators, icon: UIImage(bundleImageName: "Chat/Info/GroupAdminsIcon"), action: {
|
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, label: .text(cachedData.participantsSummary.adminCount.flatMap { "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" } ?? ""), text: presentationData.strings.GroupInfo_Administrators, icon: UIImage(bundleImageName: "Chat/Info/GroupAdminsIcon"), action: {
|
||||||
interaction.openParticipantsSection(.admins)
|
interaction.openParticipantsSection(.admins)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if channel.flags.contains(.isGigagroup) {
|
|
||||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemRemovedUsers, label: .text(cachedData.participantsSummary.kickedCount.flatMap { $0 > 0 ? "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" : "" } ?? ""), text: presentationData.strings.GroupInfo_Permissions_Removed, icon: UIImage(bundleImageName: "Chat/Info/GroupRemovedIcon"), action: {
|
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemRemovedUsers, label: .text(cachedData.participantsSummary.kickedCount.flatMap { $0 > 0 ? "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" : "" } ?? ""), text: presentationData.strings.GroupInfo_Permissions_Removed, icon: UIImage(bundleImageName: "Chat/Info/GroupRemovedIcon"), action: {
|
||||||
interaction.openParticipantsSection(.banned)
|
interaction.openParticipantsSection(.banned)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isCreator {
|
||||||
|
items[.peerActions]!.append(PeerInfoScreenActionItem(id: ItemDeleteGroup, text: presentationData.strings.Group_DeleteGroup, color: .destructive, icon: nil, alignment: .natural, action: {
|
||||||
|
interaction.openDeletePeer()
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1691,6 +1672,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
},
|
},
|
||||||
updateBio: { [weak self] bio in
|
updateBio: { [weak self] bio in
|
||||||
self?.updateBio(bio)
|
self?.updateBio(bio)
|
||||||
|
},
|
||||||
|
openDeletePeer: { [weak self] in
|
||||||
|
self?.openDeletePeer()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1987,7 +1971,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 +2710,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 +3031,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3045,6 +3052,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
self?.openPeer(peerId: peerId, navigation: navigation)
|
self?.openPeer(peerId: peerId, navigation: navigation)
|
||||||
}, sendFile: nil,
|
}, sendFile: nil,
|
||||||
sendSticker: nil,
|
sendSticker: nil,
|
||||||
|
requestMessageActionUrlAuth: nil,
|
||||||
present: { c, a in
|
present: { c, a in
|
||||||
self?.controller?.present(c, in: .window(.root), with: a)
|
self?.controller?.present(c, in: .window(.root), with: a)
|
||||||
}, dismissInput: {
|
}, dismissInput: {
|
||||||
@ -3645,11 +3653,18 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
if let navigationController = (strongSelf.controller?.navigationController as? NavigationController) {
|
if let navigationController = (strongSelf.controller?.navigationController as? NavigationController) {
|
||||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId)))
|
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId)))
|
||||||
}
|
}
|
||||||
}, error: { _ in
|
}, error: { error in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.controller?.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
let text: String
|
||||||
|
switch error {
|
||||||
|
case .limitExceeded:
|
||||||
|
text = strongSelf.presentationData.strings.TwoStepAuth_FloodError
|
||||||
|
default:
|
||||||
|
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||||
|
}
|
||||||
|
strongSelf.controller?.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
}))
|
}))
|
||||||
})]), in: .window(.root))
|
})]), in: .window(.root))
|
||||||
}
|
}
|
||||||
@ -4091,6 +4106,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 +4937,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)
|
||||||
}
|
}
|
||||||
@ -6564,7 +6580,13 @@ func presentAddMembers(context: AccountContext, parentController: ViewController
|
|||||||
case .notMutualContact:
|
case .notMutualContact:
|
||||||
let _ = (context.account.postbox.loadedPeerWithId(memberId)
|
let _ = (context.account.postbox.loadedPeerWithId(memberId)
|
||||||
|> deliverOnMainQueue).start(next: { peer in
|
|> deliverOnMainQueue).start(next: { peer in
|
||||||
parentController?.present(textAlertController(context: context, title: nil, text: presentationData.strings.GroupInfo_AddUserLeftError, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
let text: String
|
||||||
|
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||||
|
text = presentationData.strings.Channel_AddUserLeftError
|
||||||
|
} else {
|
||||||
|
text = presentationData.strings.GroupInfo_AddUserLeftError
|
||||||
|
}
|
||||||
|
parentController?.present(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
})
|
})
|
||||||
return .complete()
|
return .complete()
|
||||||
case .tooManyChannels:
|
case .tooManyChannels:
|
||||||
@ -6683,7 +6705,14 @@ func presentAddMembers(context: AccountContext, parentController: ViewController
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else if peers.count == 1, case .notMutualContact = error {
|
} else if peers.count == 1, case .notMutualContact = error {
|
||||||
parentController?.present(textAlertController(context: context, title: nil, text: presentationData.strings.GroupInfo_AddUserLeftError, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
let text: String
|
||||||
|
if let peer = groupPeer as? TelegramChannel, case .broadcast = peer.info {
|
||||||
|
text = presentationData.strings.Channel_AddUserLeftError
|
||||||
|
} else {
|
||||||
|
text = presentationData.strings.GroupInfo_AddUserLeftError
|
||||||
|
}
|
||||||
|
|
||||||
|
parentController?.present(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
} else if case .tooMuchJoined = error {
|
} else if case .tooMuchJoined = error {
|
||||||
parentController?.present(textAlertController(context: context, title: nil, text: presentationData.strings.Invite_ChannelsTooMuch, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
parentController?.present(textAlertController(context: context, title: nil, text: presentationData.strings.Invite_ChannelsTooMuch, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
}
|
}
|
||||||
|
@ -744,7 +744,7 @@ public class ShareRootControllerImpl {
|
|||||||
errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked
|
errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked
|
||||||
case .limitExceeded:
|
case .limitExceeded:
|
||||||
errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded
|
errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded
|
||||||
case .userIsNotMutualContact:
|
case .notMutualContact:
|
||||||
errorText = presentationData.strings.ChatImport_UserErrorNotMutual
|
errorText = presentationData.strings.ChatImport_UserErrorNotMutual
|
||||||
}
|
}
|
||||||
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||||
@ -868,7 +868,7 @@ public class ShareRootControllerImpl {
|
|||||||
errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked
|
errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked
|
||||||
case .limitExceeded:
|
case .limitExceeded:
|
||||||
errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded
|
errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded
|
||||||
case .userIsNotMutualContact:
|
case .notMutualContact:
|
||||||
errorText = presentationData.strings.ChatImport_UserErrorNotMutual
|
errorText = presentationData.strings.ChatImport_UserErrorNotMutual
|
||||||
}
|
}
|
||||||
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||||
@ -989,7 +989,7 @@ public class ShareRootControllerImpl {
|
|||||||
errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked
|
errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked
|
||||||
case .limitExceeded:
|
case .limitExceeded:
|
||||||
errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded
|
errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded
|
||||||
case .userIsNotMutualContact:
|
case .notMutualContact:
|
||||||
errorText = presentationData.strings.ChatImport_UserErrorNotMutual
|
errorText = presentationData.strings.ChatImport_UserErrorNotMutual
|
||||||
}
|
}
|
||||||
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||||
|
@ -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: {
|
||||||
|
@ -49,7 +49,7 @@ func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Message, da
|
|||||||
if let author = message.author as? TelegramUser {
|
if let author = message.author as? TelegramUser {
|
||||||
if let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
if let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
||||||
authorTitle = author.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
authorTitle = author.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||||
} else if let forwardInfo = message.forwardInfo {
|
} else if let forwardInfo = message.forwardInfo, forwardInfo.sourceMessageId?.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
authorTitle = forwardInfo.authorSignature
|
authorTitle = forwardInfo.authorSignature
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,8 +418,8 @@ public final class PeerChannelMemberCategoriesContextsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func join(account: Account, peerId: PeerId) -> Signal<Never, JoinChannelError> {
|
public func join(account: Account, peerId: PeerId, hash: String?) -> Signal<Never, JoinChannelError> {
|
||||||
return joinChannel(account: account, peerId: peerId)
|
return joinChannel(account: account, peerId: peerId, hash: hash)
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> beforeNext { [weak self] result in
|
|> beforeNext { [weak self] result in
|
||||||
if let strongSelf = self, let updated = result {
|
if let strongSelf = self, let updated = result {
|
||||||
|
@ -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] {
|
||||||
return UrlHandlingConfiguration(token: token, domains: domains)
|
return UrlHandlingConfiguration(token: token, domains: domains, urlAuthDomains: [])
|
||||||
} else {
|
} else {
|
||||||
return .defaultValue
|
return .defaultValue
|
||||||
}
|
}
|
||||||
@ -522,15 +524,21 @@ public func resolveUrlImpl(account: Account, url: String) -> Signal<ResolvedUrl,
|
|||||||
let urlHandlingConfiguration = UrlHandlingConfiguration.with(appConfiguration: appConfiguration)
|
let urlHandlingConfiguration = UrlHandlingConfiguration.with(appConfiguration: appConfiguration)
|
||||||
|
|
||||||
var url = url
|
var url = url
|
||||||
|
if !url.contains("://") && !url.hasPrefix("tel:") && !url.hasPrefix("mailto:") && !url.hasPrefix("calshow:") {
|
||||||
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() {
|
||||||
|
if urlHandlingConfiguration.domains.contains(host), var components = URLComponents(string: url) {
|
||||||
components.scheme = "https"
|
components.scheme = "https"
|
||||||
var queryItems = components.queryItems ?? []
|
var queryItems = components.queryItems ?? []
|
||||||
queryItems.append(URLQueryItem(name: "autologin_token", value: urlHandlingConfiguration.token))
|
queryItems.append(URLQueryItem(name: "autologin_token", value: urlHandlingConfiguration.token))
|
||||||
components.queryItems = queryItems
|
components.queryItems = queryItems
|
||||||
url = components.url?.absoluteString ?? url
|
url = components.url?.absoluteString ?? url
|
||||||
|
} else if urlHandlingConfiguration.urlAuthDomains.contains(host) {
|
||||||
|
return .single(.urlAuth(url))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for basePath in baseTelegramMePaths {
|
for basePath in baseTelegramMePaths {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"app": "7.5",
|
"app": "7.5.1",
|
||||||
"bazel": "4.0.0",
|
"bazel": "4.0.0",
|
||||||
"xcode": "12.4"
|
"xcode": "12.4"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user