Folder improvements

This commit is contained in:
Ali 2023-04-02 17:55:48 +04:00
parent 65bcef3ee2
commit 7b0f6ed7ca
10 changed files with 187 additions and 72 deletions

View File

@ -97,7 +97,7 @@ public final class ActivityIndicator: ASDisplayNode {
super.init()
if case let .custom(_, _, _, forceCustom) = self.type, forceCustom {
self.isLayerBacked = true
//self.isLayerBacked = true
}
switch type {

View File

@ -2705,6 +2705,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self?.push(c)
}, presentController: { [weak self] c in
self?.present(c, in: .window(.root))
}, completed: {
}, linkUpdated: { _ in
})
}
@ -2816,33 +2817,38 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
return
}
let previewScreen = ChatFolderLinkPreviewScreen(
context: self.context,
subject: .remove(folderId: id, defaultSelectedPeerIds: defaultSelectedPeerIds),
contents: ChatFolderLinkContents(
localFilterId: id,
title: title,
peers: peers.compactMap { $0 }.filter { peer in
if case .channel = peer {
return true
} else {
return false
}
},
alreadyMemberPeerIds: Set(),
memberCounts: memberCounts
),
completion: { [weak self] in
guard let self else {
return
}
if self.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id == id {
self.chatListDisplayNode.mainContainerNode.switchToFilter(id: .all, completion: {
})
}
let filteredPeers = peers.compactMap { $0 }.filter { peer in
if case .channel = peer {
return true
} else {
return false
}
)
self.push(previewScreen)
}
if filteredPeers.isEmpty {
apply()
} else {
let previewScreen = ChatFolderLinkPreviewScreen(
context: self.context,
subject: .remove(folderId: id, defaultSelectedPeerIds: defaultSelectedPeerIds),
contents: ChatFolderLinkContents(
localFilterId: id,
title: title,
peers: filteredPeers,
alreadyMemberPeerIds: Set(),
memberCounts: memberCounts
),
completion: { [weak self] in
guard let self else {
return
}
if self.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id == id {
self.chatListDisplayNode.mainContainerNode.switchToFilter(id: .all, completion: {
})
}
}
)
self.push(previewScreen)
}
}
if hasLinks {

View File

@ -1084,7 +1084,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
var dismissImpl: (() -> Void)?
var focusOnNameImpl: (() -> Void)?
var clearFocusImpl: (() -> Void)?
var applyImpl: ((@escaping () -> Void) -> Void)?
var applyImpl: ((Bool, @escaping () -> Void) -> Void)?
let sharedLinks = Promise<[ExportedChatFolderLink]?>(nil)
if let currentPreset {
@ -1297,11 +1297,16 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
},
createLink: {
if currentPreset == nil {
//TODO:localize
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let text = "Please finish creating this folder to share it."
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
} else {
applyImpl?({
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let statusController = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
presentControllerImpl?(statusController, nil)
applyImpl?(true, { [weak statusController] in
let state = stateValue.with({ $0 })
if let currentPreset, let data = currentPreset.data {
@ -1315,17 +1320,24 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
unavailableText = "You cant share folders which have chat types or excluded chats."
}
if let unavailableText {
statusController?.dismiss()
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: unavailableText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
return
}
var statusController = statusController
var previousLink: ExportedChatFolderLink?
openCreateChatListFolderLink(context: context, folderId: currentPreset.id, checkIfExists: false, title: currentPreset.title, peerIds: state.additionallyIncludePeers, pushController: { c in
pushControllerImpl?(c)
}, presentController: { c in
presentControllerImpl?(c, nil)
}, completed: {
statusController?.dismiss()
statusController = nil
}, linkUpdated: { updatedLink in
let previousLinkValue = previousLink
previousLink = updatedLink
@ -1347,12 +1359,14 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
sharedLinks.set(.single(links))
})
})
} else {
statusController?.dismiss()
}
})
}
}, openLink: { link in
if let currentPreset, let _ = currentPreset.data {
applyImpl?({
applyImpl?(false, {
let state = stateValue.with({ $0 })
pushControllerImpl?(folderInviteLinkListController(context: context, filterId: currentPreset.id, title: currentPreset.title, allPeerIds: state.additionallyIncludePeers, currentInvitation: link, linkUpdated: { updatedLink in
if updatedLink != link {
@ -1394,14 +1408,13 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
)
var attemptNavigationImpl: (() -> Bool)?
applyImpl = { completed in
applyImpl = { waitForSync, completed in
let state = stateValue.with { $0 }
var includePeers = ChatListFilterIncludePeers()
includePeers.setPeers(state.additionallyIncludePeers)
let _ = (context.engine.peers.updateChatListFiltersInteractively { filters in
var filterId = currentPreset?.id ?? -1
if currentPreset == nil {
filterId = context.engine.peers.generateNewChatListFilterId(filters: filters)
@ -1440,7 +1453,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|> deliverOnMainQueue).start(next: { filters in
updated(filters)
if let currentPreset {
if let currentPreset, waitForSync {
let _ = (context.engine.peers.updatedChatListFilters()
|> filter { filters -> Bool in
for filter in filters {
@ -1490,7 +1503,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
}
})
let rightNavigationButton = ItemListNavigationButton(content: .text(currentPreset == nil ? presentationData.strings.Common_Create : presentationData.strings.Common_Done), style: .bold, enabled: state.isComplete, action: {
applyImpl?({
applyImpl?(false, {
dismissImpl?()
})
})
@ -1559,7 +1572,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
}),
//TODO:localize
TextAlertAction(type: .defaultAction, title: "Save", action: {
applyImpl?({
applyImpl?(false, {
dismissImpl?()
})
})]), nil)
@ -1594,8 +1607,9 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
return controller
}
func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, checkIfExists: Bool, title: String, peerIds: [EnginePeer.Id], pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController) -> Void, linkUpdated: @escaping (ExportedChatFolderLink?) -> Void) {
func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, checkIfExists: Bool, title: String, peerIds: [EnginePeer.Id], pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController) -> Void, completed: @escaping () -> Void, linkUpdated: @escaping (ExportedChatFolderLink?) -> Void) {
if peerIds.isEmpty {
completed()
return
}
@ -1610,6 +1624,9 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec
|> map { result, peers -> ExportedChatFolderLink? in
var enabledPeerIds: [EnginePeer.Id] = []
for peer in peers {
if case let .legacyGroup(group) = peer, group.migrationReference != nil {
continue
}
if let peer, canShareLinkToPeer(peer: peer) {
enabledPeerIds.append(peer.id)
}
@ -1634,6 +1651,7 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec
let _ = (existingLink
|> deliverOnMainQueue).start(next: { existingLink in
if let existingLink {
completed()
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peerIds, currentInvitation: existingLink, linkUpdated: linkUpdated))
return
@ -1643,9 +1661,18 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec
EngineDataList(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))
)
|> deliverOnMainQueue).start(next: { peers in
let peers = peers.compactMap({ $0 })
let peers = peers.compactMap({ peer -> EnginePeer? in
guard let peer else {
return nil
}
if case let .legacyGroup(group) = peer, group.migrationReference != nil {
return nil
}
return peer
})
if peers.allSatisfy({ !canShareLinkToPeer(peer: $0) }) {
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peerIds, currentInvitation: nil, linkUpdated: linkUpdated))
completed()
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peers.map(\.id), currentInvitation: nil, linkUpdated: linkUpdated))
} else {
var enabledPeerIds: [EnginePeer.Id] = []
for peer in peers {
@ -1656,10 +1683,12 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec
let _ = (context.engine.peers.exportChatFolder(filterId: folderId, title: "", peerIds: enabledPeerIds)
|> deliverOnMainQueue).start(next: { link in
completed()
linkUpdated(link)
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peerIds, currentInvitation: link, linkUpdated: linkUpdated))
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peers.map(\.id), currentInvitation: link, linkUpdated: linkUpdated))
}, error: { error in
completed()
//TODO:localize
let text: String
switch error {

View File

@ -416,24 +416,37 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch
}
let confirmDeleteFolder: () -> Void = {
let previewScreen = ChatFolderLinkPreviewScreen(
context: context,
subject: .remove(folderId: id, defaultSelectedPeerIds: defaultSelectedPeerIds),
contents: ChatFolderLinkContents(
localFilterId: id,
title: title,
peers: peers.compactMap { $0 }.filter { peer in
if case .channel = peer {
return true
} else {
return false
}
},
alreadyMemberPeerIds: Set(),
memberCounts: memberCounts
let filteredPeers = peers.compactMap { $0 }.filter { peer in
if case .channel = peer {
return true
} else {
return false
}
}
if filteredPeers.isEmpty {
let _ = (context.engine.peers.updateChatListFiltersInteractively { filters in
var filters = filters
if let index = filters.firstIndex(where: { $0.id == id }) {
filters.remove(at: index)
}
return filters
}
|> deliverOnMainQueue).start()
} else {
let previewScreen = ChatFolderLinkPreviewScreen(
context: context,
subject: .remove(folderId: id, defaultSelectedPeerIds: defaultSelectedPeerIds),
contents: ChatFolderLinkContents(
localFilterId: id,
title: title,
peers: filteredPeers,
alreadyMemberPeerIds: Set(),
memberCounts: memberCounts
)
)
)
pushControllerImpl?(previewScreen)
pushControllerImpl?(previewScreen)
}
}
if hasLinks {

View File

@ -521,7 +521,15 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
text = "You can't share private chats"
}
} else {
text = "You don't have the admin rights to share invite links to this group chat."
var isGroup = true
if case let .channel(channel) = peer, case .broadcast = channel.info {
isGroup = false
}
if isGroup {
text = "You don't have the admin rights to share invite links to this group chat."
} else {
text = "You don't have the admin rights to share invite links to this channel."
}
}
dismissTooltipsImpl?()
displayTooltipImpl?(.peers(context: context, peers: [peer], title: nil, text: text, customUndoText: nil))
@ -557,6 +565,17 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
let allPeers = context.engine.data.subscribe(
EngineDataList(combinedPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))
)
|> map { peers -> [EnginePeer] in
return peers.compactMap({ peer -> EnginePeer? in
guard let peer else {
return nil
}
if case let .legacyGroup(group) = peer, group.migrationReference != nil {
return nil
}
return peer
})
}
let applyChangesImpl: (() -> Void)? = {
let state = stateValue.with({ $0 })
@ -608,9 +627,9 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
state.selectedPeerIds.insert(peerId)
}
} else {
for peerId in allPeerIds {
if let peer = peers.first(where: { $0?.id == peerId }), let peerValue = peer {
if canShareLinkToPeer(peer: peerValue) {
for peerId in peers.map(\.id) {
if let peer = peers.first(where: { $0.id == peerId }) {
if canShareLinkToPeer(peer: peer) {
state.selectedPeerIds.insert(peerId)
}
}

View File

@ -366,12 +366,10 @@ func _internal_checkChatFolderLink(account: Account, slug: String) -> Signal<Cha
continue
}
if let peerValue = transaction.getPeer(peerId) {
if canShareLinkToPeer(peer: EnginePeer(peerValue)) {
resultPeers.append(EnginePeer(peerValue))
if transaction.getPeerChatListIndex(peerId) != nil {
alreadyMemberPeerIds.insert(peerId)
}
resultPeers.append(EnginePeer(peerValue))
if transaction.getPeerChatListIndex(peerId) != nil {
alreadyMemberPeerIds.insert(peerId)
}
}
}

View File

@ -13,6 +13,7 @@ swift_library(
"//submodules/Display",
"//submodules/ComponentFlow",
"//submodules/TelegramUI/Components/AnimatedTextComponent",
"//submodules/ActivityIndicator",
],
visibility = [
"//visibility:public",

View File

@ -3,6 +3,7 @@ import UIKit
import Display
import ComponentFlow
import AnimatedTextComponent
import ActivityIndicator
public final class ButtonBadgeComponent: Component {
let fillColor: UIColor
@ -267,15 +268,18 @@ public final class ButtonTextContentComponent: Component {
public final class ButtonComponent: Component {
public struct Background: Equatable {
public var color: UIColor
public var foreground: UIColor
public var pressedColor: UIColor
public var cornerRadius: CGFloat
public init(
color: UIColor,
foreground: UIColor,
pressedColor: UIColor,
cornerRadius: CGFloat = 10.0
) {
self.color = color
self.foreground = foreground
self.pressedColor = pressedColor
self.cornerRadius = cornerRadius
}
@ -284,17 +288,20 @@ public final class ButtonComponent: Component {
public let background: Background
public let content: AnyComponentWithIdentity<Empty>
public let isEnabled: Bool
public let displaysProgress: Bool
public let action: () -> Void
public init(
background: Background,
content: AnyComponentWithIdentity<Empty>,
isEnabled: Bool,
displaysProgress: Bool,
action: @escaping () -> Void
) {
self.background = background
self.content = content
self.isEnabled = isEnabled
self.displaysProgress = displaysProgress
self.action = action
}
@ -308,6 +315,9 @@ public final class ButtonComponent: Component {
if lhs.isEnabled != rhs.isEnabled {
return false
}
if lhs.displaysProgress != rhs.displaysProgress {
return false
}
return true
}
@ -326,6 +336,8 @@ public final class ButtonComponent: Component {
private var contentItem: ContentItem?
private var activityIndicator: ActivityIndicator?
override init(frame: CGRect) {
super.init(frame: frame)
@ -363,10 +375,17 @@ public final class ButtonComponent: Component {
self.component = component
self.componentState = state
self.isEnabled = component.isEnabled
self.isEnabled = component.isEnabled && !component.displaysProgress
transition.setBackgroundColor(view: self, color: component.background.color)
transition.setCornerRadius(layer: self.layer, cornerRadius: component.background.cornerRadius)
var contentAlpha: CGFloat = 1.0
if component.displaysProgress {
contentAlpha = 0.0
} else if !component.isEnabled {
contentAlpha = 0.7
}
var previousContentItem: ContentItem?
let contentItem: ContentItem
@ -398,11 +417,11 @@ public final class ButtonComponent: Component {
let contentFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - contentSize.width) * 0.5), y: floor((availableSize.height - contentSize.height) * 0.5)), size: contentSize)
contentTransition.setFrame(view: contentView, frame: contentFrame)
contentTransition.setAlpha(view: contentView, alpha: component.isEnabled ? 1.0 : 0.7)
contentTransition.setAlpha(view: contentView, alpha: contentAlpha)
if animateIn && previousContentItem != nil && !transition.animation.isImmediate {
contentView.layer.animateScale(from: 0.4, to: 1.0, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring)
contentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
contentView.layer.animateAlpha(from: 0.0, to: contentAlpha, duration: 0.1)
contentView.layer.animatePosition(from: CGPoint(x: 0.0, y: -availableSize.height * 0.15), to: CGPoint(), duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
}
}
@ -410,7 +429,7 @@ public final class ButtonComponent: Component {
if let previousContentItem, let previousContentView = previousContentItem.view.view {
if !transition.animation.isImmediate {
previousContentView.layer.animateScale(from: 1.0, to: 0.0, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
previousContentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousContentView] _ in
previousContentView.layer.animateAlpha(from: contentAlpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousContentView] _ in
previousContentView?.removeFromSuperview()
})
previousContentView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: availableSize.height * 0.35), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
@ -419,6 +438,30 @@ public final class ButtonComponent: Component {
}
}
if component.displaysProgress {
let activityIndicator: ActivityIndicator
var activityIndicatorTransition = transition
if let current = self.activityIndicator {
activityIndicator = current
} else {
activityIndicatorTransition = .immediate
activityIndicator = ActivityIndicator(type: .custom(component.background.foreground, 22.0, 2.0, true))
activityIndicator.view.alpha = 0.0
self.activityIndicator = activityIndicator
self.addSubview(activityIndicator.view)
}
let indicatorSize = CGSize(width: 22.0, height: 22.0)
transition.setAlpha(view: activityIndicator.view, alpha: 1.0)
activityIndicatorTransition.setFrame(view: activityIndicator.view, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - indicatorSize.width) / 2.0), y: floor((availableSize.height - indicatorSize.height) / 2.0)), size: indicatorSize))
} else {
if let activityIndicator = self.activityIndicator {
self.activityIndicator = nil
transition.setAlpha(view: activityIndicator.view, alpha: 0.0, completion: { [weak activityIndicator] _ in
activityIndicator?.view.removeFromSuperview()
})
}
}
return availableSize
}
}

View File

@ -716,15 +716,19 @@ private final class ChatFolderLinkPreviewScreenComponent: Component {
initialContentHeight += 24.0
let actionButtonTitle: String
var actionButtonBadge: Int = 0
if case .remove = component.subject {
actionButtonBadge = self.selectedItems.count
if self.selectedItems.isEmpty {
actionButtonTitle = "Remove Folder"
} else {
actionButtonTitle = "Remove Folder and Chats"
}
} else if allChatsAdded {
actionButtonBadge = 0
actionButtonTitle = "OK"
} else if let linkContents = component.linkContents {
actionButtonBadge = self.selectedItems.count
if linkContents.localFilterId != nil {
if self.selectedItems.isEmpty {
actionButtonTitle = "Do Not Join Any Chats"
@ -743,19 +747,21 @@ private final class ChatFolderLinkPreviewScreenComponent: Component {
component: AnyComponent(ButtonComponent(
background: ButtonComponent.Background(
color: environment.theme.list.itemCheckColors.fillColor,
foreground: environment.theme.list.itemCheckColors.foregroundColor,
pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
),
content: AnyComponentWithIdentity(
id: actionButtonTitle,
component: AnyComponent(ButtonTextContentComponent(
text: actionButtonTitle,
badge: self.selectedItems.count,
badge: actionButtonBadge,
textColor: environment.theme.list.itemCheckColors.foregroundColor,
badgeBackground: environment.theme.list.itemCheckColors.foregroundColor,
badgeForeground: environment.theme.list.itemCheckColors.fillColor
))
),
isEnabled: !self.selectedItems.isEmpty || component.linkContents?.localFilterId != nil,
displaysProgress: self.inProgress,
action: { [weak self] in
guard let self, let component = self.component, let controller = self.environment?.controller() else {
return

View File

@ -321,7 +321,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
case "telegram_botapp":
title = item.presentationData.strings.Conversation_BotApp
actionTitle = item.presentationData.strings.Conversation_OpenBotApp
case "telegram_community":
case "telegram_chatlist":
actionTitle = item.presentationData.strings.Conversation_OpenChatFolder
default:
break