mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
1b8b4562ba
@ -90,6 +90,7 @@ swift_library(
|
||||
"//submodules/AvatarVideoNode:AvatarVideoNode",
|
||||
"//submodules/InviteLinksUI",
|
||||
"//submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen",
|
||||
"//submodules/ItemListUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -1547,8 +1547,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
for filter in filters {
|
||||
if filter.id == filterId, case let .filter(_, title, _, data) = filter {
|
||||
if !data.includePeers.peers.isEmpty {
|
||||
items.append(.action(ContextMenuActionItem(text: "Share", textColor: .primary, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor)
|
||||
items.append(.action(ContextMenuActionItem(text: "Share", textColor: .primary, badge: ContextMenuActionBadge(value: "NEW", color: .accent, style: .label), icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { c, f in
|
||||
c.dismiss(completion: {
|
||||
guard let strongSelf = self else {
|
||||
@ -2701,6 +2701,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
|
||||
private func shareFolder(filterId: Int32, data: ChatListFilterData, title: String) {
|
||||
openCreateChatListFolderLink(context: self.context, folderId: filterId, title: title, peerIds: data.includePeers.peers, pushController: { [weak self] c in
|
||||
self?.push(c)
|
||||
}, presentController: { [weak self] c in
|
||||
self?.present(c, in: .window(.root))
|
||||
}, linkUpdated: { _ in
|
||||
})
|
||||
|
||||
/*self.push(folderInviteLinkListController(context: self.context, filterId: filterId, title: title, allPeerIds: data.includePeers.peers, currentInvitation: nil, linkUpdated: { _ in
|
||||
}))*/
|
||||
}
|
||||
|
@ -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: {}, openChatFolderUpdates: {})
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {}, hideChatFolderUpdates: {})
|
||||
interaction.isInlineMode = isInlineMode
|
||||
|
||||
let items = (0 ..< 2).map { _ -> ChatListItem in
|
||||
|
@ -519,7 +519,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
case .inviteLinkHeader:
|
||||
//TODO:localize
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: "INVITE LINK", badge: "NEW", sectionId: self.section)
|
||||
case let.inviteLinkCreate(hasLinks):
|
||||
case let .inviteLinkCreate(hasLinks):
|
||||
//TODO:localize
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.linkIcon(presentationData.theme), title: hasLinks ? "Create a New Link" : "Share Folder", sectionId: self.section, editing: false, action: {
|
||||
arguments.createLink()
|
||||
@ -654,15 +654,22 @@ private func chatListFilterPresetControllerEntries(presentationData: Presentatio
|
||||
entries.append(.excludePeerInfo(presentationData.strings.ChatListFolder_ExcludeSectionInfo))
|
||||
}
|
||||
|
||||
if !isNewFilter, let inviteLinks {
|
||||
if !isNewFilter {
|
||||
entries.append(.inviteLinkHeader)
|
||||
entries.append(.inviteLinkCreate(hasLinks: !inviteLinks.isEmpty))
|
||||
|
||||
var hasLinks = false
|
||||
if let inviteLinks, !inviteLinks.isEmpty {
|
||||
hasLinks = true
|
||||
}
|
||||
entries.append(.inviteLinkCreate(hasLinks: hasLinks))
|
||||
|
||||
if let inviteLinks {
|
||||
var index = 0
|
||||
for link in inviteLinks {
|
||||
entries.append(.inviteLink(index, link))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
entries.append(.inviteLinkInfo)
|
||||
}
|
||||
@ -1065,6 +1072,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
var focusOnNameImpl: (() -> Void)?
|
||||
var applyImpl: ((@escaping () -> Void) -> Void)?
|
||||
|
||||
let sharedLinks = Promise<[ExportedChatFolderLink]?>(nil)
|
||||
if let currentPreset {
|
||||
@ -1273,85 +1281,47 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
}
|
||||
},
|
||||
createLink: {
|
||||
applyImpl?({
|
||||
let state = stateValue.with({ $0 })
|
||||
|
||||
if let currentPreset, !state.additionallyIncludePeers.isEmpty {
|
||||
let _ = (context.engine.data.get(
|
||||
EngineDataList(state.additionallyIncludePeers.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { peers in
|
||||
let peers = peers.compactMap({ $0 })
|
||||
if peers.allSatisfy({ !canShareLinkToPeer(peer: $0) }) {
|
||||
pushControllerImpl?(folderInviteLinkListController(context: context, filterId: currentPreset.id, title: currentPreset.title, allPeerIds: state.additionallyIncludePeers, currentInvitation: nil, linkUpdated: { updatedLink in
|
||||
let _ = (sharedLinks.get() |> take(1) |> deliverOnMainQueue).start(next: { links in
|
||||
guard var links else {
|
||||
return
|
||||
}
|
||||
|
||||
if let updatedLink {
|
||||
links.insert(updatedLink, at: 0)
|
||||
sharedLinks.set(.single(links))
|
||||
}
|
||||
})
|
||||
}))
|
||||
} else {
|
||||
let _ = (context.engine.peers.exportChatFolder(filterId: currentPreset.id, title: "", peerIds: state.additionallyIncludePeers)
|
||||
|> deliverOnMainQueue).start(next: { link in
|
||||
let _ = (sharedLinks.get() |> take(1) |> deliverOnMainQueue).start(next: { links in
|
||||
guard var links else {
|
||||
return
|
||||
}
|
||||
|
||||
links.insert(link, at: 0)
|
||||
sharedLinks.set(.single(links))
|
||||
})
|
||||
|
||||
pushControllerImpl?(folderInviteLinkListController(context: context, filterId: currentPreset.id, title: currentPreset.title, allPeerIds: state.additionallyIncludePeers, currentInvitation: link, linkUpdated: { updatedLink in
|
||||
if updatedLink != link {
|
||||
let _ = (sharedLinks.get() |> take(1) |> deliverOnMainQueue).start(next: { links in
|
||||
guard var links else {
|
||||
return
|
||||
}
|
||||
|
||||
if let updatedLink {
|
||||
if let index = links.firstIndex(where: { $0 == link }) {
|
||||
links.remove(at: index)
|
||||
}
|
||||
links.insert(updatedLink, at: 0)
|
||||
sharedLinks.set(.single(links))
|
||||
} else {
|
||||
if let index = links.firstIndex(where: { $0 == link }) {
|
||||
links.remove(at: index)
|
||||
sharedLinks.set(.single(links))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
}, error: { error in
|
||||
if let currentPreset, let data = currentPreset.data {
|
||||
//TODO:localize
|
||||
let text: String
|
||||
switch error {
|
||||
case .generic:
|
||||
text = "An error occurred"
|
||||
case let .limitExceeded(limit, premiumLimit):
|
||||
if limit < premiumLimit {
|
||||
let limitController = context.sharedContext.makePremiumLimitController(context: context, subject: .linksPerSharedFolder, count: limit, action: {
|
||||
})
|
||||
pushControllerImpl?(limitController)
|
||||
var unavailableText: String?
|
||||
if !data.categories.isEmpty || data.excludeArchived || data.excludeRead || data.excludeMuted || !data.excludePeers.isEmpty {
|
||||
unavailableText = "You can't share a link to this folder."
|
||||
}
|
||||
if let unavailableText {
|
||||
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
|
||||
}
|
||||
text = "You can't create more links."
|
||||
|
||||
openCreateChatListFolderLink(context: context, folderId: currentPreset.id, title: currentPreset.title, peerIds: state.additionallyIncludePeers, pushController: { c in
|
||||
pushControllerImpl?(c)
|
||||
}, presentController: { c in
|
||||
presentControllerImpl?(c, nil)
|
||||
}, linkUpdated: { updatedLink in
|
||||
let _ = (sharedLinks.get() |> take(1) |> deliverOnMainQueue).start(next: { links in
|
||||
guard var links else {
|
||||
return
|
||||
}
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
|
||||
if let updatedLink {
|
||||
if let index = links.firstIndex(where: { $0.link == updatedLink.link }) {
|
||||
links[index] = updatedLink
|
||||
} else {
|
||||
links.insert(updatedLink, at: 0)
|
||||
}
|
||||
sharedLinks.set(.single(links))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}, openLink: { link in
|
||||
if let currentPreset, let _ = currentPreset.data {
|
||||
applyImpl?({
|
||||
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 {
|
||||
@ -1375,12 +1345,13 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
})
|
||||
}
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
var attemptNavigationImpl: (() -> Bool)?
|
||||
let applyImpl: (() -> Void)? = {
|
||||
applyImpl = { completed in
|
||||
let state = stateValue.with { $0 }
|
||||
let _ = (context.engine.peers.updateChatListFiltersInteractively { filters in
|
||||
var includePeers = ChatListFilterIncludePeers()
|
||||
@ -1422,7 +1393,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { filters in
|
||||
updated(filters)
|
||||
dismissImpl?()
|
||||
completed()
|
||||
})
|
||||
}
|
||||
|
||||
@ -1449,7 +1420,9 @@ 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?({
|
||||
dismissImpl?()
|
||||
})
|
||||
})
|
||||
|
||||
let previousStateValue = previousState
|
||||
@ -1531,3 +1504,42 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
return controller
|
||||
}
|
||||
|
||||
func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, title: String, peerIds: [EnginePeer.Id], pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController) -> Void, linkUpdated: @escaping (ExportedChatFolderLink?) -> Void) {
|
||||
if peerIds.isEmpty {
|
||||
return
|
||||
}
|
||||
let _ = (context.engine.data.get(
|
||||
EngineDataList(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { peers in
|
||||
let peers = peers.compactMap({ $0 })
|
||||
if peers.allSatisfy({ !canShareLinkToPeer(peer: $0) }) {
|
||||
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peerIds, currentInvitation: nil, linkUpdated: linkUpdated))
|
||||
} else {
|
||||
let _ = (context.engine.peers.exportChatFolder(filterId: folderId, title: "", peerIds: peerIds)
|
||||
|> deliverOnMainQueue).start(next: { link in
|
||||
linkUpdated(link)
|
||||
|
||||
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: link.peerIds, currentInvitation: link, linkUpdated: linkUpdated))
|
||||
}, error: { error in
|
||||
//TODO:localize
|
||||
let text: String
|
||||
switch error {
|
||||
case .generic:
|
||||
text = "An error occurred"
|
||||
case let .limitExceeded(limit, premiumLimit):
|
||||
if limit < premiumLimit {
|
||||
let limitController = context.sharedContext.makePremiumLimitController(context: context, subject: .linksPerSharedFolder, count: limit, action: {
|
||||
})
|
||||
pushController(limitController)
|
||||
|
||||
return
|
||||
}
|
||||
text = "You can't create more links."
|
||||
}
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
presentController(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -2165,6 +2165,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}, openPasswordSetup: {
|
||||
}, openPremiumIntro: {
|
||||
}, openChatFolderUpdates: {
|
||||
}, hideChatFolderUpdates: {
|
||||
})
|
||||
chatListInteraction.isSearchMode = true
|
||||
|
||||
@ -3398,7 +3399,8 @@ 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: {}, openChatFolderUpdates: {})
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
|
||||
})
|
||||
var isInlineMode = false
|
||||
if case .topics = key {
|
||||
isInlineMode = false
|
||||
|
@ -97,6 +97,7 @@ public final class ChatListNodeInteraction {
|
||||
let openPasswordSetup: () -> Void
|
||||
let openPremiumIntro: () -> Void
|
||||
let openChatFolderUpdates: () -> Void
|
||||
let hideChatFolderUpdates: () -> Void
|
||||
|
||||
public var searchTextHighightState: String?
|
||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||
@ -142,7 +143,8 @@ public final class ChatListNodeInteraction {
|
||||
openStorageManagement: @escaping () -> Void,
|
||||
openPasswordSetup: @escaping () -> Void,
|
||||
openPremiumIntro: @escaping () -> Void,
|
||||
openChatFolderUpdates: @escaping () -> Void
|
||||
openChatFolderUpdates: @escaping () -> Void,
|
||||
hideChatFolderUpdates: @escaping () -> Void
|
||||
) {
|
||||
self.activateSearch = activateSearch
|
||||
self.peerSelected = peerSelected
|
||||
@ -176,6 +178,7 @@ public final class ChatListNodeInteraction {
|
||||
self.openPasswordSetup = openPasswordSetup
|
||||
self.openPremiumIntro = openPremiumIntro
|
||||
self.openChatFolderUpdates = openChatFolderUpdates
|
||||
self.hideChatFolderUpdates = hideChatFolderUpdates
|
||||
}
|
||||
}
|
||||
|
||||
@ -616,7 +619,9 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
case let .ArchiveIntro(presentationData):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
|
||||
case let .Notice(presentationData, notice):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, notice: notice, action: { [weak nodeInteraction] in
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, notice: notice, action: { [weak nodeInteraction] action in
|
||||
switch action {
|
||||
case .activate:
|
||||
switch notice {
|
||||
case .clearStorage:
|
||||
nodeInteraction?.openStorageManagement()
|
||||
@ -627,6 +632,14 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
case .chatFolderUpdates:
|
||||
nodeInteraction?.openChatFolderUpdates()
|
||||
}
|
||||
case .hide:
|
||||
switch notice {
|
||||
case .chatFolderUpdates:
|
||||
nodeInteraction?.hideChatFolderUpdates()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}), directionHint: entry.directionHint)
|
||||
}
|
||||
}
|
||||
@ -871,7 +884,9 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
case let .ArchiveIntro(presentationData):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
|
||||
case let .Notice(presentationData, notice):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, notice: notice, action: { [weak nodeInteraction] in
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, notice: notice, action: { [weak nodeInteraction] action in
|
||||
switch action {
|
||||
case .activate:
|
||||
switch notice {
|
||||
case .clearStorage:
|
||||
nodeInteraction?.openStorageManagement()
|
||||
@ -882,6 +897,14 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
case .chatFolderUpdates:
|
||||
nodeInteraction?.openChatFolderUpdates()
|
||||
}
|
||||
case .hide:
|
||||
switch notice {
|
||||
case .chatFolderUpdates:
|
||||
nodeInteraction?.hideChatFolderUpdates()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}), directionHint: entry.directionHint)
|
||||
case .HeaderEntry:
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyHeaderItem(), directionHint: entry.directionHint)
|
||||
@ -1076,8 +1099,9 @@ public final class ChatListNode: ListView {
|
||||
|
||||
let hideArhiveIntro = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
|
||||
private let chatFolderUpdates = Promise<ChatFolderUpdates?>(nil)
|
||||
private let chatFolderUpdates = Promise<ChatFolderUpdates?>()
|
||||
private var pollFilterUpdatesDisposable: Disposable?
|
||||
private var chatFilterUpdatesDisposable: 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
|
||||
@ -1413,6 +1437,21 @@ public final class ChatListNode: ListView {
|
||||
|
||||
self.push?(ChatFolderLinkPreviewScreen(context: self.context, subject: .updates(result), contents: result.chatFolderLinkContents))
|
||||
})
|
||||
}, hideChatFolderUpdates: { [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
|
||||
}
|
||||
|
||||
if let localFilterId = result.chatFolderLinkContents.localFilterId {
|
||||
let _ = self.context.engine.peers.hideChatFolderUpdates(folderId: localFilterId).start()
|
||||
}
|
||||
})
|
||||
})
|
||||
nodeInteraction.isInlineMode = isInlineMode
|
||||
|
||||
@ -1973,6 +2012,7 @@ public final class ChatListNode: ListView {
|
||||
var didIncludeRemovingPeerId = false
|
||||
var didIncludeHiddenByDefaultArchive = false
|
||||
var didIncludeHiddenThread = false
|
||||
var didIncludeNotice = false
|
||||
if let previous = previousView {
|
||||
for entry in previous.filteredEntries {
|
||||
if case let .PeerEntry(peerEntry) = entry {
|
||||
@ -1999,12 +2039,15 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
} else if case let .GroupReferenceEntry(_, _, _, _, _, _, _, _, hiddenByDefault) = entry {
|
||||
didIncludeHiddenByDefaultArchive = hiddenByDefault
|
||||
} else if case .Notice = entry {
|
||||
didIncludeNotice = true
|
||||
}
|
||||
}
|
||||
}
|
||||
var doesIncludeRemovingPeerId = false
|
||||
var doesIncludeArchive = false
|
||||
var doesIncludeHiddenByDefaultArchive = false
|
||||
var doesIncludeNotice = false
|
||||
|
||||
var doesIncludeHiddenThread = false
|
||||
for entry in processedView.filteredEntries {
|
||||
@ -2033,6 +2076,8 @@ public final class ChatListNode: ListView {
|
||||
} else if case let .GroupReferenceEntry(_, _, _, _, _, _, _, _, hiddenByDefault) = entry {
|
||||
doesIncludeArchive = true
|
||||
doesIncludeHiddenByDefaultArchive = hiddenByDefault
|
||||
} else if case .Notice = entry {
|
||||
doesIncludeNotice = true
|
||||
}
|
||||
}
|
||||
if previousPinnedChats != updatedPinnedChats || previousPinnedThreads != updatedPinnedThreads {
|
||||
@ -2059,6 +2104,9 @@ public final class ChatListNode: ListView {
|
||||
if didIncludeHiddenThread != doesIncludeHiddenThread {
|
||||
disableAnimations = false
|
||||
}
|
||||
if didIncludeNotice != doesIncludeNotice {
|
||||
disableAnimations = false
|
||||
}
|
||||
}
|
||||
|
||||
if let _ = previousHideArchivedFolderByDefaultValue, previousHideArchivedFolderByDefaultValue != hideArchivedFolderByDefault {
|
||||
@ -2578,7 +2626,7 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
}
|
||||
|
||||
self.pollFilterUpdates(shouldDelay: false)
|
||||
self.pollFilterUpdates()
|
||||
self.resetFilter()
|
||||
|
||||
let selectionRecognizer = ChatHistoryListSelectionRecognizer(target: self, action: #selector(self.selectionPanGesture(_:)))
|
||||
@ -2596,6 +2644,7 @@ public final class ChatListNode: ListView {
|
||||
self.activityStatusesDisposable?.dispose()
|
||||
self.updatedFilterDisposable.dispose()
|
||||
self.pollFilterUpdatesDisposable?.dispose()
|
||||
self.chatFilterUpdatesDisposable?.dispose()
|
||||
}
|
||||
|
||||
func updateFilter(_ filter: ChatListFilter?) {
|
||||
@ -2605,17 +2654,18 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
}
|
||||
|
||||
private func pollFilterUpdates(shouldDelay: Bool) {
|
||||
private func pollFilterUpdates() {
|
||||
guard let chatListFilter, case let .filter(id, _, _, data) = chatListFilter, data.isShared else {
|
||||
self.chatFolderUpdates.set(.single(nil))
|
||||
return
|
||||
}
|
||||
self.pollFilterUpdatesDisposable = (context.engine.peers.getChatFolderUpdates(folderId: id)
|
||||
|> delay(shouldDelay ? 5.0 : 0.0, queue: .mainQueue())).start(next: { [weak self] result in
|
||||
self.pollFilterUpdatesDisposable = self.context.engine.peers.pollChatFolderUpdates(folderId: id).start()
|
||||
self.chatFilterUpdatesDisposable = (self.context.engine.peers.subscribedChatFolderUpdates(folderId: id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.chatFolderUpdates.set(.single(result))
|
||||
self.pollFilterUpdates(shouldDelay: true)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -7,16 +7,22 @@ import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import ListSectionHeaderNode
|
||||
import AppBundle
|
||||
import ItemListUI
|
||||
|
||||
class ChatListStorageInfoItem: ListViewItem {
|
||||
enum Action {
|
||||
case activate
|
||||
case hide
|
||||
}
|
||||
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let notice: ChatListNotice
|
||||
let action: () -> Void
|
||||
let action: (Action) -> Void
|
||||
|
||||
let selectable: Bool = true
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, notice: ChatListNotice, action: @escaping () -> Void) {
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, notice: ChatListNotice, action: @escaping (Action) -> Void) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.notice = notice
|
||||
@ -26,7 +32,7 @@ class ChatListStorageInfoItem: ListViewItem {
|
||||
func selected(listView: ListView) {
|
||||
listView.clearHighlightAnimated(true)
|
||||
|
||||
self.action()
|
||||
self.action(.activate)
|
||||
}
|
||||
|
||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
@ -72,7 +78,8 @@ private let separatorHeight = 1.0 / UIScreen.main.scale
|
||||
private let titleFont = Font.semibold(15.0)
|
||||
private let textFont = Font.regular(15.0)
|
||||
|
||||
class ChatListStorageInfoItemNode: ListViewItemNode {
|
||||
class ChatListStorageInfoItemNode: ItemListRevealOptionsItemNode {
|
||||
private let contentContainer: ASDisplayNode
|
||||
private let titleNode: TextNode
|
||||
private let textNode: TextNode
|
||||
private let arrowNode: ASImageNode
|
||||
@ -81,17 +88,23 @@ class ChatListStorageInfoItemNode: ListViewItemNode {
|
||||
private var item: ChatListStorageInfoItem?
|
||||
|
||||
required init() {
|
||||
self.contentContainer = ASDisplayNode()
|
||||
|
||||
self.titleNode = TextNode()
|
||||
self.textNode = TextNode()
|
||||
self.arrowNode = ASImageNode()
|
||||
self.separatorNode = ASDisplayNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.arrowNode)
|
||||
self.contentContainer.addSubnode(self.titleNode)
|
||||
self.contentContainer.addSubnode(self.textNode)
|
||||
self.contentContainer.addSubnode(self.arrowNode)
|
||||
|
||||
self.addSubnode(self.contentContainer)
|
||||
|
||||
self.zPosition = 1.0
|
||||
}
|
||||
@ -201,8 +214,35 @@ class ChatListStorageInfoItemNode: ListViewItemNode {
|
||||
|
||||
strongSelf.contentSize = layout.contentSize
|
||||
strongSelf.insets = layout.insets
|
||||
|
||||
strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset)
|
||||
|
||||
strongSelf.contentContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
|
||||
switch item.notice {
|
||||
case .chatFolderUpdates:
|
||||
//TODO:locallize
|
||||
strongSelf.setRevealOptions((left: [], right: [ItemListRevealOption(key: 0, title: "Hide", icon: .none, color: item.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.theme.list.itemDisclosureActions.destructive.foregroundColor)]))
|
||||
default:
|
||||
strongSelf.setRevealOptions((left: [], right: []))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override public func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
super.updateRevealOffset(offset: offset, transition: transition)
|
||||
|
||||
transition.updateSublayerTransformOffset(layer: self.contentContainer.layer, offset: CGPoint(x: offset, y: 0.0))
|
||||
}
|
||||
|
||||
override public func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) {
|
||||
if let item = self.item {
|
||||
item.action(.hide)
|
||||
}
|
||||
|
||||
self.setRevealOptionsOpened(false, animated: true)
|
||||
self.revealOptionsInteractivelyClosed()
|
||||
}
|
||||
}
|
||||
|
@ -75,13 +75,20 @@ public enum ContextMenuActionBadgeColor {
|
||||
case inactive
|
||||
}
|
||||
|
||||
public struct ContextMenuActionBadge {
|
||||
public struct ContextMenuActionBadge: Equatable {
|
||||
public enum Style {
|
||||
case badge
|
||||
case label
|
||||
}
|
||||
|
||||
public var value: String
|
||||
public var color: ContextMenuActionBadgeColor
|
||||
public var style: Style
|
||||
|
||||
public init(value: String, color: ContextMenuActionBadgeColor) {
|
||||
public init(value: String, color: ContextMenuActionBadgeColor, style: Style = .badge) {
|
||||
self.value = value
|
||||
self.color = color
|
||||
self.style = style
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,8 +64,11 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
private let titleLabelNode: ImmediateTextNode
|
||||
private let subtitleNode: ImmediateTextNode
|
||||
private let iconNode: ASImageNode
|
||||
private var badgeIconNode: ASImageNode?
|
||||
private var animationNode: AnimationNode?
|
||||
|
||||
private var currentBadge: (badge: ContextMenuActionBadge, image: UIImage)?
|
||||
|
||||
private var iconDisposable: Disposable?
|
||||
|
||||
init(
|
||||
@ -302,14 +305,66 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
iconSize = iconImage?.size
|
||||
}
|
||||
|
||||
let badgeSize: CGSize?
|
||||
if let badge = self.item.badge {
|
||||
var badgeImage: UIImage?
|
||||
if let currentBadge = self.currentBadge, currentBadge.badge == badge {
|
||||
badgeImage = currentBadge.image
|
||||
} else {
|
||||
let badgeTextColor: UIColor = presentationData.theme.list.itemCheckColors.foregroundColor
|
||||
let badgeString = NSAttributedString(string: badge.value, font: Font.semibold(11.0), textColor: badgeTextColor)
|
||||
let badgeTextBounds = badgeString.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil)
|
||||
let badgeSideInset: CGFloat = 3.0
|
||||
let badgeVerticalInset: CGFloat = 1.0
|
||||
let badgeBackgroundSize = CGSize(width: badgeSideInset * 2.0 + ceil(badgeTextBounds.width), height: badgeVerticalInset * 2.0 + ceil(badgeTextBounds.height))
|
||||
badgeImage = generateImage(badgeBackgroundSize, rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: 5.0).cgPath)
|
||||
context.fillPath()
|
||||
|
||||
UIGraphicsPushContext(context)
|
||||
|
||||
badgeString.draw(at: CGPoint(x: badgeTextBounds.minX + badgeSideInset + UIScreenPixel, y: badgeTextBounds.minY + badgeVerticalInset + UIScreenPixel))
|
||||
|
||||
UIGraphicsPopContext()
|
||||
})
|
||||
}
|
||||
|
||||
let badgeIconNode: ASImageNode
|
||||
if let current = self.badgeIconNode {
|
||||
badgeIconNode = current
|
||||
} else {
|
||||
badgeIconNode = ASImageNode()
|
||||
self.badgeIconNode = badgeIconNode
|
||||
self.addSubnode(badgeIconNode)
|
||||
}
|
||||
badgeIconNode.image = badgeImage
|
||||
|
||||
badgeSize = badgeImage?.size
|
||||
} else {
|
||||
if let badgeIconNode = self.badgeIconNode {
|
||||
self.badgeIconNode = nil
|
||||
badgeIconNode.removeFromSupernode()
|
||||
}
|
||||
badgeSize = nil
|
||||
}
|
||||
|
||||
var maxTextWidth: CGFloat = constrainedSize.width
|
||||
maxTextWidth -= sideInset
|
||||
|
||||
if let iconSize = iconSize {
|
||||
maxTextWidth -= max(standardIconWidth, iconSize.width)
|
||||
maxTextWidth -= iconSpacing
|
||||
} else {
|
||||
maxTextWidth -= sideInset
|
||||
}
|
||||
|
||||
if let badgeSize = badgeSize {
|
||||
maxTextWidth -= badgeSize.width
|
||||
maxTextWidth -= 8.0
|
||||
}
|
||||
|
||||
maxTextWidth = max(1.0, maxTextWidth)
|
||||
|
||||
let titleSize = self.titleLabelNode.updateLayout(CGSize(width: maxTextWidth, height: 1000.0))
|
||||
@ -351,6 +406,12 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
transition.updateFrameAdditive(node: self.titleLabelNode, frame: titleFrame)
|
||||
transition.updateFrameAdditive(node: self.subtitleNode, frame: subtitleFrame)
|
||||
|
||||
if let badgeIconNode = self.badgeIconNode {
|
||||
if let iconSize = badgeIconNode.image?.size {
|
||||
transition.updateFrame(node: badgeIconNode, frame: CGRect(origin: CGPoint(x: titleFrame.maxX + 8.0, y: titleFrame.minY + floor((titleFrame.height - iconSize.height) * 0.5)), size: iconSize))
|
||||
}
|
||||
}
|
||||
|
||||
if let iconSize = iconSize {
|
||||
let iconWidth = max(standardIconWidth, iconSize.width)
|
||||
let iconFrame = CGRect(
|
||||
|
@ -95,6 +95,7 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
}, openPasswordSetup: {
|
||||
}, openPremiumIntro: {
|
||||
}, openChatFolderUpdates: {
|
||||
}, hideChatFolderUpdates: {
|
||||
})
|
||||
|
||||
let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil)
|
||||
|
@ -118,6 +118,7 @@ private class PremiumLimitAnimationComponent: Component {
|
||||
self.activeContainer.masksToBounds = true
|
||||
|
||||
self.activeBackground = SimpleLayer()
|
||||
self.activeBackground.anchorPoint = CGPoint()
|
||||
|
||||
self.badgeView = UIView()
|
||||
self.badgeView.alpha = 0.0
|
||||
@ -244,13 +245,16 @@ private class PremiumLimitAnimationComponent: Component {
|
||||
let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - lineHeight), size: CGSize(width: availableSize.width, height: lineHeight))
|
||||
self.container.frame = containerFrame
|
||||
|
||||
if !component.isPremiumDisabled {
|
||||
self.inactiveBackground.frame = CGRect(origin: .zero, size: CGSize(width: containerFrame.width / 2.0, height: lineHeight))
|
||||
self.activeContainer.frame = CGRect(origin: CGPoint(x: containerFrame.width / 2.0, y: 0.0), size: CGSize(width: containerFrame.width / 2.0, height: lineHeight))
|
||||
let activityPosition: CGFloat = floor(containerFrame.width * component.badgePosition)
|
||||
let activeWidth: CGFloat = containerFrame.width - activityPosition
|
||||
|
||||
self.activeBackground.bounds = CGRect(origin: .zero, size: CGSize(width: containerFrame.width * 3.0 / 2.0, height: lineHeight))
|
||||
if !component.isPremiumDisabled {
|
||||
self.inactiveBackground.frame = CGRect(origin: .zero, size: CGSize(width: activityPosition, height: lineHeight))
|
||||
self.activeContainer.frame = CGRect(origin: CGPoint(x: activityPosition, y: 0.0), size: CGSize(width: activeWidth, height: lineHeight))
|
||||
|
||||
self.activeBackground.frame = CGRect(origin: .zero, size: CGSize(width: activeWidth * (1.0 + 0.35), height: lineHeight))
|
||||
if self.activeBackground.animation(forKey: "movement") == nil {
|
||||
self.activeBackground.position = CGPoint(x: containerFrame.width * 3.0 / 4.0 - self.activeBackground.frame.width * 0.35, y: lineHeight / 2.0)
|
||||
self.activeBackground.position = CGPoint(x: -self.activeContainer.frame.width * 0.35, y: lineHeight / 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,7 +310,7 @@ private class PremiumLimitAnimationComponent: Component {
|
||||
if let _ = self.badgeView.layer.animation(forKey: "appearance1") {
|
||||
|
||||
} else {
|
||||
self.badgeView.center = CGPoint(x: 3.0 + (availableSize.width - 6.0) * badgePosition, y: 82.0)
|
||||
self.badgeView.center = CGPoint(x: availableSize.width * badgePosition, y: 82.0)
|
||||
}
|
||||
|
||||
if self.badgeView.frame.maxX > availableSize.width {
|
||||
@ -375,13 +379,15 @@ private class PremiumLimitAnimationComponent: Component {
|
||||
}
|
||||
self.badgeForeground.position = CGPoint(x: badgeNewValue, y: self.badgeForeground.bounds.size.height / 2.0)
|
||||
|
||||
let lineOffset = (self.activeBackground.frame.width - self.activeContainer.bounds.width) / 2.0
|
||||
let lineOffset = 0.0
|
||||
let linePreviousValue = self.activeBackground.position.x
|
||||
var lineNewValue: CGFloat = lineOffset
|
||||
if lineOffset - linePreviousValue < self.activeBackground.frame.width * 0.25 {
|
||||
lineNewValue -= self.activeBackground.frame.width * 0.35
|
||||
if linePreviousValue < 0.0 {
|
||||
lineNewValue = 0.0
|
||||
} else {
|
||||
lineNewValue = -self.activeContainer.bounds.width * 0.35
|
||||
}
|
||||
self.activeBackground.position = CGPoint(x: lineNewValue, y: self.activeBackground.bounds.size.height / 2.0)
|
||||
self.activeBackground.position = CGPoint(x: lineNewValue, y: 0.0)
|
||||
|
||||
let badgeAnimation = CABasicAnimation(keyPath: "position.x")
|
||||
badgeAnimation.duration = 4.5
|
||||
@ -585,16 +591,25 @@ public final class PremiumLimitDisplayComponent: CombinedComponent {
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let activityPosition = floor(context.availableSize.width * component.badgePosition)
|
||||
|
||||
var inactiveValueOpacity: CGFloat = 1.0
|
||||
if inactiveValue.size.width + inactiveTitle.size.width >= activityPosition - 8.0 {
|
||||
inactiveValueOpacity = 0.0
|
||||
}
|
||||
|
||||
context.add(inactiveTitle
|
||||
.position(CGPoint(x: inactiveTitle.size.width / 2.0 + 12.0, y: height - lineHeight / 2.0))
|
||||
.opacity(inactiveValueOpacity)
|
||||
)
|
||||
|
||||
context.add(inactiveValue
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0 - inactiveValue.size.width / 2.0 - 12.0, y: height - lineHeight / 2.0))
|
||||
.position(CGPoint(x: activityPosition - inactiveValue.size.width / 2.0 - 12.0, y: height - lineHeight / 2.0))
|
||||
.opacity(inactiveValueOpacity)
|
||||
)
|
||||
|
||||
context.add(activeTitle
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0 + activeTitle.size.width / 2.0 + 12.0, y: height - lineHeight / 2.0))
|
||||
.position(CGPoint(x: activityPosition + activeTitle.size.width / 2.0 + 12.0, y: height - lineHeight / 2.0))
|
||||
)
|
||||
|
||||
context.add(activeValue
|
||||
@ -766,22 +781,26 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
string = strings.Premium_MaxChatsInFolderNoPremiumText("\(limit)").string
|
||||
}
|
||||
case .linksPerSharedFolder:
|
||||
//TODO:localize
|
||||
let limit = state.limits.maxSharedFolderInviteLinks
|
||||
let premiumLimit = state.premiumLimits.maxSharedFolderInviteLinks
|
||||
/*let count: Int32 = 5 + Int32("".count)// component.count
|
||||
let limit: Int32 = 5 + Int32("".count)//state.limits.maxSharedFolderInviteLinks
|
||||
let premiumLimit: Int32 = 100 + Int32("".count)//state.premiumLimits.maxSharedFolderInviteLinks*/
|
||||
|
||||
let count: Int32 = component.count
|
||||
let limit: Int32 = state.limits.maxSharedFolderInviteLinks
|
||||
let premiumLimit: Int32 = state.premiumLimits.maxSharedFolderInviteLinks
|
||||
|
||||
iconName = "Premium/Link"
|
||||
badgeText = "\(component.count)"
|
||||
string = component.count >= premiumLimit ? strings.Premium_MaxSharedFolderLinksFinalText("\(premiumLimit)").string : strings.Premium_MaxSharedFolderLinksText("\(limit)", "\(premiumLimit)").string
|
||||
defaultValue = component.count > limit ? "\(limit)" : ""
|
||||
premiumValue = component.count >= premiumLimit ? "" : "\(premiumLimit)"
|
||||
badgePosition = CGFloat(component.count) / CGFloat(premiumLimit)
|
||||
badgeText = "\(count)"
|
||||
string = count >= premiumLimit ? strings.Premium_MaxSharedFolderLinksFinalText("\(premiumLimit)").string : strings.Premium_MaxSharedFolderLinksText("\(limit)", "\(premiumLimit)").string
|
||||
defaultValue = count > limit ? "\(limit)" : ""
|
||||
premiumValue = count >= premiumLimit ? "" : "\(premiumLimit)"
|
||||
badgePosition = max(0.1, CGFloat(count) / CGFloat(premiumLimit))
|
||||
|
||||
if isPremiumDisabled {
|
||||
badgeText = "\(limit)"
|
||||
string = strings.Premium_MaxSharedFolderLinksNoPremiumText("\(limit)").string
|
||||
}
|
||||
case .membershipInSharedFolders:
|
||||
//TODO:localize
|
||||
let limit = state.limits.maxSharedFolderJoin
|
||||
let premiumLimit = state.premiumLimits.maxSharedFolderJoin
|
||||
iconName = "Premium/Folder"
|
||||
|
@ -47,7 +47,7 @@ public final class QrCodeScreen: ViewController {
|
||||
case let .invite(invite, _):
|
||||
return invite.link ?? ""
|
||||
case let .chatFolder(slug):
|
||||
return "https://t.me/folder/\(slug)"
|
||||
return "https://t.me/list/\(slug)"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +222,8 @@ 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: {}, openChatFolderUpdates: {})
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
|
||||
})
|
||||
|
||||
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,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
}, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {})
|
||||
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
|
||||
})
|
||||
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,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {})
|
||||
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
|
||||
})
|
||||
|
||||
func makeChatListItem(
|
||||
peer: EnginePeer,
|
||||
|
@ -256,6 +256,7 @@ private enum PreferencesKeyValues: Int32 {
|
||||
case globalMessageAutoremoveTimeoutSettings = 27
|
||||
case accountSpecificCacheStorageSettings = 28
|
||||
case linksConfiguration = 29
|
||||
case chatListFilterUpdates = 30
|
||||
}
|
||||
|
||||
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
|
||||
@ -402,6 +403,12 @@ public struct PreferencesKeys {
|
||||
key.setInt32(0, value: PreferencesKeyValues.linksConfiguration.rawValue)
|
||||
return key
|
||||
}()
|
||||
|
||||
public static let chatListFilterUpdates: ValueBoxKey = {
|
||||
let key = ValueBoxKey(length: 4)
|
||||
key.setInt32(0, value: PreferencesKeyValues.chatListFilterUpdates.rawValue)
|
||||
return key
|
||||
}()
|
||||
}
|
||||
|
||||
private enum SharedDataKeyValues: Int32 {
|
||||
|
@ -872,14 +872,29 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox,
|
||||
}
|
||||
|
||||
struct ChatListFiltersState: Codable, Equatable {
|
||||
struct ChatListFilterUpdates: Codable, Equatable {
|
||||
var folderId: Int32
|
||||
var timestamp: Int32
|
||||
var peerIds: [PeerId]
|
||||
|
||||
init(folderId: Int32, timestamp: Int32, peerIds: [PeerId]) {
|
||||
self.folderId = folderId
|
||||
self.timestamp = timestamp
|
||||
self.peerIds = peerIds
|
||||
}
|
||||
}
|
||||
|
||||
var filters: [ChatListFilter]
|
||||
var remoteFilters: [ChatListFilter]?
|
||||
|
||||
static var `default` = ChatListFiltersState(filters: [], remoteFilters: nil)
|
||||
var updates: [ChatListFilterUpdates]
|
||||
|
||||
fileprivate init(filters: [ChatListFilter], remoteFilters: [ChatListFilter]?) {
|
||||
static var `default` = ChatListFiltersState(filters: [], remoteFilters: nil, updates: [])
|
||||
|
||||
fileprivate init(filters: [ChatListFilter], remoteFilters: [ChatListFilter]?, updates: [ChatListFilterUpdates]) {
|
||||
self.filters = filters
|
||||
self.remoteFilters = remoteFilters
|
||||
self.updates = updates
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@ -887,6 +902,7 @@ struct ChatListFiltersState: Codable, Equatable {
|
||||
|
||||
self.filters = try container.decode([ChatListFilter].self, forKey: "filters")
|
||||
self.remoteFilters = try container.decodeIfPresent([ChatListFilter].self, forKey: "remoteFilters")
|
||||
self.updates = try container.decodeIfPresent([ChatListFilterUpdates].self, forKey: "updates") ?? []
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
@ -894,6 +910,14 @@ struct ChatListFiltersState: Codable, Equatable {
|
||||
|
||||
try container.encode(self.filters, forKey: "filters")
|
||||
try container.encodeIfPresent(self.remoteFilters, forKey: "remoteFilters")
|
||||
try container.encode(self.updates, forKey: "updates")
|
||||
}
|
||||
|
||||
mutating func normalize() {
|
||||
if self.updates.isEmpty {
|
||||
return
|
||||
}
|
||||
self.updates.removeAll(where: { update in !self.filters.contains(where: { $0.id == update.folderId }) })
|
||||
}
|
||||
}
|
||||
|
||||
@ -918,6 +942,9 @@ func _internal_updateChatListFiltersInteractively(postbox: Postbox, _ f: @escapi
|
||||
hasUpdates = true
|
||||
}
|
||||
updated = updatedFilters
|
||||
|
||||
state.normalize()
|
||||
|
||||
return PreferencesEntry(state)
|
||||
})
|
||||
if hasUpdates {
|
||||
@ -936,6 +963,7 @@ func _internal_updateChatListFiltersInteractively(transaction: Transaction, _ f:
|
||||
state.filters = updatedFilters
|
||||
hasUpdates = true
|
||||
}
|
||||
state.normalize()
|
||||
return PreferencesEntry(state)
|
||||
})
|
||||
if hasUpdates {
|
||||
@ -943,7 +971,6 @@ func _internal_updateChatListFiltersInteractively(transaction: Transaction, _ f:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func _internal_updatedChatListFilters(postbox: Postbox) -> Signal<[ChatListFilter], NoError> {
|
||||
return postbox.preferencesView(keys: [PreferencesKeys.chatListFilters])
|
||||
|> map { preferences -> [ChatListFilter] in
|
||||
@ -953,6 +980,15 @@ func _internal_updatedChatListFilters(postbox: Postbox) -> Signal<[ChatListFilte
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
|
||||
func _internal_updatedChatListFiltersState(postbox: Postbox) -> Signal<ChatListFiltersState, NoError> {
|
||||
return postbox.preferencesView(keys: [PreferencesKeys.chatListFilters])
|
||||
|> map { preferences -> ChatListFiltersState in
|
||||
let filtersState = preferences.values[PreferencesKeys.chatListFilters]?.get(ChatListFiltersState.self) ?? ChatListFiltersState.default
|
||||
return filtersState
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
|
||||
func _internal_updatedChatListFiltersInfo(postbox: Postbox) -> Signal<(filters: [ChatListFilter], synchronized: Bool), NoError> {
|
||||
return postbox.preferencesView(keys: [PreferencesKeys.chatListFilters])
|
||||
|> map { preferences -> (filters: [ChatListFilter], synchronized: Bool) in
|
||||
@ -982,11 +1018,17 @@ func _internal_currentChatListFilters(transaction: Transaction) -> [ChatListFilt
|
||||
return settings.filters
|
||||
}
|
||||
|
||||
func _internal_currentChatListFiltersState(transaction: Transaction) -> ChatListFiltersState {
|
||||
let settings = transaction.getPreferencesEntry(key: PreferencesKeys.chatListFilters)?.get(ChatListFiltersState.self) ?? ChatListFiltersState.default
|
||||
return settings
|
||||
}
|
||||
|
||||
func updateChatListFiltersState(transaction: Transaction, _ f: (ChatListFiltersState) -> ChatListFiltersState) -> ChatListFiltersState {
|
||||
var result: ChatListFiltersState?
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.chatListFilters, { entry in
|
||||
let settings = entry?.get(ChatListFiltersState.self) ?? ChatListFiltersState.default
|
||||
let updated = f(settings)
|
||||
var updated = f(settings)
|
||||
updated.normalize()
|
||||
result = updated
|
||||
return PreferencesEntry(updated)
|
||||
})
|
||||
|
@ -60,7 +60,7 @@ func _internal_exportChatFolder(account: Account, filterId: Int32, title: String
|
||||
|> mapToSignal { inputPeers -> Signal<ExportedChatFolderLink, ExportChatFolderError> in
|
||||
return account.network.request(Api.functions.communities.exportCommunityInvite(community: .inputCommunityDialogFilter(filterId: filterId), title: title, peers: inputPeers))
|
||||
|> `catch` { error -> Signal<Api.communities.ExportedCommunityInvite, ExportChatFolderError> in
|
||||
if error.errorDescription == "INVITES_TOO_MUCH" {
|
||||
if error.errorDescription == "INVITES_TOO_MUCH" || error.errorDescription == "FILTERS_TOO_MUCH" {
|
||||
return account.postbox.transaction { transaction -> (AppConfiguration, Bool) in
|
||||
return (currentAppConfiguration(transaction: transaction), transaction.getPeer(account.peerId)?.isPremium ?? false)
|
||||
}
|
||||
@ -69,12 +69,20 @@ func _internal_exportChatFolder(account: Account, filterId: Int32, title: String
|
||||
let userDefaultLimits = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: false)
|
||||
let userPremiumLimits = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: true)
|
||||
|
||||
if error.errorDescription == "FILTERS_TOO_MUCH" {
|
||||
if isPremium {
|
||||
return .fail(.limitExceeded(limit: userPremiumLimits.maxSharedFolderJoin, premiumLimit: userPremiumLimits.maxSharedFolderJoin))
|
||||
} else {
|
||||
return .fail(.limitExceeded(limit: userDefaultLimits.maxSharedFolderJoin, premiumLimit: userPremiumLimits.maxSharedFolderJoin))
|
||||
}
|
||||
} else {
|
||||
if isPremium {
|
||||
return .fail(.limitExceeded(limit: userPremiumLimits.maxSharedFolderInviteLinks, premiumLimit: userPremiumLimits.maxSharedFolderInviteLinks))
|
||||
} else {
|
||||
return .fail(.limitExceeded(limit: userDefaultLimits.maxSharedFolderInviteLinks, premiumLimit: userPremiumLimits.maxSharedFolderInviteLinks))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
@ -246,7 +254,9 @@ func _internal_checkChatFolderLink(account: Account, slug: String) -> Signal<Cha
|
||||
|> mapToSignal { result -> Signal<ChatFolderLinkContents, CheckChatFolderLinkError> in
|
||||
return account.postbox.transaction { transaction -> ChatFolderLinkContents in
|
||||
switch result {
|
||||
case let .communityInvite(title, peers, chats, users):
|
||||
case let .communityInvite(_, title, emoticon, peers, chats, users):
|
||||
let _ = emoticon
|
||||
|
||||
var allPeers: [Peer] = []
|
||||
var peerPresences: [PeerId: Api.User] = [:]
|
||||
|
||||
@ -321,7 +331,7 @@ func _internal_checkChatFolderLink(account: Account, slug: String) -> Signal<Cha
|
||||
if let peerValue = transaction.getPeer(peer.peerId) {
|
||||
resultPeers.append(EnginePeer(peerValue))
|
||||
|
||||
if transaction.getPeerChatListIndex(peer.peerId) != nil {
|
||||
if currentFilterPeers.contains(where: { $0 == peer.peerId }) && transaction.getPeerChatListIndex(peer.peerId) != nil {
|
||||
alreadyMemberPeerIds.insert(peer.peerId)
|
||||
}
|
||||
}
|
||||
@ -407,74 +417,148 @@ func _internal_joinChatFolderLink(account: Account, slug: String, peerIds: [Engi
|
||||
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]
|
||||
fileprivate let missingPeers: [EnginePeer]
|
||||
|
||||
public var availableChatsToJoin: Int {
|
||||
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())
|
||||
return ChatFolderLinkContents(localFilterId: self.folderId, title: self.title, peers: self.missingPeers, alreadyMemberPeerIds: Set())
|
||||
}
|
||||
|
||||
fileprivate init(
|
||||
folderId: Int32,
|
||||
title: String,
|
||||
missingPeers: [Api.Peer],
|
||||
chats: [Api.Chat],
|
||||
users: [Api.User]
|
||||
missingPeers: [EnginePeer]
|
||||
) {
|
||||
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) {
|
||||
if lhs.missingPeers.map(\.id) != rhs.missingPeers.map(\.id) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_getChatFolderUpdates(account: Account, folderId: Int32) -> Signal<ChatFolderUpdates?, NoError> {
|
||||
func _internal_pollChatFolderUpdatesOnce(account: Account, folderId: Int32) -> Signal<Never, NoError> {
|
||||
return account.postbox.transaction { transaction -> ChatListFiltersState in
|
||||
return _internal_currentChatListFiltersState(transaction: transaction)
|
||||
}
|
||||
|> mapToSignal { state -> Signal<Never, NoError> in
|
||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
if let current = state.updates.first(where: { $0.folderId == folderId }) {
|
||||
let updateInterval: Int32
|
||||
#if DEBUG
|
||||
updateInterval = 5
|
||||
#else
|
||||
updateInterval = 60 * 60
|
||||
#endif
|
||||
|
||||
if current.timestamp + updateInterval >= timestamp {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.communities.getCommunityUpdates(community: .inputCommunityDialogFilter(filterId: folderId)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.communities.CommunityUpdates?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<ChatFolderUpdates?, NoError> in
|
||||
|> mapToSignal { result -> Signal<Never, NoError> in
|
||||
guard let result = result else {
|
||||
return .single(nil)
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
let _ = updateChatListFiltersState(transaction: transaction, { state in
|
||||
var state = state
|
||||
|
||||
state.updates.removeAll(where: { $0.folderId == folderId })
|
||||
state.updates.append(ChatListFiltersState.ChatListFilterUpdates(folderId: folderId, timestamp: Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970), peerIds: []))
|
||||
|
||||
return state
|
||||
})
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
switch result {
|
||||
case let .communityUpdates(missingPeers, chats, 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 account.postbox.transaction { transaction -> Void in
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: Api.User] = [:]
|
||||
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
peerPresences[telegramUser.id] = user
|
||||
}
|
||||
for chat in chats {
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
|
||||
|
||||
let _ = updateChatListFiltersState(transaction: transaction, { state in
|
||||
var state = state
|
||||
|
||||
state.updates.removeAll(where: { $0.folderId == folderId })
|
||||
state.updates.append(ChatListFiltersState.ChatListFilterUpdates(folderId: folderId, timestamp: Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970), peerIds: missingPeers.map(\.peerId)))
|
||||
|
||||
return state
|
||||
})
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_subscribedChatFolderUpdates(account: Account, folderId: Int32) -> Signal<ChatFolderUpdates?, NoError> {
|
||||
struct InternalData: Equatable {
|
||||
var title: String
|
||||
var peerIds: [EnginePeer.Id]
|
||||
}
|
||||
|
||||
return _internal_updatedChatListFiltersState(postbox: account.postbox)
|
||||
|> map { state -> InternalData? in
|
||||
guard let update = state.updates.first(where: { $0.folderId == folderId }) else {
|
||||
return nil
|
||||
}
|
||||
guard let folder = state.filters.first(where: { $0.id == folderId }) else {
|
||||
return nil
|
||||
}
|
||||
guard case let .filter(_, title, _, data) = folder, data.isShared else {
|
||||
return nil
|
||||
}
|
||||
let filteredPeerIds: [PeerId] = update.peerIds.filter { !data.includePeers.peers.contains($0) }
|
||||
return InternalData(title: title, peerIds: filteredPeerIds)
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { internalData -> Signal<ChatFolderUpdates?, NoError> in
|
||||
guard let internalData = internalData else {
|
||||
return .single(nil)
|
||||
}
|
||||
if internalData.peerIds.isEmpty {
|
||||
return .single(nil)
|
||||
}
|
||||
return account.postbox.transaction { transaction -> ChatFolderUpdates? in
|
||||
var peers: [EnginePeer] = []
|
||||
for peerId in internalData.peerIds {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
peers.append(EnginePeer(peer))
|
||||
}
|
||||
}
|
||||
return ChatFolderUpdates(folderId: folderId, title: internalData.title, missingPeers: peers)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -530,11 +614,22 @@ func _internal_joinAvailableChatsInFolder(account: Account, updates: ChatFolderU
|
||||
}
|
||||
|
||||
func _internal_hideChatFolderUpdates(account: Account, folderId: Int32) -> Signal<Never, NoError> {
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
let _ = updateChatListFiltersState(transaction: transaction, { state in
|
||||
var state = state
|
||||
|
||||
state.updates.removeAll(where: { $0.folderId == folderId })
|
||||
|
||||
return state
|
||||
})
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Never, NoError> in
|
||||
return account.network.request(Api.functions.communities.hideCommunityUpdates(community: .inputCommunityDialogFilter(filterId: folderId)))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_leaveChatFolder(account: Account, folderId: Int32, removePeerIds: [EnginePeer.Id]) -> Signal<Never, NoError> {
|
||||
|
@ -1058,8 +1058,20 @@ public extension TelegramEngine {
|
||||
return _internal_joinChatFolderLink(account: self.account, slug: slug, peerIds: peerIds)
|
||||
}
|
||||
|
||||
public func getChatFolderUpdates(folderId: Int32) -> Signal<ChatFolderUpdates?, NoError> {
|
||||
return _internal_getChatFolderUpdates(account: self.account, folderId: folderId)
|
||||
public func pollChatFolderUpdates(folderId: Int32) -> Signal<Never, NoError> {
|
||||
let signal = _internal_pollChatFolderUpdatesOnce(account: self.account, folderId: folderId)
|
||||
return (
|
||||
signal
|
||||
|> then(
|
||||
Signal<Never, NoError>.complete()
|
||||
|> delay(10.0, queue: .concurrentDefaultQueue())
|
||||
)
|
||||
)
|
||||
|> restart
|
||||
}
|
||||
|
||||
public func subscribedChatFolderUpdates(folderId: Int32) -> Signal<ChatFolderUpdates?, NoError> {
|
||||
return _internal_subscribedChatFolderUpdates(account: self.account, folderId: folderId)
|
||||
}
|
||||
|
||||
public func joinAvailableChatsInFolder(updates: ChatFolderUpdates, peerIds: [EnginePeer.Id]) -> Signal<Never, JoinChatFolderLinkError> {
|
||||
|
@ -370,7 +370,8 @@ private final class ChatFolderLinkPreviewScreenComponent: Component {
|
||||
contentHeight += 14.0
|
||||
|
||||
var topBadge: String?
|
||||
if !allChatsAdded, let linkContents = component.linkContents, linkContents.localFilterId != nil {
|
||||
if case .remove = component.subject {
|
||||
} else if !allChatsAdded, let linkContents = component.linkContents, linkContents.localFilterId != nil {
|
||||
topBadge = "+\(linkContents.peers.count)"
|
||||
}
|
||||
|
||||
|
@ -266,6 +266,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
||||
}, openPasswordSetup: {
|
||||
}, openPremiumIntro: {
|
||||
}, openChatFolderUpdates: {
|
||||
}, hideChatFolderUpdates: {
|
||||
})
|
||||
interaction.searchTextHighightState = searchQuery
|
||||
self.interaction = interaction
|
||||
|
Loading…
x
Reference in New Issue
Block a user