diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index 8841d18c50..8adde8655c 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -529,8 +529,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { return ItemListSectionHeaderItem(presentationData: presentationData, text: "SHARE FOLDER", badge: hasLinks ? nil : "NEW", sectionId: self.section) case let .inviteLinkCreate(hasLinks): //TODO:localize - let _ = hasLinks - return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.linkIcon(presentationData.theme), title: "Create an Invite Link", sectionId: self.section, editing: false, action: { + return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.linkIcon(presentationData.theme), title: hasLinks ? "Create a new Link" : "Create an Invite Link", sectionId: self.section, editing: false, action: { arguments.createLink() }) case let .inviteLink(_, link): @@ -583,7 +582,7 @@ private struct ChatListFilterPresetControllerState: Equatable { } } -private func chatListFilterPresetControllerEntries(presentationData: PresentationData, isNewFilter: Bool, currentPreset: ChatListFilter?, state: ChatListFilterPresetControllerState, includePeers: [EngineRenderedPeer], excludePeers: [EngineRenderedPeer], isPremium: Bool, limit: Int32, inviteLinks: [ExportedChatFolderLink]?) -> [ChatListFilterPresetEntry] { +private func chatListFilterPresetControllerEntries(presentationData: PresentationData, isNewFilter: Bool, currentPreset: ChatListFilter?, state: ChatListFilterPresetControllerState, includePeers: [EngineRenderedPeer], excludePeers: [EngineRenderedPeer], isPremium: Bool, limit: Int32, inviteLinks: [ExportedChatFolderLink]?, hadLinks: Bool) -> [ChatListFilterPresetEntry] { var entries: [ChatListFilterPresetEntry] = [] if isNewFilter { @@ -667,7 +666,7 @@ private func chatListFilterPresetControllerEntries(presentationData: Presentatio hasLinks = true } - entries.append(.inviteLinkHeader(hasLinks: hasLinks)) + entries.append(.inviteLinkHeader(hasLinks: hasLinks || hadLinks)) entries.append(.inviteLinkCreate(hasLinks: hasLinks)) if let inviteLinks { @@ -1027,6 +1026,8 @@ private extension ChatListFilter { } func chatListFilterPresetController(context: AccountContext, currentPreset: ChatListFilter?, updated: @escaping ([ChatListFilter]) -> Void) -> ViewController { + var currentPreset = currentPreset + let initialName: String if let currentPreset = currentPreset { initialName = currentPreset.title @@ -1304,11 +1305,11 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat //TODO:localize var unavailableText: String? if !data.categories.isEmpty { - unavailableText = "You can’t share folders with include chat types." + unavailableText = "You can’t share folders which have chat types or excluded chats." } else if data.excludeArchived || data.excludeRead || data.excludeMuted { - unavailableText = "You can only share folders without chat types and excluded chats." + unavailableText = "You can’t share folders which have chat types or excluded chats." } else if !data.excludePeers.isEmpty { - unavailableText = "You can’t share folders with excluded chats" + unavailableText = "You can’t share folders which have chat types or excluded chats." } if let unavailableText { let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -1431,6 +1432,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat } filters.append(updatedFilter) } + currentPreset = updatedFilter } else { filters.append(updatedFilter) } @@ -1443,6 +1445,8 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat } var previousState = stateValue.with { $0 } + var previousSharedLinks: [ExportedChatFolderLink]? + var hadLinks: Bool = false let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, @@ -1475,9 +1479,17 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat if previousStateValue.expandedSections != state.expandedSections { skipStateAnimation = true } + if previousSharedLinks == nil && sharedLinks != nil { + skipStateAnimation = true + } + previousSharedLinks = sharedLinks + + if let sharedLinks, !sharedLinks.isEmpty { + hadLinks = true + } let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(currentPreset != nil ? presentationData.strings.ChatListFolder_TitleEdit : presentationData.strings.ChatListFolder_TitleCreate), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetControllerEntries(presentationData: presentationData, isNewFilter: currentPreset == nil, currentPreset: currentPreset, state: state, includePeers: includePeers, excludePeers: excludePeers, isPremium: isPremium, limit: premiumLimits.maxFolderChatsCount, inviteLinks: sharedLinks), style: .blocks, emptyStateItem: nil, animateChanges: !skipStateAnimation) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetControllerEntries(presentationData: presentationData, isNewFilter: currentPreset == nil, currentPreset: currentPreset, state: state, includePeers: includePeers, excludePeers: excludePeers, isPremium: isPremium, limit: premiumLimits.maxFolderChatsCount, inviteLinks: sharedLinks, hadLinks: hadLinks), style: .blocks, emptyStateItem: nil, animateChanges: !skipStateAnimation) skipStateAnimation = false return (controllerState, (listState, arguments)) @@ -1522,7 +1534,11 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat TextAlertAction(type: .genericAction, title: presentationData.strings.ChatListFolder_DiscardDiscard, action: { dismissImpl?() }), - TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatListFolder_DiscardCancel, action: { + //TODO:localize + TextAlertAction(type: .defaultAction, title: "Save", action: { + applyImpl?({ + dismissImpl?() + }) })]), nil) } attemptNavigationImpl = { diff --git a/submodules/Display/Source/AlertControllerNode.swift b/submodules/Display/Source/AlertControllerNode.swift index 51ee92168e..ad3418d977 100644 --- a/submodules/Display/Source/AlertControllerNode.swift +++ b/submodules/Display/Source/AlertControllerNode.swift @@ -120,25 +120,28 @@ final class AlertControllerNode: ASDisplayNode { self.existingAlertControllerNode = nil } else { + self.centerDimView.backgroundColor = nil + self.centerDimView.image = generateStretchableFilledCircleImage(radius: 16.0, color: nil, backgroundColor: UIColor(white: 0.0, alpha: 0.5)) self.centerDimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.topDimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) self.bottomDimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) self.leftDimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) self.rightDimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { [weak self] finished in + self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)/*, completion: { [weak self] finished in if finished { self?.centerDimView.backgroundColor = nil self?.centerDimView.image = generateStretchableFilledCircleImage(radius: 16.0, color: nil, backgroundColor: UIColor(white: 0.0, alpha: 0.5)) } - }) + })*/ self.containerNode.layer.animateSpring(from: 0.8 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5, initialVelocity: 0.0, removeOnCompletion: true, additive: false, completion: nil) } } func animateOut(completion: @escaping () -> Void) { self.containerNode.layer.removeAllAnimations() - self.centerDimView.backgroundColor = UIColor(white: 0.0, alpha: 0.5) - self.centerDimView.image = nil + //self.centerDimView.backgroundColor = UIColor(white: 0.0, alpha: 0.5) + //self.centerDimView.image = nil self.centerDimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) self.topDimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) diff --git a/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift b/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift index 26d1421dd8..d3a21e4360 100644 --- a/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift +++ b/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift @@ -245,13 +245,13 @@ private func folderInviteLinkListControllerEntries( chatCountString = "There are no chats in this folder that you can share with others." peersHeaderString = "THESE CHATS CANNOT BE SHARED" } else if state.selectedPeerIds.isEmpty { - chatCountString = "Anyone with this link can add \(title) folder and the chats selected below." + chatCountString = "Anyone with this link can add **\(title)** folder and the chats selected below." peersHeaderString = "CHATS" } else if state.selectedPeerIds.count == 1 { - chatCountString = "Anyone with this link can add \(title) folder and the 1 chat selected below." + chatCountString = "Anyone with this link can add **\(title)** folder and the 1 chat selected below." peersHeaderString = "1 CHAT SELECTED" } else { - chatCountString = "Anyone with this link can add \(title) folder and the \(state.selectedPeerIds.count) chats selected below." + chatCountString = "Anyone with this link can add **\(title)** folder and the \(state.selectedPeerIds.count) chats selected below." peersHeaderString = "\(state.selectedPeerIds.count) CHATS SELECTED" } entries.append(.header(chatCountString)) @@ -283,7 +283,7 @@ private func folderInviteLinkListControllerEntries( disabledReasonText = "you can't share private chats" } } else { - disabledReasonText = "you can't invite other here" + disabledReasonText = "you can't invite others here" } } entries.append(.peer(index: entries.count, peer: peer, isSelected: state.selectedPeerIds.contains(peer.id), disabledReasonText: disabledReasonText)) @@ -402,7 +402,7 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese let state = stateValue.with({ $0 }) - let promptController = promptController(sharedContext: context.sharedContext, updatedPresentationData: updatedPresentationData, text: "Name This Link", titleFont: .bold, value: state.title ?? "", apply: { value in + let promptController = promptController(sharedContext: context.sharedContext, updatedPresentationData: updatedPresentationData, text: "Name This Link", titleFont: .bold, value: state.title ?? "", characterLimit: 32, apply: { value in if let value { updateState { state in var state = state @@ -487,12 +487,15 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese } } else { //TODO:localize - var text = "You can't invite others here." - switch peer { - case .channel: - text = "You don't have the admin rights to share invite links to this group chat." - default: - break + let text: String + if case let .user(user) = peer { + if user.botInfo != nil { + text = "You can't share chats with bots" + } else { + text = "You can't share private chats" + } + } else { + text = "you can't invite others here" } dismissTooltipsImpl?() displayTooltipImpl?(.peers(context: context, peers: [peer], title: nil, text: text, customUndoText: nil)) diff --git a/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift b/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift index 3568985df7..21969299e6 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift @@ -131,7 +131,7 @@ class InviteLinkHeaderItemNode: ListViewItemNode { let attributedTitle = NSAttributedString(string: item.title ?? "", font: titleFont, textColor: item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) - let attributedText = parseMarkdownIntoAttributedString(item.text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: item.theme.list.freeTextColor), bold: MarkdownAttributeSet(font: textFont, textColor: item.theme.list.freeTextColor), link: MarkdownAttributeSet(font: textFont, textColor: item.theme.list.itemAccentColor), linkAttribute: { contents in + let attributedText = parseMarkdownIntoAttributedString(item.text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: item.theme.list.freeTextColor), bold: MarkdownAttributeSet(font: Font.semibold(14.0), textColor: item.theme.list.freeTextColor), link: MarkdownAttributeSet(font: textFont, textColor: item.theme.list.itemAccentColor), linkAttribute: { contents in return (TelegramTextAttributes.URL, contents) })) diff --git a/submodules/PromptUI/Sources/PromptController.swift b/submodules/PromptUI/Sources/PromptController.swift index eae781c4db..e6e9e19889 100644 --- a/submodules/PromptUI/Sources/PromptController.swift +++ b/submodules/PromptUI/Sources/PromptController.swift @@ -14,6 +14,8 @@ private final class PromptInputFieldNode: ASDisplayNode, ASEditableTextNodeDeleg private let textInputNode: EditableTextNode private let placeholderNode: ASTextNode + private let characterLimit: Int + var updateHeight: (() -> Void)? var complete: (() -> Void)? var textChanged: ((String) -> Void)? @@ -37,8 +39,9 @@ private final class PromptInputFieldNode: ASDisplayNode, ASEditableTextNodeDeleg } } - init(theme: PresentationTheme, placeholder: String) { + init(theme: PresentationTheme, placeholder: String, characterLimit: Int) { self.theme = theme + self.characterLimit = characterLimit self.backgroundNode = ASImageNode() self.backgroundNode.isLayerBacked = true @@ -114,6 +117,15 @@ private final class PromptInputFieldNode: ASDisplayNode, ASEditableTextNodeDeleg } func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + let currentText = (editableTextNode.attributedText?.string ?? "") as NSString + var resultText = currentText.replacingCharacters(in: range, with: text) + if resultText.count > self.characterLimit { + resultText = String(resultText[resultText.startIndex ..< resultText.index(resultText.startIndex, offsetBy: self.characterLimit)]) + + editableTextNode.attributedText = NSAttributedString(string: resultText, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputTextColor) + return false + } + if text == "\n" { self.complete?() return false @@ -175,7 +187,7 @@ private final class PromptAlertContentNode: AlertContentNode { return self.isUserInteractionEnabled } - init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], text: String, titleFont: PromptControllerTitleFont, value: String?) { + init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], text: String, titleFont: PromptControllerTitleFont, value: String?, characterLimit: Int) { self.strings = strings self.text = text self.titleFont = titleFont @@ -183,7 +195,7 @@ private final class PromptAlertContentNode: AlertContentNode { self.textNode = ASTextNode() self.textNode.maximumNumberOfLines = 2 - self.inputFieldNode = PromptInputFieldNode(theme: ptheme, placeholder: "") + self.inputFieldNode = PromptInputFieldNode(theme: ptheme, placeholder: "", characterLimit: characterLimit) self.inputFieldNode.text = value ?? "" self.actionNodesSeparator = ASDisplayNode() @@ -393,7 +405,7 @@ public enum PromptControllerTitleFont { case bold } -public func promptController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, text: String, titleFont: PromptControllerTitleFont = .regular, value: String?, apply: @escaping (String?) -> Void) -> AlertController { +public func promptController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, text: String, titleFont: PromptControllerTitleFont = .regular, value: String?, characterLimit: Int = 1000, apply: @escaping (String?) -> Void) -> AlertController { let presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 } var dismissImpl: ((Bool) -> Void)? @@ -407,7 +419,7 @@ public func promptController(sharedContext: SharedAccountContext, updatedPresent applyImpl?() })] - let contentNode = PromptAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, text: text, titleFont: titleFont, value: value) + let contentNode = PromptAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, text: text, titleFont: titleFont, value: value, characterLimit: characterLimit) contentNode.complete = { applyImpl?() } diff --git a/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift b/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift index 018235513b..aa76507f54 100644 --- a/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift +++ b/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift @@ -375,10 +375,10 @@ private final class ChatFolderLinkPreviewScreenComponent: Component { if allChatsAdded { titleString = "Add Folder" - } else if self.selectedItems.count == 1 { - titleString = "Add \(self.selectedItems.count) chat" + } else if linkContents.peers.count == 1 { + titleString = "Add \(linkContents.peers.count) chat" } else { - titleString = "Add \(self.selectedItems.count) chats" + titleString = "Add \(linkContents.peers.count) chats" } } else { titleString = "Add Folder" @@ -444,10 +444,10 @@ private final class ChatFolderLinkPreviewScreenComponent: Component { text = "Do you want to add a new chat folder\nand join its groups and channels?" } else { let chatCountString: String - if self.selectedItems.count == 1 { + if linkContents.peers.count == 1 { chatCountString = "1 chat" } else { - chatCountString = "\(self.selectedItems.count) chats" + chatCountString = "\(linkContents.peers.count) chats" } if let title = linkContents.title { text = "Do you want to add **\(chatCountString)** to your\nfolder **\(title)**?" @@ -482,7 +482,8 @@ private final class ChatFolderLinkPreviewScreenComponent: Component { if descriptionTextView.superview == nil { self.scrollContentView.addSubview(descriptionTextView) } - contentTransition.setFrame(view: descriptionTextView, frame: descriptionTextFrame) + descriptionTextView.bounds = CGRect(origin: CGPoint(), size: descriptionTextFrame.size) + contentTransition.setPosition(view: descriptionTextView, position: descriptionTextFrame.center) } contentHeight += descriptionTextFrame.height @@ -694,7 +695,12 @@ private final class ChatFolderLinkPreviewScreenComponent: Component { let listHeaderActionFrame = CGRect(origin: CGPoint(x: availableSize.width - sideInset - 15.0 - listHeaderActionSize.width, y: contentHeight), size: listHeaderActionSize) contentTransition.setPosition(view: listHeaderActionView, position: CGPoint(x: listHeaderActionFrame.maxX, y: listHeaderActionFrame.minY)) listHeaderActionView.bounds = CGRect(origin: CGPoint(), size: listHeaderActionFrame.size) - listHeaderActionView.isHidden = component.linkContents == nil || allChatsAdded + + if let linkContents = component.linkContents, !allChatsAdded, linkContents.peers.count > 1 { + listHeaderActionView.isHidden = false + } else { + listHeaderActionView.isHidden = true + } } contentHeight += listHeaderTextSize.height