Invite requests update

This commit is contained in:
Ilya Laktyushin 2021-10-08 19:56:49 +04:00
parent c649d0926f
commit 5114a1d068
14 changed files with 116 additions and 48 deletions

Binary file not shown.

View File

@ -16,13 +16,15 @@ class InviteLinkHeaderItem: ListViewItem, ItemListItem {
let context: AccountContext
let theme: PresentationTheme
let text: String
let animationName: String
let sectionId: ItemListSectionId
let linkAction: ((ItemListTextItemLinkAction) -> Void)?
init(context: AccountContext, theme: PresentationTheme, text: String, sectionId: ItemListSectionId, linkAction: ((ItemListTextItemLinkAction) -> Void)? = nil) {
init(context: AccountContext, theme: PresentationTheme, text: String, animationName: String, sectionId: ItemListSectionId, linkAction: ((ItemListTextItemLinkAction) -> Void)? = nil) {
self.context = context
self.theme = theme
self.text = text
self.animationName = animationName
self.sectionId = sectionId
self.linkAction = linkAction
}
@ -117,7 +119,7 @@ class InviteLinkHeaderItemNode: ListViewItemNode {
return (layout, { [weak self] in
if let strongSelf = self {
if strongSelf.item == nil {
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "Invite"), width: 192, height: 192, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: item.animationName), width: 192, height: 192, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
strongSelf.animationNode.visibility = true
}
strongSelf.item = item

View File

@ -210,7 +210,7 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
let arguments = arguments as! InviteLinkListControllerArguments
switch self {
case let .header(theme, text):
return InviteLinkHeaderItem(context: arguments.context, theme: theme, text: text, sectionId: self.section)
return InviteLinkHeaderItem(context: arguments.context, theme: theme, text: text, animationName: "Invite", sectionId: self.section)
case let .mainLinkHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .mainLink(_, invite, peers, importersCount, isPublic):

View File

@ -341,6 +341,7 @@ public final class InviteLinkViewController: ViewController {
private let invite: ExportedInvitation
private let importersContext: PeerInvitationImportersContext
private let requestsContext: PeerInvitationImportersContext?
private var interaction: InviteLinkViewInteraction?
@ -377,6 +378,11 @@ public final class InviteLinkViewController: ViewController {
self.controller = controller
self.importersContext = importersContext ?? context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .invite(invite: invite, requested: false))
if invite.requestApproval {
self.requestsContext = context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .invite(invite: invite, requested: true))
} else {
self.requestsContext = nil
}
self.dimNode = ASDisplayNode()
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
@ -572,36 +578,32 @@ public final class InviteLinkViewController: ViewController {
let previousCount = Atomic<Int32?>(value: nil)
let previousLoading = Atomic<Bool?>(value: nil)
let requestsState: Signal<PeerInvitationImportersState, NoError>
if let requestsContext = self.requestsContext {
requestsState = requestsContext.state
} else {
requestsState = .single(PeerInvitationImportersState.Empty)
}
let creatorPeer = context.account.postbox.loadedPeerWithId(invite.adminId)
self.disposable = (combineLatest(self.presentationDataPromise.get(), self.importersContext.state, creatorPeer)
|> deliverOnMainQueue).start(next: { [weak self] presentationData, state, creatorPeer in
self.disposable = (combineLatest(self.presentationDataPromise.get(), self.importersContext.state, requestsState, creatorPeer)
|> deliverOnMainQueue).start(next: { [weak self] presentationData, state, requestsState, creatorPeer in
if let strongSelf = self {
var entries: [InviteLinkViewEntry] = []
entries.append(.link(presentationData.theme, invite))
entries.append(.creatorHeader(presentationData.theme, presentationData.strings.InviteLink_CreatedBy.uppercased()))
entries.append(.creator(presentationData.theme, presentationData.dateTimeFormat, EnginePeer(creatorPeer), invite.date))
if !state.importers.isEmpty || (state.isLoadingMore && state.count > 0) {
let subtitle: String
let subtitleExpired: Bool
if let usageLimit = invite.usageLimit {
let remaining = max(0, usageLimit - state.count)
subtitle = presentationData.strings.InviteLink_PeopleRemaining(remaining).uppercased()
subtitleExpired = remaining <= 0
} else {
subtitle = ""
subtitleExpired = false
}
entries.append(.importerHeader(presentationData.theme, presentationData.strings.InviteLink_PeopleJoined(Int32(state.count)).uppercased(), subtitle, subtitleExpired))
if !requestsState.importers.isEmpty || (state.isLoadingMore && requestsState.count > 0) {
entries.append(.importerHeader(presentationData.theme, presentationData.strings.MemberRequests_PeopleRequested(Int32(requestsState.count)).uppercased(), "", false))
}
let count: Int32
let loading: Bool
var index: Int32 = 0
if state.importers.isEmpty && state.isLoadingMore {
if requestsState.importers.isEmpty && requestsState.isLoadingMore {
count = min(4, state.count)
loading = true
let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
@ -609,9 +611,9 @@ public final class InviteLinkViewController: ViewController {
entries.append(.importer(Int32(i), presentationData.theme, presentationData.dateTimeFormat, EnginePeer.user(fakeUser), 0, true))
}
} else {
count = min(4, Int32(state.importers.count))
count = min(4, Int32(requestsState.importers.count))
loading = false
for importer in state.importers {
for importer in requestsState.importers {
if let peer = importer.peer.peer {
entries.append(.importer(index, presentationData.theme, presentationData.dateTimeFormat, EnginePeer(peer), importer.date, false))
}
@ -619,6 +621,43 @@ public final class InviteLinkViewController: ViewController {
}
}
// if !state.importers.isEmpty || (state.isLoadingMore && state.count > 0) {
// let subtitle: String
// let subtitleExpired: Bool
// if let usageLimit = invite.usageLimit {
// let remaining = max(0, usageLimit - state.count)
// subtitle = presentationData.strings.InviteLink_PeopleRemaining(remaining).uppercased()
// subtitleExpired = remaining <= 0
// } else {
// subtitle = ""
// subtitleExpired = false
// }
//
// entries.append(.importerHeader(presentationData.theme, presentationData.strings.InviteLink_PeopleJoined(Int32(state.count)).uppercased(), subtitle, subtitleExpired))
// }
// let count: Int32
// let loading: Bool
//
// var index: Int32 = 0
// if state.importers.isEmpty && state.isLoadingMore {
// count = min(4, state.count)
// loading = true
// let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
// for i in 0 ..< count {
// entries.append(.importer(Int32(i), presentationData.theme, presentationData.dateTimeFormat, EnginePeer.user(fakeUser), 0, true))
// }
// } else {
// count = min(4, Int32(state.importers.count))
// loading = false
// for importer in state.importers {
// if let peer = importer.peer.peer {
// entries.append(.importer(index, presentationData.theme, presentationData.dateTimeFormat, EnginePeer(peer), importer.date, false))
// }
// index += 1
// }
// }
let previousCount = previousCount.swap(count)
let previousLoading = previousLoading.swap(loading)

View File

@ -100,7 +100,7 @@ private enum InviteRequestsEntry: ItemListNodeEntry {
let arguments = arguments as! InviteRequestsControllerArguments
switch self {
case let .header(theme, text):
return InviteLinkHeaderItem(context: arguments.context, theme: theme, text: text, sectionId: self.section, linkAction: { _ in
return InviteLinkHeaderItem(context: arguments.context, theme: theme, text: text, animationName: "Requests", sectionId: self.section, linkAction: { _ in
arguments.openLinks()
})
case let .requestsHeader(_, text):
@ -166,7 +166,7 @@ public func inviteRequestsController(context: AccountContext, updatedPresentatio
var getControllerImpl: (() -> ViewController?)?
let importersContext = existingContext ?? context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests)
let importersContext = existingContext ?? context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests(query: nil))
let arguments = InviteRequestsControllerArguments(context: context, openLinks: {
let controller = inviteLinkListController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, admin: nil)

View File

@ -277,11 +277,7 @@ public class ItemListInviteRequestItemNode: ListViewItemNode, ItemListItemNode {
if let importer = item.importer, let peer = importer.peer.peer.flatMap({ EnginePeer($0) }) {
titleText = peer.displayTitle(strings: item.presentationData.strings, displayOrder: item.nameDisplayOrder)
if case .user = peer {
subtitleText = " "
} else {
subtitleText = ""
}
subtitleText = importer.about ?? ""
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
dateText = stringForRelativeTimestamp(strings: item.presentationData.strings, relativeTimestamp: importer.date, relativeTo: timestamp, dateTimeFormat: item.dateTimeFormat)
} else {
@ -305,8 +301,10 @@ public class ItemListInviteRequestItemNode: ListViewItemNode, ItemListItemNode {
let titleSpacing: CGFloat = 1.0
let minHeight: CGFloat = titleLayout.size.height + verticalInset * 2.0
let rawHeight: CGFloat = verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height + 24.0
var rawHeight: CGFloat = verticalInset * 2.0 + titleLayout.size.height + titleSpacing + 41.0
if !subtitleLayout.size.height.isZero {
rawHeight += subtitleLayout.size.height + 5.0
}
var insets: UIEdgeInsets
let itemBackgroundColor: UIColor
let itemSeparatorColor: UIColor
@ -452,10 +450,10 @@ public class ItemListInviteRequestItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.dismissButton.setTitle(item.presentationData.strings.MemberRequests_Dismiss, with: Font.bold(15.0), with: item.presentationData.theme.list.itemAccentColor, for: .normal)
let addHeight = strongSelf.addButton.updateLayout(width: 138.0, transition: .immediate)
strongSelf.addButton.frame = CGRect(x: leftInset, y: verticalInset + titleLayout.size.height + 7.0, width: 138.0, height: addHeight)
strongSelf.addButton.frame = CGRect(x: leftInset, y: contentSize.height - addHeight - 12.0, width: 138.0, height: addHeight)
let dismissSize = strongSelf.dismissButton.measure(layout.size)
strongSelf.dismissButton.frame = CGRect(origin: CGPoint(x: leftInset + 138.0 + 24.0, y: verticalInset + titleLayout.size.height + 14.0), size: dismissSize)
strongSelf.dismissButton.frame = CGRect(origin: CGPoint(x: leftInset + 138.0 + 24.0, y: verticalInset + contentSize.height - addHeight - 14.0), size: dismissSize)
if item.importer == nil {
let shimmerNode: ShimmerEffectNode

View File

@ -157,6 +157,8 @@ public final class JoinLinkPreviewController: ViewController {
}
case .tooMuchUsers:
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_UsersTooMuchError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
case .requestSent:
break
case .generic:
break
}

View File

@ -4371,16 +4371,17 @@ public extension Api {
})
}
public static func getChatInviteImporters(flags: Int32, peer: Api.InputPeer, link: String?, offsetDate: Int32, offsetUser: Api.InputUser, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ChatInviteImporters>) {
public static func getChatInviteImporters(flags: Int32, peer: Api.InputPeer, link: String?, q: String?, offsetDate: Int32, offsetUser: Api.InputUser, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ChatInviteImporters>) {
let buffer = Buffer()
buffer.appendInt32(-1742901790)
buffer.appendInt32(-553329330)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
if Int(flags) & Int(1 << 1) != 0 {serializeString(link!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeString(q!, buffer: buffer, boxed: false)}
serializeInt32(offsetDate, buffer: buffer, boxed: false)
offsetUser.serialize(buffer, true)
serializeInt32(limit, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.getChatInviteImporters", parameters: [("flags", flags), ("peer", peer), ("link", link), ("offsetDate", offsetDate), ("offsetUser", offsetUser), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatInviteImporters? in
return (FunctionDescription(name: "messages.getChatInviteImporters", parameters: [("flags", flags), ("peer", peer), ("link", link), ("q", q), ("offsetDate", offsetDate), ("offsetUser", offsetUser), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatInviteImporters? in
let reader = BufferReader(buffer)
var result: Api.messages.ChatInviteImporters?
if let signature = reader.readInt32() {

View File

@ -655,6 +655,8 @@ public struct PeerInvitationImportersState: Equatable {
public var waitingCount: Int {
return importers.filter { $0.approvedBy == nil }.count
}
public static var Empty = PeerInvitationImportersState(importers: [], isLoadingMore: false, hasLoadedOnce: true, canLoadMore: false, count: 0)
}
final class CachedPeerInvitationImporters: Codable {
@ -723,6 +725,7 @@ private final class PeerInvitationImportersContextImpl {
private let peerId: PeerId
private let link: String?
private let requested: Bool
private let query: String?
private let disposable = MetaDisposable()
private let updateDisposable = MetaDisposable()
private let actionDisposables = DisposableSet()
@ -743,18 +746,26 @@ private final class PeerInvitationImportersContextImpl {
var invite: ExportedInvitation?
var requested = false
if case let .invite(subjectInvite, subjectRequested) = subject {
invite = subjectInvite
requested = subjectRequested
var query: String?
switch subject {
case let .invite(subjectInvite, subjectRequested):
invite = subjectInvite
requested = subjectRequested
case let .requests(maybeQuery):
query = maybeQuery
}
self.link = invite?.link
self.requested = requested
self.query = query
let count = invite?.count ?? 0
self.count = count
self.isLoadingMore = true
self.disposable.set((account.postbox.transaction { transaction -> (peers: [PeerInvitationImportersState.Importer], count: Int32, canLoadMore: Bool)? in
guard query == nil else {
return nil
}
let cachedResult = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerInvitationImporters, key: CachedPeerInvitationImporters.key(peerId: peerId, link: invite?.link ?? "requests", requested: self.requested)))?.get(CachedPeerInvitationImporters.self)
if let cachedResult = cachedResult, (Int(cachedResult.count) == count || invite == nil) {
var result: [PeerInvitationImportersState.Importer] = []
@ -803,6 +814,7 @@ private final class PeerInvitationImportersContextImpl {
let peerId = self.peerId
let link = self.link
let populateCache = self.populateCache
let query = self.query
var lastResult = self.results.last
if self.loadedFromCache {
@ -828,7 +840,11 @@ private final class PeerInvitationImportersContextImpl {
flags |= (1 << 0)
}
let signal = account.network.request(Api.functions.messages.getChatInviteImporters(flags: flags, peer: inputPeer, link: link, offsetDate: offsetDate, offsetUser: offsetUser, limit: lastResult == nil ? 10 : 50))
if let _ = query {
flags |= (1 << 2)
}
let signal = account.network.request(Api.functions.messages.getChatInviteImporters(flags: flags, peer: inputPeer, link: link, q: query, offsetDate: offsetDate, offsetUser: offsetUser, limit: lastResult == nil ? 10 : 50))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.messages.ChatInviteImporters?, NoError> in
return .single(nil)
@ -864,7 +880,7 @@ private final class PeerInvitationImportersContextImpl {
resultImporters.append(PeerInvitationImportersState.Importer(peer: RenderedPeer(peer: peer), date: date, about: about, approvedBy: approvedBy))
}
}
if populateCache {
if populateCache && query == nil {
if let entry = CodableEntry(CachedPeerInvitationImporters(importers: resultImporters, count: count)) {
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerInvitationImporters, key: CachedPeerInvitationImporters.key(peerId: peerId, link: link ?? "requests", requested: self.requested)), entry: entry, collectionSpec: cachedPeerInvitationImportersCollectionSpec)
}
@ -918,7 +934,7 @@ private final class PeerInvitationImportersContextImpl {
}
private func updateCache() {
guard self.hasLoadedOnce && !self.isLoadingMore else {
guard self.hasLoadedOnce && !self.isLoadingMore && self.query == nil else {
return
}
@ -941,7 +957,7 @@ private final class PeerInvitationImportersContextImpl {
public final class PeerInvitationImportersContext {
public enum Subject {
case invite(invite: ExportedInvitation, requested: Bool)
case requests
case requests(query: String?)
}
public enum UpdateAction {

View File

@ -1,7 +1,17 @@
{
"images" : [
{
"idiom" : "universal"
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Requests@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -4802,7 +4802,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if canManageInvitations, let inviteRequestsPending = inviteRequestsPending, inviteRequestsPending >= 0, strongSelf.inviteRequestsContext == nil {
let inviteRequestsContext = strongSelf.context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests)
let inviteRequestsContext = strongSelf.context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests(query: nil))
strongSelf.inviteRequestsContext = inviteRequestsContext
strongSelf.inviteRequestsDisposable.set((inviteRequestsContext.state

View File

@ -666,7 +666,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
if currentRequestsContext == nil {
if canManageInvitations {
let requestsContext = context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests)
let requestsContext = context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests(query: nil))
requestsContextPromise.set(.single(requestsContext))
requestsStatePromise.set(requestsContext.state |> map(Optional.init))
}
@ -843,7 +843,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
if currentRequestsContext == nil {
if canManageInvitations {
let requestsContext = context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests)
let requestsContext = context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests(query: nil))
requestsContextPromise.set(.single(requestsContext))
requestsStatePromise.set(requestsContext.state |> map(Optional.init))
}

View File

@ -1112,7 +1112,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
}))
if let count = data.requests?.count, count > 0 {
items[.peerInfo]!.append(PeerInfoScreenDisclosureItem(id: ItemMemberRequests, label: .badge(presentationStringsFormattedNumber(count, presentationData.dateTimeFormat.groupingSeparator), presentationData.theme.list.itemAccentColor), text: presentationData.strings.GroupInfo_MemberRequests, icon: UIImage(bundleImageName: "Chat/Info/GroupMembersIcon"), action: {
items[.peerInfo]!.append(PeerInfoScreenDisclosureItem(id: ItemMemberRequests, label: .badge(presentationStringsFormattedNumber(count, presentationData.dateTimeFormat.groupingSeparator), presentationData.theme.list.itemAccentColor), text: presentationData.strings.GroupInfo_MemberRequests, icon: UIImage(bundleImageName: "Chat/Info/GroupRequestsIcon"), action: {
interaction.openParticipantsSection(.memberRequests)
}))
}