diff --git a/TelegramUI/ChannelAdminController.swift b/TelegramUI/ChannelAdminController.swift index ec77c065f4..3c106981a4 100644 --- a/TelegramUI/ChannelAdminController.swift +++ b/TelegramUI/ChannelAdminController.swift @@ -374,6 +374,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s .canChangeInfo, .canDeleteMessages, .canBanUsers, + .canInviteUsers, .canPinMessages, .canAddAdmins ] diff --git a/TelegramUI/ChannelAdminsController.swift b/TelegramUI/ChannelAdminsController.swift index c3d6020557..22098b12ea 100644 --- a/TelegramUI/ChannelAdminsController.swift +++ b/TelegramUI/ChannelAdminsController.swift @@ -456,7 +456,8 @@ private func channelAdminsControllerEntries(presentationData: PresentationData, } if peer.hasAdminRights(.canAddAdmins) { - entries.append(.adminsInfo(presentationData.theme, presentationData.strings.Channel_Management_AddModeratorHelp)) + let info = isGroup ? presentationData.strings.Group_Management_AddModeratorHelp : presentationData.strings.Channel_Management_AddModeratorHelp + entries.append(.adminsInfo(presentationData.theme, info)) } } } diff --git a/TelegramUI/ChannelMembersController.swift b/TelegramUI/ChannelMembersController.swift index ff949c1e5e..a27c764f3b 100644 --- a/TelegramUI/ChannelMembersController.swift +++ b/TelegramUI/ChannelMembersController.swift @@ -323,9 +323,6 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo let peersPromise = Promise<[RenderedChannelParticipant]?>(nil) let arguments = ChannelMembersControllerArguments(account: account, addMember: { - - let presentationData = account.telegramApplicationContext.currentPresentationData.with {$0} - actionsDisposable.add((peersPromise.get() |> take(1) |> deliverOnMainQueue).start(next: { members in let disabledIds = members?.compactMap({$0.peer.id}) ?? [] let contactsController = ContactMultiselectionController(account: account, mode: .peerSelection, options: [], filters: [.excludeSelf, .disable(disabledIds)]) diff --git a/TelegramUI/ChannelVisibilityController.swift b/TelegramUI/ChannelVisibilityController.swift index aad0b81826..f5280937a5 100644 --- a/TelegramUI/ChannelVisibilityController.swift +++ b/TelegramUI/ChannelVisibilityController.swift @@ -437,7 +437,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa case .privateLink: break case .initialSetup, .generic: - entries.append(.typeHeader(presentationData.theme, isGroup ? presentationData.strings.GroupInfo_GroupType : presentationData.strings.Channel_Edit_LinkItem)) + entries.append(.typeHeader(presentationData.theme, isGroup ? presentationData.strings.Group_Setup_TypeHeader : presentationData.strings.Channel_Edit_LinkItem)) entries.append(.typePublic(presentationData.theme, presentationData.strings.Channel_Setup_TypePublic, selectedType == .publicChannel)) entries.append(.typePrivate(presentationData.theme, presentationData.strings.Channel_Setup_TypePrivate, selectedType == .privateChannel)) @@ -817,6 +817,8 @@ public func channelVisibilityController(account: Account, peerId: PeerId, mode: default: doneEnabled = false } + } else { + doneEnabled = false } } } @@ -829,7 +831,6 @@ public func channelVisibilityController(account: Account, peerId: PeerId, mode: } if let updatedAddressNameValue = updatedAddressNameValue { - let invokeAction: ()->Void = { updateState { state in return state.withUpdatedUpdatingAddressName(true) diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index 159ff40912..2f4ab4c0c1 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -2849,7 +2849,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV self.validLayout = layout - self.chatTitleView?.layoutMetrics = layout.metrics + self.chatTitleView?.layout = layout self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop) @@ -3161,8 +3161,20 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) case .clearHistory: if case let .peer(peerId) = self.chatLocation { + let text: String + if peerId == account.peerId { + text = self.presentationData.strings.Conversation_ClearSelfHistory + } else if peerId.namespace == Namespaces.Peer.SecretChat { + text = self.presentationData.strings.Conversation_ClearSecretHistory + } else if peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudChannel { + text = self.presentationData.strings.Conversation_ClearGroupHistory + } else { + text = self.presentationData.strings.Conversation_ClearPrivateHistory + } + let actionSheet = ActionSheetController(presentationTheme: self.presentationData.theme) actionSheet.setItemGroups([ActionSheetItemGroup(items: [ + ActionSheetTextItem(title: text), ActionSheetButtonItem(title: self.presentationData.strings.Conversation_ClearAll, color: .destructive, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { @@ -3175,6 +3187,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV actionSheet?.dismissAnimated() }) ])]) + self.chatDisplayNode.dismissInput() self.present(actionSheet, in: .window(.root)) } @@ -4180,6 +4193,8 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedBotStartPayload(botStart.payload) }) + default: + break } } else { if let peerId = peerId { @@ -4221,6 +4236,8 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV } case let .withBotStartPayload(botStart): (self.navigationController as? NavigationController)?.pushViewController(ChatController(account: self.account, chatLocation: .peer(peerId), messageId: nil, botStart: botStart)) + default: + break } case .group: (self.navigationController as? NavigationController)?.pushViewController(ChatController(account: self.account, chatLocation: .peer(peerId), messageId: fromMessage?.id, botStart: nil)) @@ -4273,7 +4290,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV self.chatDisplayNode.dismissInput() self.present(controller, in: .window(.root)) } - case let .withBotStartPayload(_): + default: break } } @@ -4290,9 +4307,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV } disposable.set((resolvePeerByName(account: self.account, name: name, ageLimit: 10) |> take(1) |> deliverOnMainQueue).start(next: { [weak self] peerId in if let strongSelf = self { - if let peerId = peerId { - (strongSelf.navigationController as? NavigationController)?.pushViewController(ChatController(account: strongSelf.account, chatLocation: .peer(peerId), messageId: nil)) - } + strongSelf.openResolved(.peer(peerId, .default)) } })) } @@ -4396,6 +4411,48 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV })) } + private func openResolved(_ result: ResolvedUrl) { + openResolvedUrl(result, account: self.account, context: .chat, navigationController: self.navigationController as? NavigationController, openPeer: { [weak self] peerId, navigation in + guard let strongSelf = self else { + return + } + switch navigation { + case let .chat(_, messageId): + if case .peer(peerId) = strongSelf.chatLocation { + if let messageId = messageId { + strongSelf.navigateToMessage(from: nil, to: .id(messageId)) + } + } else if let navigationController = strongSelf.navigationController as? NavigationController { + navigateToChatController(navigationController: navigationController, account: strongSelf.account, chatLocation: .peer(peerId), messageId: messageId, keepStack: .always) + } + case .info: + strongSelf.navigationActionDisposable.set((strongSelf.account.postbox.loadedPeerWithId(peerId) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] peer in + if let strongSelf = self, peer.restrictionText == nil { + if let infoController = peerInfoController(account: strongSelf.account, peer: peer) { + (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) + } + } + })) + case let .withBotStartPayload(startPayload): + if case .peer(peerId) = strongSelf.chatLocation { + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { + $0.updatedBotStartPayload(startPayload.payload) + }) + } else if let navigationController = strongSelf.navigationController as? NavigationController { + navigateToChatController(navigationController: navigationController, account: strongSelf.account, chatLocation: .peer(peerId), botStart: startPayload) + } + default: + break + } + }, present: { [weak self] c, a in + self?.present(c, in: .window(.root), with: a) + }, dismissInput: { [weak self] in + self?.chatDisplayNode.dismissInput() + }) + } + private func openUrl(_ url: String, concealed: Bool) { let openImpl: () -> Void = { [weak self] in guard let strongSelf = self else { @@ -4411,47 +4468,9 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV } disposable.set((resolveUrl(account: strongSelf.account, url: url) |> deliverOnMainQueue).start(next: { [weak self] result in - guard let strongSelf = self else { - return + if let strongSelf = self { + strongSelf.openResolved(result) } - - openResolvedUrl(result, account: strongSelf.account, context: .chat, navigationController: strongSelf.navigationController as? NavigationController, openPeer: { peerId, navigation in - guard let strongSelf = self else { - return - } - switch navigation { - case let .chat(_, messageId): - if case .peer(peerId) = strongSelf.chatLocation { - if let messageId = messageId { - strongSelf.navigateToMessage(from: nil, to: .id(messageId)) - } - } else if let navigationController = strongSelf.navigationController as? NavigationController { - navigateToChatController(navigationController: navigationController, account: strongSelf.account, chatLocation: .peer(peerId), messageId: messageId, keepStack: .always) - } - case .info: - strongSelf.navigationActionDisposable.set((strongSelf.account.postbox.loadedPeerWithId(peerId) - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] peer in - if let strongSelf = self, peer.restrictionText == nil { - if let infoController = peerInfoController(account: strongSelf.account, peer: peer) { - (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) - } - } - })) - case let .withBotStartPayload(startPayload): - if case .peer(peerId) = strongSelf.chatLocation { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { - $0.updatedBotStartPayload(startPayload.payload) - }) - } else if let navigationController = strongSelf.navigationController as? NavigationController { - navigateToChatController(navigationController: navigationController, account: strongSelf.account, chatLocation: .peer(peerId), botStart: startPayload) - } - } - }, present: { c, a in - self?.present(c, in: .window(.root), with: a) - }, dismissInput: { - self?.chatDisplayNode.dismissInput() - }) })) } @@ -4722,72 +4741,75 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV } } } - if canBan { - let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme) - var items: [ActionSheetItem] = [] - - var actions = Set([0]) - - let toggleCheck: (Int, Int) -> Void = { [weak actionSheet] category, itemIndex in - if actions.contains(category) { - actions.remove(category) - } else { - actions.insert(category) - } - actionSheet?.updateItem(groupIndex: 0, itemIndex: itemIndex, { item in - if let item = item as? ActionSheetCheckboxItem { - return ActionSheetCheckboxItem(title: item.title, label: item.label, value: !item.value, action: item.action) - } - return item - }) + + let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme) + var items: [ActionSheetItem] = [] + + var actions = Set([0]) + + let toggleCheck: (Int, Int) -> Void = { [weak actionSheet] category, itemIndex in + if actions.contains(category) { + actions.remove(category) + } else { + actions.insert(category) } - - var itemIndex = 0 - for categoryId in [0, 1, 2, 3] as [Int] { - var title = "" - if categoryId == 0 { - title = strongSelf.presentationData.strings.Conversation_Moderate_Delete - } else if categoryId == 1 { - title = strongSelf.presentationData.strings.Conversation_Moderate_Ban - } else if categoryId == 2 { - title = strongSelf.presentationData.strings.Conversation_Moderate_Report - } else if categoryId == 3 { - title = strongSelf.presentationData.strings.Conversation_Moderate_DeleteAllMessages(author.displayTitle).0 + actionSheet?.updateItem(groupIndex: 0, itemIndex: itemIndex, { item in + if let item = item as? ActionSheetCheckboxItem { + return ActionSheetCheckboxItem(title: item.title, label: item.label, value: !item.value, action: item.action) } - let index = itemIndex - items.append(ActionSheetCheckboxItem(title: title, label: "", value: actions.contains(categoryId), action: { value in - toggleCheck(categoryId, index) - })) - itemIndex += 1 - } - - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Done, action: { [weak self, weak actionSheet] in - actionSheet?.dismissAnimated() - if let strongSelf = self { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) - if actions.contains(3) { - let _ = strongSelf.account.postbox.transaction({ transaction -> Void in - transaction.removeAllMessagesWithAuthor(peerId, authorId: author.id) - }).start() - let _ = clearAuthorHistory(account: strongSelf.account, peerId: peerId, memberId: author.id).start() - } else if actions.contains(0) { - let _ = deleteMessagesInteractively(postbox: strongSelf.account.postbox, messageIds: Array(messageIds), type: .forEveryone).start() - } - if actions.contains(1) { - let _ = removePeerMember(account: strongSelf.account, peerId: peerId, memberId: author.id).start() - } - } - })) - - actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) - ])]) - strongSelf.present(actionSheet, in: .window(.root)) - } else { - strongSelf.presentDeleteMessageOptions(messageIds: messageIds, options: options) + return item + }) } + + var itemIndex = 0 + var categories: [Int] = [0] + if canBan { + categories.append(1) + } + categories.append(contentsOf: [2, 3]) + + for categoryId in categories as [Int] { + var title = "" + if categoryId == 0 { + title = strongSelf.presentationData.strings.Conversation_Moderate_Delete + } else if categoryId == 1 { + title = strongSelf.presentationData.strings.Conversation_Moderate_Ban + } else if categoryId == 2 { + title = strongSelf.presentationData.strings.Conversation_Moderate_Report + } else if categoryId == 3 { + title = strongSelf.presentationData.strings.Conversation_Moderate_DeleteAllMessages(author.displayTitle).0 + } + let index = itemIndex + items.append(ActionSheetCheckboxItem(title: title, label: "", value: actions.contains(categoryId), action: { value in + toggleCheck(categoryId, index) + })) + itemIndex += 1 + } + + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Done, action: { [weak self, weak actionSheet] in + actionSheet?.dismissAnimated() + if let strongSelf = self { + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) + if actions.contains(3) { + let _ = strongSelf.account.postbox.transaction({ transaction -> Void in + transaction.removeAllMessagesWithAuthor(peerId, authorId: author.id) + }).start() + let _ = clearAuthorHistory(account: strongSelf.account, peerId: peerId, memberId: author.id).start() + } else if actions.contains(0) { + let _ = deleteMessagesInteractively(postbox: strongSelf.account.postbox, messageIds: Array(messageIds), type: .forEveryone).start() + } + if actions.contains(1) { + let _ = removePeerMember(account: strongSelf.account, peerId: peerId, memberId: author.id).start() + } + } + })) + + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + strongSelf.present(actionSheet, in: .window(.root)) } })) } diff --git a/TelegramUI/ChatControllerInteraction.swift b/TelegramUI/ChatControllerInteraction.swift index 4cad1d9d2a..3f10e350aa 100644 --- a/TelegramUI/ChatControllerInteraction.swift +++ b/TelegramUI/ChatControllerInteraction.swift @@ -15,6 +15,7 @@ public struct ChatControllerInitialBotStart { } public enum ChatControllerInteractionNavigateToPeer { + case `default` case chat(textInputState: ChatTextInputState?, messageId: MessageId?) case info case withBotStartPayload(ChatControllerInitialBotStart) diff --git a/TelegramUI/ChatInterfaceInputContextPanels.swift b/TelegramUI/ChatInterfaceInputContextPanels.swift index f797331ee7..53d2acaad8 100644 --- a/TelegramUI/ChatInterfaceInputContextPanels.swift +++ b/TelegramUI/ChatInterfaceInputContextPanels.swift @@ -82,14 +82,16 @@ func inputContextPanelForChatPresentationIntefaceState(_ chatPresentationInterfa } } case let .hashtags(results): - if let currentPanel = currentPanel as? HashtagChatInputContextPanelNode { - currentPanel.updateResults(results) - return currentPanel - } else { - let panel = HashtagChatInputContextPanelNode(account: account, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings) - panel.interfaceInteraction = interfaceInteraction - panel.updateResults(results) - return panel + if !results.isEmpty { + if let currentPanel = currentPanel as? HashtagChatInputContextPanelNode { + currentPanel.updateResults(results) + return currentPanel + } else { + let panel = HashtagChatInputContextPanelNode(account: account, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings) + panel.interfaceInteraction = interfaceInteraction + panel.updateResults(results) + return panel + } } case let .emojis(results): if !results.isEmpty { diff --git a/TelegramUI/ChatInterfaceStateContextMenus.swift b/TelegramUI/ChatInterfaceStateContextMenus.swift index 66332dda67..2c2b444dbf 100644 --- a/TelegramUI/ChatInterfaceStateContextMenus.swift +++ b/TelegramUI/ChatInterfaceStateContextMenus.swift @@ -537,7 +537,7 @@ func chatAvailableMessageActions(postbox: Postbox, accountPeerId: PeerId, messag } } if let channel = peer as? TelegramChannel { - if message.flags.contains(.Incoming), channel.adminRights == nil, !channel.flags.contains(.isCreator) { + if message.flags.contains(.Incoming) { optionsMap[id]!.insert(.report) } if channel.hasAdminRights(.canBanUsers), case .group = channel.info { diff --git a/TelegramUI/ChatListController.swift b/TelegramUI/ChatListController.swift index fe14fb2e6d..5098caeeaf 100644 --- a/TelegramUI/ChatListController.swift +++ b/TelegramUI/ChatListController.swift @@ -52,7 +52,6 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie private var didSetup3dTouch = false - private let passcodeDisposable = MetaDisposable() private var passcodeLockTooltipDisposable = MetaDisposable() private var didShowPasscodeLockTooltipController = false @@ -76,7 +75,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie if groupId == nil { self.navigationBar?.item = nil - self.titleView.title = NetworkStatusTitle(text: self.presentationData.strings.DialogList_Title, activity: false, hasProxy: false, connectsViaProxy: false) + self.titleView.title = NetworkStatusTitle(text: self.presentationData.strings.DialogList_Title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false) self.navigationItem.titleView = self.titleView self.tabBarItem.title = self.presentationData.strings.DialogList_Title self.tabBarItem.image = tabImageNone @@ -128,23 +127,33 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie return lhs == rhs }) - self.titleDisposable = (combineLatest(account.networkState |> deliverOnMainQueue, hasProxy |> deliverOnMainQueue)).start(next: { [weak self] state, proxy in + let passcode = account.postbox.combinedView(keys: [.accessChallengeData]) + |> map { view -> (Bool, Bool) in + let data = (view.views[.accessChallengeData] as! AccessChallengeDataView).data + return (data.isLockable, data.autolockDeadline == 0) + } + + self.titleDisposable = (combineLatest(account.networkState |> deliverOnMainQueue, hasProxy |> deliverOnMainQueue, passcode |> deliverOnMainQueue)).start(next: { [weak self] state, proxy, passcode in if let strongSelf = self { let (hasProxy, connectsViaProxy) = proxy + let (isPasscodeSet, isManuallyLocked) = passcode var checkProxy = false switch state { case .waitingForNetwork: - strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_WaitingForNetwork, activity: true, hasProxy: hasProxy, connectsViaProxy: connectsViaProxy) + strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_WaitingForNetwork, activity: true, hasProxy: hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isPasscodeSet, isManuallyLocked: isManuallyLocked) case let .connecting(proxy): - let text = strongSelf.presentationData.strings.State_Connecting + var text = strongSelf.presentationData.strings.State_Connecting + if let layout = strongSelf.validLayout, proxy != nil && layout.metrics.widthClass != .regular && layout.size.width > 320.0 { + text = strongSelf.presentationData.strings.State_ConnectingToProxy + } if let proxy = proxy, proxy.hasConnectionIssues { checkProxy = true } - strongSelf.titleView.title = NetworkStatusTitle(text: text, activity: true, hasProxy: hasProxy, connectsViaProxy: connectsViaProxy) + strongSelf.titleView.title = NetworkStatusTitle(text: text, activity: true, hasProxy: hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isPasscodeSet, isManuallyLocked: isManuallyLocked) case .updating: - strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_Updating, activity: true, hasProxy: hasProxy, connectsViaProxy: connectsViaProxy) + strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_Updating, activity: true, hasProxy: hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isPasscodeSet, isManuallyLocked: isManuallyLocked) case .online: - strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.DialogList_Title, activity: false, hasProxy: hasProxy, connectsViaProxy: connectsViaProxy) + strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.DialogList_Title, activity: false, hasProxy: hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isPasscodeSet, isManuallyLocked: isManuallyLocked) } if checkProxy { if strongSelf.proxyUnavailableTooltipController == nil && !strongSelf.didShowProxyUnavailableTooltipController && strongSelf.isNodeLoaded && strongSelf.displayNode.view.window != nil { @@ -187,13 +196,6 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie } }) - self.passcodeDisposable.set((account.postbox.combinedView(keys: [.accessChallengeData]) |> deliverOnMainQueue).start(next: { [weak self] view in - if let strongSelf = self { - let data = (view.views[.accessChallengeData] as! AccessChallengeDataView).data - strongSelf.titleView.updatePasscode(isPasscodeSet: data.isLockable, isManuallyLocked: data.autolockDeadline == 0) - } - })) - self.titleView.toggleIsLocked = { [weak self] in if let strongSelf = self { let _ = strongSelf.account.postbox.transaction({ transaction -> Void in @@ -241,7 +243,6 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie self.badgeDisposable?.dispose() self.badgeIconDisposable?.dispose() self.passcodeLockTooltipDisposable.dispose() - self.passcodeDisposable.dispose() self.presentationDataDisposable?.dispose() } diff --git a/TelegramUI/ChatListItem.swift b/TelegramUI/ChatListItem.swift index 17b7e4f7ea..f3b2aa1376 100644 --- a/TelegramUI/ChatListItem.swift +++ b/TelegramUI/ChatListItem.swift @@ -941,9 +941,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let contentDeltaX = contentRect.origin.x - (strongSelf.titleNode.frame.minX - titleOffset) strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: contentRect.origin.x + titleOffset, y: contentRect.origin.y + UIScreenPixel), size: titleLayout.size) - let authorNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: contentRect.minY + titleLayout.size.height), size: authorLayout.size) + let authorNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x, y: contentRect.minY + titleLayout.size.height), size: authorLayout.size) strongSelf.authorNode.frame = authorNodeFrame - let textNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: contentRect.minY + titleLayout.size.height - 1.0 + UIScreenPixel + (authorLayout.size.height.isZero ? 0.0 : (authorLayout.size.height - 3.0))), size: textLayout.size) + let textNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x, y: contentRect.minY + titleLayout.size.height - 1.0 + UIScreenPixel + (authorLayout.size.height.isZero ? 0.0 : (authorLayout.size.height - 3.0))), size: textLayout.size) strongSelf.textNode.frame = textNodeFrame var animateInputActivitiesFrame = false @@ -1091,12 +1091,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + titleOffset, y: titleFrame.origin.y), size: titleFrame.size)) let authorFrame = self.authorNode.frame - transition.updateFrame(node: self.authorNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: authorFrame.origin.y), size: authorFrame.size)) + transition.updateFrame(node: self.authorNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x, y: authorFrame.origin.y), size: authorFrame.size)) transition.updateFrame(node: self.inputActivitiesNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x, y: self.inputActivitiesNode.frame.minY), size: self.inputActivitiesNode.bounds.size)) let textFrame = self.textNode.frame - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: textFrame.origin.y), size: textFrame.size)) + transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x, y: textFrame.origin.y), size: textFrame.size)) let dateFrame = self.dateNode.frame transition.updateFrame(node: self.dateNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateFrame.size.width, y: dateFrame.minY), size: dateFrame.size)) @@ -1104,8 +1104,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let statusFrame = self.statusNode.frame transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateFrame.size.width - 2.0 - statusFrame.size.width, y: statusFrame.minY), size: statusFrame.size)) - - var nextTitleIconOrigin: CGFloat = contentRect.origin.x + titleFrame.size.width + 3.0 + titleOffset if let verificationIconNode = self.verificationIconNode { diff --git a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift index 20bdf2226c..d71739ce57 100644 --- a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift +++ b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift @@ -261,7 +261,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } if let secretVideoPlaceholderBackgroundImage = secretVideoPlaceholderBackgroundImage { - strongSelf.secretVideoPlaceholderBackground.image = secretVideoPlaceholderBackgroundImage + strongSelf.secretVideoPlaceholderBackground.image = secretVideoPlaceholderBackgroundImage } strongSelf.telegramFile = updatedFile @@ -285,8 +285,6 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { })) } - - dateAndStatusApply(false) switch layoutData { case let .unconstrained(width): diff --git a/TelegramUI/ChatRecentActionsControllerNode.swift b/TelegramUI/ChatRecentActionsControllerNode.swift index f75742f921..23b0dabe24 100644 --- a/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/TelegramUI/ChatRecentActionsControllerNode.swift @@ -707,12 +707,14 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self?.view.endEditing(true) }) } - case let .peer(peerId): - strongSelf.openPeer(peerId: peerId, peer: nil) - case let .botStart(peerId, payload): + case let .peer(peerId, _): + if let peerId = peerId { + strongSelf.openPeer(peerId: peerId, peer: nil) + } + case .botStart: break //strongSelf.openPeer(peerId: peerId, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive)), fromMessage: nil) - case let .groupBotStart(peerId, payload): + case .groupBotStart: break case let .channelMessage(peerId, messageId): if let navigationController = strongSelf.getNavigationController() { diff --git a/TelegramUI/ChatRecentActionsHistoryTransition.swift b/TelegramUI/ChatRecentActionsHistoryTransition.swift index 3025ffaec6..102e70f712 100644 --- a/TelegramUI/ChatRecentActionsHistoryTransition.swift +++ b/TelegramUI/ChatRecentActionsHistoryTransition.swift @@ -649,6 +649,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { (.canDeleteMessages, self.presentationData.strings.Channel_AdminLog_CanDeleteMessages), (.canEditMessages, self.presentationData.strings.Channel_AdminLog_CanEditMessages), (.canInviteUsers, self.presentationData.strings.Channel_AdminLog_CanInviteUsers), + (.canPinMessages, self.presentationData.strings.Channel_AdminLog_CanPinMessages), (.canAddAdmins, self.presentationData.strings.Channel_AdminLog_CanAddAdmins) ] } else { @@ -656,8 +657,9 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { (.canChangeInfo, self.presentationData.strings.Channel_AdminLog_CanChangeInfo), (.canDeleteMessages, self.presentationData.strings.Channel_AdminLog_CanDeleteMessages), (.canBanUsers, self.presentationData.strings.Channel_AdminLog_CanBanUsers), + (.canInviteUsers, self.presentationData.strings.Channel_AdminLog_CanInviteUsers), + (.canChangeInviteLink, self.presentationData.strings.Channel_AdminLog_CanChangeInviteLink), (.canPinMessages, self.presentationData.strings.Channel_AdminLog_CanPinMessages), - (.canChangeInviteLink, self.presentationData.strings.Channel_AdminLog_CanInviteUsers), (.canAddAdmins, self.presentationData.strings.Channel_AdminLog_CanAddAdmins) ] } diff --git a/TelegramUI/ChatTitleView.swift b/TelegramUI/ChatTitleView.swift index c6ac3b82e8..2cabbc13f8 100644 --- a/TelegramUI/ChatTitleView.swift +++ b/TelegramUI/ChatTitleView.swift @@ -184,13 +184,13 @@ final class ChatTitleView: UIView, NavigationBarTitleView { } } - private func updateNetworkStatusNode(networkState: AccountNetworkState, metrics: LayoutMetrics) { + private func updateNetworkStatusNode(networkState: AccountNetworkState, layout: ContainerViewLayout) { var isOnline = false if case .online = networkState { isOnline = true } - if isOnline || metrics.widthClass == .regular { + if isOnline || layout.metrics.widthClass == .regular { self.contentContainer.isHidden = false if let networkStatusNode = self.networkStatusNode { self.networkStatusNode = nil @@ -207,14 +207,18 @@ final class ChatTitleView: UIView, NavigationBarTitleView { self.insertSubview(statusNode.view, belowSubview: self.button.view) } switch self.networkState { - case .waitingForNetwork: - statusNode.title = self.strings.State_WaitingForNetwork - case .connecting: - statusNode.title = self.strings.State_Connecting - case .updating: - statusNode.title = self.strings.State_Updating - case .online: - break + case .waitingForNetwork: + statusNode.title = self.strings.State_WaitingForNetwork + case let .connecting(proxy): + if proxy != nil && layout.size.width > 320.0 { + statusNode.title = self.strings.State_ConnectingToProxy + } else { + statusNode.title = self.strings.State_Connecting + } + case .updating: + statusNode.title = self.strings.State_Updating + case .online: + break } } @@ -224,15 +228,15 @@ final class ChatTitleView: UIView, NavigationBarTitleView { var networkState: AccountNetworkState = .online(proxy: nil) { didSet { if self.networkState != oldValue { - updateNetworkStatusNode(networkState: self.networkState, metrics: self.layoutMetrics) + updateNetworkStatusNode(networkState: self.networkState, layout: self.layout) } } } - var layoutMetrics: LayoutMetrics = LayoutMetrics() { + var layout: ContainerViewLayout = ContainerViewLayout()() { didSet { - if self.layoutMetrics != oldValue { - updateNetworkStatusNode(networkState: self.networkState, metrics: self.layoutMetrics) + if self.layout != oldValue { + updateNetworkStatusNode(networkState: self.networkState, layout: self.layout) } } } @@ -543,8 +547,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView { self.titleRightIconNode.removeFromSupernode() } + let titleSideInset: CGFloat = 3.0 if size.height > 40.0 { - let titleSize = self.titleNode.updateLayout(CGSize(width: clearBounds.width - leftIconWidth - rightIconWidth, height: size.height)) + let titleSize = self.titleNode.updateLayout(CGSize(width: clearBounds.width - leftIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height)) let infoSize = self.infoNode.updateLayout(clearBounds.size) let typingSize = self.typingNode.updateLayout(clearBounds.size) let titleInfoSpacing: CGFloat = 0.0 @@ -586,7 +591,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + 3.0, y: titleFrame.minY + 7.0), size: image.size) } } else { - let titleSize = self.titleNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - rightIconWidth), height: size.height)) + let titleSize = self.titleNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - rightIconWidth - titleSideInset * 2.0), height: size.height)) let infoSize = self.infoNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0), height: size.height)) let typingSize = self.typingNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0), height: size.height)) diff --git a/TelegramUI/DeclareEncodables.swift b/TelegramUI/DeclareEncodables.swift index 1e2a82914d..ed9bd2c35c 100644 --- a/TelegramUI/DeclareEncodables.swift +++ b/TelegramUI/DeclareEncodables.swift @@ -24,6 +24,7 @@ private var telegramUIDeclaredEncodables: Void = { declareEncodable(ContactSynchronizationSettings.self, f: { ContactSynchronizationSettings(decoder: $0) }) declareEncodable(CachedChannelAdminIds.self, f: { CachedChannelAdminIds(decoder: $0) }) declareEncodable(StickerSettings.self, f: { StickerSettings(decoder: $0) }) + declareEncodable(InstantPagePresentationSettings.self, f: { InstantPagePresentationSettings(decoder: $0) }) return }() diff --git a/TelegramUI/FetchPhotoLibraryImageResource.swift b/TelegramUI/FetchPhotoLibraryImageResource.swift index de7c5cf26f..9fcc47bde1 100644 --- a/TelegramUI/FetchPhotoLibraryImageResource.swift +++ b/TelegramUI/FetchPhotoLibraryImageResource.swift @@ -50,7 +50,7 @@ func fetchPhotoLibraryResource(localIdentifier: String) -> Signal Void let convertToSupergroup: () -> Void let leave: () -> Void + let displayUsernameShareMenu: (String) -> Void let displayUsernameContextMenu: (String) -> Void let displayAboutContextMenu: (String) -> Void let aboutLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void let openStickerPackSetup: () -> Void - init(account: Account, peerId: PeerId, avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext, tapAvatarAction: @escaping () -> Void, changeProfilePhoto: @escaping () -> Void, pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController, ViewControllerPresentationArguments) -> Void, changeNotificationMuteSettings: @escaping () -> Void, changeNotificationSoundSettings: @escaping () -> Void, openPreHistory: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openAdminManagement: @escaping () -> Void, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, updateEditingDescriptionText: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, addMember: @escaping () -> Void, promotePeer: @escaping (RenderedChannelParticipant) -> Void, restrictPeer: @escaping (RenderedChannelParticipant) -> Void, removePeer: @escaping (PeerId) -> Void, convertToSupergroup: @escaping () -> Void, leave: @escaping () -> Void, displayUsernameContextMenu: @escaping (String) -> Void, displayAboutContextMenu: @escaping (String) -> Void, aboutLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, openStickerPackSetup: @escaping () -> Void) { + init(account: Account, peerId: PeerId, avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext, tapAvatarAction: @escaping () -> Void, changeProfilePhoto: @escaping () -> Void, pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController, ViewControllerPresentationArguments) -> Void, changeNotificationMuteSettings: @escaping () -> Void, changeNotificationSoundSettings: @escaping () -> Void, openPreHistory: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openAdminManagement: @escaping () -> Void, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, updateEditingDescriptionText: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, addMember: @escaping () -> Void, promotePeer: @escaping (RenderedChannelParticipant) -> Void, restrictPeer: @escaping (RenderedChannelParticipant) -> Void, removePeer: @escaping (PeerId) -> Void, convertToSupergroup: @escaping () -> Void, leave: @escaping () -> Void, displayUsernameShareMenu: @escaping (String) -> Void, displayUsernameContextMenu: @escaping (String) -> Void, displayAboutContextMenu: @escaping (String) -> Void, aboutLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, openStickerPackSetup: @escaping () -> Void) { self.account = account self.peerId = peerId self.avatarAndNameInfoContext = avatarAndNameInfoContext @@ -57,6 +58,7 @@ private final class GroupInfoArguments { self.removePeer = removePeer self.convertToSupergroup = convertToSupergroup self.leave = leave + self.displayUsernameShareMenu = displayUsernameShareMenu self.displayUsernameContextMenu = displayUsernameContextMenu self.displayAboutContextMenu = displayAboutContextMenu self.aboutLinkAction = aboutLinkAction @@ -77,6 +79,7 @@ private enum GroupInfoSection: ItemListSectionId { private enum GroupInfoEntryTag { case about + case link } private enum GroupInfoMemberStatus { @@ -130,6 +133,9 @@ private struct ParticipantRevealAction: Equatable { private enum GroupInfoEntry: ItemListNodeEntry { case info(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, peer: Peer?, cachedData: CachedPeerData?, state: ItemListAvatarAndNameInfoItemState, updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?) case setGroupPhoto(PresentationTheme, String) + case groupDescriptionSetup(PresentationTheme, String, String) + case groupDescriptionSetupInfo(PresentationTheme, String) + case aboutHeader(PresentationTheme, String) case about(PresentationTheme, String) case link(PresentationTheme, String) case sharedMedia(PresentationTheme, String) @@ -139,7 +145,6 @@ private enum GroupInfoEntry: ItemListNodeEntry { case adminManagement(PresentationTheme, String) case groupTypeSetup(PresentationTheme, String, String) case preHistory(PresentationTheme, String, String) - case groupDescriptionSetup(PresentationTheme, String, String) case groupManagementInfoLabel(PresentationTheme, String, String) case membersAdmins(PresentationTheme, String, String) case membersBlacklist(PresentationTheme, String, String) @@ -150,11 +155,11 @@ private enum GroupInfoEntry: ItemListNodeEntry { var section: ItemListSectionId { switch self { - case .info, .setGroupPhoto: + case .info, .setGroupPhoto, .groupDescriptionSetup, .groupDescriptionSetupInfo: return GroupInfoSection.info.rawValue - case .about, .link: + case .aboutHeader, .about, .link: return GroupInfoSection.about.rawValue - case .groupTypeSetup, .preHistory, .groupDescriptionSetup, .groupManagementInfoLabel: + case .groupTypeSetup, .preHistory, .groupManagementInfoLabel: return GroupInfoSection.infoManagement.rawValue case .sharedMedia, .notifications, .notificationSound, .adminManagement: return GroupInfoSection.sharedMediaAndNotifications.rawValue @@ -171,43 +176,55 @@ private enum GroupInfoEntry: ItemListNodeEntry { static func ==(lhs: GroupInfoEntry, rhs: GroupInfoEntry) -> Bool { switch lhs { - case let .info(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsCachedData, lhsState, lhsUpdatingAvatar): - if case let .info(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsCachedData, rhsState, rhsUpdatingAvatar) = rhs { - if lhsTheme !== rhsTheme { - return false - } - if lhsStrings !== rhsStrings { - return false - } - if lhsDateTimeFormat != rhsDateTimeFormat { - return false - } - if let lhsPeer = lhsPeer, let rhsPeer = rhsPeer { - if !lhsPeer.isEqual(rhsPeer) { + case let .info(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsCachedData, lhsState, lhsUpdatingAvatar): + if case let .info(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsCachedData, rhsState, rhsUpdatingAvatar) = rhs { + if lhsTheme !== rhsTheme { return false } - } else if (lhsPeer == nil) != (rhsPeer != nil) { - return false - } - if let lhsCachedData = lhsCachedData, let rhsCachedData = rhsCachedData { - if !lhsCachedData.isEqual(to: rhsCachedData) { + if lhsStrings !== rhsStrings { return false } - } else if (lhsCachedData != nil) != (rhsCachedData != nil) { - return false - } - if lhsState != rhsState { - return false - } - if lhsUpdatingAvatar != rhsUpdatingAvatar { + if lhsDateTimeFormat != rhsDateTimeFormat { + return false + } + if let lhsPeer = lhsPeer, let rhsPeer = rhsPeer { + if !lhsPeer.isEqual(rhsPeer) { + return false + } + } else if (lhsPeer == nil) != (rhsPeer != nil) { + return false + } + if let lhsCachedData = lhsCachedData, let rhsCachedData = rhsCachedData { + if !lhsCachedData.isEqual(to: rhsCachedData) { + return false + } + } else if (lhsCachedData != nil) != (rhsCachedData != nil) { + return false + } + if lhsState != rhsState { + return false + } + if lhsUpdatingAvatar != rhsUpdatingAvatar { + return false + } + return true + } else { return false } + case let .setGroupPhoto(lhsTheme, lhsText): + if case let .setGroupPhoto(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true } else { return false } - case let .setGroupPhoto(lhsTheme, lhsText): - if case let .setGroupPhoto(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + case let .groupDescriptionSetup(lhsTheme, lhsPlaceholder, lhsText): + if case let .groupDescriptionSetup(rhsTheme, rhsPlaceholder, rhsText) = rhs, lhsTheme === rhsTheme, lhsPlaceholder == rhsPlaceholder, lhsText == rhsText { + return true + } else { + return false + } + case let .groupDescriptionSetupInfo(lhsTheme, lhsText): + if case let .groupDescriptionSetupInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true } else { return false @@ -236,6 +253,12 @@ private enum GroupInfoEntry: ItemListNodeEntry { } else { return false } + case let .aboutHeader(lhsTheme, lhsText): + if case let .aboutHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } case let .about(lhsTheme, lhsText): if case let .about(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true @@ -296,12 +319,6 @@ private enum GroupInfoEntry: ItemListNodeEntry { } else { return false } - case let .groupDescriptionSetup(lhsTheme, lhsPlaceholder, lhsText): - if case let .groupDescriptionSetup(rhsTheme, rhsPlaceholder, rhsText) = rhs, lhsTheme === rhsTheme, lhsPlaceholder == rhsPlaceholder, lhsText == rhsText { - return true - } else { - return false - } case let .groupManagementInfoLabel(lhsTheme, lhsTitle, lhsText): if case let .groupManagementInfoLabel(rhsTheme, rhsTitle, rhsText) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsText == rhsText { return true @@ -390,34 +407,38 @@ private enum GroupInfoEntry: ItemListNodeEntry { return 0 case .setGroupPhoto: return 1 - case .about: - return 2 - case .link: - return 3 - case .adminManagement: - return 4 - case .groupTypeSetup: - return 5 - case .preHistory: - return 6 case .groupDescriptionSetup: + return 2 + case .groupDescriptionSetupInfo: + return 3 + case .aboutHeader: + return 4 + case .about: + return 5 + case .link: + return 6 + case .adminManagement: return 7 - case .notifications: + case .groupTypeSetup: return 8 - case .notificationSound: + case .preHistory: return 9 - case .stickerPack: + case .notifications: return 10 - case .sharedMedia: + case .notificationSound: return 11 - case .groupManagementInfoLabel: + case .stickerPack: return 12 - case .membersAdmins: + case .sharedMedia: return 13 - case .membersBlacklist: + case .groupManagementInfoLabel: return 14 - case .addMember: + case .membersAdmins: return 15 + case .membersBlacklist: + return 16 + case .addMember: + return 17 case let .member(_, _, _, index, _, _, _, _, _, _, _, _): return 20 + index case .convertToSupergroup: @@ -443,6 +464,8 @@ private enum GroupInfoEntry: ItemListNodeEntry { return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { arguments.changeProfilePhoto() }) + case let .aboutHeader(theme, text): + return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) case let .about(theme, text): return ItemListMultilineTextItem(theme: theme, text: text, enabledEntitiyTypes: [.url, .mention, .hashtag], sectionId: self.section, style: .blocks, longTapAction: { arguments.displayAboutContextMenu(text) @@ -451,8 +474,10 @@ private enum GroupInfoEntry: ItemListNodeEntry { }, tag: GroupInfoEntryTag.about) case let .link(theme, url): return ItemListActionItem(theme: theme, title: url, kind: .neutral, alignment: .natural, sectionId: self.section, style: .blocks, action: { + arguments.displayUsernameShareMenu(url) + }, longTapAction: { arguments.displayUsernameContextMenu(url) - }) + }, tag: GroupInfoEntryTag.link) case let .notifications(theme, title, text): return ItemListDisclosureItem(theme: theme, title: title, label: text, sectionId: self.section, style: .blocks, action: { arguments.changeNotificationMuteSettings() @@ -491,6 +516,8 @@ private enum GroupInfoEntry: ItemListNodeEntry { }, action: { }) + case let .groupDescriptionSetupInfo(theme, text): + return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) case let .membersAdmins(theme, title, text): return ItemListDisclosureItem(theme: theme, title: title, label: text, sectionId: self.section, style: .blocks, action: { arguments.pushController(channelAdminsController(account: arguments.account, peerId: arguments.peerId)) @@ -503,7 +530,7 @@ private enum GroupInfoEntry: ItemListNodeEntry { let label: String? switch memberStatus { case .admin: - label = strings.ChatAdmins_AdminLabel + label = strings.GroupInfo_LabelAdmin case .member: label = nil } @@ -689,7 +716,9 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa var canAddMembers = false var isPublic = false var isCreator = false + var isGroup = false if let group = view.peers[view.peerId] as? TelegramGroup { + isGroup = true if case .creator = group.role { isCreator = true } @@ -714,6 +743,9 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa } } } else if let channel = view.peers[view.peerId] as? TelegramChannel { + if case .group = channel.info { + isGroup = true + } highlightAdmins = true isPublic = channel.username != nil isCreator = channel.flags.contains(.isCreator) @@ -749,6 +781,8 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa if let editingState = state.editingState { if canEditGroupInfo { entries.append(GroupInfoEntry.setGroupPhoto(presentationData.theme, presentationData.strings.GroupInfo_SetGroupPhoto)) + entries.append(GroupInfoEntry.groupDescriptionSetup(presentationData.theme, presentationData.strings.Channel_Edit_AboutItem, editingState.editingDescriptionText)) + entries.append(GroupInfoEntry.groupDescriptionSetupInfo(presentationData.theme, isGroup ? presentationData.strings.Group_About_Help : presentationData.strings.Channel_About_Help)) } if let group = view.peers[view.peerId] as? TelegramGroup { @@ -765,9 +799,6 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa entries.append(GroupInfoEntry.preHistory(presentationData.theme, presentationData.strings.GroupInfo_GroupHistory, cachedData.flags.contains(.preHistoryEnabled) ? presentationData.strings.GroupInfo_GroupHistoryVisible : presentationData.strings.GroupInfo_GroupHistoryHidden)) } } - if canEditGroupInfo { - entries.append(GroupInfoEntry.groupDescriptionSetup(presentationData.theme, presentationData.strings.Channel_Edit_AboutItem, editingState.editingDescriptionText)) - } entries.append(GroupInfoEntry.notifications(presentationData.theme, presentationData.strings.GroupInfo_Notifications, notificationsText)) entries.append(GroupInfoEntry.notificationSound(presentationData.theme, presentationData.strings.GroupInfo_Sound, localizedPeerNotificationSoundString(strings: presentationData.strings, sound: peerNotificationSettings.messageSound, default: globalNotificationSettings.effective.groupChats.sound))) @@ -801,6 +832,7 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa } else { if let cachedChannelData = view.cachedData as? CachedChannelData { if let about = cachedChannelData.about, !about.isEmpty { + entries.append(.aboutHeader(presentationData.theme, presentationData.strings.Channel_About_Title.uppercased())) entries.append(.about(presentationData.theme, about)) } if let peer = view.peers[view.peerId] as? TelegramChannel, let username = peer.username, !username.isEmpty { @@ -1142,7 +1174,7 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl let avatarAndNameInfoContext = ItemListAvatarAndNameInfoItemContext() var updateHiddenAvatarImpl: (() -> Void)? - var displayAboutContextMenuImpl: ((String) -> Void)? + var displayCopyContextMenuImpl: ((String, GroupInfoEntryTag) -> Void)? var aboutLinkActionImpl: ((TextLinkItemActionType, TextLinkItem) -> Void)? let arguments = GroupInfoArguments(account: account, peerId: peerId, avatarAndNameInfoContext: avatarAndNameInfoContext, tapAvatarAction: { @@ -1339,7 +1371,6 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl } } }, addMember: { - let members: Promise<[PeerId]> = Promise() if peerId.namespace == Namespaces.Peer.CloudChannel { var membersDisposable: Disposable? @@ -1375,9 +1406,6 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl contactsController = ContactMultiselectionController(account: account, mode: .peerSelection, options: options, filters: [.excludeSelf, .disable(recentIds)]) } - - - confirmationImpl = { [weak contactsController] peerId in return account.postbox.loadedPeerWithId(peerId) |> deliverOnMainQueue @@ -1629,24 +1657,30 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl let dismissAction: () -> Void = { [weak controller] in controller?.dismissAnimated() } - controller.setItemGroups([ - ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: presentationData.strings.DialogList_DeleteConversationConfirmation, color: .destructive, action: { - dismissAction() - let _ = (removePeerChat(postbox: account.postbox, peerId: peerId, reportChatSpam: false) - |> deliverOnMainQueue).start(completed: { - popToRootImpl?() - }) + + var items: [ActionSheetItem] = [] + if peerId.namespace == Namespaces.Peer.CloudGroup { + items.append(ActionSheetTextItem(title: presentationData.strings.GroupInfo_DeleteAndExitConfirmation)) + } + items.append(ActionSheetButtonItem(title: presentationData.strings.DialogList_DeleteConversationConfirmation, color: .destructive, action: { + dismissAction() + let _ = (removePeerChat(postbox: account.postbox, peerId: peerId, reportChatSpam: false) + |> deliverOnMainQueue).start(completed: { + popToRootImpl?() }) - ]), + })) + controller.setItemGroups([ + ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ]) presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - }, displayUsernameContextMenu: { text in + }, displayUsernameShareMenu: { text in let shareController = ShareController(account: account, subject: .url(text)) presentControllerImpl?(shareController, nil) + }, displayUsernameContextMenu: { text in + displayCopyContextMenuImpl?(text, .link) }, displayAboutContextMenu: { text in - displayAboutContextMenuImpl?(text) + displayCopyContextMenuImpl?(text, .about) }, aboutLinkAction: { action, itemLink in aboutLinkActionImpl?(action, itemLink) }, openStickerPackSetup: { @@ -1801,19 +1835,26 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl popToRootImpl = { [weak controller] in (controller?.navigationController as? NavigationController)?.popToRoot(animated: true) } - displayAboutContextMenuImpl = { [weak controller] text in + displayCopyContextMenuImpl = { [weak controller] text, tag in if let strongController = controller { let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } var resultItemNode: ListViewItemNode? let _ = strongController.frameForItemNode({ itemNode in + var itemTag: GroupInfoEntryTag? = nil if let itemNode = itemNode as? ItemListMultilineTextItemNode { if let tag = itemNode.tag as? GroupInfoEntryTag { - if tag == .about { - resultItemNode = itemNode - return true - } + itemTag = tag } } + else if let itemNode = itemNode as? ItemListActionItemNode { + if let tag = itemNode.tag as? GroupInfoEntryTag { + itemTag = tag + } + } + if itemTag == tag { + resultItemNode = itemNode + return true + } return false }) if let resultItemNode = resultItemNode { @@ -1871,16 +1912,31 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl } func handlePeerInfoAboutTextAction(account: Account, peerId: PeerId, navigateDisposable: MetaDisposable, controller: ViewController, action: TextLinkItemActionType, itemLink: TextLinkItem) { - let openPeerImpl: (PeerId) -> Void = { [weak controller] peerId in - let peerSignal: Signal - peerSignal = account.postbox.loadedPeerWithId(peerId) |> map(Optional.init) - navigateDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { peer in - if let controller = controller, let peer = peer { - if let infoController = peerInfoController(account: account, peer: peer) { - (controller.navigationController as? NavigationController)?.pushViewController(infoController) - } + let presentImpl: (ViewController, Any?) -> Void = { controllerToPresent, _ in + controller.present(controllerToPresent, in: .window(.root)) + } + + let openResolvedPeerImpl: (PeerId?, ChatControllerInteractionNavigateToPeer) -> Void = { [weak controller] peerId, navigation in + openResolvedUrl(.peer(peerId, navigation), account: account, navigationController: (controller?.navigationController as? NavigationController), openPeer: { (peerId, navigation) in + switch navigation { + case let .chat(_, messageId): + if let navigationController = controller?.navigationController as? NavigationController { + navigateToChatController(navigationController: navigationController, account: account, chatLocation: .peer(peerId), messageId: messageId, keepStack: .always) + } + case .info: + let peerSignal: Signal + peerSignal = account.postbox.loadedPeerWithId(peerId) |> map(Optional.init) + navigateDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { peer in + if let controller = controller, let peer = peer { + if let infoController = peerInfoController(account: account, peer: peer) { + (controller.navigationController as? NavigationController)?.pushViewController(infoController) + } + } + })) + default: + break } - })) + }, present: presentImpl, dismissInput: {}) } let openLinkImpl: (String) -> Void = { [weak controller] url in @@ -1889,8 +1945,8 @@ func handlePeerInfoAboutTextAction(account: Account, peerId: PeerId, navigateDis switch result { case let .externalUrl(url): account.telegramApplicationContext.applicationBindings.openUrl(url) - case let .peer(peerId): - openPeerImpl(peerId) + case let .peer(peerId, _): + openResolvedPeerImpl(peerId, .default) case let .channelMessage(peerId, messageId): if let navigationController = controller.navigationController as? NavigationController { navigateToChatController(navigationController: navigationController, account: account, chatLocation: .peer(peerId), messageId: messageId) @@ -1901,7 +1957,7 @@ func handlePeerInfoAboutTextAction(account: Account, peerId: PeerId, navigateDis (controller.navigationController as? NavigationController)?.pushViewController(InstantPageController(account: account, webPage: webpage, anchor: anchor)) case let .join(link): controller.present(JoinLinkPreviewController(account: account, link: link, navigateToPeer: { peerId in - openPeerImpl(peerId) + openResolvedPeerImpl(peerId, .chat(textInputState: nil, messageId: nil)) }), in: .window(.root)) default: break @@ -1910,11 +1966,9 @@ func handlePeerInfoAboutTextAction(account: Account, peerId: PeerId, navigateDis })) } - let openPeerMentionImpl: (String) -> Void = { [weak controller] mention in + let openPeerMentionImpl: (String) -> Void = { mention in navigateDisposable.set((resolvePeerByName(account: account, name: mention, ageLimit: 10) |> take(1) |> deliverOnMainQueue).start(next: { peerId in - if let controller = controller, let peerId = peerId { - (controller.navigationController as? NavigationController)?.pushViewController(ChatController(account: account, chatLocation: .peer(peerId), messageId: nil)) - } + openResolvedPeerImpl(peerId, .default) })) } diff --git a/TelegramUI/InstantPageControllerNode.swift b/TelegramUI/InstantPageControllerNode.swift index 4e3b17f95b..a7f2ebfa73 100644 --- a/TelegramUI/InstantPageControllerNode.swift +++ b/TelegramUI/InstantPageControllerNode.swift @@ -752,7 +752,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } } }) - case .withBotStartPayload: + default: break } }, present: { c, a in diff --git a/TelegramUI/ItemListActionItem.swift b/TelegramUI/ItemListActionItem.swift index 9a9b22cea8..133564a3e4 100644 --- a/TelegramUI/ItemListActionItem.swift +++ b/TelegramUI/ItemListActionItem.swift @@ -23,9 +23,10 @@ class ItemListActionItem: ListViewItem, ItemListItem { let sectionId: ItemListSectionId let style: ItemListStyle let action: () -> Void + let longTapAction: (() -> Void)? let tag: Any? - init(theme: PresentationTheme, title: String, kind: ItemListActionKind, alignment: ItemListActionAlignment, sectionId: ItemListSectionId, style: ItemListStyle, action: @escaping () -> Void, tag: Any? = nil) { + init(theme: PresentationTheme, title: String, kind: ItemListActionKind, alignment: ItemListActionAlignment, sectionId: ItemListSectionId, style: ItemListStyle, action: @escaping () -> Void, longTapAction: (() -> Void)? = nil, tag: Any? = nil) { self.theme = theme self.title = title self.kind = kind @@ -33,6 +34,7 @@ class ItemListActionItem: ListViewItem, ItemListItem { self.sectionId = sectionId self.style = style self.action = action + self.longTapAction = longTapAction self.tag = tag } @@ -281,4 +283,12 @@ class ItemListActionItemNode: ListViewItemNode { override func animateRemoved(_ currentTimestamp: Double, duration: Double) { self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) } + + override func longTapped() { + self.item?.longTapAction?() + } + + override var canBeLongTapped: Bool { + return self.item?.longTapAction != nil + } } diff --git a/TelegramUI/ItemListAvatarAndNameItem.swift b/TelegramUI/ItemListAvatarAndNameItem.swift index 93e629b77a..11c5187d25 100644 --- a/TelegramUI/ItemListAvatarAndNameItem.swift +++ b/TelegramUI/ItemListAvatarAndNameItem.swift @@ -388,7 +388,12 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite additionalTitleInset += 3.0 + verificationIconImage.size.width } - let (nameNodeLayout, nameNodeApply) = layoutNameNode(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayTitle.composedDisplayTitle(strings: item.strings), font: nameFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: baseWidth - 20 - 94.0 - (item.call != nil ? 36.0 : 0.0) - additionalTitleInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + var nameMaximumNumberOfLines = 1 + if case .generic = item.mode { + nameMaximumNumberOfLines = 2 + } + + let (nameNodeLayout, nameNodeApply) = layoutNameNode(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayTitle.composedDisplayTitle(strings: item.strings), font: nameFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: nameMaximumNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: baseWidth - 20 - 94.0 - (item.call != nil ? 36.0 : 0.0) - additionalTitleInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) var statusText: String = "" let statusColor: UIColor diff --git a/TelegramUI/ItemListPeerItem.swift b/TelegramUI/ItemListPeerItem.swift index 0fa3622b66..bc6a837d5d 100644 --- a/TelegramUI/ItemListPeerItem.swift +++ b/TelegramUI/ItemListPeerItem.swift @@ -382,7 +382,7 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { break case let .text(text): labelAttributedString = NSAttributedString(string: text, font: labelFont, textColor: item.theme.list.itemSecondaryTextColor) - rightInset += 10.0 + labelInset += 15.0 case let .disclosure(text): if let currentLabelArrowNode = currentLabelArrowNode { updatedLabelArrowNode = currentLabelArrowNode @@ -394,14 +394,14 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { arrowNode.image = PresentationResourcesItemList.disclosureArrowImage(item.theme) updatedLabelArrowNode = arrowNode } - labelInset += 30.0 + labelInset += 40.0 labelAttributedString = NSAttributedString(string: text, font: labelDisclosureFont, textColor: item.theme.list.itemSecondaryTextColor) } let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: labelAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 12.0 - labelLayout.size.width - editingOffset - rightInset - labelInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - let (statusLayout, statusApply) = makeStatusLayout(TextNodeLayoutArguments(attributedString: statusAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - (labelLayout.size.width > 0.0 ? (labelLayout.size.width) + 15.0 : 0.0) - editingOffset - rightInset - labelInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (statusLayout, statusApply) = makeStatusLayout(TextNodeLayoutArguments(attributedString: statusAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset - labelInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let insets = itemListNeighborsGroupedInsets(neighbors) let contentSize = CGSize(width: params.width, height: 48.0) diff --git a/TelegramUI/MapResources.swift b/TelegramUI/MapResources.swift index 25649c5e2b..0bc93565b7 100644 --- a/TelegramUI/MapResources.swift +++ b/TelegramUI/MapResources.swift @@ -99,7 +99,7 @@ func fetchMapSnapshotResource(resource: MapSnapshotMediaResource) -> Signal Void) { diff --git a/TelegramUI/NetworkStatusTitleView.swift b/TelegramUI/NetworkStatusTitleView.swift index 7af8a99cd5..173e347830 100644 --- a/TelegramUI/NetworkStatusTitleView.swift +++ b/TelegramUI/NetworkStatusTitleView.swift @@ -8,6 +8,8 @@ struct NetworkStatusTitle: Equatable { let activity: Bool let hasProxy: Bool let connectsViaProxy: Bool + let isPasscodeSet: Bool + let isManuallyLocked: Bool } final class NetworkStatusTitleView: UIView, NavigationBarTitleView, NavigationBarTitleTransitionNode { @@ -20,7 +22,7 @@ final class NetworkStatusTitleView: UIView, NavigationBarTitleView, NavigationBa private var validLayout: (CGSize, CGRect)? - var title: NetworkStatusTitle = NetworkStatusTitle(text: "", activity: false, hasProxy: false, connectsViaProxy: false) { + var title: NetworkStatusTitle = NetworkStatusTitle(text: "", activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false) { didSet { if self.title != oldValue { self.titleNode.attributedText = NSAttributedString(string: title.text, font: Font.bold(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor) @@ -32,7 +34,17 @@ final class NetworkStatusTitleView: UIView, NavigationBarTitleView, NavigationBa } self.proxyNode.isHidden = !self.title.hasProxy self.proxyButton.isHidden = !self.title.hasProxy - + + if self.title.isPasscodeSet && !self.title.activity { + self.buttonView.isHidden = false + self.lockView.isHidden = false + self.lockView.setIsLocked(self.title.isManuallyLocked, theme: self.theme, animated: !self.bounds.size.width.isZero) + } else { + self.buttonView.isHidden = true + self.lockView.isHidden = true + self.lockView.setIsLocked(false, theme: self.theme, animated: false) + } + self.setNeedsLayout() } } @@ -187,25 +199,6 @@ final class NetworkStatusTitleView: UIView, NavigationBarTitleView, NavigationBa self.activityIndicator.frame = CGRect(origin: CGPoint(x: titleFrame.minX - indicatorSize.width - 6.0, y: titleFrame.minY - 1.0), size: indicatorSize) } - func updatePasscode(isPasscodeSet: Bool, isManuallyLocked: Bool) { - if self.isPasscodeSet == isPasscodeSet && self.isManuallyLocked == isManuallyLocked { - return - } - - self.isPasscodeSet = isPasscodeSet - self.isManuallyLocked = isManuallyLocked - - if isPasscodeSet { - self.buttonView.isHidden = false - self.lockView.isHidden = false - self.lockView.setIsLocked(isManuallyLocked, theme: self.theme, animated: !self.bounds.size.width.isZero) - } else { - self.buttonView.isHidden = true - self.lockView.isHidden = true - self.lockView.setIsLocked(false, theme: self.theme, animated: false) - } - } - @objc private func buttonPressed() { self.toggleIsLocked?() } diff --git a/TelegramUI/NetworkUsageStatsController.swift b/TelegramUI/NetworkUsageStatsController.swift index 08576b4623..2dbccbebf9 100644 --- a/TelegramUI/NetworkUsageStatsController.swift +++ b/TelegramUI/NetworkUsageStatsController.swift @@ -303,24 +303,24 @@ private func networkUsageStatsControllerEntries(presentationData: PresentationDa switch section { case .cellular: entries.append(.messagesHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_GeneralDataSection)) - entries.append(.messagesSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(Int(stats.generic.cellular.outgoing)))) - entries.append(.messagesReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(Int(stats.generic.cellular.incoming)))) + entries.append(.messagesSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.generic.cellular.outgoing))) + entries.append(.messagesReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.generic.cellular.incoming))) entries.append(.imageHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaImageDataSection)) - entries.append(.imageSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(Int(stats.image.cellular.outgoing)))) - entries.append(.imageReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(Int(stats.image.cellular.incoming)))) + entries.append(.imageSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.image.cellular.outgoing))) + entries.append(.imageReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.image.cellular.incoming))) entries.append(.videoHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaVideoDataSection)) - entries.append(.videoSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(Int(stats.video.cellular.outgoing)))) - entries.append(.videoReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(Int(stats.video.cellular.incoming)))) + entries.append(.videoSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.video.cellular.outgoing))) + entries.append(.videoReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.video.cellular.incoming))) entries.append(.audioHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaAudioDataSection)) - entries.append(.audioSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(Int(stats.audio.cellular.outgoing)))) - entries.append(.audioReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(Int(stats.audio.cellular.incoming)))) + entries.append(.audioSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.audio.cellular.outgoing))) + entries.append(.audioReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.audio.cellular.incoming))) entries.append(.fileHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaDocumentDataSection)) - entries.append(.fileSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(Int(stats.file.cellular.outgoing)))) - entries.append(.fileReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(Int(stats.file.cellular.incoming)))) + entries.append(.fileSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.file.cellular.outgoing))) + entries.append(.fileReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.file.cellular.incoming))) entries.append(.callHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_CallDataSection)) entries.append(.callSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(0))) @@ -337,24 +337,24 @@ private func networkUsageStatsControllerEntries(presentationData: PresentationDa } case .wifi: entries.append(.messagesHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_GeneralDataSection)) - entries.append(.messagesSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(Int(stats.generic.wifi.outgoing)))) - entries.append(.messagesReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(Int(stats.generic.wifi.incoming)))) + entries.append(.messagesSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.generic.wifi.outgoing))) + entries.append(.messagesReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.generic.wifi.incoming))) entries.append(.imageHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaImageDataSection)) - entries.append(.imageSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(Int(stats.image.wifi.outgoing)))) - entries.append(.imageReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(Int(stats.image.wifi.incoming)))) + entries.append(.imageSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.image.wifi.outgoing))) + entries.append(.imageReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.image.wifi.incoming))) entries.append(.videoHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaVideoDataSection)) - entries.append(.videoSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(Int(stats.video.wifi.outgoing)))) - entries.append(.videoReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(Int(stats.video.wifi.incoming)))) + entries.append(.videoSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.video.wifi.outgoing))) + entries.append(.videoReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.video.wifi.incoming))) entries.append(.audioHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaAudioDataSection)) - entries.append(.audioSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(Int(stats.audio.wifi.outgoing)))) - entries.append(.audioReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(Int(stats.audio.wifi.incoming)))) + entries.append(.audioSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.audio.wifi.outgoing))) + entries.append(.audioReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.audio.wifi.incoming))) entries.append(.fileHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaDocumentDataSection)) - entries.append(.fileSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(Int(stats.file.wifi.outgoing)))) - entries.append(.fileReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(Int(stats.file.wifi.incoming)))) + entries.append(.fileSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.file.wifi.outgoing))) + entries.append(.fileReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.file.wifi.incoming))) entries.append(.callHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_CallDataSection)) entries.append(.callSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(0))) diff --git a/TelegramUI/OpenResolvedUrl.swift b/TelegramUI/OpenResolvedUrl.swift index f6b11436b3..87e1cc814b 100644 --- a/TelegramUI/OpenResolvedUrl.swift +++ b/TelegramUI/OpenResolvedUrl.swift @@ -4,12 +4,33 @@ import Postbox import Display import SwiftSignalKit +private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer { + if case .default = navigation { + if let peerId = peerId { + if peerId.namespace == Namespaces.Peer.CloudUser { + return .info + } else { + return .chat(textInputState: nil, messageId: nil) + } + } else { + return .info + } + } else { + return navigation + } +} + func openResolvedUrl(_ resolvedUrl: ResolvedUrl, account: Account, context: OpenURLContext = .generic, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, present: (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void) { switch resolvedUrl { case let .externalUrl(url): openExternalUrl(account: account, context: context, url: url, presentationData: account.telegramApplicationContext.currentPresentationData.with { $0 }, applicationContext: account.telegramApplicationContext, navigationController: navigationController, dismissInput: dismissInput) - case let .peer(peerId): - openPeer(peerId, .chat(textInputState: nil, messageId: nil)) + case let .peer(peerId, navigation): + if let peerId = peerId { + openPeer(peerId, defaultNavigationForPeerId(peerId, navigation: navigation)) + } else { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Resolve_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + } case let .botStart(peerId, payload): openPeer(peerId, .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive))) case let .groupBotStart(botPeerId, payload): diff --git a/TelegramUI/OpenUrl.swift b/TelegramUI/OpenUrl.swift index 8a401f310d..e09eb60dfd 100644 --- a/TelegramUI/OpenUrl.swift +++ b/TelegramUI/OpenUrl.swift @@ -445,6 +445,8 @@ public func openExternalUrl(account: Account, context: OpenURLContext = .generic navigationController.view.window?.rootViewController?.dismiss(animated: true, completion: nil) navigateToChatController(navigationController: navigationController, account: account, chatLocation: .peer(peerId), botStart: payload) } + default: + break } }, present: { c, a in if let navigationController = navigationController { diff --git a/TelegramUI/PeerMediaCollectionController.swift b/TelegramUI/PeerMediaCollectionController.swift index 98cdfe56f9..e0192536bb 100644 --- a/TelegramUI/PeerMediaCollectionController.swift +++ b/TelegramUI/PeerMediaCollectionController.swift @@ -630,6 +630,8 @@ public class PeerMediaCollectionController: TelegramController { if let navigationController = strongSelf.navigationController as? NavigationController { navigateToChatController(navigationController: navigationController, account: strongSelf.account, chatLocation: .peer(peerId), botStart: startPayload) } + default: + break } } }, present: { c, a in diff --git a/TelegramUI/ShareController.swift b/TelegramUI/ShareController.swift index b7beee563e..09d441bb87 100644 --- a/TelegramUI/ShareController.swift +++ b/TelegramUI/ShareController.swift @@ -228,13 +228,24 @@ public final class ShareController: ViewController { self.defaultAction = ShareControllerAction(title: self.presentationData.strings.Preview_SaveToCameraRoll, action: { [weak self] in self?.saveToCameraRoll(messages: messages) }) - } else if messages.count == 1, let message = messages.first { - if let showInChat = showInChat { + } else if let message = messages.first { + let groupingKey: Int64? = message.groupingKey + var sameGroupingKey = groupingKey != nil + if sameGroupingKey { + for message in messages { + if message.groupingKey != groupingKey { + sameGroupingKey = false + break + } + } + } + if let showInChat = showInChat, messages.count == 1 { self.defaultAction = ShareControllerAction(title: self.presentationData.strings.SharedMedia_ViewInChat, action: { [weak self] in self?.controllerNode.cancel?() showInChat(message) }) - } else if let chatPeer = message.peers[message.id.peerId] as? TelegramChannel { + } + if let chatPeer = message.peers[message.id.peerId] as? TelegramChannel, messages.count == 1 || sameGroupingKey { if message.id.namespace == Namespaces.Message.Cloud, let addressName = chatPeer.addressName, !addressName.isEmpty { self.defaultAction = ShareControllerAction(title: self.presentationData.strings.ShareMenu_CopyShareLink, action: { [weak self] in UIPasteboard.general.string = "https://t.me/\(addressName)/\(message.id.id)" diff --git a/TelegramUI/StickerPaneSearchContainerNode.swift b/TelegramUI/StickerPaneSearchContainerNode.swift index da93e0ac37..fc7e74a3f1 100644 --- a/TelegramUI/StickerPaneSearchContainerNode.swift +++ b/TelegramUI/StickerPaneSearchContainerNode.swift @@ -23,17 +23,17 @@ final class StickerPaneSearchInteraction { } private enum StickerSearchEntryId: Equatable, Hashable { - case sticker(String, Int64) + case sticker(String?, Int64) case global(ItemCollectionId) } private enum StickerSearchEntry: Identifiable, Comparable { - case sticker(index: Int, code: String, stickerItem: FoundStickerItem, theme: PresentationTheme) + case sticker(index: Int, code: String?, stickerItem: FoundStickerItem, theme: PresentationTheme) case global(index: Int, info: StickerPackCollectionInfo, topItems: [StickerPackItem], installed: Bool) var stableId: StickerSearchEntryId { switch self { - case let .sticker(index, code, stickerItem, _): + case let .sticker(_, code, stickerItem, _): return .sticker(code, stickerItem.file.fileId.id) case let .global(_, info, _, _): return .global(info.id) @@ -252,20 +252,27 @@ final class StickerPaneSearchContainerNode: ASDisplayNode { return } - let signal: Signal<([(String, FoundStickerItem)], FoundStickerSets, Bool, FoundStickerSets?)?, NoError> + let signal: Signal<([(String?, FoundStickerItem)], FoundStickerSets, Bool, FoundStickerSets?)?, NoError> if !text.isEmpty { - let stickers: Signal<[(String, FoundStickerItem)], NoError> = Signal { subscriber in - var signals: [Signal<(String, [FoundStickerItem]), NoError>] = [] - for entry in TGEmojiSuggestions.suggestions(forQuery: text.lowercased()) { - if let entry = entry as? TGAlphacodeEntry { - signals.append(searchStickers(account: account, query: entry.emoji) - |> take(1) - |> map { (entry.emoji, $0) }) + let stickers: Signal<[(String?, FoundStickerItem)], NoError> = Signal { subscriber in + var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = [] + + if text.isSingleEmoji { + signals.append(searchStickers(account: account, query: text.firstEmoji) + |> take(1) + |> map { (nil, $0) }) + } else { + for entry in TGEmojiSuggestions.suggestions(forQuery: text.lowercased()) { + if let entry = entry as? TGAlphacodeEntry { + signals.append(searchStickers(account: account, query: entry.emoji) + |> take(1) + |> map { (entry.emoji, $0) }) + } } } return combineLatest(signals).start(next: { results in - var result: [(String, FoundStickerItem)] = [] + var result: [(String?, FoundStickerItem)] = [] for (emoji, stickers) in results { for sticker in stickers { result.append((emoji, sticker)) @@ -292,7 +299,7 @@ final class StickerPaneSearchContainerNode: ASDisplayNode { }) } signal = combineLatest(stickers, packs) - |> map { stickers, packs -> ([(String, FoundStickerItem)], FoundStickerSets, Bool, FoundStickerSets?)? in + |> map { stickers, packs -> ([(String?, FoundStickerItem)], FoundStickerSets, Bool, FoundStickerSets?)? in return (stickers, packs.0, packs.1, packs.2) } strongSelf.searchBar.activity = true @@ -322,7 +329,7 @@ final class StickerPaneSearchContainerNode: ASDisplayNode { var index = 0 for (code, sticker) in stickers { - entries.append(StickerSearchEntry.sticker(index: index, code: code, stickerItem: sticker, theme: theme)) + entries.append(.sticker(index: index, code: code, stickerItem: sticker, theme: theme)) index += 1 } for (collectionId, info, _, installed) in packs.infos { diff --git a/TelegramUI/StickerPaneSearchStickerItem.swift b/TelegramUI/StickerPaneSearchStickerItem.swift index 44c9d25a5a..77e1bcfc56 100644 --- a/TelegramUI/StickerPaneSearchStickerItem.swift +++ b/TelegramUI/StickerPaneSearchStickerItem.swift @@ -61,20 +61,25 @@ final class StickerPaneSearchStickerSectionNode: ASDisplayNode { final class StickerPaneSearchStickerItem: GridItem { let account: Account - let code: String + let code: String? let stickerItem: FoundStickerItem let selected: () -> Void let inputNodeInteraction: ChatMediaInputNodeInteraction let section: GridSection? - init(account: Account, code: String, stickerItem: FoundStickerItem, inputNodeInteraction: ChatMediaInputNodeInteraction, theme: PresentationTheme, selected: @escaping () -> Void) { + init(account: Account, code: String?, stickerItem: FoundStickerItem, inputNodeInteraction: ChatMediaInputNodeInteraction, theme: PresentationTheme, selected: @escaping () -> Void) { self.account = account - self.code = code self.stickerItem = stickerItem self.inputNodeInteraction = inputNodeInteraction self.selected = selected - self.section = StickerPaneSearchStickerSection(code: self.code, theme: theme) + if let code = code { + self.code = code + self.section = StickerPaneSearchStickerSection(code: code, theme: theme) + } else { + self.code = nil + self.section = nil + } } func node(layout: GridNodeLayout) -> GridItemNode { diff --git a/TelegramUI/StorageUsageController.swift b/TelegramUI/StorageUsageController.swift index a148b77313..c9c0174f76 100644 --- a/TelegramUI/StorageUsageController.swift +++ b/TelegramUI/StorageUsageController.swift @@ -188,7 +188,7 @@ private func storageUsageControllerEntries(presentationData: PresentationData, c peerSizes += combinedSize } - let totalSize = Int(peerSizes + stats.otherSize + stats.cacheSize + stats.tempSize) + let totalSize = Int64(peerSizes + stats.otherSize + stats.cacheSize + stats.tempSize) entries.append(.clearAll(presentationData.theme, presentationData.strings.Cache_ClearCache, totalSize > 0 ? dataSizeString(totalSize) : presentationData.strings.Cache_ClearEmpty, totalSize > 0)) @@ -206,7 +206,7 @@ private func storageUsageControllerEntries(presentationData: PresentationData, c chatPeer = mainPeer mainPeer = associatedPeer } - entries.append(.peer(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, mainPeer, chatPeer, dataSizeString(Int(size)))) + entries.append(.peer(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, mainPeer, chatPeer, dataSizeString(size))) index += 1 } } @@ -326,7 +326,7 @@ func storageUsageController(account: Account) -> ViewController { if filteredSize == 0 { title = presentationData.strings.Cache_ClearNone } else { - title = presentationData.strings.Cache_Clear("\(dataSizeString(Int(filteredSize)))").0 + title = presentationData.strings.Cache_Clear("\(dataSizeString(filteredSize))").0 } if let item = item as? ActionSheetButtonItem { @@ -363,7 +363,7 @@ func storageUsageController(account: Account) -> ViewController { let categorySize: Int64 = size totalSize += categorySize let index = itemIndex - items.append(ActionSheetCheckboxItem(title: stringForCategory(strings: presentationData.strings, category: categoryId), label: dataSizeString(Int(categorySize)), value: true, action: { value in + items.append(ActionSheetCheckboxItem(title: stringForCategory(strings: presentationData.strings, category: categoryId), label: dataSizeString(categorySize), value: true, action: { value in toggleCheck(categoryId, index) })) itemIndex += 1 @@ -380,7 +380,7 @@ func storageUsageController(account: Account) -> ViewController { } if !items.isEmpty { - items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(Int(totalSize)))").0, action: { + items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(totalSize))").0, action: { if let statsPromise = statsPromise { let clearCategories = sizeIndex.keys.filter({ sizeIndex[$0]!.0 }) @@ -476,7 +476,7 @@ func storageUsageController(account: Account) -> ViewController { if filteredSize == 0 { title = presentationData.strings.Cache_ClearNone } else { - title = presentationData.strings.Cache_Clear("\(dataSizeString(Int(filteredSize)))").0 + title = presentationData.strings.Cache_Clear("\(dataSizeString(filteredSize))").0 } if let item = item as? ActionSheetButtonItem { @@ -513,7 +513,7 @@ func storageUsageController(account: Account) -> ViewController { sizeIndex[categoryId] = (true, categorySize) totalSize += categorySize let index = itemIndex - items.append(ActionSheetCheckboxItem(title: stringForCategory(strings: presentationData.strings, category: categoryId), label: dataSizeString(Int(categorySize)), value: true, action: { value in + items.append(ActionSheetCheckboxItem(title: stringForCategory(strings: presentationData.strings, category: categoryId), label: dataSizeString(categorySize), value: true, action: { value in toggleCheck(categoryId, index) })) itemIndex += 1 @@ -521,7 +521,7 @@ func storageUsageController(account: Account) -> ViewController { } if !items.isEmpty { - items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(Int(totalSize)))").0, action: { + items.append(ActionSheetButtonItem(title: presentationData.strings.Cache_Clear("\(dataSizeString(totalSize))").0, action: { if let statsPromise = statsPromise { var clearCategories = sizeIndex.keys.filter({ sizeIndex[$0]!.0 }) //var clearSize: Int64 = 0 diff --git a/TelegramUI/UrlHandling.swift b/TelegramUI/UrlHandling.swift index 16fa69754d..907f65e405 100644 --- a/TelegramUI/UrlHandling.swift +++ b/TelegramUI/UrlHandling.swift @@ -24,7 +24,7 @@ private enum ParsedUrl { enum ResolvedUrl { case externalUrl(String) - case peer(PeerId) + case peer(PeerId?, ChatControllerInteractionNavigateToPeer) case botStart(peerId: PeerId, payload: String) case groupBotStart(peerId: PeerId, payload: String) case channelMessage(peerId: PeerId, messageId: MessageId) @@ -124,7 +124,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig return .channelMessage(peerId: peerId, messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id)) } } else { - return .peer(peerId) + return .peer(peerId, .chat(textInputState: nil, messageId: nil)) } } else { return nil