mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Update folders
This commit is contained in:
parent
c40b9abc69
commit
8d79979422
@ -9092,3 +9092,8 @@ Sorry for the inconvenience.";
|
||||
"StickerPacksSettings.SuggestAnimatedEmojiInfo" = "Each time you enter an emoji you can replace it with an animated emoji.";
|
||||
|
||||
"DialogList.DeleteBotClearHistory" = "Clear Chat History";
|
||||
|
||||
"ChatList.ChatFolderUpdateCount_1" = "1 new chat";
|
||||
"ChatList.ChatFolderUpdateCount_any" = "%d new chats";
|
||||
"ChatList.ChatFolderUpdateHintTitle" = "You can join %@";
|
||||
"ChatList.ChatFolderUpdateHintText" = "Tap here to view them";
|
||||
|
@ -89,6 +89,7 @@ swift_library(
|
||||
"//submodules/AvatarNode:AvatarNode",
|
||||
"//submodules/AvatarVideoNode:AvatarVideoNode",
|
||||
"//submodules/InviteLinksUI",
|
||||
"//submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -2705,50 +2705,78 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
|
||||
private func askForFilterRemoval(id: Int32) {
|
||||
let actionSheet = ActionSheetController(presentationData: self.presentationData)
|
||||
let apply: () -> Void = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let commit: () -> Void = {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id == id {
|
||||
if strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.currentState.editing {
|
||||
strongSelf.donePressed()
|
||||
}
|
||||
}
|
||||
|
||||
let _ = (strongSelf.context.engine.peers.updateChatListFiltersInteractively { filters in
|
||||
return filters.filter({ $0.id != id })
|
||||
}).start()
|
||||
}
|
||||
|
||||
if strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id == id {
|
||||
strongSelf.chatListDisplayNode.mainContainerNode.switchToFilter(id: .all, completion: {
|
||||
commit()
|
||||
})
|
||||
} else {
|
||||
commit()
|
||||
}
|
||||
}
|
||||
|
||||
actionSheet.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: self.presentationData.strings.ChatList_RemoveFolderConfirmation),
|
||||
ActionSheetButtonItem(title: self.presentationData.strings.ChatList_RemoveFolderAction, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let commit: () -> Void = {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id == id {
|
||||
if strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.currentState.editing {
|
||||
strongSelf.donePressed()
|
||||
}
|
||||
}
|
||||
|
||||
let _ = (strongSelf.context.engine.peers.updateChatListFiltersInteractively { filters in
|
||||
return filters.filter({ $0.id != id })
|
||||
}).start()
|
||||
}
|
||||
|
||||
if strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id == id {
|
||||
strongSelf.chatListDisplayNode.mainContainerNode.switchToFilter(id: .all, completion: {
|
||||
commit()
|
||||
let _ = (context.engine.peers.currentChatListFilters()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] filters in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let filter = filters.first(where: { $0.id == id }) else {
|
||||
return
|
||||
}
|
||||
|
||||
if case let .filter(_, _, _, data) = filter, data.isShared {
|
||||
let presentationData = self.presentationData
|
||||
|
||||
//TODO:localize
|
||||
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: "Delete Folder", text: "Are you sure you want to delete this folder? This will also deactivate all the invite links used to share this folder.", actions: [
|
||||
TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: {
|
||||
apply()
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
})
|
||||
]), in: .window(.root))
|
||||
} else {
|
||||
let actionSheet = ActionSheetController(presentationData: self.presentationData)
|
||||
|
||||
actionSheet.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: self.presentationData.strings.ChatList_RemoveFolderConfirmation),
|
||||
ActionSheetButtonItem(title: self.presentationData.strings.ChatList_RemoveFolderAction, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
apply()
|
||||
})
|
||||
} else {
|
||||
commit()
|
||||
}
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])
|
||||
])
|
||||
self.present(actionSheet, in: .window(.root))
|
||||
]),
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])
|
||||
])
|
||||
self.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public private(set) var isSearchActive: Bool = false
|
||||
|
@ -186,7 +186,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
|
||||
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {})
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {})
|
||||
interaction.isInlineMode = isInlineMode
|
||||
|
||||
let items = (0 ..< 2).map { _ -> ChatListItem in
|
||||
|
@ -380,32 +380,59 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch
|
||||
return state
|
||||
}
|
||||
}, removePreset: { id in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
|
||||
actionSheet.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: presentationData.strings.ChatList_RemoveFolderConfirmation),
|
||||
ActionSheetButtonItem(title: presentationData.strings.ChatList_RemoveFolderAction, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
let _ = (context.engine.peers.updateChatListFiltersInteractively { filters in
|
||||
var filters = filters
|
||||
if let index = filters.firstIndex(where: { $0.id == id }) {
|
||||
filters.remove(at: index)
|
||||
let _ = (context.engine.peers.currentChatListFilters()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { filters in
|
||||
guard let filter = filters.first(where: { $0.id == id }) else {
|
||||
return
|
||||
}
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
if case let .filter(_, _, _, data) = filter, data.isShared {
|
||||
//TODO:localize
|
||||
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: "Delete Folder", text: "Are you sure you want to delete this folder? This will also deactivate all the invite links used to share this folder.", actions: [
|
||||
TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: {
|
||||
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
|
||||
}
|
||||
return filters
|
||||
}
|
||||
|> deliverOnMainQueue).start()
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])
|
||||
])
|
||||
presentControllerImpl?(actionSheet)
|
||||
|> deliverOnMainQueue).start()
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
})
|
||||
]))
|
||||
} else {
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
|
||||
actionSheet.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: presentationData.strings.ChatList_RemoveFolderConfirmation),
|
||||
ActionSheetButtonItem(title: presentationData.strings.ChatList_RemoveFolderAction, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
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()
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])
|
||||
])
|
||||
presentControllerImpl?(actionSheet)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
let featuredFilters = context.account.postbox.preferencesView(keys: [PreferencesKeys.chatListFiltersFeaturedState])
|
||||
|
@ -2164,6 +2164,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}, openStorageManagement: {
|
||||
}, openPasswordSetup: {
|
||||
}, openPremiumIntro: {
|
||||
}, openChatFolderUpdates: {
|
||||
})
|
||||
chatListInteraction.isSearchMode = true
|
||||
|
||||
@ -3397,7 +3398,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {})
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {})
|
||||
var isInlineMode = false
|
||||
if case .topics = key {
|
||||
isInlineMode = false
|
||||
|
@ -17,6 +17,7 @@ import PremiumUI
|
||||
import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import Postbox
|
||||
import ChatFolderLinkPreviewScreen
|
||||
|
||||
public enum ChatListNodeMode {
|
||||
case chatList
|
||||
@ -95,6 +96,7 @@ public final class ChatListNodeInteraction {
|
||||
let openStorageManagement: () -> Void
|
||||
let openPasswordSetup: () -> Void
|
||||
let openPremiumIntro: () -> Void
|
||||
let openChatFolderUpdates: () -> Void
|
||||
|
||||
public var searchTextHighightState: String?
|
||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||
@ -139,7 +141,8 @@ public final class ChatListNodeInteraction {
|
||||
openForumThread: @escaping (EnginePeer.Id, Int64) -> Void,
|
||||
openStorageManagement: @escaping () -> Void,
|
||||
openPasswordSetup: @escaping () -> Void,
|
||||
openPremiumIntro: @escaping () -> Void
|
||||
openPremiumIntro: @escaping () -> Void,
|
||||
openChatFolderUpdates: @escaping () -> Void
|
||||
) {
|
||||
self.activateSearch = activateSearch
|
||||
self.peerSelected = peerSelected
|
||||
@ -172,6 +175,7 @@ public final class ChatListNodeInteraction {
|
||||
self.openStorageManagement = openStorageManagement
|
||||
self.openPasswordSetup = openPasswordSetup
|
||||
self.openPremiumIntro = openPremiumIntro
|
||||
self.openChatFolderUpdates = openChatFolderUpdates
|
||||
}
|
||||
}
|
||||
|
||||
@ -620,6 +624,8 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
nodeInteraction?.openPasswordSetup()
|
||||
case .premiumUpgrade, .premiumAnnualDiscount:
|
||||
nodeInteraction?.openPremiumIntro()
|
||||
case .chatFolderUpdates:
|
||||
nodeInteraction?.openChatFolderUpdates()
|
||||
}
|
||||
}), directionHint: entry.directionHint)
|
||||
}
|
||||
@ -873,6 +879,8 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
nodeInteraction?.openPasswordSetup()
|
||||
case .premiumUpgrade, .premiumAnnualDiscount:
|
||||
nodeInteraction?.openPremiumIntro()
|
||||
case .chatFolderUpdates:
|
||||
nodeInteraction?.openChatFolderUpdates()
|
||||
}
|
||||
}), directionHint: entry.directionHint)
|
||||
case .HeaderEntry:
|
||||
@ -1068,6 +1076,9 @@ public final class ChatListNode: ListView {
|
||||
|
||||
let hideArhiveIntro = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
|
||||
private let chatFolderUpdates = Promise<ChatFolderUpdates?>(nil)
|
||||
private var pollFilterUpdatesDisposable: Disposable?
|
||||
|
||||
public init(context: AccountContext, location: ChatListControllerLocation, chatListFilter: ChatListFilter? = nil, previewing: Bool, fillPreloadItems: Bool, mode: ChatListNodeMode, isPeerEnabled: ((EnginePeer) -> Bool)? = nil, theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, disableAnimations: Bool, isInlineMode: Bool) {
|
||||
self.context = context
|
||||
self.location = location
|
||||
@ -1389,6 +1400,19 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .ads)
|
||||
self.push?(controller)
|
||||
}, openChatFolderUpdates: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = (self.chatFolderUpdates.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let self, let result else {
|
||||
return
|
||||
}
|
||||
|
||||
self.push?(ChatFolderLinkPreviewScreen(context: self.context, subject: .updates(result), contents: result.chatFolderLinkContents))
|
||||
})
|
||||
})
|
||||
nodeInteraction.isInlineMode = isInlineMode
|
||||
|
||||
@ -1614,15 +1638,18 @@ public final class ChatListNode: ListView {
|
||||
suggestedChatListNotice,
|
||||
savedMessagesPeer,
|
||||
chatListViewUpdate,
|
||||
self.chatFolderUpdates.get() |> distinctUntilChanged,
|
||||
self.statePromise.get()
|
||||
)
|
||||
|> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, suggestedChatListNotice, savedMessagesPeer, updateAndFilter, state) -> Signal<ChatListNodeListViewTransition, NoError> in
|
||||
|> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, suggestedChatListNotice, savedMessagesPeer, updateAndFilter, chatFolderUpdates, state) -> Signal<ChatListNodeListViewTransition, NoError> in
|
||||
let (update, filter) = updateAndFilter
|
||||
|
||||
let previousHideArchivedFolderByDefaultValue = previousHideArchivedFolderByDefault.swap(hideArchivedFolderByDefault)
|
||||
|
||||
let notice: ChatListNotice?
|
||||
if let suggestedChatListNotice {
|
||||
if let chatFolderUpdates, chatFolderUpdates.availableChatsToJoin != 0 {
|
||||
notice = .chatFolderUpdates(count: chatFolderUpdates.availableChatsToJoin)
|
||||
} else if let suggestedChatListNotice {
|
||||
notice = suggestedChatListNotice
|
||||
} else if let storageInfo {
|
||||
notice = .clearStorage(sizeFraction: storageInfo)
|
||||
@ -2551,6 +2578,7 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
}
|
||||
|
||||
self.pollFilterUpdates(shouldDelay: false)
|
||||
self.resetFilter()
|
||||
|
||||
let selectionRecognizer = ChatHistoryListSelectionRecognizer(target: self, action: #selector(self.selectionPanGesture(_:)))
|
||||
@ -2567,6 +2595,7 @@ public final class ChatListNode: ListView {
|
||||
self.chatListDisposable.dispose()
|
||||
self.activityStatusesDisposable?.dispose()
|
||||
self.updatedFilterDisposable.dispose()
|
||||
self.pollFilterUpdatesDisposable?.dispose()
|
||||
}
|
||||
|
||||
func updateFilter(_ filter: ChatListFilter?) {
|
||||
@ -2576,6 +2605,20 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
}
|
||||
|
||||
private func pollFilterUpdates(shouldDelay: Bool) {
|
||||
guard let chatListFilter, case let .filter(id, _, _, data) = chatListFilter, data.isShared else {
|
||||
return
|
||||
}
|
||||
self.pollFilterUpdatesDisposable = (context.engine.peers.getChatFolderUpdates(folderId: id)
|
||||
|> delay(shouldDelay ? 5.0 : 0.0, queue: .mainQueue())).start(next: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.chatFolderUpdates.set(.single(result))
|
||||
self.pollFilterUpdates(shouldDelay: true)
|
||||
})
|
||||
}
|
||||
|
||||
private func resetFilter() {
|
||||
if let chatListFilter = self.chatListFilter {
|
||||
self.updatedFilterDisposable.set((self.context.engine.peers.updatedChatListFilters()
|
||||
|
@ -51,6 +51,7 @@ enum ChatListNotice: Equatable {
|
||||
case setupPassword
|
||||
case premiumUpgrade(discount: Int32)
|
||||
case premiumAnnualDiscount(discount: Int32)
|
||||
case chatFolderUpdates(count: Int)
|
||||
}
|
||||
|
||||
enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
|
@ -160,6 +160,15 @@ class ChatListStorageInfoItemNode: ListViewItemNode {
|
||||
titleString = titleStringValue
|
||||
|
||||
textString = NSAttributedString(string: item.strings.ChatList_PremiumAnnualDiscountText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
|
||||
case let .chatFolderUpdates(count):
|
||||
let rawTitleString = item.strings.ChatList_ChatFolderUpdateHintTitle(item.strings.ChatList_ChatFolderUpdateCount(Int32(count)))
|
||||
let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: rawTitleString.string, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor))
|
||||
if let range = rawTitleString.ranges.first {
|
||||
titleStringValue.addAttribute(.foregroundColor, value: item.theme.rootController.navigationBar.accentTextColor, range: range.range)
|
||||
}
|
||||
titleString = titleStringValue
|
||||
|
||||
textString = NSAttributedString(string: item.strings.ChatList_ChatFolderUpdateHintText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
|
||||
}
|
||||
|
||||
let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))
|
||||
|
@ -94,6 +94,7 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
}, openStorageManagement: {
|
||||
}, openPasswordSetup: {
|
||||
}, openPremiumIntro: {
|
||||
}, openChatFolderUpdates: {
|
||||
})
|
||||
|
||||
let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil)
|
||||
|
@ -222,7 +222,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in
|
||||
}, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {})
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {})
|
||||
|
||||
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
||||
|
@ -843,7 +843,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
}, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {})
|
||||
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {})
|
||||
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
||||
func makeChatListItem(
|
||||
|
@ -367,7 +367,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {} )
|
||||
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {})
|
||||
|
||||
func makeChatListItem(
|
||||
peer: EnginePeer,
|
||||
|
@ -346,7 +346,7 @@ func _internal_joinChatFolderLink(account: Account, slug: String, peerIds: [Engi
|
||||
|> mapToSignal { inputPeers -> Signal<Never, JoinChatFolderLinkError> in
|
||||
return account.network.request(Api.functions.communities.joinCommunityInvite(slug: slug, peers: inputPeers))
|
||||
|> mapError { error -> JoinChatFolderLinkError in
|
||||
if error.errorDescription == "DIALOG_FILTERS_TOO_MUCH" {
|
||||
if error.errorDescription.hasPrefix("DIALOG_FILTERS_TOO_MUCH") {
|
||||
return .limitExceeded
|
||||
} else {
|
||||
return .generic
|
||||
@ -360,7 +360,9 @@ func _internal_joinChatFolderLink(account: Account, slug: String, peerIds: [Engi
|
||||
}
|
||||
}
|
||||
|
||||
public final class ChatFolderUpdates {
|
||||
public final class ChatFolderUpdates: Equatable {
|
||||
fileprivate let folderId: Int32
|
||||
fileprivate let title: String
|
||||
fileprivate let missingPeers: [Api.Peer]
|
||||
fileprivate let chats: [Api.Chat]
|
||||
fileprivate let users: [Api.User]
|
||||
@ -369,15 +371,44 @@ public final class ChatFolderUpdates {
|
||||
return self.missingPeers.count
|
||||
}
|
||||
|
||||
public var chatFolderLinkContents: ChatFolderLinkContents {
|
||||
var peers: [EnginePeer] = []
|
||||
for missingPeer in self.missingPeers {
|
||||
for chat in chats {
|
||||
if chat.peerId == missingPeer.peerId {
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(EnginePeer(peer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ChatFolderLinkContents(localFilterId: self.folderId, title: self.title, peers: peers, alreadyMemberPeerIds: Set())
|
||||
}
|
||||
|
||||
fileprivate init(
|
||||
folderId: Int32,
|
||||
title: String,
|
||||
missingPeers: [Api.Peer],
|
||||
chats: [Api.Chat],
|
||||
users: [Api.User]
|
||||
) {
|
||||
self.folderId = folderId
|
||||
self.title = title
|
||||
self.missingPeers = missingPeers
|
||||
self.chats = chats
|
||||
self.users = users
|
||||
}
|
||||
|
||||
public static func ==(lhs: ChatFolderUpdates, rhs: ChatFolderUpdates) -> Bool {
|
||||
if lhs.folderId != rhs.folderId {
|
||||
return false
|
||||
}
|
||||
if lhs.missingPeers.map(\.peerId) != rhs.missingPeers.map(\.peerId) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_getChatFolderUpdates(account: Account, folderId: Int32) -> Signal<ChatFolderUpdates?, NoError> {
|
||||
@ -392,18 +423,25 @@ func _internal_getChatFolderUpdates(account: Account, folderId: Int32) -> Signal
|
||||
}
|
||||
switch result {
|
||||
case let .communityUpdates(missingPeers, chats, users):
|
||||
return .single(ChatFolderUpdates(missingPeers: missingPeers, chats: chats, users: users))
|
||||
return account.postbox.transaction { transaction -> ChatFolderUpdates? in
|
||||
for filter in _internal_currentChatListFilters(transaction: transaction) {
|
||||
if case let .filter(id, title, _, _) = filter, id == folderId {
|
||||
return ChatFolderUpdates(folderId: folderId, title: title, missingPeers: missingPeers, chats: chats, users: users)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_joinAvailableChatsInFolder(account: Account, folderId: Int32, peerIds: [EnginePeer.Id]) -> Signal<Never, JoinChatFolderLinkError> {
|
||||
func _internal_joinAvailableChatsInFolder(account: Account, updates: ChatFolderUpdates, peerIds: [EnginePeer.Id]) -> Signal<Never, JoinChatFolderLinkError> {
|
||||
return account.postbox.transaction { transaction -> [Api.InputPeer] in
|
||||
return peerIds.compactMap(transaction.getPeer).compactMap(apiInputPeer)
|
||||
}
|
||||
|> castError(JoinChatFolderLinkError.self)
|
||||
|> mapToSignal { inputPeers -> Signal<Never, JoinChatFolderLinkError> in
|
||||
return account.network.request(Api.functions.communities.joinCommunityUpdates(community: .inputCommunityDialogFilter(filterId: folderId), peers: inputPeers))
|
||||
return account.network.request(Api.functions.communities.joinCommunityUpdates(community: .inputCommunityDialogFilter(filterId: updates.folderId), peers: inputPeers))
|
||||
|> mapError { error -> JoinChatFolderLinkError in
|
||||
if error.errorDescription == "DIALOG_FILTERS_TOO_MUCH" {
|
||||
return .limitExceeded
|
||||
|
@ -1054,8 +1054,8 @@ public extension TelegramEngine {
|
||||
return _internal_getChatFolderUpdates(account: self.account, folderId: folderId)
|
||||
}
|
||||
|
||||
public func joinAvailableChatsInFolder(folderId: Int32, peerIds: [EnginePeer.Id]) -> Signal<Never, JoinChatFolderLinkError> {
|
||||
return _internal_joinAvailableChatsInFolder(account: self.account, folderId: folderId, peerIds: peerIds)
|
||||
public func joinAvailableChatsInFolder(updates: ChatFolderUpdates, peerIds: [EnginePeer.Id]) -> Signal<Never, JoinChatFolderLinkError> {
|
||||
return _internal_joinAvailableChatsInFolder(account: self.account, updates: updates, peerIds: peerIds)
|
||||
}
|
||||
|
||||
public func hideChatFolderUpdates(folderId: Int32) -> Signal<Never, NoError> {
|
||||
|
@ -21,16 +21,16 @@ private final class ChatFolderLinkPreviewScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let slug: String
|
||||
let subject: ChatFolderLinkPreviewScreen.Subject
|
||||
let linkContents: ChatFolderLinkContents?
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
slug: String,
|
||||
subject: ChatFolderLinkPreviewScreen.Subject,
|
||||
linkContents: ChatFolderLinkContents?
|
||||
) {
|
||||
self.context = context
|
||||
self.slug = slug
|
||||
self.subject = subject
|
||||
self.linkContents = linkContents
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ private final class ChatFolderLinkPreviewScreenComponent: Component {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.slug != rhs.slug {
|
||||
if lhs.subject != rhs.subject {
|
||||
return false
|
||||
}
|
||||
if lhs.linkContents !== rhs.linkContents {
|
||||
@ -660,7 +660,15 @@ private final class ChatFolderLinkPreviewScreenComponent: Component {
|
||||
|
||||
if let _ = component.linkContents {
|
||||
if self.joinDisposable == nil, !self.selectedItems.isEmpty {
|
||||
self.joinDisposable = (component.context.engine.peers.joinChatFolderLink(slug: component.slug, peerIds: Array(self.selectedItems))
|
||||
let joinSignal: Signal<Never, JoinChatFolderLinkError>
|
||||
switch component.subject {
|
||||
case let .slug(slug):
|
||||
joinSignal = component.context.engine.peers.joinChatFolderLink(slug: slug, peerIds: Array(self.selectedItems))
|
||||
case let .updates(updates):
|
||||
joinSignal = component.context.engine.peers.joinAvailableChatsInFolder(updates: updates, peerIds: Array(self.selectedItems))
|
||||
}
|
||||
|
||||
self.joinDisposable = (joinSignal
|
||||
|> deliverOnMainQueue).start(error: { [weak self] error in
|
||||
guard let self, let component = self.component, let controller = self.environment?.controller() else {
|
||||
return
|
||||
@ -782,29 +790,33 @@ private final class ChatFolderLinkPreviewScreenComponent: Component {
|
||||
}
|
||||
|
||||
public class ChatFolderLinkPreviewScreen: ViewControllerComponentContainer {
|
||||
public enum Subject: Equatable {
|
||||
case slug(String)
|
||||
case updates(ChatFolderUpdates)
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private var linkContents: ChatFolderLinkContents?
|
||||
private var linkContentsDisposable: Disposable?
|
||||
|
||||
private var isDismissed: Bool = false
|
||||
|
||||
public init(context: AccountContext, slug: String) {
|
||||
public init(context: AccountContext, subject: Subject, contents: ChatFolderLinkContents) {
|
||||
self.context = context
|
||||
|
||||
super.init(context: context, component: ChatFolderLinkPreviewScreenComponent(context: context, slug: slug, linkContents: nil), navigationBarAppearance: .none)
|
||||
super.init(context: context, component: ChatFolderLinkPreviewScreenComponent(context: context, subject: subject, linkContents: contents), navigationBarAppearance: .none)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
self.navigationPresentation = .flatModal
|
||||
self.blocksBackgroundWhenInOverlay = true
|
||||
self.automaticallyControlPresentationContextLayout = false
|
||||
|
||||
self.linkContentsDisposable = (context.engine.peers.checkChatFolderLink(slug: slug)
|
||||
/*self.linkContentsDisposable = (context.engine.peers.checkChatFolderLink(subject: subject)
|
||||
|> delay(0.2, queue: .mainQueue())
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.linkContents = result
|
||||
self.updateComponent(component: AnyComponent(ChatFolderLinkPreviewScreenComponent(context: context, slug: slug, linkContents: result)), transition: Transition(animation: .curve(duration: 0.2, curve: .easeInOut)).withUserData(ChatFolderLinkPreviewScreenComponent.AnimationHint()))
|
||||
self.updateComponent(component: AnyComponent(ChatFolderLinkPreviewScreenComponent(context: context, subject: subject, linkContents: result)), transition: Transition(animation: .curve(duration: 0.2, curve: .easeInOut)).withUserData(ChatFolderLinkPreviewScreenComponent.AnimationHint()))
|
||||
}, error: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
@ -812,9 +824,7 @@ public class ChatFolderLinkPreviewScreen: ViewControllerComponentContainer {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "The folder link has expired."), elevatedLayout: false, action: { _ in true }), in: .window(.root))
|
||||
self.dismiss()
|
||||
})
|
||||
|
||||
self.automaticallyControlPresentationContextLayout = false
|
||||
})*/
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
|
@ -265,6 +265,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
||||
}, openStorageManagement: {
|
||||
}, openPasswordSetup: {
|
||||
}, openPremiumIntro: {
|
||||
}, openChatFolderUpdates: {
|
||||
})
|
||||
interaction.searchTextHighightState = searchQuery
|
||||
self.interaction = interaction
|
||||
|
@ -753,7 +753,48 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
}
|
||||
case let .chatFolder(slug):
|
||||
if let navigationController = navigationController {
|
||||
navigationController.pushViewController(ChatFolderLinkPreviewScreen(context: context, slug: slug))
|
||||
let signal = context.engine.peers.checkChatFolderLink(slug: slug)
|
||||
|
||||
var cancelImpl: (() -> Void)?
|
||||
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
present(controller, nil)
|
||||
return ActionDisposable { [weak controller] in
|
||||
Queue.mainQueue().async() {
|
||||
controller?.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
|> delay(0.35, queue: Queue.mainQueue())
|
||||
|
||||
let disposable = MetaDisposable()
|
||||
let progressDisposable = progressSignal.start()
|
||||
cancelImpl = {
|
||||
disposable.set(nil)
|
||||
}
|
||||
disposable.set((signal
|
||||
|> afterDisposed {
|
||||
Queue.mainQueue().async {
|
||||
progressDisposable.dispose()
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak navigationController] result in
|
||||
guard let navigationController else {
|
||||
return
|
||||
}
|
||||
navigationController.pushViewController(ChatFolderLinkPreviewScreen(context: context, subject: .slug(slug), contents: result))
|
||||
}, error: { error in
|
||||
let errorText: String
|
||||
switch error {
|
||||
case .generic:
|
||||
errorText = "The folder link has expired."
|
||||
}
|
||||
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
}))
|
||||
dismissInput()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user