From 6af5dcf3323f24b6780d72bafa00ad1e5495510b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 20 Oct 2022 08:03:43 +0300 Subject: [PATCH] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 6 + .../Sources/ChatListController.swift | 8 +- .../Sources/ChatListControllerNode.swift | 15 ++- .../Sources/ChatListEmptyNode.swift | 12 +- .../Sources/ChatListSearchContainerNode.swift | 9 ++ .../Sources/Node/ChatListItem.swift | 28 ++--- .../Sources/AdditionalLinkItem.swift | 10 +- .../Sources/ChannelVisibilityController.swift | 15 ++- .../SearchBarNode/Sources/SearchBarNode.swift | 2 +- .../Sources/SearchDisplayController.swift | 9 +- .../Sources/UsernameSetupController.swift | 11 +- .../TelegramEngine/Peers/AddressNames.swift | 41 +++++++ .../Peers/TelegramEnginePeers.swift | 4 + .../Sources/ForumCreateTopicScreen.swift | 103 ++++++++++++------ .../Unlink.imageset/Contents.json | 12 ++ .../Unlink.imageset/linkexpired.pdf | Bin 0 -> 3884 bytes .../ChatInterfaceStateContextMenus.swift | 4 + 17 files changed, 223 insertions(+), 66 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unlink.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unlink.imageset/linkexpired.pdf diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 53d72796a8..7dd672027b 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8093,6 +8093,8 @@ Sorry for the inconvenience."; "Group.Setup.ActivateAlertText" = "Do you want to show this link on the group info page?"; "Group.Setup.ActivateAlertShow" = "Show"; +"Group.Setup.ActiveLimitReachedError" = "Sorry, you have too many active public links already. Please hide one of your active public links first."; + "Group.Setup.DeactivateAlertTitle" = "Deactivate Link"; "Group.Setup.DeactivateAlertText" = "Do you want to hide this link from the group info page?"; "Group.Setup.DeactivateAlertHide" = "Hide"; @@ -8105,6 +8107,8 @@ Sorry for the inconvenience."; "Channel.Setup.ActivateAlertText" = "Do you want to show this link on the channel info page?"; "Channel.Setup.ActivateAlertShow" = "Show"; +"Channel.Setup.ActiveLimitReachedError" = "Sorry, you have too many active public links already. Please hide one of your active public links first."; + "Channel.Setup.DeactivateAlertTitle" = "Deactivate Link"; "Channel.Setup.DeactivateAlertText" = "Do you want to hide this link from the channel info page?"; "Channel.Setup.DeactivateAlertHide" = "Hide"; @@ -8117,6 +8121,8 @@ Sorry for the inconvenience."; "Username.ActivateAlertText" = "Do you want to show this link on your info page?"; "Username.ActivateAlertShow" = "Show"; +"Username.ActiveLimitReachedError" = "Sorry, you have too many active public links already. Please hide one of your active public links first."; + "Username.DeactivateAlertTitle" = "Deactivate Username"; "Username.DeactivateAlertText" = "Do you want to hide this link from your info page?"; "Username.DeactivateAlertHide" = "Hide"; diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 84dd97455d..0537a33e06 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1591,9 +1591,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController controller.navigationPresentation = .modal controller.completion = { title, fileId in - let availableColors: [Int32] = [0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, 0xFB6F5F] - - let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: availableColors.randomElement()!, iconFileId: fileId) + let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId) |> deliverOnMainQueue).start(next: { topicId in let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text).start() }) @@ -2634,9 +2632,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create) controller.navigationPresentation = .modal controller.completion = { title, fileId in - let availableColors: [Int32] = [0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, 0xFB6F5F] - - let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: availableColors.randomElement()!, iconFileId: fileId) + let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId) |> deliverOnMainQueue).start(next: { topicId in if let navigationController = (sourceController.navigationController as? NavigationController) { let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text).start() diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 39fd4a3efb..ddf696a788 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -337,8 +337,14 @@ private final class ChatListContainerItemNode: ASDisplayNode { currentNode.updateIsLoading(isLoading) } else { let subject: ChatListEmptyNode.Subject - if filter != nil { - subject = .filter + if let filter = filter { + var showEdit = true + if case let .filter(_, _, _, data) = filter { + if data.excludeRead && data.includePeers.peers.isEmpty && data.includePeers.pinnedPeers.isEmpty { + showEdit = false + } + } + subject = .filter(showEdit: showEdit) } else { if case .forum = location { subject = .forum @@ -1309,7 +1315,10 @@ final class ChatListControllerNode: ASDisplayNode { return nil } - let filter: ChatListNodePeersFilter = [] + var filter: ChatListNodePeersFilter = [] + if case .forum = self.location { + filter.insert(.excludeRecent) + } let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filter: filter, location: location, displaySearchFilters: displaySearchFilters, hasDownloads: hasDownloads, initialFilter: initialFilter, openPeer: { [weak self] peer, _, threadId, dismissSearch in self?.requestOpenPeerFromSearch?(peer, threadId, dismissSearch) diff --git a/submodules/ChatListUI/Sources/ChatListEmptyNode.swift b/submodules/ChatListUI/Sources/ChatListEmptyNode.swift index 2ca8fab6e6..c83e113635 100644 --- a/submodules/ChatListUI/Sources/ChatListEmptyNode.swift +++ b/submodules/ChatListUI/Sources/ChatListEmptyNode.swift @@ -13,7 +13,7 @@ import AccountContext final class ChatListEmptyNode: ASDisplayNode { enum Subject { case chats - case filter + case filter(showEdit: Bool) case forum } private let action: () -> Void @@ -54,7 +54,12 @@ final class ChatListEmptyNode: ASDisplayNode { self.descriptionNode.textAlignment = .center self.descriptionNode.lineSpacing = 0.1 - self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: theme), cornerRadius: 11.0, gloss: true) + var gloss = true + if case .filter = subject { + gloss = false + } + + self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: theme), cornerRadius: 11.0, gloss: gloss) self.secondaryButtonNode = HighlightableButtonNode() @@ -70,8 +75,9 @@ final class ChatListEmptyNode: ASDisplayNode { self.addSubnode(self.activityIndicator) let animationName: String - if case .filter = subject { + if case let .filter(showEdit) = subject { animationName = "ChatListFilterEmpty" + self.buttonNode.isHidden = !showEdit } else { animationName = "ChatListEmpty" } diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 5eb58e080f..fa5c562064 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -483,6 +483,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.copyProtectionTooltipController?.dismiss() } + public override var hasDim: Bool { + return self.peersFilter.contains(.excludeRecent) + } + private func updateState(_ f: (ChatListSearchContainerNodeSearchState) -> ChatListSearchContainerNodeSearchState) { let state = f(self.stateValue) if state != self.stateValue { @@ -606,6 +610,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo override public func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) + let isFirstTime = self.validLayout == nil self.validLayout = (layout, navigationBarHeight) let topInset = navigationBarHeight @@ -626,6 +631,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo let overflowInset: CGFloat = 20.0 self.filterContainerNode.update(size: CGSize(width: layout.size.width - overflowInset * 2.0, height: 38.0), sideInset: layout.safeInsets.left - overflowInset, filters: filters.map { .filter($0) }, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) + if isFirstTime { + self.filterContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + var bottomIntrinsicInset = layout.intrinsicInsets.bottom if case .chatList(.root) = self.location { if layout.safeInsets.left > overflowInset { diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index d8628f0ecd..7cfe272b56 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -330,10 +330,16 @@ private func groupReferenceRevealOptions(strings: PresentationStrings, theme: Pr return options } -private func forumRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isMuted: Bool?, isClosed: Bool, isEditing: Bool, canManage: Bool) -> [ItemListRevealOption] { +private func forumRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isMuted: Bool?, isClosed: Bool, isPinned: Bool, isEditing: Bool, canManage: Bool) -> [ItemListRevealOption] { var options: [ItemListRevealOption] = [] if !isEditing { - if let isMuted = isMuted { + if canManage { + if isPinned { + options.append(ItemListRevealOption(key: RevealOptionKey.unpin.rawValue, title: strings.DialogList_Unpin, icon: unpinIcon, color: theme.list.itemDisclosureActions.constructive.fillColor, textColor: theme.list.itemDisclosureActions.constructive.foregroundColor)) + } else { + options.append(ItemListRevealOption(key: RevealOptionKey.pin.rawValue, title: strings.DialogList_Pin, icon: pinIcon, color: theme.list.itemDisclosureActions.constructive.fillColor, textColor: theme.list.itemDisclosureActions.constructive.foregroundColor)) + } + } else if let isMuted = isMuted { if isMuted { options.append(ItemListRevealOption(key: RevealOptionKey.unmute.rawValue, title: strings.ChatList_Unmute, icon: unmuteIcon, color: theme.list.itemDisclosureActions.neutral2.fillColor, textColor: theme.list.itemDisclosureActions.neutral2.foregroundColor)) } else { @@ -354,20 +360,6 @@ private func forumRevealOptions(strings: PresentationStrings, theme: Presentatio return options } -private func forumLeftRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isUnread: Bool, isEditing: Bool, isPinned: Bool, location: ChatListControllerLocation, peer: EnginePeer) -> [ItemListRevealOption] { - var options: [ItemListRevealOption] = [] - - if case let .channel(channel) = peer, channel.hasPermission(.pinMessages) { - if isPinned { - options.append(ItemListRevealOption(key: RevealOptionKey.unpin.rawValue, title: strings.DialogList_Unpin, icon: unpinIcon, color: theme.list.itemDisclosureActions.constructive.fillColor, textColor: theme.list.itemDisclosureActions.constructive.foregroundColor)) - } else { - options.append(ItemListRevealOption(key: RevealOptionKey.pin.rawValue, title: strings.DialogList_Pin, icon: pinIcon, color: theme.list.itemDisclosureActions.constructive.fillColor, textColor: theme.list.itemDisclosureActions.constructive.foregroundColor)) - } - } - - return options -} - private func leftRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isUnread: Bool, isEditing: Bool, isPinned: Bool, isSavedMessages: Bool, location: ChatListControllerLocation, peer: EnginePeer, filterData: ChatListItemFilterData?) -> [ItemListRevealOption] { switch location { case let .chatList(groupId): @@ -1869,8 +1861,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if let threadInfo { isClosed = threadInfo.isClosed } - peerRevealOptions = forumRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isMuted: (currentMutedIconImage != nil), isClosed: isClosed, isEditing: item.editing, canManage: canManage) - peerLeftRevealOptions = forumLeftRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isUnread: unreadCount.unread, isEditing: item.editing, isPinned: isPinned, location: item.chatListLocation, peer: itemPeer.peers[itemPeer.peerId]!) + peerRevealOptions = forumRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isMuted: (currentMutedIconImage != nil), isClosed: isClosed, isPinned: isPinned, isEditing: item.editing, canManage: canManage) + peerLeftRevealOptions = [] } else { peerRevealOptions = [] peerLeftRevealOptions = [] diff --git a/submodules/InviteLinksUI/Sources/AdditionalLinkItem.swift b/submodules/InviteLinksUI/Sources/AdditionalLinkItem.swift index f8a02d9b45..17f0989bf0 100644 --- a/submodules/InviteLinksUI/Sources/AdditionalLinkItem.swift +++ b/submodules/InviteLinksUI/Sources/AdditionalLinkItem.swift @@ -193,6 +193,7 @@ public class AdditionalLinkItemNode: ListViewItemNode, ItemListItemNode { return { item, params, neighbors, firstWithHeader, last in var updatedTheme: PresentationTheme? + var updatedIsActive = false let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize) let subtitleFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0)) @@ -200,6 +201,9 @@ public class AdditionalLinkItemNode: ListViewItemNode, ItemListItemNode { if currentItem?.presentationData.theme !== item.presentationData.theme { updatedTheme = item.presentationData.theme } + if currentItem?.username?.isActive != item.username?.isActive { + updatedIsActive = true + } let iconColor: UIColor if let username = item.username { @@ -303,7 +307,11 @@ public class AdditionalLinkItemNode: ListViewItemNode, ItemListItemNode { strongSelf.backgroundNode.backgroundColor = itemBackgroundColor strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor - strongSelf.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: item.presentationData.theme.list.itemCheckColors.foregroundColor) + + } + + if updatedIsActive || updatedTheme != nil { + strongSelf.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: item.username?.isActive == true ? "Chat/Context Menu/Link" : "Chat/Context Menu/Unlink"), color: item.presentationData.theme.list.itemCheckColors.foregroundColor) } let transition = ContainedViewLayoutTransition.immediate diff --git a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift index 4e6b4101df..b69f9668c2 100644 --- a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift @@ -1673,7 +1673,20 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta action = presentationData.strings.Channel_Setup_ActivateAlertShow } presentControllerImpl?(textAlertController(context: context, title: title, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: action, action: { - let _ = context.engine.peers.toggleAddressNameActive(domain: .peer(peerId), name: name, active: true).start() + let _ = context.engine.peers.toggleAddressNameActive(domain: .peer(peerId), name: name, active: true).start(error: { error in + let errorText: String + switch error { + case .activeLimitReached: + if isGroup { + errorText = presentationData.strings.Group_Setup_ActiveLimitReachedError + } else { + errorText = presentationData.strings.Channel_Setup_ActiveLimitReachedError + } + default: + errorText = presentationData.strings.Login_UnknownError + } + presentControllerImpl?(textAlertController(context: context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + }) })]), nil) }) }, deactivateLink: { name in diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index da7e7d4983..76d53fd934 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -1042,7 +1042,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { public func deactivate(clear: Bool = true) { self.textField.resignFirstResponder() if clear { - self.textField.text = nil + self.textField.text = nil self.textField.tokens = [] self.textField.prefixString = nil self.textField.placeholderLabel.alpha = 1.0 diff --git a/submodules/SearchUI/Sources/SearchDisplayController.swift b/submodules/SearchUI/Sources/SearchDisplayController.swift index 506d3d7e9a..3dc9dafaa6 100644 --- a/submodules/SearchUI/Sources/SearchDisplayController.swift +++ b/submodules/SearchUI/Sources/SearchDisplayController.swift @@ -42,7 +42,6 @@ public final class SearchDisplayController { self.inline = inline self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: presentationData.theme, hasBackground: hasBackground, hasSeparator: hasSeparator, inline: inline), strings: presentationData.strings, fieldStyle: .modern, forceSeparator: hasSeparator, displayBackground: hasBackground) self.backgroundNode = BackgroundNode() - self.backgroundNode.backgroundColor = presentationData.theme.chatList.backgroundColor self.backgroundNode.allowsGroupOpacity = true self.mode = mode @@ -105,6 +104,14 @@ public final class SearchDisplayController { |> deliverOnMainQueue).start(next: { [weak self] value in self?.searchBar.activity = value }) + + if self.contentNode.hasDim { + self.backgroundNode.backgroundColor = .clear + self.backgroundNode.isTransparent = true + } else { + self.backgroundNode.backgroundColor = presentationData.theme.chatList.backgroundColor + self.backgroundNode.isTransparent = false + } } public func updatePresentationData(_ presentationData: PresentationData) { diff --git a/submodules/SettingsUI/Sources/UsernameSetupController.swift b/submodules/SettingsUI/Sources/UsernameSetupController.swift index f256291c5b..0878b6a2e6 100644 --- a/submodules/SettingsUI/Sources/UsernameSetupController.swift +++ b/submodules/SettingsUI/Sources/UsernameSetupController.swift @@ -443,7 +443,16 @@ public func usernameSetupController(context: AccountContext) -> ViewController { dismissInputImpl?() let presentationData = context.sharedContext.currentPresentationData.with { $0 } presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Username_ActivateAlertTitle, text: presentationData.strings.Username_ActivateAlertText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Username_ActivateAlertShow, action: { - let _ = context.engine.peers.toggleAddressNameActive(domain: .account, name: name, active: true).start() + let _ = context.engine.peers.toggleAddressNameActive(domain: .account, name: name, active: true).start(error: { error in + let errorText: String + switch error { + case .activeLimitReached: + errorText = presentationData.strings.Username_ActiveLimitReachedError + default: + errorText = presentationData.strings.Login_UnknownError + } + presentControllerImpl?(textAlertController(context: context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + }) })]), nil) }, deactivateLink: { name in dismissInputImpl?() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift index 5cb76f4eb6..2fb8ac007f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift @@ -172,8 +172,49 @@ func _internal_updateAddressName(account: Account, domain: AddressNameDomain, na } |> mapError { _ -> UpdateAddressNameError in } |> switchToLatest } +public enum DeactivateAllAddressNamesError { + case generic +} + +func _internal_deactivateAllAddressNames(account: Account, peerId: EnginePeer.Id) -> Signal { + return account.postbox.transaction { transaction -> Signal in + if let peer = transaction.getPeer(peerId), let inputChannel = apiInputChannel(peer) { + return account.network.request(Api.functions.channels.deactivateAllUsernames(channel: inputChannel), automaticFloodWait: false) + |> mapError { _ -> DeactivateAllAddressNamesError in + return .generic + } + |> mapToSignal { result -> Signal in + return account.postbox.transaction { transaction -> Signal in + if case .boolTrue = result, let peer = transaction.getPeer(account.peerId) as? TelegramChannel { + var updatedNames: [TelegramPeerUsername] = [] + for username in peer.usernames { + var updatedFlags = username.flags + updatedFlags.remove(.isActive) + updatedNames.append(TelegramPeerUsername(flags: updatedFlags, username: username.username)) + } + let updatedUser = peer.withUpdatedAddressNames(updatedNames) + updatePeers(transaction: transaction, peers: [updatedUser], update: { _, updated in + return updated + }) + } + return .complete() + } + |> castError(DeactivateAllAddressNamesError.self) + |> switchToLatest + |> ignoreValues + } + } else { + return .never() + } + } + |> mapError { _ -> DeactivateAllAddressNamesError in + } + |> switchToLatest +} + public enum ToggleAddressNameActiveError { case generic + case activeLimitReached } func _internal_toggleAddressNameActive(account: Account, domain: AddressNameDomain, name: String, active: Bool) -> Signal { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 50671f5fa4..39c7794410 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -47,6 +47,10 @@ public extension TelegramEngine { return _internal_updateAddressName(account: self.account, domain: domain, name: name) } + public func deactivateAllAddressNames(peerId: EnginePeer.Id) -> Signal { + return _internal_deactivateAllAddressNames(account: self.account, peerId: peerId) + } + public func toggleAddressNameActive(domain: AddressNameDomain, name: String, active: Bool) -> Signal { return _internal_toggleAddressNameActive(account: self.account, domain: domain, name: name, active: active) } diff --git a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift index 73e30e027e..e776083d51 100644 --- a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift +++ b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift @@ -27,6 +27,7 @@ private final class TitleFieldComponent: Component { let iconColor: Int32 let text: String let textUpdated: (String) -> Void + let iconPressed: () -> Void init( context: AccountContext, @@ -36,7 +37,8 @@ private final class TitleFieldComponent: Component { fileId: Int64, iconColor: Int32, text: String, - textUpdated: @escaping (String) -> Void + textUpdated: @escaping (String) -> Void, + iconPressed: @escaping () -> Void ) { self.context = context self.textColor = textColor @@ -46,6 +48,7 @@ private final class TitleFieldComponent: Component { self.iconColor = iconColor self.text = text self.textUpdated = textUpdated + self.iconPressed = iconPressed } static func ==(lhs: TitleFieldComponent, rhs: TitleFieldComponent) -> Bool { @@ -74,33 +77,51 @@ private final class TitleFieldComponent: Component { } final class View: UIView { + private let iconButton: HighlightTrackingButton private let iconView: ComponentView + private let placeholderView: ComponentView private let textField: TextFieldNodeView private var component: TitleFieldComponent? private weak var state: EmptyComponentState? override init(frame: CGRect) { + self.iconButton = HighlightTrackingButton() self.iconView = ComponentView() + self.placeholderView = ComponentView() self.textField = TextFieldNodeView(frame: .zero) super.init(frame: frame) - self.textField.placeholder = "What do you want to discuss?" self.textField.addTarget(self, action: #selector(self.textChanged(_:)), for: .editingChanged) self.addSubview(self.textField) + self.addSubview(self.iconButton) + + self.iconButton.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self, let iconView = strongSelf.iconView.view { + if highlighted { + iconView.layer.animateScale(from: 1.0, to: 0.8, duration: 0.25, removeOnCompletion: false) + } else if let presentationLayer = iconView.layer.presentation() { + iconView.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.2, removeOnCompletion: false) + } + } + } + self.iconButton.addTarget(self, action: #selector(self.iconButtonPressed), for: .touchUpInside) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - deinit { + @objc func iconButtonPressed() { + self.component?.iconPressed() } @objc func textChanged(_ sender: Any) { - self.component?.textUpdated(self.textField.text ?? "") + let text = self.textField.text ?? "" + self.component?.textUpdated(text) + self.placeholderView.view?.isHidden = !text.isEmpty } func update(component: TitleFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { @@ -114,8 +135,31 @@ private final class TitleFieldComponent: Component { let iconContent: EmojiStatusComponent.Content if component.fileId == 0 { iconContent = .topic(title: String(component.text.prefix(1)), color: component.iconColor, size: CGSize(width: 32.0, height: 32.0)) + self.iconButton.isUserInteractionEnabled = true } else { iconContent = .animation(content: .customEmoji(fileId: component.fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: component.placeholderColor, themeColor: component.accentColor, loopMode: .count(2)) + self.iconButton.isUserInteractionEnabled = false + } + + let placeholderSize = self.placeholderView.update( + transition: .easeInOut(duration: 0.2), + component: AnyComponent( + Text( + text: "What do you want to discuss?", + font: Font.regular(17.0), + color: component.placeholderColor + ) + ), + environment: {}, + containerSize: availableSize + ) + + if let placeholderComponentView = self.placeholderView.view { + if placeholderComponentView.superview == nil { + self.insertSubview(placeholderComponentView, at: 0) + } + + placeholderComponentView.frame = CGRect(origin: CGPoint(x: 62.0, y: floorToScreenPixels((availableSize.height - placeholderSize.height) / 2.0) + 1.0 - UIScreenPixel), size: placeholderSize) } let iconSize = self.iconView.update( @@ -134,11 +178,11 @@ private final class TitleFieldComponent: Component { if let iconComponentView = self.iconView.view { if iconComponentView.superview == nil { - self.addSubview(iconComponentView) + self.insertSubview(iconComponentView, at: 0) } iconComponentView.frame = CGRect(origin: CGPoint(x: 15.0, y: floorToScreenPixels((availableSize.height - iconSize.height) / 2.0)), size: iconSize) - + self.iconButton.frame = iconComponentView.frame.insetBy(dx: -4.0, dy: -4.0) self.textField.becomeFirstResponder() } @@ -443,9 +487,11 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { func updateTitle(_ text: String) { self.title = text self.updated(transition: .immediate) - self.titleUpdated(text) - + self.updateEmojiContent() + } + + func updateEmojiContent() { self.emojiContentDisposable.set(( EmojiPagerContentComponent.emojiInputData( context: self.context, @@ -469,6 +515,18 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { })) } + func switchIcon() { + let colors = ForumCreateTopicScreen.iconColors + if let index = colors.firstIndex(where: { $0 == self.iconColor }) { + let nextIndex = (index + 1) % colors.count + self.iconColor = colors[nextIndex] + } else { + self.iconColor = colors.first ?? 0 + } + self.updated(transition: .immediate) + self.updateEmojiContent() + } + func applyItem(groupId: AnyHashable, item: EmojiPagerContentComponent.Item?) { guard let item = item else { return @@ -486,30 +544,8 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { } self.updated(transition: .immediate) - self.iconUpdated(self.fileId != 0 ? self.fileId : nil) - - self.emojiContentDisposable.set(( - EmojiPagerContentComponent.emojiInputData( - context: self.context, - animationCache: self.context.animationCache, - animationRenderer: self.context.animationRenderer, - isStandalone: false, - isStatusSelection: false, - isReactionSelection: false, - isTopicIconSelection: true, - topReactionItems: [], - areUnicodeEmojiEnabled: false, - areCustomEmojiEnabled: true, - chatPeerId: self.context.account.peerId, - selectedItems: Set([MediaId(namespace: Namespaces.Media.CloudFile, id: self.fileId)]), - topicTitle: self.title, - topicColor: self.iconColor - ) - |> deliverOnMainQueue).start(next: { [weak self] content in - self?.emojiContent = content - self?.updated(transition: .immediate) - })) + self.updateEmojiContent() } } @@ -599,6 +635,9 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { text: state.title, textUpdated: { [weak state] text in state?.updateTitle(text) + }, + iconPressed: { [weak state] in + state?.switchIcon() } ), availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 44.0), @@ -736,6 +775,8 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { } public class ForumCreateTopicScreen: ViewControllerComponentContainer { + public static let iconColors: [Int32] = [0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, 0xFB6F5F] + public enum Mode: Equatable { case create case edit(topic: EngineMessageHistoryThread.Info) diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unlink.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unlink.imageset/Contents.json new file mode 100644 index 0000000000..7b46ce32f4 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unlink.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "linkexpired.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unlink.imageset/linkexpired.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unlink.imageset/linkexpired.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9c817ee0314fefcc135f46194d0642272e43ebf2 GIT binary patch literal 3884 zcmcgu3s@6Z7FGd`0}7QY)<+%67apQy9(f{&kO$SEA_2uhTtgTjA!KkesX>a;c0pO( zEd@nXEcN|BsDdbNMTBbg^9jCKd{9veYVBhcUEjNR1_DUj-F|hq^X1N+IXQFBx#vIk zKZhGGQ%pwv1Q56C$MRN2pbOxHBz--XagpAOV z@dPw=DrBN5LX!eniZkXa%g2eFuKn=#Uo(lEr+HU81}z{`_=6HrUY$}XIb1p;@X=`0 z$?jHpp=@NX{8943o8K+Zo-p>w66Mc#R7+&lAG6Ppc{tFBH;9(wuXC|)I*S%e=@Uz>6^Z9=nt zzK4tFN&WbbPV(=4#myd7Qze_xlA5vVwqyFoqMS92@;#wm6@={F#9?83t2`X*FQvbC zcX@DWOtMi{*^C?F@o__4Mg$cPsEwL<|M-mS@Omorv0`bfMSXgMWN`h$W#S8ow{yq3 z89$l0=;GgfPDcLnxoeBZaBgT^8j+bAEv%-KW2BuqxNO#{^A?sps**qlolk|&uqy?Zno68j~KM}`qjmEs-~Y4 z~y&cd0c>Qtymg;tT{DLMs-ya5!^llZee3$Y%Fv0amv0eTg zx^1~&;-RP+ckWq!@yI`Q#__3q7U!H{TuZ^3Z40K1ObZ|&{})HlDGLo-_is2dvuVja z`-@kv+aLO-^~#j)RUWQT#DYw*KO$B>cb6n*xvc9E;VQo z?DcKBe%<5_w}S6)b*!s>zCP&Xf&1jL?X%b6u?l0^)Q+%KHEyLGXZHq0v|nlLBK^E$ zL)2eK8r`z;_?;o#VMQI)+1rf|{<1V-dW`fXwkyYH#!S<2RH$Q`X+7 zPB@jX)Gkb35nH|MPcIfWrz|`jx2B+e@rUuso3nBnxMaS(aFg2dEdHqe9@#_|sPo>NUe1poeEX znx;%jFW|UOUtMz8xnWnuw$s&j9ryfH9#=K?Yfg>*kTE5fw(#!6N*cwJprR!w#Ic;_ zfoqStq$KP(JVq^VkF?$E^u)YHWqW1EE^Wzs2p%s;Tv)EQ_rz@#;3}h;wRN@pPSC4Z7YZ_ z?SN`U%KD>at>wsL^Y$Ho<76~EXz{w6Yiua-_J6+a+&>>Yd)RhAo!a`9*6X+zL>S|{ zxdA*etin(UGXpX~5kAT&Ft2zI>98~-x6q*~8c+-z5Won)#nT|*1?EYEVHD=S9zs(-T^n)v*X+AwyX(&mw|F>=V!eziw+-k2RPVnU+exyl_n-0drxLwH~ga z2qO&)SXpl-5wFvPkQO)wh!7a#Ge0$}D2d3I)Xl`T%f0UKF=luwa~#b^ML|+2tR&?1oA32RZ1tq468N@3k=|`sIMRS)$UFqQ zT)B@wXij_Kw~%}%)C#4=X2oVh(;OfdOtMB9)Huz(COWvbzF}G&x5U?lGjOE8^Kjej zVRqAa{!=zEa*rH4x{rkH3hQa;)(g^L!bgy2PVxEX(+s9{uqK` zC}UNK@&&jrf_ou|Pv6*B%M4RTyP_-A&>91o$nKiy6-?dTK`$Z}ib#M!Z-79+^X>~4 z3IwMD0ET|Ph$S}u;Q+<{dN|JS z;~!0FbOwTAyCRit3Bh2*P